Various fixes
* Code formatting * Both "type" and "name" are used to refer to the validation type. Changed all to "type". * Added proper abstraction of realtime validation classes on server * UI improvements in admin and wizard
Dieser Commit ist enthalten in:
Ursprung
8a6b240b46
Commit
c45e51fcb6
19 geänderte Dateien mit 186 neuen und 82 gelöschten Zeilen
|
@ -30,6 +30,7 @@ export default Component.extend(UndoChanges, {
|
||||||
@discourseComputed('field.type')
|
@discourseComputed('field.type')
|
||||||
validations(type) {
|
validations(type) {
|
||||||
const applicableToField = [];
|
const applicableToField = [];
|
||||||
|
|
||||||
for(let validation in wizardSchema.field.validations) {
|
for(let validation in wizardSchema.field.validations) {
|
||||||
if ((wizardSchema.field.validations[validation]["types"]).includes(type)) {
|
if ((wizardSchema.field.validations[validation]["types"]).includes(type)) {
|
||||||
applicableToField.push(validation)
|
applicableToField.push(validation)
|
||||||
|
|
|
@ -12,6 +12,7 @@ export default Component.extend({
|
||||||
|
|
||||||
if (!this.field.validations) {
|
if (!this.field.validations) {
|
||||||
const validations = {};
|
const validations = {};
|
||||||
|
|
||||||
this.validations.forEach((validation) => {
|
this.validations.forEach((validation) => {
|
||||||
validations[validation] = {};
|
validations[validation] = {};
|
||||||
});
|
});
|
||||||
|
@ -32,10 +33,10 @@ export default Component.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
updateValidationCategories(name, validation, categories) {
|
updateValidationCategories(type, validation, categories) {
|
||||||
this.set(`validationBuffer.${name}.categories`, categories);
|
this.set(`validationBuffer.${type}.categories`, categories);
|
||||||
this.set(
|
this.set(
|
||||||
`field.validations.${name}.categories`,
|
`field.validations.${type}.categories`,
|
||||||
categories.map((category) => category.id)
|
categories.map((category) => category.id)
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
<h3>{{i18n 'admin.wizard.field.validations.header'}}</h3>
|
<h3>{{i18n 'admin.wizard.field.validations.header'}}</h3>
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
{{#each-in field.validations as |name props|}}
|
{{#each-in field.validations as |type props|}}
|
||||||
<li>
|
<li>
|
||||||
<span class="setting-title">
|
<span class="setting-title">
|
||||||
<h4>{{i18n (concat 'admin.wizard.field.validations.' name)}}</h4>
|
<h4>{{i18n (concat 'admin.wizard.field.validations.' type)}}</h4>
|
||||||
{{input type="checkbox" checked=props.status}}
|
{{input type="checkbox" checked=props.status}}
|
||||||
{{i18n 'admin.wizard.field.validations.enabled'}}
|
{{i18n 'admin.wizard.field.validations.enabled'}}
|
||||||
</span>
|
</span>
|
||||||
|
@ -15,8 +15,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-value">
|
<div class="setting-value">
|
||||||
{{category-selector
|
{{category-selector
|
||||||
categories=(get this (concat 'validationBuffer.' name '.categories'))
|
categories=(get this (concat 'validationBuffer.' type '.categories'))
|
||||||
onChange=(action 'updateValidationCategories' name props)
|
onChange=(action 'updateValidationCategories' type props)
|
||||||
class="wizard"}}
|
class="wizard"}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -36,9 +36,9 @@
|
||||||
<label>{{i18n 'admin.wizard.field.validations.position'}}</label>
|
<label>{{i18n 'admin.wizard.field.validations.position'}}</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-value">
|
<div class="setting-value">
|
||||||
{{radio-button name=(concat name field.id) value="above" selection=props.position}}
|
{{radio-button name=(concat type field.id) value="above" selection=props.position}}
|
||||||
{{i18n 'admin.wizard.field.validations.above'}}
|
{{i18n 'admin.wizard.field.validations.above'}}
|
||||||
{{radio-button name=(concat name field.id) value="below" selection=props.position}}
|
{{radio-button name=(concat type field.id) value="below" selection=props.position}}
|
||||||
{{i18n 'admin.wizard.field.validations.below'}}
|
{{i18n 'admin.wizard.field.validations.below'}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,15 +3,34 @@ import { deepMerge } from "discourse-common/lib/object";
|
||||||
import { observes } from "discourse-common/utils/decorators";
|
import { observes } from "discourse-common/utils/decorators";
|
||||||
import { cancel, later } from "@ember/runloop";
|
import { cancel, later } from "@ember/runloop";
|
||||||
import { A } from "@ember/array";
|
import { A } from "@ember/array";
|
||||||
import EmberObject from "@ember/object";
|
import EmberObject, { computed } from "@ember/object";
|
||||||
|
import { notEmpty, and, equal, empty } from "@ember/object/computed";
|
||||||
|
|
||||||
export default WizardFieldValidator.extend({
|
export default WizardFieldValidator.extend({
|
||||||
similarTopics: [],
|
classNames: ['similar-topics-validator'],
|
||||||
|
similarTopics: null,
|
||||||
|
hasInput: notEmpty('field.value'),
|
||||||
|
hasSimilarTopics: notEmpty('similarTopics'),
|
||||||
|
hasNotSearched: equal('similarTopics', null),
|
||||||
|
noSimilarTopics: computed('similarTopics', function() {
|
||||||
|
return this.similarTopics !== null && this.similarTopics.length == 0;
|
||||||
|
}),
|
||||||
|
showDefault: and('hasNotSearched', 'hasInput'),
|
||||||
|
|
||||||
validate() {},
|
validate() {},
|
||||||
|
|
||||||
@observes("field.value")
|
@observes("field.value")
|
||||||
customValidate() {
|
customValidate() {
|
||||||
|
const field = this.field;
|
||||||
|
|
||||||
|
if (!field.value) return;
|
||||||
|
const value = field.value;
|
||||||
|
|
||||||
|
if (value && value.length < 5) {
|
||||||
|
this.set('similarTopics', null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const lastKeyUp = new Date();
|
const lastKeyUp = new Date();
|
||||||
this._lastKeyUp = lastKeyUp;
|
this._lastKeyUp = lastKeyUp;
|
||||||
|
|
||||||
|
@ -28,6 +47,8 @@ export default WizardFieldValidator.extend({
|
||||||
},
|
},
|
||||||
|
|
||||||
updateSimilarTopics() {
|
updateSimilarTopics() {
|
||||||
|
this.set('updating', true);
|
||||||
|
|
||||||
this.backendValidate({
|
this.backendValidate({
|
||||||
title: this.get("field.value"),
|
title: this.get("field.value"),
|
||||||
categories: this.get("validation.categories"),
|
categories: this.get("validation.categories"),
|
||||||
|
@ -41,7 +62,7 @@ export default WizardFieldValidator.extend({
|
||||||
});
|
});
|
||||||
|
|
||||||
this.set("similarTopics", similarTopics);
|
this.set("similarTopics", similarTopics);
|
||||||
});
|
}).finally(() => this.set('updating', false));
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
|
|
@ -1,15 +1,17 @@
|
||||||
import Component from "@ember/component";
|
import Component from "@ember/component";
|
||||||
import { not } from "@ember/object/computed";
|
import { equal } from "@ember/object/computed";
|
||||||
import { ajax } from "discourse/lib/ajax";
|
import { ajax } from "discourse/lib/ajax";
|
||||||
import { getToken } from "wizard/lib/ajax";
|
import { getToken } from "wizard/lib/ajax";
|
||||||
|
|
||||||
export default Component.extend({
|
export default Component.extend({
|
||||||
|
classNames: ['validator'],
|
||||||
classNameBindings: ["isValid", "isInvalid"],
|
classNameBindings: ["isValid", "isInvalid"],
|
||||||
validMessageKey: null,
|
validMessageKey: null,
|
||||||
invalidMessageKey: null,
|
invalidMessageKey: null,
|
||||||
isValid: null,
|
isValid: null,
|
||||||
isInvalid: not("isValid"),
|
isInvalid: equal("isValid", false),
|
||||||
layoutName: "components/validator", // useful for sharing the template with extending components
|
layoutName: "components/validator", // useful for sharing the template with extending components
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
|
|
||||||
|
@ -19,7 +21,7 @@ export default Component.extend({
|
||||||
this.backendValidate = (params) => {
|
this.backendValidate = (params) => {
|
||||||
return ajax("/realtime-validations", {
|
return ajax("/realtime-validations", {
|
||||||
data: {
|
data: {
|
||||||
validation: this.get("name"),
|
type: this.get("type"),
|
||||||
authenticity_token: getToken(),
|
authenticity_token: getToken(),
|
||||||
...params,
|
...params,
|
||||||
},
|
},
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
{{#if field.validations}}
|
{{#if field.validations}}
|
||||||
{{#each-in field.validations.above as |name validation|}}
|
{{#each-in field.validations.above as |type validation|}}
|
||||||
{{component validation.component field=field name=name validation=validation}}
|
{{component validation.component field=field type=type validation=validation}}
|
||||||
{{/each-in}}
|
{{/each-in}}
|
||||||
|
|
||||||
{{yield (hash perform=(action 'perform'))}}
|
{{yield (hash perform=(action 'perform'))}}
|
||||||
|
|
||||||
{{#each-in field.validations.below as |name validation|}}
|
{{#each-in field.validations.below as |type validation|}}
|
||||||
{{component validation.component field=field name=name validation=validation}}
|
{{component validation.component field=field type=type validation=validation}}
|
||||||
{{/each-in}}
|
{{/each-in}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{yield}}
|
{{yield}}
|
||||||
|
|
|
@ -1,8 +1,16 @@
|
||||||
{{#if similarTopics}}
|
{{#if loading}}
|
||||||
<h3>{{i18n 'realtime_validations.similar_topics_heading'}}</h3>
|
<label>{{i18n 'realtime_validations.similar_topics.loading'}}</label>
|
||||||
<div class="wizard-similar-topics">
|
{{else if hasSimilarTopics}}
|
||||||
|
<label>{{i18n 'realtime_validations.similar_topics.results'}}</label>
|
||||||
|
<ul class="wizard-similar-topics">
|
||||||
{{#each similarTopics as |similarTopic|}}
|
{{#each similarTopics as |similarTopic|}}
|
||||||
{{wizard-similar-topic topic=similarTopic}}
|
<li>{{wizard-similar-topic topic=similarTopic}}</li>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</ul>
|
||||||
|
{{else if noSimilarTopics}}
|
||||||
|
<label>{{i18n 'realtime_validations.similar_topics.no_results'}}</label>
|
||||||
|
{{else if showDefault}}
|
||||||
|
<label>{{i18n 'realtime_validations.similar_topics.default'}}</label>
|
||||||
|
{{else}}
|
||||||
|
<label></label>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -644,14 +644,18 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.realtime-validations ul {
|
.realtime-validations > ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
||||||
li {
|
> li {
|
||||||
background-color: var(--tertiary-low);
|
background-color: var(--primary-low);
|
||||||
padding: 0 1em;
|
padding: 1em;
|
||||||
margin: 0 0 1em 0;
|
margin: 0 0 1em 0;
|
||||||
|
|
||||||
|
input {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -155,18 +155,8 @@
|
||||||
.select-kit.combo-box.group-dropdown {
|
.select-kit.combo-box.group-dropdown {
|
||||||
min-width: 220px;
|
min-width: 220px;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.wizard-similar-topics {
|
.text-field input {
|
||||||
background-color: var(--tertiary-low);
|
margin-bottom: 0;
|
||||||
padding: 5px;
|
|
||||||
.title {
|
|
||||||
color: var(--primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.blurb {
|
|
||||||
margin-left: 0.5em;
|
|
||||||
color: var(--primary-high);
|
|
||||||
font-size: $font-down-1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
30
assets/stylesheets/wizard/custom/validators.scss
Normale Datei
30
assets/stylesheets/wizard/custom/validators.scss
Normale Datei
|
@ -0,0 +1,30 @@
|
||||||
|
.similar-topics-validator {
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
label {
|
||||||
|
min-height: 20px;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.wizard-similar-topics {
|
||||||
|
background-color: var(--tertiary-low);
|
||||||
|
padding: 5px;
|
||||||
|
margin: 0;
|
||||||
|
list-style: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 25px;
|
||||||
|
box-shadow: shadow("dropdown");
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
color: var(--primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.blurb {
|
||||||
|
margin-left: 0.5em;
|
||||||
|
color: var(--primary-high);
|
||||||
|
font-size: $font-down-1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@
|
||||||
@import "custom/wizard";
|
@import "custom/wizard";
|
||||||
@import "custom/step";
|
@import "custom/step";
|
||||||
@import "custom/field";
|
@import "custom/field";
|
||||||
|
@import "custom/validators";
|
||||||
@import "custom/mobile";
|
@import "custom/mobile";
|
||||||
@import "custom/autocomplete";
|
@import "custom/autocomplete";
|
||||||
@import "custom/composer";
|
@import "custom/composer";
|
||||||
|
|
|
@ -178,7 +178,7 @@ en:
|
||||||
instructions: "<a href='https://momentjs.com/docs/#/displaying/format/' target='_blank'>Moment.js format</a>"
|
instructions: "<a href='https://momentjs.com/docs/#/displaying/format/' target='_blank'>Moment.js format</a>"
|
||||||
validations:
|
validations:
|
||||||
header: "Realtime Validations"
|
header: "Realtime Validations"
|
||||||
enabled: "Enable this validation"
|
enabled: "Enabled"
|
||||||
similar_topics: "Similar Topics"
|
similar_topics: "Similar Topics"
|
||||||
position: "Position"
|
position: "Position"
|
||||||
above: "Above"
|
above: "Above"
|
||||||
|
@ -521,4 +521,8 @@ en:
|
||||||
title: "Did you forget to add recipients?"
|
title: "Did you forget to add recipients?"
|
||||||
body: "Right now this message is only being sent to yourself!"
|
body: "Right now this message is only being sent to yourself!"
|
||||||
realtime_validations:
|
realtime_validations:
|
||||||
similar_topics_heading: "Your topic is similar to..."
|
similar_topics:
|
||||||
|
default: "When you stop typing we'll look for similar topics."
|
||||||
|
results: "Your topic is similar to..."
|
||||||
|
no_results: "No similar topics."
|
||||||
|
loading: "Looking for similar topics..."
|
||||||
|
|
|
@ -2,10 +2,17 @@
|
||||||
|
|
||||||
class CustomWizard::RealtimeValidationsController < ::ApplicationController
|
class CustomWizard::RealtimeValidationsController < ::ApplicationController
|
||||||
def validate
|
def validate
|
||||||
params.require(:validation)
|
klass_str = "CustomWizard::RealtimeValidation::#{validation_params[:type].camelize}"
|
||||||
params.require(::CustomWizard::RealtimeValidation.types[params[:validation].to_sym][:required_params])
|
result = klass_str.constantize.new(current_user).perform(validation_params)
|
||||||
|
render_serialized(result.items, "#{klass_str}Serializer".constantize, result.serializer_opts)
|
||||||
|
end
|
||||||
|
|
||||||
result = ::CustomWizard::RealtimeValidation.send(params[:validation], params, current_user)
|
private
|
||||||
render_serialized(result[:items], result[:serializer], result[:opts])
|
|
||||||
|
def validation_params
|
||||||
|
params.require(:type)
|
||||||
|
settings = ::CustomWizard::RealtimeValidation.types[params[:type].to_sym]
|
||||||
|
params.require(settings[:required_params]) if settings[:required_params].present?
|
||||||
|
params
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,36 +2,13 @@
|
||||||
|
|
||||||
class CustomWizard::RealtimeValidation
|
class CustomWizard::RealtimeValidation
|
||||||
cattr_accessor :types
|
cattr_accessor :types
|
||||||
|
|
||||||
@@types ||= {
|
@@types ||= {
|
||||||
similar_topics: { types: [:text], component: "similar-topics-validator", backend: true, required_params: [] }
|
similar_topics: {
|
||||||
|
types: [:text],
|
||||||
|
component: "similar-topics-validator",
|
||||||
|
backend: true,
|
||||||
|
required_params: []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class SimilarTopic
|
|
||||||
def initialize(topic)
|
|
||||||
@topic = topic
|
|
||||||
end
|
|
||||||
|
|
||||||
attr_reader :topic
|
|
||||||
|
|
||||||
def blurb
|
|
||||||
Search::GroupedSearchResults.blurb_for(cooked: @topic.try(:blurb))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.similar_topics(params, current_user)
|
|
||||||
title = params[:title]
|
|
||||||
raw = params[:raw]
|
|
||||||
categories = params[:categories]
|
|
||||||
date_after = params[:date_after]
|
|
||||||
|
|
||||||
if title.length < SiteSetting.min_title_similar_length || !Topic.count_exceeds_minimum?
|
|
||||||
return []
|
|
||||||
end
|
|
||||||
|
|
||||||
topics = Topic.similar_to(title, raw, current_user).to_a
|
|
||||||
topics.select! { |t| categories.include?(t.category.id.to_s) } if categories.present?
|
|
||||||
topics.select! { |t| t.created_at > DateTime.parse(date_after) } if date_after.present?
|
|
||||||
topics.map! { |t| SimilarTopic.new(t) }
|
|
||||||
{ items: topics, serializer: SimilarTopicSerializer, opts: { root: :similar_topics } }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
11
lib/custom_wizard/realtime_validations/result.rb
Normale Datei
11
lib/custom_wizard/realtime_validations/result.rb
Normale Datei
|
@ -0,0 +1,11 @@
|
||||||
|
class CustomWizard::RealtimeValidation::Result
|
||||||
|
attr_accessor :type,
|
||||||
|
:items,
|
||||||
|
:serializer_opts
|
||||||
|
|
||||||
|
def initialize(type)
|
||||||
|
@type = type
|
||||||
|
@items = []
|
||||||
|
@serializer_opts = {}
|
||||||
|
end
|
||||||
|
end
|
42
lib/custom_wizard/realtime_validations/similar_topics.rb
Normale Datei
42
lib/custom_wizard/realtime_validations/similar_topics.rb
Normale Datei
|
@ -0,0 +1,42 @@
|
||||||
|
class CustomWizard::RealtimeValidation::SimilarTopics
|
||||||
|
attr_accessor :user
|
||||||
|
|
||||||
|
def initialize(user)
|
||||||
|
@user = user
|
||||||
|
end
|
||||||
|
|
||||||
|
class SimilarTopic
|
||||||
|
def initialize(topic)
|
||||||
|
@topic = topic
|
||||||
|
end
|
||||||
|
|
||||||
|
attr_reader :topic
|
||||||
|
|
||||||
|
def blurb
|
||||||
|
Search::GroupedSearchResults.blurb_for(cooked: @topic.try(:blurb))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def perform(params)
|
||||||
|
title = params[:title]
|
||||||
|
raw = params[:raw]
|
||||||
|
categories = params[:categories]
|
||||||
|
date_after = params[:date_after]
|
||||||
|
|
||||||
|
result = CustomWizard::RealtimeValidation::Result.new(:similar_topic)
|
||||||
|
|
||||||
|
if title.length < SiteSetting.min_title_similar_length || !Topic.count_exceeds_minimum?
|
||||||
|
return result
|
||||||
|
end
|
||||||
|
|
||||||
|
topics = Topic.similar_to(title, raw, user).to_a
|
||||||
|
topics.select! { |t| categories.include?(t.category.id.to_s) } if categories.present?
|
||||||
|
topics.select! { |t| t.created_at > DateTime.parse(date_after) } if date_after.present?
|
||||||
|
topics.map! { |t| SimilarTopic.new(t) }
|
||||||
|
|
||||||
|
result.items = topics
|
||||||
|
result.serializer_opts = { root: :similar_topics }
|
||||||
|
|
||||||
|
result
|
||||||
|
end
|
||||||
|
end
|
|
@ -60,6 +60,8 @@ after_initialize do
|
||||||
../lib/custom_wizard/custom_field.rb
|
../lib/custom_wizard/custom_field.rb
|
||||||
../lib/custom_wizard/field.rb
|
../lib/custom_wizard/field.rb
|
||||||
../lib/custom_wizard/realtime_validation.rb
|
../lib/custom_wizard/realtime_validation.rb
|
||||||
|
../lib/custom_wizard/realtime_validations/result.rb
|
||||||
|
../lib/custom_wizard/realtime_validations/similar_topics.rb
|
||||||
../lib/custom_wizard/mapper.rb
|
../lib/custom_wizard/mapper.rb
|
||||||
../lib/custom_wizard/log.rb
|
../lib/custom_wizard/log.rb
|
||||||
../lib/custom_wizard/step_updater.rb
|
../lib/custom_wizard/step_updater.rb
|
||||||
|
@ -81,6 +83,7 @@ after_initialize do
|
||||||
../serializers/custom_wizard/wizard_step_serializer.rb
|
../serializers/custom_wizard/wizard_step_serializer.rb
|
||||||
../serializers/custom_wizard/wizard_serializer.rb
|
../serializers/custom_wizard/wizard_serializer.rb
|
||||||
../serializers/custom_wizard/log_serializer.rb
|
../serializers/custom_wizard/log_serializer.rb
|
||||||
|
../serializers/custom_wizard/realtime_validation/similar_topics_serializer.rb
|
||||||
../extensions/extra_locales_controller.rb
|
../extensions/extra_locales_controller.rb
|
||||||
../extensions/invites_controller.rb
|
../extensions/invites_controller.rb
|
||||||
../extensions/users_controller.rb
|
../extensions/users_controller.rb
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
class ::CustomWizard::RealtimeValidation::SimilarTopicsSerializer < ::SimilarTopicSerializer
|
||||||
|
end
|
|
@ -61,10 +61,10 @@ class CustomWizard::FieldSerializer < ::WizardFieldSerializer
|
||||||
|
|
||||||
def validations
|
def validations
|
||||||
validations = {}
|
validations = {}
|
||||||
object.validations&.each do |name, props|
|
object.validations&.each do |type, props|
|
||||||
next unless props["status"]
|
next unless props["status"]
|
||||||
validations[props["position"]] ||= {}
|
validations[props["position"]] ||= {}
|
||||||
validations[props["position"]][name] = props.merge CustomWizard::RealtimeValidation.types[name.to_sym]
|
validations[props["position"]][type] = props.merge CustomWizard::RealtimeValidation.types[type.to_sym]
|
||||||
end
|
end
|
||||||
|
|
||||||
validations
|
validations
|
||||||
|
|
Laden …
In neuem Issue referenzieren