Переглянути джерело

feat(admin-ui): Implement generic custom field relation selector

This commit introduces a new generic selector component which allows you to select the ID of any
relation custom field where no dedicated selection component exists.
Michael Bromley 3 роки тому
батько
коміт
f3ea8a35e2
22 змінених файлів з 137 додано та 27 видалено
  1. 2 6
      docs/content/developer-guide/customizing-models.md
  2. 16 16
      packages/admin-ui/i18n-coverage.json
  3. 1 0
      packages/admin-ui/src/lib/core/src/public_api.ts
  4. 11 3
      packages/admin-ui/src/lib/core/src/shared/components/object-tree/object-tree.component.ts
  5. 35 0
      packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/relation-form-input/generic/relation-generic-input.component.html
  6. 4 0
      packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/relation-form-input/generic/relation-generic-input.component.scss
  7. 47 0
      packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/relation-form-input/generic/relation-generic-input.component.ts
  8. 5 1
      packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/relation-form-input/relation-form-input.component.html
  9. 2 0
      packages/admin-ui/src/lib/core/src/shared/shared.module.ts
  10. 1 0
      packages/admin-ui/src/lib/static/i18n-messages/cs.json
  11. 1 0
      packages/admin-ui/src/lib/static/i18n-messages/de.json
  12. 2 1
      packages/admin-ui/src/lib/static/i18n-messages/en.json
  13. 1 0
      packages/admin-ui/src/lib/static/i18n-messages/es.json
  14. 1 0
      packages/admin-ui/src/lib/static/i18n-messages/fr.json
  15. 1 0
      packages/admin-ui/src/lib/static/i18n-messages/it.json
  16. 1 0
      packages/admin-ui/src/lib/static/i18n-messages/pl.json
  17. 1 0
      packages/admin-ui/src/lib/static/i18n-messages/pt_BR.json
  18. 1 0
      packages/admin-ui/src/lib/static/i18n-messages/pt_PT.json
  19. 1 0
      packages/admin-ui/src/lib/static/i18n-messages/ru.json
  20. 1 0
      packages/admin-ui/src/lib/static/i18n-messages/uk.json
  21. 1 0
      packages/admin-ui/src/lib/static/i18n-messages/zh_Hans.json
  22. 1 0
      packages/admin-ui/src/lib/static/i18n-messages/zh_Hant.json

+ 2 - 6
docs/content/developer-guide/customizing-models.md

@@ -401,11 +401,7 @@ mutation {
 {{% alert %}}
 {{% alert %}}
 **UI for relation type**
 **UI for relation type**
 
 
-The Admin UI app has built-in selection components for "relation" custom fields which reference certain common entity types, such as Asset, Product, ProductVariant and Customer. If you are relating to an entity not covered by the built-in selection components, you will instead see the message:
+The Admin UI app has built-in selection components for "relation" custom fields which reference certain common entity types, such as Asset, Product, ProductVariant and Customer. If you are relating to an entity not covered by the built-in selection components, you will see a generic relation component which allows you to manually enter the ID of the entity you wish to select.
 
 
-```text
-No input component configured for "<entity>" type
-```
-
-In this case, you will need to create a UI extension which defines a custom field control for that custom field. You can read more about this in the [custom form input guide]({{< relref "custom-form-inputs" >}})
+If the generic selector is not suitable, or is you wish to replace one of the built-in selector components, you can create a UI extension which defines a custom field control for that custom field. You can read more about this in the [custom form input guide]({{< relref "custom-form-inputs" >}})
 {{< /alert >}}
 {{< /alert >}}

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

@@ -1,69 +1,69 @@
 {
 {
-  "generatedOn": "2022-03-11T13:47:49.230Z",
-  "lastCommit": "af3a705250d7cc135707875138cc8b8136b42c12",
+  "generatedOn": "2022-03-14T12:52:29.280Z",
+  "lastCommit": "1ab01194344fd5310fa4d7cd487c22de826d2744",
   "translationStatus": {
   "translationStatus": {
     "cs": {
     "cs": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 591,
       "translatedCount": 591,
       "percentage": 92
       "percentage": 92
     },
     },
     "de": {
     "de": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 570,
       "translatedCount": 570,
       "percentage": 89
       "percentage": 89
     },
     },
     "en": {
     "en": {
-      "tokenCount": 639,
-      "translatedCount": 638,
+      "tokenCount": 640,
+      "translatedCount": 640,
       "percentage": 100
       "percentage": 100
     },
     },
     "es": {
     "es": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 623,
       "translatedCount": 623,
       "percentage": 97
       "percentage": 97
     },
     },
     "fr": {
     "fr": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 613,
       "translatedCount": 613,
       "percentage": 96
       "percentage": 96
     },
     },
     "it": {
     "it": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 621,
       "translatedCount": 621,
       "percentage": 97
       "percentage": 97
     },
     },
     "pl": {
     "pl": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 405,
       "translatedCount": 405,
       "percentage": 63
       "percentage": 63
     },
     },
     "pt_BR": {
     "pt_BR": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 589,
       "translatedCount": 589,
       "percentage": 92
       "percentage": 92
     },
     },
     "pt_PT": {
     "pt_PT": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 634,
       "translatedCount": 634,
       "percentage": 99
       "percentage": 99
     },
     },
     "ru": {
     "ru": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 620,
       "translatedCount": 620,
       "percentage": 97
       "percentage": 97
     },
     },
     "uk": {
     "uk": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 620,
       "translatedCount": 620,
       "percentage": 97
       "percentage": 97
     },
     },
     "zh_Hans": {
     "zh_Hans": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 557,
       "translatedCount": 557,
       "percentage": 87
       "percentage": 87
     },
     },
     "zh_Hant": {
     "zh_Hant": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 385,
       "translatedCount": 385,
       "percentage": 60
       "percentage": 60
     }
     }

