Browse Source

feat(admin-ui): Implement combination mode toggle for Collection filters

Michael Bromley 3 years ago
parent
commit
cb1e1377db
25 changed files with 188 additions and 37 deletions
  1. 28 28
      packages/admin-ui/i18n-coverage.json
  2. 1 0
      packages/admin-ui/src/lib/catalog/src/components/collection-detail/collection-detail.component.html
  3. 12 1
      packages/admin-ui/src/lib/catalog/src/components/collection-detail/collection-detail.component.ts
  4. 5 2
      packages/admin-ui/src/lib/core/src/shared/components/configurable-input/configurable-input.component.scss
  5. 15 2
      packages/admin-ui/src/lib/core/src/shared/components/configurable-input/configurable-input.component.ts
  6. 21 0
      packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/combination-mode-form-input/combination-mode-form-input.component.html
  7. 4 0
      packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/combination-mode-form-input/combination-mode-form-input.component.scss
  8. 51 0
      packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/combination-mode-form-input/combination-mode-form-input.component.ts
  9. 2 0
      packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/register-dynamic-input-components.ts
  10. 4 2
      packages/admin-ui/src/lib/core/src/shared/shared.module.ts
  11. 2 0
      packages/admin-ui/src/lib/marketing/src/components/promotion-detail/promotion-detail.component.html
  12. 3 0
      packages/admin-ui/src/lib/static/i18n-messages/cs.json
  13. 3 0
      packages/admin-ui/src/lib/static/i18n-messages/de.json
  14. 4 1
      packages/admin-ui/src/lib/static/i18n-messages/en.json
  15. 3 0
      packages/admin-ui/src/lib/static/i18n-messages/es.json
  16. 3 0
      packages/admin-ui/src/lib/static/i18n-messages/fr.json
  17. 3 0
      packages/admin-ui/src/lib/static/i18n-messages/it.json
  18. 3 0
      packages/admin-ui/src/lib/static/i18n-messages/pl.json
  19. 3 0
      packages/admin-ui/src/lib/static/i18n-messages/pt_BR.json
  20. 3 0
      packages/admin-ui/src/lib/static/i18n-messages/pt_PT.json
  21. 3 0
      packages/admin-ui/src/lib/static/i18n-messages/ru.json
  22. 3 0
      packages/admin-ui/src/lib/static/i18n-messages/uk.json
  23. 3 0
      packages/admin-ui/src/lib/static/i18n-messages/zh_Hans.json
  24. 3 0
      packages/admin-ui/src/lib/static/i18n-messages/zh_Hant.json
  25. 3 1
      packages/common/src/shared-types.ts

+ 28 - 28
packages/admin-ui/i18n-coverage.json

