Browse Source

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 years ago
parent
commit
f3ea8a35e2
22 changed files with 137 additions and 27 deletions
  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 %}}
 **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 >}}

+ 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": {
     "cs": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 591,
       "percentage": 92
     },
     "de": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 570,
       "percentage": 89
     },
     "en": {
-      "tokenCount": 639,
-      "translatedCount": 638,
+      "tokenCount": 640,
+      "translatedCount": 640,
       "percentage": 100
     },
     "es": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 623,
       "percentage": 97
     },
     "fr": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 613,
       "percentage": 96
     },
     "it": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 621,
       "percentage": 97
     },
     "pl": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 405,
       "percentage": 63
     },
     "pt_BR": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 589,
       "percentage": 92
     },
     "pt_PT": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 634,
       "percentage": 99
     },
     "ru": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 620,
       "percentage": 97
     },
     "uk": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 620,
       "percentage": 97
     },
     "zh_Hans": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 557,
       "percentage": 87
     },
     "zh_Hant": {
-      "tokenCount": 639,
+      "tokenCount": 640,
       "translatedCount": 385,
       "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/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/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-variant/relation-product-variant-input.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
@@ -17,7 +25,7 @@ import { ChangeDetectionStrategy, Component, Input, OnInit, Optional, SkipSelf }
     styleUrls: ['./object-tree.component.scss'],
     changeDetection: ChangeDetectionStrategy.OnPush,
 })
-export class ObjectTreeComponent implements OnInit {
+export class ObjectTreeComponent implements OnChanges {
     @Input() value: { [key: string]: any } | string;
     @Input() isArrayItem = false;
     depth: number;
@@ -32,7 +40,7 @@ export class ObjectTreeComponent implements OnInit {
         }
     }
 
-    ngOnInit() {
+    ngOnChanges() {
         this.entries = this.getEntries(this.value);
         this.expanded = this.depth === 0 || this.isArrayItem;
         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"
     ></vdr-relation-product-variant-input>
     <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>
 </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 { 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 { 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 { RelationProductInputComponent } from './dynamic-form-inputs/relation-form-input/product/relation-product-input.component';
 import {
@@ -253,6 +254,7 @@ const DYNAMIC_FORM_INPUTS = [
     RelationCardPreviewDirective,
     RelationCardDetailDirective,
     RelationSelectorDialogComponent,
+    RelationGenericInputComponent,
     TextareaFormInputComponent,
     RichTextFormInputComponent,
     JsonEditorFormInputComponent,

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@@ -239,6 +239,7 @@
     "sample-formatting": "",
     "select": "Selecione...",
     "select-display-language": "Selecionar idioma de exibição",
+    "select-relation-id": "",
     "select-today": "Selecione hoje",
     "set-language": "",
     "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",
     "select": "Seleccione...",
     "select-display-language": "Seleccionar idioma",
+    "select-relation-id": "",
     "select-today": "Seleccione a data de hoje",
     "set-language": "Definir idioma",
     "short-date": "Data abreviada",

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

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

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

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

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

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

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

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