+ 1 - 0
packages/admin-ui/src/lib/core/src/public_api.ts

@@ -186,6 +186,7 @@ export * from './shared/dynamic-form-inputs/product-selector-form-input/product-
 export * from './shared/dynamic-form-inputs/register-dynamic-input-components';
 export * from './shared/dynamic-form-inputs/register-dynamic-input-components';
 export * from './shared/dynamic-form-inputs/relation-form-input/asset/relation-asset-input.component';
 export * from './shared/dynamic-form-inputs/relation-form-input/asset/relation-asset-input.component';
 export * from './shared/dynamic-form-inputs/relation-form-input/customer/relation-customer-input.component';
 export * from './shared/dynamic-form-inputs/relation-form-input/customer/relation-customer-input.component';
+export * from './shared/dynamic-form-inputs/relation-form-input/generic/relation-generic-input.component';
 export * from './shared/dynamic-form-inputs/relation-form-input/product/relation-product-input.component';
 export * from './shared/dynamic-form-inputs/relation-form-input/product/relation-product-input.component';
 export * from './shared/dynamic-form-inputs/relation-form-input/product-variant/relation-product-variant-input.component';
 export * from './shared/dynamic-form-inputs/relation-form-input/product-variant/relation-product-variant-input.component';
 export * from './shared/dynamic-form-inputs/relation-form-input/relation-card/relation-card.component';
 export * from './shared/dynamic-form-inputs/relation-form-input/relation-card/relation-card.component';

+ 11 - 3
packages/admin-ui/src/lib/core/src/shared/components/object-tree/object-tree.component.ts