@@ -1,71 +1,71 @@
 {
-  "generatedOn": "2022-04-21T20:04:59.938Z",
-  "lastCommit": "dbf3f1412a2da6f6dd53930557326f022eae62e7",
+  "generatedOn": "2022-04-27T11:59:25.441Z",
+  "lastCommit": "47c9b0ead7057d2060590fc43a82d5c8876beacd",
   "translationStatus": {
     "cs": {
-      "tokenCount": 641,
+      "tokenCount": 649,
       "translatedCount": 591,
-      "percentage": 92
+      "percentage": 91
     },
     "de": {
-      "tokenCount": 641,
+      "tokenCount": 649,
       "translatedCount": 570,
-      "percentage": 89
+      "percentage": 88
     },
     "en": {
-      "tokenCount": 641,
-      "translatedCount": 640,
+      "tokenCount": 649,
+      "translatedCount": 646,
       "percentage": 100
     },
     "es": {
-      "tokenCount": 641,
+      "tokenCount": 649,
       "translatedCount": 623,
-      "percentage": 97
+      "percentage": 96
     },
     "fr": {
-      "tokenCount": 641,
+      "tokenCount": 649,
       "translatedCount": 613,
-      "percentage": 96
+      "percentage": 94
     },
     "it": {
-      "tokenCount": 641,
+      "tokenCount": 649,
       "translatedCount": 621,
-      "percentage": 97
+      "percentage": 96
     },
     "pl": {
-      "tokenCount": 641,
+      "tokenCount": 649,
       "translatedCount": 405,
-      "percentage": 63
+      "percentage": 62
     },
     "pt_BR": {
-      "tokenCount": 641,
+      "tokenCount": 649,
       "translatedCount": 589,
-      "percentage": 92
+      "percentage": 91
     },
     "pt_PT": {
-      "tokenCount": 641,
+      "tokenCount": 649,
       "translatedCount": 634,
-      "percentage": 99
+      "percentage": 98
     },
     "ru": {
-      "tokenCount": 641,
+      "tokenCount": 649,
       "translatedCount": 620,
-      "percentage": 97
+      "percentage": 96
     },
     "uk": {
-      "tokenCount": 641,
+      "tokenCount": 649,
       "translatedCount": 620,
-      "percentage": 97
+      "percentage": 96
     },
     "zh_Hans": {
-      "tokenCount": 641,
+      "tokenCount": 649,
       "translatedCount": 557,
-      "percentage": 87
+      "percentage": 86
     },
     "zh_Hant": {
-      "tokenCount": 641,
+      "tokenCount": 649,
       "translatedCount": 385,
-      "percentage": 60
+      "percentage": 59
     }
   }
 }

+ 1 - 0
packages/admin-ui/src/lib/catalog/src/components/collection-detail/collection-detail.component.html

@@ -109,6 +109,7 @@
             <ng-container *ngFor="let filter of filters; index as i; trackBy:trackByFn">
                 <vdr-configurable-input
                     (remove)="removeFilter(i)"
+                    [position]="i"
                     [operation]="filter"
                     [operationDefinition]="getFilterDefinition(filter)"
                     [formControlName]="i"

+ 12 - 1
packages/admin-ui/src/lib/catalog/src/components/collection-detail/collection-detail.component.ts

@@ -94,7 +94,18 @@ export class CollectionDetailComponent
         this.updatedFilters$ = filtersFormArray.statusChanges.pipe(
             debounceTime(200),
             filter(() => filtersFormArray.touched),
-            map(status => this.mapOperationsToInputs(this.filters, filtersFormArray.value)),
+            map(status =>
+                this.mapOperationsToInputs(this.filters, filtersFormArray.value).filter(filter => {
+                    // ensure all the arguments have valid values. E.g. a newly-added
+                    // filter will not yet have valid values
+                    for (const arg of filter.arguments) {
+                        if (arg.value === '') {
+                            return false;
+                        }
+                    }
+                    return true;
+                }),
+            ),
         );
         this.parentId$ = this.route.paramMap.pipe(
             map(pm => pm.get('parentId') || undefined),

+ 5 - 2
packages/admin-ui/src/lib/core/src/shared/components/configurable-input/configurable-input.component.scss

@@ -2,13 +2,12 @@
     display: block;
     margin-bottom: 12px;
 
-    >.card {
+    > .card {
         margin-top: 6px;
     }
 }
 
 .operation-inputs {
-
     padding-top: 0;
 
     .arg-row:not(:last-child) {
@@ -27,4 +26,8 @@
     .hidden {
         display: none;
     }
+    label {
+        min-width: 130px;
+        display: inline-block;
+    }
 }

+ 15 - 2
packages/admin-ui/src/lib/core/src/shared/components/configurable-input/configurable-input.component.ts

@@ -6,6 +6,7 @@ import {
     Input,
     OnChanges,
     OnDestroy,
+    OnInit,
     Output,
     SimpleChanges,
 } from '@angular/core';
@@ -22,7 +23,7 @@ import {
 } from '@angular/forms';
 import { ConfigArgType } from '@vendure/common/lib/shared-types';
 import { assertNever } from '@vendure/common/lib/shared-utils';
-import { Subscription } from 'rxjs';
+import { BehaviorSubject, Observable, Subscription } from 'rxjs';
 
 import { InputComponentConfig } from '../../../common/component-registry-types';
 import {
@@ -55,16 +56,21 @@ import { interpolateDescription } from '../../../common/utilities/interpolate-de
         },
     ],
 })
