From 5f4d02ac8400ec2963ffba8bfcb19a13de1ebb5a Mon Sep 17 00:00:00 2001 From: Kelani Tolulope Date: Thu, 8 Dec 2022 15:16:09 +0100 Subject: [PATCH] Update de-duplication UI on module and fields configuration pages --- .../components/Admin/Module/UniqueValues.vue | 215 ++++++++++++++++++ .../ModuleFields/Configurator/validation.vue | 139 ++++++++++- .../compose/src/views/Admin/Modules/Edit.vue | 9 +- lib/js/src/compose/types/module.ts | 26 ++- locale/en/corteza-webapp-compose/field.yaml | 13 ++ locale/en/corteza-webapp-compose/module.yaml | 21 +- 6 files changed, 402 insertions(+), 21 deletions(-) create mode 100644 client/web/compose/src/components/Admin/Module/UniqueValues.vue diff --git a/client/web/compose/src/components/Admin/Module/UniqueValues.vue b/client/web/compose/src/components/Admin/Module/UniqueValues.vue new file mode 100644 index 000000000..01ed41ca6 --- /dev/null +++ b/client/web/compose/src/components/Admin/Module/UniqueValues.vue @@ -0,0 +1,215 @@ + + + diff --git a/client/web/compose/src/components/ModuleFields/Configurator/validation.vue b/client/web/compose/src/components/ModuleFields/Configurator/validation.vue index ec63133fa..1855a9978 100644 --- a/client/web/compose/src/components/ModuleFields/Configurator/validation.vue +++ b/client/web/compose/src/components/ModuleFields/Configurator/validation.vue @@ -56,7 +56,7 @@ {{ $t('sanitizers.add') }} @@ -116,6 +116,60 @@ {{ $t('validators.description') }} + +
+
+
{{ $t('constraints.label') }}
+ + {{ $t('constraints.description') }} + +
+ +
+ + + + + + + + + + + + + +
+ + {{ $t('constraints.totalFieldConstraintCount', { total: fieldConstraint.total }) }} + +
+
+
@@ -149,6 +203,13 @@ export default { data () { return { loaded: false, + fieldConstraint: { + ruleIndex: null, + total: 0, + exists: false, + index: null, + }, + rule: {}, } }, @@ -158,9 +219,43 @@ export default { const [year, month] = VERSION.split('.') return `https://docs.cortezaproject.org/corteza-docs/${year}.${month}/integrator-guide/compose-configuration/index.html` }, + + modifierOptions () { + return [ + { value: 'ignore-case', text: this.$t('constraints.ignoreCase') }, + { value: 'fuzzy-match', text: this.$t('constraints.fuzzyMatch') }, + { value: 'sounds-like', text: this.$t('constraints.soundsLike') }, + { value: 'case-sensitive', text: this.$t('constraints.caseSensitive') }, + ] + }, + + multiValueOptions () { + return [ + { value: 'one-of', text: this.$t('constraints.oneOf') }, + { value: 'equal', text: this.$t('constraints.equal') }, + ] + }, + + constraint: { + get () { + if (this.module.config.recordDeDup.rules[this.fieldConstraint.ruleIndex]) { + return this.module.config.recordDeDup.rules[this.fieldConstraint.ruleIndex].constraints[this.fieldConstraint.index] + } + + return {} + }, + + set (value) { + if (this.module.config.recordDeDup.rules[this.fieldConstraint.ruleIndex]) { + this.module.config.recordDeDup.rules[this.fieldConstraint.ruleIndex].constraints[this.fieldConstraint.index] = value + } + }, + }, }, mounted () { + this.checkForFieldConstraint() + if (!this.field.expressions.sanitizers) { this.$set(this.field.expressions, 'sanitizers', []) } @@ -189,9 +284,45 @@ export default { return !(value.validatorID && value.validatorID !== NoID) }, - add () { - this.field.expressions.validators - .push({ test: '', error: '' }) + checkForFieldConstraint () { + this.module.config.recordDeDup.rules.forEach((rule, x) => { + const { constraints } = rule + + constraints.forEach((constraint, i) => { + if (constraint.attribute === this.field.name) { + if (constraints.length === 1) { + this.fieldConstraint.exists = true + this.fieldConstraint.index = i + this.fieldConstraint.ruleIndex = x + } + + this.fieldConstraint.total += 1 + } + }) + }) + }, + + toggleFieldConstraint (value) { + if (!value) { + this.module.config.recordDeDup.rules.splice(this.fieldConstraint.ruleIndex, 1) + + this.fieldConstraint.ruleIndex = null + this.fieldConstraint.index = null + } else if (this.fieldConstraint.ruleIndex == null) { + this.module.config.recordDeDup.rules.push({ + name: '', + strict: true, + constraints: [{ + attribute: this.field.name, + modifier: 'case-sensitive', + multiValue: 'equal', + type: this.field.kind, + }], + }) + + this.fieldConstraint.ruleIndex = this.module.config.recordDeDup.rules.length - 1 + this.fieldConstraint.index = this.module.config.recordDeDup.rules[this.fieldConstraint.ruleIndex].constraints.length - 1 + } }, }, } diff --git a/client/web/compose/src/views/Admin/Modules/Edit.vue b/client/web/compose/src/views/Admin/Modules/Edit.vue index c7bb5b05e..9c034b3e5 100644 --- a/client/web/compose/src/views/Admin/Modules/Edit.vue +++ b/client/web/compose/src/views/Admin/Modules/Edit.vue @@ -340,10 +340,9 @@ - @@ -458,7 +457,7 @@ import DalSettings from 'corteza-webapp-compose/src/components/Admin/Module/DalS import RecordRevisionsSettings from 'corteza-webapp-compose/src/components/Admin/Module/RecordRevisionsSettings' import DataPrivacySettings from 'corteza-webapp-compose/src/components/Admin/Module/DataPrivacySettings' import ModuleTranslator from 'corteza-webapp-compose/src/components/Admin/Module/ModuleTranslator' -import Validation from 'corteza-webapp-compose/src/components/Admin/Module/Validation' +import UniqueValues from 'corteza-webapp-compose/src/components/Admin/Module/UniqueValues' import RelatedPages from 'corteza-webapp-compose/src/components/Admin/Module/RelatedPages' import { compose, NoID } from '@cortezaproject/corteza-js' import EditorToolbar from 'corteza-webapp-compose/src/components/Admin/EditorToolbar' @@ -485,8 +484,8 @@ export default { ModuleTranslator, RelatedPages, EditorToolbar, - Validation, Export, + UniqueValues, }, props: { diff --git a/lib/js/src/compose/types/module.ts b/lib/js/src/compose/types/module.ts index 553a42cd3..228489dc7 100644 --- a/lib/js/src/compose/types/module.ts +++ b/lib/js/src/compose/types/module.ts @@ -58,8 +58,6 @@ interface Config { }; recordDeDup: { - enabled: boolean; - strict: boolean; rules: RecordDeDupRule[]; }; } @@ -71,10 +69,17 @@ interface ConfigDiscoveryAccess { }; } +interface Constraint { + attribute: string; + modifier: string; + multiValue: string; + type: string; +} + interface RecordDeDupRule { - name: string; + name?: string; strict: boolean; - attributes: string[]; + constraints: Constraint[]; } /** @@ -163,10 +168,12 @@ export class Module { }, recordDeDup: { - // Always true for now since empty array of rules is the same as disabling it - enabled: true, - strict: false, - rules: [], + rules: [ + { + strict: true, + constraints: [] + } + ], }, } @@ -224,9 +231,6 @@ export class Module { this.config = merge({}, this.config, m.config) // Remove when we improve duplicate detection, for now its always enabled - if (this.config.recordDeDup) { - this.config.recordDeDup.enabled = true - } } if (IsOf(m, 'labels')) { diff --git a/locale/en/corteza-webapp-compose/field.yaml b/locale/en/corteza-webapp-compose/field.yaml index 42e78bb55..464299ad5 100644 --- a/locale/en/corteza-webapp-compose/field.yaml +++ b/locale/en/corteza-webapp-compose/field.yaml @@ -222,3 +222,16 @@ valueExpr: label: required: Required multi: Multi value +constraints: + label: Unique value constraint + description: Use unique value constraints for this field + ignoreCase: Ignore case + fuzzyMatch: Fuzzy match + soundsLike: Sounds like + caseSensitive: Case sensitive + oneOf: One of + none: None + equal: Equal + valueModifiers: Value modifiers + multiValues: Multi-field values + totalFieldConstraintCount: The field is used in {{total}} other unique value constraint. See "unique values" tab on module editor. diff --git a/locale/en/corteza-webapp-compose/module.yaml b/locale/en/corteza-webapp-compose/module.yaml index bbcd1f3f0..3daaeab02 100644 --- a/locale/en/corteza-webapp-compose/module.yaml +++ b/locale/en/corteza-webapp-compose/module.yaml @@ -165,6 +165,26 @@ edit: label: Soft duplicate value validation description: Record will be saved and user will be presented with a warning when a duplicate value is detected in the selected fields in any existing record of this module + uniqueValues: + title: Unique values + preventRecordsSave: Prevent record saving if duplicate values are found + warningLabel: Warning or error message toast when constraint matches + valueModifiers: Value modifiers + multiValues: Multi-field values + add: Add + searchFields: Search fields + ignoreCase: Ignore case + fuzzyMatch: Fuzzy match + soundsLike: Sounds like + caseSensitive: Case sensitive + oneOf: One of + equal: Equal + warningMessage: Warning or error message toast when constraint matches + field: Field + type: Type + none: None + addNewConstraint: Add new constraint + uniqueValueConstraint: Unique value constraint #{{index}} federated: Federated forModule: @@ -210,4 +230,3 @@ searchPlaceholder: Type here to search all modules in this namespace title: List of Modules tooltip: permissions: Module permissions -