@@ -1,4 +1,12 @@
-import { ChangeDetectionStrategy, Component, Input, OnInit, Optional, SkipSelf } from '@angular/core';
+import {
+    ChangeDetectionStrategy,
+    Component,
+    Input,
+    OnChanges,
+    OnInit,
+    Optional,
+    SkipSelf,
+} from '@angular/core';
 
 
 /**
 /**
  * @description
  * @description
@@ -17,7 +25,7 @@ import { ChangeDetectionStrategy, Component, Input, OnInit, Optional, SkipSelf }
     styleUrls: ['./object-tree.component.scss'],
     styleUrls: ['./object-tree.component.scss'],
     changeDetection: ChangeDetectionStrategy.OnPush,
     changeDetection: ChangeDetectionStrategy.OnPush,
 })
 })
-export class ObjectTreeComponent implements OnInit {
+export class ObjectTreeComponent implements OnChanges {
     @Input() value: { [key: string]: any } | string;
     @Input() value: { [key: string]: any } | string;
     @Input() isArrayItem = false;
     @Input() isArrayItem = false;
     depth: number;
     depth: number;
@@ -32,7 +40,7 @@ export class ObjectTreeComponent implements OnInit {
         }
         }
     }
     }
 
 
-    ngOnInit() {
+    ngOnChanges() {
         this.entries = this.getEntries(this.value);
         this.entries = this.getEntries(this.value);
         this.expanded = this.depth === 0 || this.isArrayItem;
         this.expanded = this.depth === 0 || this.isArrayItem;
         this.valueIsArray = Object.keys(this.value).every(v => Number.isInteger(+v));
         this.valueIsArray = Object.keys(this.value).every(v => Number.isInteger(+v));

+ 35 - 0
packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/relation-form-input/generic/relation-generic-input.component.html

@@ -0,0 +1,35 @@
+<vdr-relation-card
+    (select)="selectRelationId()"
+    (remove)="remove()"
+    placeholderIcon="objects"
+    [entity]="parentFormControl.value"
+    [selectLabel]="'common.select-relation-id' | translate"
+    [removable]="!config.list"
+    [readonly]="readonly"
+>
+    {{ parentFormControl.value | json }}
+    <ng-template vdrRelationCardPreview>
+        <div class="placeholder">
+            <clr-icon shape="objects" size="50"></clr-icon>
+        </div>
+    </ng-template>
+    <ng-template vdrRelationCardDetail let-entity="entity">
+        <div class="">
+            {{ config.entity }}: <strong>{{ entity.id }}</strong>
+        </div>
+        <vdr-object-tree [value]="{ properties: parentFormControl.value }"></vdr-object-tree>
+    </ng-template>
+</vdr-relation-card>
+
+<ng-template #selector let-select="select">
+    <div class="id-select-wrapper">
+        <clr-input-container>
+            <input [(ngModel)]="relationId" type="text" clrInput [readonly]="readonly" />
+        </clr-input-container>
+        <div>
+            <button class="btn btn-primary m0" (click)="select(relationId)">
+                {{ 'common.confirm' | translate }}
+            </button>
+        </div>
+    </div>
+</ng-template>

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

@@ -0,0 +1,4 @@
+.id-select-wrapper {
+    display: flex;
+    align-items: flex-end;
+}

+ 47 - 0
packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/relation-form-input/generic/relation-generic-input.component.ts

@@ -0,0 +1,47 @@
+import { ChangeDetectionStrategy, Component, Input, TemplateRef, ViewChild } from '@angular/core';
+import { FormControl } from '@angular/forms';
+import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
+
+import { RelationCustomFieldConfig } from '../../../../common/generated-types';
+import { ModalService } from '../../../../providers/modal/modal.service';
+import { RelationSelectorDialogComponent } from '../relation-selector-dialog/relation-selector-dialog.component';
+
+@Component({
+    selector: 'vdr-relation-generic-input',
+    templateUrl: './relation-generic-input.component.html',
+    styleUrls: ['./relation-generic-input.component.scss'],
+    changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class RelationGenericInputComponent {
+    @Input() readonly: boolean;
+    @Input() parentFormControl: FormControl;
+    @Input() config: RelationCustomFieldConfig;
+    relationId: string;
+
+    @ViewChild('selector') template: TemplateRef<any>;
+
+    constructor(private modalService: ModalService) {}
+
+    selectRelationId() {
+        this.modalService
+            .fromComponent(RelationSelectorDialogComponent, {
+                size: 'md',
+                closable: true,
+                locals: {
+                    title: _('common.select-relation-id'),
+                    selectorTemplate: this.template,
+                },
+            })
+            .subscribe(result => {
+                if (result) {
+                    this.parentFormControl.setValue({ id: result });
+                    this.parentFormControl.markAsDirty();
+                }
+            });
+    }
+
+    remove() {
+        this.parentFormControl.setValue(null);
+        this.parentFormControl.markAsDirty();
+    }
+}

+ 5 - 1
packages/admin-ui/src/lib/core/src/shared/dynamic-form-inputs/relation-form-input/relation-form-input.component.html

@@ -24,6 +24,10 @@
         [readonly]="readonly"
         [readonly]="readonly"
     ></vdr-relation-product-variant-input>
     ></vdr-relation-product-variant-input>
     <ng-template ngSwitchDefault>
     <ng-template ngSwitchDefault>
-        No input component configured for "{{ config.entity }}" type
+        <vdr-relation-generic-input
+            [parentFormControl]="formControl"
+               [config]="config"
+               [readonly]="readonly"
+        ></vdr-relation-generic-input>
     </ng-template>
     </ng-template>
 </div>
 </div>

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

@@ -98,6 +98,7 @@ import { PasswordFormInputComponent } from './dynamic-form-inputs/password-form-
 import { ProductSelectorFormInputComponent } from './dynamic-form-inputs/product-selector-form-input/product-selector-form-input.component';
 import { ProductSelectorFormInputComponent } from './dynamic-form-inputs/product-selector-form-input/product-selector-form-input.component';
 import { RelationAssetInputComponent } from './dynamic-form-inputs/relation-form-input/asset/relation-asset-input.component';
 import { RelationAssetInputComponent } from './dynamic-form-inputs/relation-form-input/asset/relation-asset-input.component';
 import { RelationCustomerInputComponent } from './dynamic-form-inputs/relation-form-input/customer/relation-customer-input.component';
 import { RelationCustomerInputComponent } from './dynamic-form-inputs/relation-form-input/customer/relation-customer-input.component';
+import { RelationGenericInputComponent } from './dynamic-form-inputs/relation-form-input/generic/relation-generic-input.component';
 import { RelationProductVariantInputComponent } from './dynamic-form-inputs/relation-form-input/product-variant/relation-product-variant-input.component';
 import { RelationProductVariantInputComponent } from './dynamic-form-inputs/relation-form-input/product-variant/relation-product-variant-input.component';
 import { RelationProductInputComponent } from './dynamic-form-inputs/relation-form-input/product/relation-product-input.component';
 import { RelationProductInputComponent } from './dynamic-form-inputs/relation-form-input/product/relation-product-input.component';
 import {
 import {
@@ -253,6 +254,7 @@ const DYNAMIC_FORM_INPUTS = [
     RelationCardPreviewDirective,
     RelationCardPreviewDirective,
     RelationCardDetailDirective,
     RelationCardDetailDirective,
     RelationSelectorDialogComponent,
     RelationSelectorDialogComponent,
+    RelationGenericInputComponent,
     TextareaFormInputComponent,
     TextareaFormInputComponent,
     RichTextFormInputComponent,
     RichTextFormInputComponent,
     JsonEditorFormInputComponent,
     JsonEditorFormInputComponent,

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

@@ -239,6 +239,7 @@
     "sample-formatting": "",
     "sample-formatting": "",
     "select": "Vybrat...",
     "select": "Vybrat...",
     "select-display-language": "Vyberte jazyk",
     "select-display-language": "Vyberte jazyk",
+    "select-relation-id": "",
     "select-today": "Vybrat dnešní datum",
     "select-today": "Vybrat dnešní datum",
     "set-language": "",
     "set-language": "",
     "short-date": "",
     "short-date": "",

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

@@ -239,6 +239,7 @@
     "sample-formatting": "",
     "sample-formatting": "",
     "select": "Auswählen...",
     "select": "Auswählen...",
     "select-display-language": "Anzeigesprache wählen",
     "select-display-language": "Anzeigesprache wählen",
+    "select-relation-id": "",
     "select-today": "Heute auswählen",
     "select-today": "Heute auswählen",
     "set-language": "",
     "set-language": "",
     "short-date": "",
     "short-date": "",

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

@@ -239,6 +239,7 @@
     "sample-formatting": "Sample formatting",
     "sample-formatting": "Sample formatting",
     "select": "Select...",
     "select": "Select...",
     "select-display-language": "Select display language",
     "select-display-language": "Select display language",
+    "select-relation-id": "Select relation ID",
     "select-today": "Select today",
     "select-today": "Select today",
     "set-language": "Set language",
     "set-language": "Set language",
     "short-date": "Short date",
     "short-date": "Short date",
@@ -670,4 +671,4 @@
     "job-result": "Job result",
     "job-result": "Job result",
     "job-state": "Job state"
     "job-state": "Job state"
   }
   }
-}
+}

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

@@ -239,6 +239,7 @@
     "sample-formatting": "",
     "sample-formatting": "",
     "select": "Seleccionar...",
     "select": "Seleccionar...",
     "select-display-language": "Seleccionar idioma de interfaz",
     "select-display-language": "Seleccionar idioma de interfaz",
+    "select-relation-id": "",
     "select-today": "Hoy",
     "select-today": "Hoy",
     "set-language": "",
     "set-language": "",
     "short-date": "",
     "short-date": "",

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

@@ -239,6 +239,7 @@
     "sample-formatting": "",
     "sample-formatting": "",
     "select": "Selectionner...",
     "select": "Selectionner...",
     "select-display-language": "Choisir la langue d'affichage",
     "select-display-language": "Choisir la langue d'affichage",
+    "select-relation-id": "",
     "select-today": "Choisir aujourd'hui",
     "select-today": "Choisir aujourd'hui",
     "set-language": "",
     "set-language": "",
     "short-date": "",
     "short-date": "",

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

@@ -239,6 +239,7 @@
     "sample-formatting": "",
     "sample-formatting": "",
     "select": "Seleziona...",
     "select": "Seleziona...",
     "select-display-language": "Seleziona lingua",
     "select-display-language": "Seleziona lingua",
+    "select-relation-id": "",
     "select-today": "Seleziona oggi",
     "select-today": "Seleziona oggi",
     "set-language": "",
     "set-language": "",
     "short-date": "",
     "short-date": "",

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

@@ -239,6 +239,7 @@
     "sample-formatting": "",
     "sample-formatting": "",
     "select": "Wybrano...",
     "select": "Wybrano...",
     "select-display-language": "Wybierz język",
     "select-display-language": "Wybierz język",
+    "select-relation-id": "",
     "select-today": "Wybierz dzisiaj",
     "select-today": "Wybierz dzisiaj",
     "set-language": "",
     "set-language": "",
     "short-date": "",
     "short-date": "",

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

@@ -239,6 +239,7 @@
     "sample-formatting": "",
     "sample-formatting": "",
     "select": "Selecione...",
     "select": "Selecione...",
     "select-display-language": "Selecionar idioma de exibição",
     "select-display-language": "Selecionar idioma de exibição",
+    "select-relation-id": "",
     "select-today": "Selecione hoje",
     "select-today": "Selecione hoje",
     "set-language": "",
     "set-language": "",
     "short-date": "",
     "short-date": "",

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

@@ -239,6 +239,7 @@
     "sample-formatting": "Formatação de amostra",
     "sample-formatting": "Formatação de amostra",
     "select": "Seleccione...",
     "select": "Seleccione...",
     "select-display-language": "Seleccionar idioma",
     "select-display-language": "Seleccionar idioma",
+    "select-relation-id": "",
     "select-today": "Seleccione a data de hoje",
     "select-today": "Seleccione a data de hoje",
     "set-language": "Definir idioma",
     "set-language": "Definir idioma",
     "short-date": "Data abreviada",
     "short-date": "Data abreviada",

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

@@ -239,6 +239,7 @@
     "sample-formatting": "",
     "sample-formatting": "",
     "select": "Выбрать...",
     "select": "Выбрать...",
     "select-display-language": "Выберите язык отображения",
     "select-display-language": "Выберите язык отображения",
+    "select-relation-id": "",
     "select-today": "Выберите сегодня",
     "select-today": "Выберите сегодня",
     "set-language": "",
     "set-language": "",
     "short-date": "",
     "short-date": "",

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

@@ -239,6 +239,7 @@
     "sample-formatting": "",
     "sample-formatting": "",
     "select": "Вибрати...",
     "select": "Вибрати...",
     "select-display-language": "Виберіть мову відображення",
     "select-display-language": "Виберіть мову відображення",
+    "select-relation-id": "",
     "select-today": "Виберіть сьогодні",
     "select-today": "Виберіть сьогодні",
     "set-language": "",
     "set-language": "",
     "short-date": "",
     "short-date": "",

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

@@ -239,6 +239,7 @@
     "sample-formatting": "",
     "sample-formatting": "",
     "select": "选择...",
     "select": "选择...",
     "select-display-language": "选择显示语言",
     "select-display-language": "选择显示语言",
+    "select-relation-id": "",
     "select-today": "选择今天",
     "select-today": "选择今天",
     "set-language": "",
     "set-language": "",
     "short-date": "",
     "short-date": "",

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

@@ -239,6 +239,7 @@
     "sample-formatting": "",
     "sample-formatting": "",
     "select": "選擇...",
     "select": "選擇...",
     "select-display-language": "選擇顯示語言",
     "select-display-language": "選擇顯示語言",
+    "select-relation-id": "",
     "select-today": "選擇今天",
     "select-today": "選擇今天",
     "set-language": "",
     "set-language": "",
     "short-date": "",
     "short-date": "",