-export class ConfigurableInputComponent implements OnChanges, OnDestroy, ControlValueAccessor, Validator {
+export class ConfigurableInputComponent
+    implements OnInit, OnChanges, OnDestroy, ControlValueAccessor, Validator
+{
     @Input() operation?: ConfigurableOperation;
     @Input() operationDefinition?: ConfigurableOperationDefinition;
     @Input() readonly = false;
     @Input() removable = true;
+    @Input() position = 0;
     @Output() remove = new EventEmitter<ConfigurableOperation>();
     argValues: { [name: string]: any } = {};
     onChange: (val: any) => void;
     onTouch: () => void;
     form = new FormGroup({});
+    positionChange$: Observable<number>;
+    private positionChangeSubject = new BehaviorSubject<number>(0);
     private subscription: Subscription;
 
     interpolateDescription(): string {
@@ -75,10 +81,17 @@ export class ConfigurableInputComponent implements OnChanges, OnDestroy, Control
         }
     }
 
+    ngOnInit() {
+        this.positionChange$ = this.positionChangeSubject.asObservable();
+    }
+
     ngOnChanges(changes: SimpleChanges) {
         if ('operation' in changes || 'operationDefinition' in changes) {
             this.createForm();
         }
+        if ('position' in changes) {
+            this.positionChangeSubject.next(this.position);
+        }
     }
 
     ngOnDestroy() {

+ 21 - 0
packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/combination-mode-form-input/combination-mode-form-input.component.html

@@ -0,0 +1,21 @@
+<ng-container *ngIf="selectable$ | async; else default">
+    <div class="btn-group btn-outline-primary btn-sm mode-select">
+        <button
+            class="btn"
+            (click)="setCombinationModeAnd()"
+            [class.btn-primary]="formControl.value === true"
+        >
+            {{ 'common.boolean-and' | translate }}
+        </button>
+        <button
+            class="btn"
+            (click)="setCombinationModeOr()"
+            [class.btn-primary]="formControl.value === false"
+        >
+            {{ 'common.boolean-or' | translate }}
+        </button>
+    </div>
+</ng-container>
+<ng-template #default>
+    <small>{{ 'common.not-applicable' | translate }}</small>
+</ng-template>

+ 4 - 0
packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/combination-mode-form-input/combination-mode-form-input.component.scss

@@ -0,0 +1,4 @@
+
+.mode-select {
+    text-transform: uppercase;
+}

+ 51 - 0
packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/combination-mode-form-input/combination-mode-form-input.component.ts

@@ -0,0 +1,51 @@
+import { ChangeDetectionStrategy, Component, Optional } from '@angular/core';
+import { FormControl } from '@angular/forms';
+import { ConfigurableInputComponent } from '@vendure/admin-ui/core';
+import { DefaultFormComponentConfig, DefaultFormComponentId } from '@vendure/common/lib/shared-types';
+import { Observable, of } from 'rxjs';
+import { map, tap } from 'rxjs/operators';
+
+import { FormInputComponent, InputComponentConfig } from '../../../common/component-registry-types';
+
+/**
+ * @description
+ * A special input used to display the "Combination mode" AND/OR toggle.
+ *
+ * @docsCategory custom-input-components
+ * @docsPage default-inputs
+ */
+@Component({
+    selector: 'vdr-combination-mode-form-input',
+    templateUrl: './combination-mode-form-input.component.html',
+    styleUrls: ['./combination-mode-form-input.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class CombinationModeFormInputComponent implements FormInputComponent {
+    static readonly id: DefaultFormComponentId = 'combination-mode-form-input';
+    readonly: boolean;
+    formControl: FormControl;
+    config: DefaultFormComponentConfig<'combination-mode-form-input'>;
+    selectable$: Observable<boolean>;
+
+    constructor(@Optional() private configurableInputComponent: ConfigurableInputComponent) {
+        const selectable$ = configurableInputComponent
+            ? configurableInputComponent.positionChange$.pipe(map(position => 0 < position))
+            : of(true);
+
+        this.selectable$ = selectable$.pipe(
+            tap(selectable => {
+                if (!selectable) {
+                    this.setCombinationModeAnd();
+                }
+            }),
+        );
+    }
+
+    setCombinationModeAnd() {
+        this.formControl.setValue(true);
+    }
+
+    setCombinationModeOr() {
+        this.formControl.setValue(false);
+    }
+}

+ 2 - 0
packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/register-dynamic-input-components.ts

@@ -10,6 +10,7 @@ import {
 
 import { BooleanFormInputComponent } from './boolean-form-input/boolean-form-input.component';
 import { JsonEditorFormInputComponent } from './code-editor-form-input/json-editor-form-input.component';
+import { CombinationModeFormInputComponent } from './combination-mode-form-input/combination-mode-form-input.component';
 import { CurrencyFormInputComponent } from './currency-form-input/currency-form-input.component';
 import { CustomerGroupFormInputComponent } from './customer-group-form-input/customer-group-form-input.component';
 import { DateFormInputComponent } from './date-form-input/date-form-input.component';
@@ -40,6 +41,7 @@ export const defaultFormInputs = [
     RichTextFormInputComponent,
     JsonEditorFormInputComponent,
     ProductMultiSelectorFormInputComponent,
+    CombinationModeFormInputComponent,
 ];
 
 /**

+ 4 - 2
packages/admin-ui/src/lib/core/src/shared/shared.module.ts

@@ -90,6 +90,7 @@ import { IfMultichannelDirective } from './directives/if-multichannel.directive'
 import { IfPermissionsDirective } from './directives/if-permissions.directive';
 import { BooleanFormInputComponent } from './dynamic-form-inputs/boolean-form-input/boolean-form-input.component';
 import { JsonEditorFormInputComponent } from './dynamic-form-inputs/code-editor-form-input/json-editor-form-input.component';
+import { CombinationModeFormInputComponent } from './dynamic-form-inputs/combination-mode-form-input/combination-mode-form-input.component';
 import { CurrencyFormInputComponent } from './dynamic-form-inputs/currency-form-input/currency-form-input.component';
 import { CustomerGroupFormInputComponent } from './dynamic-form-inputs/customer-group-form-input/customer-group-form-input.component';
 import { DateFormInputComponent } from './dynamic-form-inputs/date-form-input/date-form-input.component';
@@ -238,8 +239,7 @@ const DECLARATIONS = [
     CustomDetailComponentHostComponent,
     AssetPreviewLinksComponent,
     ProductMultiSelectorDialogComponent,
-    ProductMultiSelectorFormInputComponent,
-    ProductSearchInputComponent
+    ProductSearchInputComponent,
 ];
 
 const DYNAMIC_FORM_INPUTS = [
@@ -264,6 +264,8 @@ const DYNAMIC_FORM_INPUTS = [
     TextareaFormInputComponent,
     RichTextFormInputComponent,
     JsonEditorFormInputComponent,
+    ProductMultiSelectorFormInputComponent,
+    CombinationModeFormInputComponent,
 ];
 
 @NgModule({

+ 2 - 0
packages/admin-ui/src/lib/marketing/src/components/promotion-detail/promotion-detail.component.html

@@ -87,6 +87,7 @@
             <ng-container *ngFor="let condition of conditions; index as i">
                 <vdr-configurable-input
                     (remove)="removeCondition($event)"
+                    [position]="i"
                     [readonly]="!('UpdatePromotion' | hasPermission)"
                     [operation]="condition"
                     [operationDefinition]="getConditionDefinition(condition)"
@@ -118,6 +119,7 @@
             <vdr-configurable-input
                 *ngFor="let action of actions; index as i"
                 (remove)="removeAction($event)"
+                [position]="i"
                 [operation]="action"
                 [readonly]="!('UpdatePromotion' | hasPermission)"
                 [operationDefinition]="getActionDefinition(action)"

+ 3 - 0
packages/admin-ui/src/lib/static/i18n-messages/cs.json

@@ -172,6 +172,8 @@
     "add-new-variants": "Přidat {count, plural, one {variantu} few {{count} varianty} other {{count} variant}}",
     "add-note": "Přidat poznámku",
     "available-languages": "Dostupné jazyky",
+    "boolean-and": "",
+    "boolean-or": "",
     "browser-default": "",
     "cancel": "Zrušit",
     "cancel-navigation": "Zrušit navigaci",
@@ -219,6 +221,7 @@
     "more": "Více...",
     "name": "jméno",
     "no-results": "Žádné výsledky",
+    "not-applicable": "",
     "not-set": "Nenastaveno",
     "notify-create-error": "Vyskytla se chyba, nebylo vytvořeno: { entity }",
     "notify-create-success": "Vytvořeno: { entity }",

+ 3 - 0
packages/admin-ui/src/lib/static/i18n-messages/de.json

@@ -172,6 +172,8 @@
     "add-new-variants": "{count, plural, one {1 Variante} other {{count} Varianten}} hinzufügen",
     "add-note": "Notiz hinzufügen",
     "available-languages": "Verfügbare Sprachen",
+    "boolean-and": "",
+    "boolean-or": "",
     "browser-default": "",
     "cancel": "Abbrechen",
     "cancel-navigation": "Navigation abbrechen",
@@ -219,6 +221,7 @@
     "more": "Mehr...",
     "name": "Name",
     "no-results": "Keine Ergebnisse",
+    "not-applicable": "",
     "not-set": "Nicht festgelegt",
     "notify-create-error": "Ein Fehler ist aufgetreten, { entity } konnte nicht erstellt werden",
     "notify-create-success": "{ entity } erstellt",

+ 4 - 1
packages/admin-ui/src/lib/static/i18n-messages/en.json

@@ -172,6 +172,8 @@
     "add-new-variants": "Add {count, plural, one {1 variant} other {{count} variants}}",
     "add-note": "Add note",
     "available-languages": "Available languages",
+    "boolean-and": "and",
+    "boolean-or": "or",
     "browser-default": "Browser default",
     "cancel": "Cancel",
     "cancel-navigation": "Cancel navigation",
@@ -219,6 +221,7 @@
     "more": "More...",
     "name": "Name",
     "no-results": "No results",
+    "not-applicable": "Not applicable",
     "not-set": "Not set",
     "notify-create-error": "An error occurred, could not create { entity }",
     "notify-create-success": "Created new { entity }",
@@ -677,4 +680,4 @@
     "job-result": "Job result",
     "job-state": "Job state"
   }
-}
+}

+ 3 - 0
packages/admin-ui/src/lib/static/i18n-messages/es.json

@@ -172,6 +172,8 @@
     "add-new-variants": "Añadir {count, plural, one {1 variante} other {{count} variantes}}",
     "add-note": "Añadir nota",
     "available-languages": "Idiomas disponibles",
+    "boolean-and": "",
+    "boolean-or": "",
     "browser-default": "",
     "cancel": "Cancelar",
     "cancel-navigation": "Cancelar navegación",
@@ -219,6 +221,7 @@
     "more": "Más...",
     "name": "Nombre",
     "no-results": "Sin resultados",
+    "not-applicable": "",
     "not-set": "Sin fijar",
     "notify-create-error": "Ha ocurrido un problema, imposible de crear { entity }",
     "notify-create-success": "Creado nuevo { entity }",

+ 3 - 0
packages/admin-ui/src/lib/static/i18n-messages/fr.json

@@ -172,6 +172,8 @@
     "add-new-variants": "Ajout {count, plural, one {d'une variation} other {de {count} variations}}",
     "add-note": "Ajouter une note",
     "available-languages": "Langues disponibles",
+    "boolean-and": "",
+    "boolean-or": "",
     "browser-default": "",
     "cancel": "Annuler",
     "cancel-navigation": "Annuler la navigation",
@@ -219,6 +221,7 @@
     "more": "Plus...",
     "name": "Nom",
     "no-results": "Aucun resultat",
+    "not-applicable": "",
     "not-set": "Non défini",
     "notify-create-error": "Une erreur est survenue, création de { entity } échouée",
     "notify-create-success": "Nouveau { entity } créé",

+ 3 - 0
packages/admin-ui/src/lib/static/i18n-messages/it.json

@@ -172,6 +172,8 @@
     "add-new-variants": "Aggiungi {count, plural, one {1 variante} other {{count} varianti}}",
     "add-note": "Aggiungi nota",
     "available-languages": "Lingue disponibili",
+    "boolean-and": "",
+    "boolean-or": "",
     "browser-default": "",
     "cancel": "Annulla",
     "cancel-navigation": "Annulla navigazione",
@@ -219,6 +221,7 @@
     "more": "Altri...",
     "name": "Nome",
     "no-results": "Nessun risultato",
+    "not-applicable": "",
     "not-set": "Non impostato",
     "notify-create-error": "Si è verificato un errore, impossibile creare { entity }",
     "notify-create-success": "Creato nuovo { entity }",

+ 3 - 0
packages/admin-ui/src/lib/static/i18n-messages/pl.json

@@ -172,6 +172,8 @@
     "add-new-variants": "Dodaj {count, plural, one {1 wariant} other {{count} wariantów}}",
     "add-note": "",
     "available-languages": "Dostępne języki",
+    "boolean-and": "",
+    "boolean-or": "",
     "browser-default": "",
     "cancel": "Anuluj",
     "cancel-navigation": "Anuluj nawigacje",
@@ -219,6 +221,7 @@
     "more": "Więcej...",
     "name": "Nazwa",
     "no-results": "Brak wyników",
+    "not-applicable": "",
     "not-set": "Nie ustawione",
     "notify-create-error": "Wystąpił błąd, nie można utworzyć { entity }",
     "notify-create-success": "Utworzono { entity }",

+ 3 - 0
packages/admin-ui/src/lib/static/i18n-messages/pt_BR.json

@@ -172,6 +172,8 @@
     "add-new-variants": "Adicionar {count, plural, one {1 variant} other {{count} variants}}",
     "add-note": "Adicionar nota",
     "available-languages": "Idiomas disponíveis",
+    "boolean-and": "",
+    "boolean-or": "",
     "browser-default": "",
     "cancel": "Cancelar",
     "cancel-navigation": "Cancelar navegação",
@@ -219,6 +221,7 @@
     "more": "Mais...",
     "name": "Nome",
     "no-results": "Sem resultados",
+    "not-applicable": "",
     "not-set": "Não configurado",
     "notify-create-error": "Ocorreu um erro, não foi possível criar { entity }",
     "notify-create-success": "Criado novo { entity }",

+ 3 - 0
packages/admin-ui/src/lib/static/i18n-messages/pt_PT.json

@@ -172,6 +172,8 @@
     "add-new-variants": "Adicionar {count, plural, one {variante} other {{count} variantes}}",
     "add-note": "Adicionar nota",
     "available-languages": "Idiomas disponíveis",
+    "boolean-and": "",
+    "boolean-or": "",
     "browser-default": "Navegador padrão",
     "cancel": "Cancelar",
     "cancel-navigation": "Continuar a editar",
@@ -219,6 +221,7 @@
     "more": "Mais...",
     "name": "Nome",
     "no-results": "Nenhum resultado encontrado",
+    "not-applicable": "",
     "not-set": "Não configurado",
     "notify-create-error": "Ocorreu um erro. Não foi possível criar { entity }",
     "notify-create-success": "Novo(a) { entity } adicionado(a)",

+ 3 - 0
packages/admin-ui/src/lib/static/i18n-messages/ru.json

@@ -172,6 +172,8 @@
     "add-new-variants": "Добавить {count, plural, one {1 вариант} other {{count} вариантов}}",
     "add-note": "Добавить заметку",
     "available-languages": "Доступные языки",
+    "boolean-and": "",
+    "boolean-or": "",
     "browser-default": "",
     "cancel": "Отмена",
     "cancel-navigation": "Отменить навигацию",
@@ -219,6 +221,7 @@
     "more": "Больше...",
     "name": "Имя",
     "no-results": "Нет результатов",
+    "not-applicable": "",
     "not-set": "Не задано",
     "notify-create-error": "Ошибка, не удалось создать { entity }",
     "notify-create-success": "Создано новое { entity }",

+ 3 - 0
packages/admin-ui/src/lib/static/i18n-messages/uk.json

@@ -172,6 +172,8 @@
     "add-new-variants": "Додати {count, plural, one {1 варіант} other {{count} варіантів}}",
     "add-note": "Додати замітку",
     "available-languages": "Доступні мови",
+    "boolean-and": "",
+    "boolean-or": "",
     "browser-default": "",
     "cancel": "Скасування",
     "cancel-navigation": "Скасувати навігацію",
@@ -219,6 +221,7 @@
     "more": "Більше...",
     "name": "Ім'я",
     "no-results": "Немає результатів",
+    "not-applicable": "",
     "not-set": "Не задано",
     "notify-create-error": "Помилка, не вдалося створити { entity }",
     "notify-create-success": "Створено нове { entity }",

+ 3 - 0
packages/admin-ui/src/lib/static/i18n-messages/zh_Hans.json

@@ -172,6 +172,8 @@
     "add-new-variants": "添加{count}个商品规格",
     "add-note": "添加注释",
     "available-languages": "可用语言",
+    "boolean-and": "",
+    "boolean-or": "",
     "browser-default": "",
     "cancel": "取消",
     "cancel-navigation": "取消",
@@ -219,6 +221,7 @@
     "more": "更多...",
     "name": "名称",
     "no-results": "没找到任何结果",
+    "not-applicable": "",
     "not-set": "未设置",
     "notify-create-error": "添加{ entity }失败",
     "notify-create-success": "{ entity }已添加",

+ 3 - 0
packages/admin-ui/src/lib/static/i18n-messages/zh_Hant.json

@@ -172,6 +172,8 @@
     "add-new-variants": "新增{count}個商品規格",
     "add-note": "",
     "available-languages": "可用語言",
+    "boolean-and": "",
+    "boolean-or": "",
     "browser-default": "",
     "cancel": "取消",
     "cancel-navigation": "取消",
@@ -219,6 +221,7 @@
     "more": "更多...",
     "name": "名稱",
     "no-results": "没找到任何結果",
+    "not-applicable": "",
     "not-set": "未設定",
     "notify-create-error": "新增{ entity }失敗",
     "notify-create-success": "{ entity }已新增",

+ 3 - 1
packages/common/src/shared-types.ts

@@ -145,7 +145,8 @@ export type DefaultFormComponentId =
     | 'text-form-input'
     | 'textarea-form-input'
     | 'asset-form-input'
-    | 'product-multi-form-input';
+    | 'product-multi-form-input'
+    | 'combination-mode-form-input';
 
 /**
  * @description
@@ -176,6 +177,7 @@ type DefaultFormConfigHash = {
     'product-multi-form-input': {
         selectionMode?: 'product' | 'variant';
     };
+    'combination-mode-form-input': {};
 };
 
 export type DefaultFormComponentUiConfig<T extends DefaultFormComponentId | string> =