geonetwork-ui 2.10.0-dev.37a1e4c4d → 2.10.0-dev.394310529

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/fesm2022/geonetwork-ui.mjs +661 -340
  2. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  3. package/index.d.ts +60 -94
  4. package/index.d.ts.map +1 -1
  5. package/package.json +1 -1
  6. package/src/libs/api/metadata-converter/src/lib/gn4/atomic-operations.ts +48 -7
  7. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +3 -2
  8. package/src/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.ts +1 -0
  9. package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +5 -5
  10. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +2 -0
  11. package/src/libs/common/domain/src/lib/model/thesaurus/thesaurus.model.ts +2 -1
  12. package/src/libs/common/fixtures/src/lib/records.fixtures.ts +51 -0
  13. package/src/libs/feature/editor/src/lib/+state/editor.actions.ts +4 -1
  14. package/src/libs/feature/editor/src/lib/+state/editor.effects.ts +26 -19
  15. package/src/libs/feature/editor/src/lib/+state/editor.facade.ts +5 -1
  16. package/src/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.html +5 -5
  17. package/src/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.ts +2 -1
  18. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-resources/form-field-online-resources.component.ts +1 -1
  19. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.ts +12 -5
  20. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-rich/form-field-rich.component.html +4 -4
  21. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-rich/form-field-rich.component.ts +7 -0
  22. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-update-frequency/form-field-update-frequency.component.html +6 -2
  23. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-update-frequency/form-field-update-frequency.component.ts +3 -1
  24. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +20 -16
  25. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +9 -0
  26. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.html +13 -4
  27. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.ts +41 -0
  28. package/src/libs/feature/editor/src/lib/expressions.ts +380 -8
  29. package/src/libs/feature/editor/src/lib/fields.config.ts +251 -290
  30. package/src/libs/feature/editor/src/lib/models/editor-config.model.ts +2 -3
  31. package/src/libs/feature/editor/src/lib/services/editor.service.ts +8 -12
  32. package/src/libs/feature/search/src/lib/results-table/results-table-container.component.ts +10 -5
  33. package/src/libs/ui/elements/src/lib/contact-details/contact-details.component.html +48 -56
  34. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +17 -7
  35. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts +29 -1
  36. package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.html +4 -5
  37. package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.ts +4 -0
  38. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +1 -1
  39. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.ts +3 -3
  40. package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.html +5 -5
  41. package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.ts +9 -1
  42. package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.html +1 -1
  43. package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.ts +3 -8
  44. package/src/libs/ui/layout/src/lib/sortable-list/sortable-list.component.html +7 -7
  45. package/src/libs/ui/layout/src/lib/sortable-list/sortable-list.component.ts +14 -1
  46. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.html +12 -8
  47. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.ts +3 -1
  48. package/src/libs/ui/search/src/lib/results-table/results-table.component.html +1 -0
  49. package/src/libs/ui/widgets/src/lib/popover/popover.component.ts +7 -1
  50. package/translations/de.json +32 -29
  51. package/translations/en.json +37 -34
  52. package/translations/es.json +6 -3
  53. package/translations/fr.json +32 -29
  54. package/translations/it.json +32 -29
  55. package/translations/nl.json +5 -2
  56. package/translations/pt.json +6 -3
  57. package/translations/sk.json +6 -3
@@ -3,6 +3,7 @@ import {
3
3
  DatasetRecord,
4
4
  DatasetSpatialExtent,
5
5
  Keyword,
6
+ ReuseRecord,
6
7
  ServiceRecord,
7
8
  } from '../../../../../libs/common/domain/src/lib/model/record'
8
9
 
@@ -1027,3 +1028,53 @@ export const multilingualDatasetFixture: () => DatasetRecord = () => ({
1027
1028
  topics: [],
1028
1029
  recordUpdated: null,
1029
1030
  })
1031
+
1032
+ export const simpleReuseRecordFixture = (): ReuseRecord => ({
1033
+ abstract: `Sample reuse record`,
1034
+ kind: 'reuse',
1035
+ reuseType: 'application',
1036
+ recordUpdated: new Date('2023-03-17T07:38:08.875Z'),
1037
+ recordPublished: null,
1038
+ ownerOrganization: null,
1039
+ licenses: [],
1040
+ legalConstraints: [],
1041
+ securityConstraints: [],
1042
+ otherConstraints: [],
1043
+ contacts: [],
1044
+ contactsForResource: [],
1045
+ keywords: [],
1046
+ topics: [],
1047
+ spatialExtents: [],
1048
+ temporalExtents: [],
1049
+ lineage: 'bla',
1050
+ sourceRecords: [],
1051
+ overviews: [],
1052
+ defaultLanguage: null,
1053
+ otherLanguages: [],
1054
+ title: 'Sites de gestion des déchets miniers - Service de visualisation WMS',
1055
+ onlineResources: [
1056
+ {
1057
+ name: 'Rapport de disponibilité du service WMS',
1058
+ description:
1059
+ 'Ce service de visualisation WMS permet de consulter la série de couches de données "Sites de gestion des déchets miniers - Série".',
1060
+ mimeType: 'text/html',
1061
+ type: 'link',
1062
+ url: new URL(
1063
+ 'https://geoservices.wallonie.be/rapportDisponibilite/wms/sites_de_gestion_des_dechets_miniers_srie.html'
1064
+ ),
1065
+ accessRestricted: false,
1066
+ },
1067
+ ],
1068
+ uniqueIdentifier: '00b22798-ec8e-4500-89e8-90eeeda45919',
1069
+ landingPage: new URL(
1070
+ 'http://localhost:4200/geonetwork/srv/fre/catalog.search#/metadata/00b22798-ec8e-4500-89e8-90eeeda45919'
1071
+ ),
1072
+ extras: {
1073
+ isPublishedToAll: true,
1074
+ id: '723',
1075
+ isHarvested: true,
1076
+ ownerInfo: 'admin|admin|admin|Administrator',
1077
+ edit: true,
1078
+ },
1079
+ recordCreated: new Date('2023-03-17T07:38:08.875Z'),
1080
+ })
@@ -29,7 +29,10 @@ export const markRecordAsChanged = createAction(
29
29
  '[Editor] Mark record as changed'
30
30
  )
31
31
 
32
- export const saveRecord = createAction('[Editor] Save record')
32
+ export const saveRecord = createAction(
33
+ '[Editor] Save record',
34
+ props<{ publish: boolean }>()
35
+ )
33
36
  export const saveRecordSuccess = createAction('[Editor] Save record success')
34
37
  export const saveRecordFailure = createAction(
35
38
  '[Editor] Save record failure',
@@ -28,30 +28,37 @@ export class EditorEffects {
28
28
  this.store.select(selectRecordSource),
29
29
  this.store.select(selectEditorConfig)
30
30
  ),
31
- switchMap(([, record, recordSource, fieldsConfig]) =>
32
- this.editorService.saveRecord(record, recordSource, fieldsConfig).pipe(
33
- switchMap(([savedRecord, savedRecordSource]) => {
34
- const actions: Action[] = [EditorActions.saveRecordSuccess()]
31
+ switchMap(([{ publish }, record, recordSource, fieldsConfig]) =>
32
+ this.editorService
33
+ .saveRecord(record, recordSource, fieldsConfig, publish)
34
+ .pipe(
35
+ switchMap(([savedRecord, savedRecordSource]) => {
36
+ const actions: Action[] = [EditorActions.saveRecordSuccess()]
37
+
38
+ if (publish) {
39
+ // saving without publishing doesn't unpublish
40
+ actions.push(EditorActions.isPublished({ isPublished: true }))
41
+ }
35
42
 
36
- if (!record?.uniqueIdentifier) {
37
- actions.push(
38
- EditorActions.openRecord({
39
- record: savedRecord,
40
- recordSource: savedRecordSource,
43
+ if (!record?.uniqueIdentifier) {
44
+ actions.push(
45
+ EditorActions.openRecord({
46
+ record: savedRecord,
47
+ recordSource: savedRecordSource,
48
+ })
49
+ )
50
+ }
51
+
52
+ return of(...actions)
53
+ }),
54
+ catchError((error) =>
55
+ of(
56
+ EditorActions.saveRecordFailure({
57
+ error,
41
58
  })
42
59
  )
43
- }
44
-
45
- return of(...actions)
46
- }),
47
- catchError((error) =>
48
- of(
49
- EditorActions.saveRecordFailure({
50
- error,
51
- })
52
60
  )
53
61
  )
54
- )
55
62
  )
56
63
  )
57
64
  )
@@ -54,7 +54,11 @@ export class EditorFacade {
54
54
  }
55
55
 
56
56
  saveRecord() {
57
- this.store.dispatch(EditorActions.saveRecord())
57
+ this.store.dispatch(EditorActions.saveRecord({ publish: false }))
58
+ }
59
+
60
+ saveAndPublishRecord() {
61
+ this.store.dispatch(EditorActions.saveRecord({ publish: true }))
58
62
  }
59
63
 
60
64
  undoRecordDraft() {
@@ -3,13 +3,13 @@
3
3
  editor.record.form.field.onlineResource.edit.protocol
4
4
  </h3>
5
5
  @if (protocolHint) {
6
- <span
7
- class="material-symbols-outlined m-2 gn-ui-icon-small"
6
+ <ng-icon
7
+ name="matHelpOutline"
8
+ size="16"
9
+ class="m-2"
8
10
  [matTooltip]="protocolHint"
9
11
  matTooltipPosition="above"
10
- >
11
- help
12
- </span>
12
+ ></ng-icon>
13
13
  }
14
14
  </div>
15
15
  <div class="flex flex-row items-center gap-4 h-[48px]">
@@ -30,6 +30,7 @@ import {
30
30
  provideNgIconsConfig,
31
31
  } from '@ng-icons/core'
32
32
  import { iconoirRefresh } from '@ng-icons/iconoir'
33
+ import { matHelpOutline } from '@ng-icons/material-icons/outline'
33
34
  import { TranslateDirective, TranslatePipe } from '@ngx-translate/core'
34
35
  import {
35
36
  BehaviorSubject,
@@ -65,7 +66,7 @@ marker(
65
66
  UrlInputComponent,
66
67
  ],
67
68
  providers: [
68
- provideIcons({ iconoirRefresh }),
69
+ provideIcons({ iconoirRefresh, matHelpOutline }),
69
70
  provideNgIconsConfig({
70
71
  size: '1.5em',
71
72
  }),
@@ -3,11 +3,11 @@ import {
3
3
  ChangeDetectorRef,
4
4
  Component,
5
5
  EventEmitter,
6
+ inject,
6
7
  Input,
7
8
  Output,
8
9
  TemplateRef,
9
10
  ViewChild,
10
- inject,
11
11
  } from '@angular/core'
12
12
  import { MatDialog } from '@angular/material/dialog'
13
13
  import { marker } from '@biesbjerg/ngx-translate-extract-marker'
@@ -3,11 +3,14 @@ import {
3
3
  ChangeDetectorRef,
4
4
  Component,
5
5
  EventEmitter,
6
+ inject,
6
7
  Input,
7
8
  Output,
8
- inject,
9
9
  } from '@angular/core'
10
- import { GraphicOverview } from '../../../../../../../../../libs/common/domain/src/lib/model/record'
10
+ import {
11
+ GraphicOverview,
12
+ RecordKind,
13
+ } from '../../../../../../../../../libs/common/domain/src/lib/model/record'
11
14
  import { ImageInputComponent } from '../../../../../../../../../libs/ui/elements/src'
12
15
  import { PlatformServiceInterface } from '../../../../../../../../../libs/common/domain/src/lib/platform.service.interface'
13
16
  import { NotificationsService } from '../../../../../../../../../libs/feature/notifications/src'
@@ -30,6 +33,7 @@ export class FormFieldOverviewsComponent {
30
33
  private cd = inject(ChangeDetectorRef)
31
34
 
32
35
  @Input() metadataUuid: string
36
+ @Input() recordKind: RecordKind
33
37
  @Input() value: Array<GraphicOverview>
34
38
  @Output() valueChange: EventEmitter<Array<GraphicOverview>> =
35
39
  new EventEmitter()
@@ -111,13 +115,16 @@ export class FormFieldOverviewsComponent {
111
115
  {
112
116
  type: 'error',
113
117
  title: this.translateService.instant(
114
- 'editor.record.resourceError.title'
118
+ 'editor.record.resourceError.title',
119
+ { recordKind: this.recordKind }
115
120
  ),
116
121
  text: `${this.translateService.instant(
117
- 'editor.record.resourceError.body'
122
+ 'editor.record.resourceError.body',
123
+ { recordKind: this.recordKind }
118
124
  )} ${error.message}`,
119
125
  closeMessage: this.translateService.instant(
120
- 'editor.record.resourceError.closeMessage'
126
+ 'editor.record.resourceError.closeMessage',
127
+ { recordKind: this.recordKind }
121
128
  ),
122
129
  },
123
130
  undefined,
@@ -9,11 +9,11 @@
9
9
  "
10
10
  (buttonClick)="togglePreview()"
11
11
  >
12
- <span
13
- class="material-symbols-outlined gn-ui-icon-small"
12
+ <ng-icon
13
+ [name]="preview ? 'matVisibilityOffOutline' : 'matVisibilityOutline'"
14
+ size="16"
14
15
  [ngClass]="{ 'text-primary': !preview }"
15
- >{{ preview ? 'visibility_off' : 'visibility' }}</span
16
- >
16
+ ></ng-icon>
17
17
  &nbsp;
18
18
  {{ preview ? 'Edit' : 'Preview' }}
19
19
  </gn-ui-button>
@@ -10,6 +10,11 @@ import { ReactiveFormsModule } from '@angular/forms'
10
10
  import { MarkdownEditorComponent } from '../../../../../../../../../libs/ui/elements/src'
11
11
  import { ButtonComponent } from '../../../../../../../../../libs/ui/inputs/src'
12
12
  import { FormFieldWrapperComponent } from '../../../../../../../../../libs/ui/layout/src'
13
+ import { NgIconComponent, provideIcons } from '@ng-icons/core'
14
+ import {
15
+ matVisibilityOffOutline,
16
+ matVisibilityOutline,
17
+ } from '@ng-icons/material-icons/outline'
13
18
 
14
19
  @Component({
15
20
  selector: 'gn-ui-form-field-rich',
@@ -23,7 +28,9 @@ import { FormFieldWrapperComponent } from '../../../../../../../../../libs/ui/la
23
28
  MarkdownEditorComponent,
24
29
  FormFieldWrapperComponent,
25
30
  ButtonComponent,
31
+ NgIconComponent,
26
32
  ],
33
+ providers: [provideIcons({ matVisibilityOutline, matVisibilityOffOutline })],
27
34
  })
28
35
  export class FormFieldRichComponent {
29
36
  @Input() label: string
@@ -1,11 +1,15 @@
1
1
  <div class="flex flex-col gap-2">
2
2
  <gn-ui-check-toggle
3
- [label]="'editor.record.form.updateFrequency.planned' | translate"
3
+ [label]="
4
+ 'editor.record.form.updateFrequency.planned' | translate: { recordKind }
5
+ "
4
6
  [value]="planned"
5
7
  (toggled)="onPlannedToggled()"
6
8
  ></gn-ui-check-toggle>
7
9
  <gn-ui-dropdown-selector
8
- [title]="'editor.record.form.field.updateFrequency' | translate"
10
+ [title]="
11
+ 'editor.record.form.field.updateFrequency' | translate: { recordKind }
12
+ "
9
13
  [showTitle]="false"
10
14
  [choices]="choices"
11
15
  [selected]="selectedFrequency"
@@ -2,12 +2,13 @@ import {
2
2
  ChangeDetectionStrategy,
3
3
  Component,
4
4
  EventEmitter,
5
+ inject,
5
6
  Input,
6
7
  OnInit,
7
8
  Output,
8
- inject,
9
9
  } from '@angular/core'
10
10
  import {
11
+ RecordKind,
11
12
  UpdateFrequency,
12
13
  UpdateFrequencyCode,
13
14
  updateFrequencyCodeValues,
@@ -37,6 +38,7 @@ export class FormFieldUpdateFrequencyComponent implements OnInit {
37
38
  private translateService = inject(TranslateService)
38
39
 
39
40
  @Input() value: UpdateFrequency
41
+ @Input() recordKind: RecordKind
40
42
  @Output() valueChange: EventEmitter<UpdateFrequency> = new EventEmitter()
41
43
 
42
44
  protected choices: DropdownChoice[] = []
@@ -12,8 +12,8 @@
12
12
  </ng-container>
13
13
  <ng-template #withGenericWrapper>
14
14
  <gn-ui-form-field-wrapper
15
- [label]="config.labelKey! | translate"
16
- [hint]="config.hintKey! | translate"
15
+ [label]="config.labelKey! | translate: { recordKind }"
16
+ [hint]="config.hintKey! | translate: { recordKind }"
17
17
  >
18
18
  <ng-container *ngTemplateOutlet="fieldContent"></ng-container>
19
19
  </gn-ui-form-field-wrapper>
@@ -34,30 +34,32 @@
34
34
  [value]="valueAsString"
35
35
  (change)="valueChange.emit($event.target.value)"
36
36
  [placeholder]="
37
- 'editor.record.form.field.title.placeholder' | translate
37
+ 'editor.record.form.field.title.placeholder'
38
+ | translate: { recordKind }
38
39
  "
39
40
  ></textarea>
40
41
  <div class="flex flex-row justify-between self-start mt-0.5">
41
- <span
42
- class="material-symbols-outlined gn-ui-icon-small m-2 cursor-pointer"
42
+ <ng-icon
43
+ name="matEditOutline"
44
+ size="16"
45
+ class="m-2 cursor-pointer"
43
46
  (click)="focusTitleInput()"
44
- >edit</span
45
- >
46
- <span
47
+ ></ng-icon>
48
+ <ng-icon
47
49
  *ngIf="config.hintKey"
48
- class="material-symbols-outlined gn-ui-icon-small m-2"
49
- [matTooltip]="config.hintKey! | translate"
50
+ name="matHelpOutline"
51
+ size="16"
52
+ class="m-2"
53
+ [matTooltip]="config.hintKey! | translate: { recordKind }"
50
54
  matTooltipPosition="above"
51
- >
52
- help
53
- </span>
55
+ ></ng-icon>
54
56
  </div>
55
57
  </div>
56
58
  </ng-container>
57
59
  <ng-container *ngSwitchCase="'abstract'">
58
60
  <gn-ui-form-field-rich
59
- [label]="config.labelKey! | translate"
60
- [hint]="config.hintKey! | translate"
61
+ [label]="config.labelKey! | translate: { recordKind }"
62
+ [hint]="config.hintKey! | translate: { recordKind }"
61
63
  [value]="valueAsString"
62
64
  (valueChange)="valueChange.emit($event)"
63
65
  ></gn-ui-form-field-rich>
@@ -65,6 +67,7 @@
65
67
  <ng-container *ngSwitchCase="'overviews'">
66
68
  <gn-ui-form-field-overviews
67
69
  [metadataUuid]="uniqueIdentifier"
70
+ [recordKind]="recordKind"
68
71
  [value]="valueAsOverviews"
69
72
  (valueChange)="valueChange.emit($event)"
70
73
  ></gn-ui-form-field-overviews>
@@ -92,6 +95,7 @@
92
95
  <gn-ui-form-field-update-frequency
93
96
  [value]="valueAsUpdateFrequency"
94
97
  (valueChange)="valueChange.emit($event)"
98
+ [recordKind]="recordKind"
95
99
  ></gn-ui-form-field-update-frequency>
96
100
  </ng-container>
97
101
  <ng-container *ngSwitchCase="'temporalExtents'">
@@ -117,7 +121,7 @@
117
121
  </ng-container>
118
122
  <ng-container *ngSwitchCase="'licenses'">
119
123
  <gn-ui-form-field-license
120
- [label]="config.labelKey! | translate"
124
+ [label]="config.labelKey! | translate: { recordKind }"
121
125
  [recordLicences]="valueAsConstraints"
122
126
  (recordLicencesChange)="valueChange.emit($event)"
123
127
  ></gn-ui-form-field-license>
@@ -18,9 +18,15 @@ import {
18
18
  Individual,
19
19
  Keyword,
20
20
  OnlineResource,
21
+ RecordKind,
21
22
  UpdateFrequency,
22
23
  } from '../../../../../../../../libs/common/domain/src/lib/model/record'
23
24
  import { FormFieldWrapperComponent } from '../../../../../../../../libs/ui/layout/src'
25
+ import { NgIconComponent, provideIcons } from '@ng-icons/core'
26
+ import {
27
+ matEditOutline,
28
+ matHelpOutline,
29
+ } from '@ng-icons/material-icons/outline'
24
30
  import { TranslatePipe } from '@ngx-translate/core'
25
31
  import {
26
32
  FieldFocusDirective,
@@ -78,11 +84,14 @@ import { FormFieldTopicsComponent } from './form-field-topics/form-field-topics.
78
84
  FormFieldSpatialToggleComponent,
79
85
  FormFieldTopicsComponent,
80
86
  TextFieldModule,
87
+ NgIconComponent,
81
88
  ],
89
+ providers: [provideIcons({ matEditOutline, matHelpOutline })],
82
90
  hostDirectives: [FieldFocusDirective],
83
91
  })
84
92
  export class FormFieldComponent {
85
93
  @Input() uniqueIdentifier: string
94
+ @Input() recordKind: RecordKind
86
95
  @Input() model: CatalogRecordKeys
87
96
  @Input() modelSpecifier: FieldModelSpecifier
88
97
  @Input() componentName: FormFieldComponentName
@@ -3,7 +3,7 @@
3
3
  section of facade.currentSections$ | async;
4
4
  track sectionTracker($index, section)
5
5
  ) {
6
- @if (!section.hidden) {
6
+ @if (evaluateExpression(section.hidden)() !== true) {
7
7
  <div
8
8
  class="flex flex-col gap-6 border p-8 rounded-[8px] shadow scroll-mt-[90px]"
9
9
  gnUiFieldFocus
@@ -11,12 +11,20 @@
11
11
  >
12
12
  <div class="flex flex-col gap-2">
13
13
  @if (section.labelKey) {
14
- <div class="text-2xl font-title text-black" translate>
14
+ <div
15
+ class="text-2xl font-title text-black"
16
+ translate
17
+ [translateParams]="{ recordKind: recordKind$ | async }"
18
+ >
15
19
  {{ section.labelKey }}
16
20
  </div>
17
21
  }
18
22
  @if (section.descriptionKey) {
19
- <div class="text-gray-800 text-sm" translate>
23
+ <div
24
+ class="text-gray-800 text-sm"
25
+ translate
26
+ [translateParams]="{ recordKind: recordKind$ | async }"
27
+ >
20
28
  {{ section.descriptionKey }}
21
29
  </div>
22
30
  }
@@ -26,7 +34,7 @@
26
34
  field of section.fieldsWithValues;
27
35
  track fieldTracker($index, field)
28
36
  ) {
29
- @if (!field.config.hidden) {
37
+ @if (evaluateExpression(field.config.hidden)() !== true) {
30
38
  <gn-ui-form-field
31
39
  [ngClass]="
32
40
  field.config.gridColumnSpan === 1
@@ -34,6 +42,7 @@
34
42
  : 'col-span-2'
35
43
  "
36
44
  [uniqueIdentifier]="recordUniqueIdentifier$ | async"
45
+ [recordKind]="recordKind$ | async"
37
46
  [model]="field.config.model!"
38
47
  [modelSpecifier]="field.config.modelSpecifier!"
39
48
  [config]="field.config.formFieldConfig"
@@ -2,9 +2,12 @@ import { CommonModule } from '@angular/common'
2
2
  import {
3
3
  ChangeDetectionStrategy,
4
4
  Component,
5
+ computed,
5
6
  inject,
6
7
  OnDestroy,
7
8
  OnInit,
9
+ Signal,
10
+ signal,
8
11
  viewChildren,
9
12
  } from '@angular/core'
10
13
  import { EditorFacade } from '../../+state/editor.facade'
@@ -18,6 +21,9 @@ import {
18
21
  import { firstValueFrom, map, Subscription, withLatestFrom } from 'rxjs'
19
22
  import { CatalogRecordKeys } from '../../../../../../../libs/common/domain/src/lib/model/record'
20
23
  import { switchMap } from 'rxjs/operators'
24
+ import { toSignal } from '@angular/core/rxjs-interop'
25
+ import { evaluate, isExpression } from '../../expressions'
26
+ import { NotificationsService } from '../../../../../../../libs/feature/notifications/src'
21
27
 
22
28
  @Component({
23
29
  selector: 'gn-ui-record-form',
@@ -34,11 +40,13 @@ import { switchMap } from 'rxjs/operators'
34
40
  })
35
41
  export class RecordFormComponent implements OnInit, OnDestroy {
36
42
  facade = inject(EditorFacade)
43
+ notifications = inject(NotificationsService)
37
44
  subscription = new Subscription()
38
45
 
39
46
  recordUniqueIdentifier$ = this.facade.record$.pipe(
40
47
  map((record) => record.uniqueIdentifier)
41
48
  )
49
+ recordKind$ = this.facade.record$.pipe(map((record) => record.kind))
42
50
 
43
51
  focusFieldWithPage$ = this.facade.focusedField$.pipe(
44
52
  switchMap(async (field) => {
@@ -112,4 +120,37 @@ export class RecordFormComponent implements OnInit, OnDestroy {
112
120
  .findIndex((s) => s.fields.some((f) => f.model === model))
113
121
  return { page, section }
114
122
  }
123
+
124
+ private recordSignal = toSignal(this.facade.record$, { requireSync: true })
125
+ private expressionCache = new Map<string, Signal<EditorFieldValue>>()
126
+
127
+ // only compiles the expression into an evaluator the first time;
128
+ // then we store a computed() signal in the cache so that the evaluator is
129
+ // run again if the record changes
130
+ evaluateExpression(
131
+ expressionOrValue: string | EditorFieldValue
132
+ ): Signal<EditorFieldValue> {
133
+ if (!isExpression(expressionOrValue)) {
134
+ return signal(expressionOrValue as EditorFieldValue)
135
+ }
136
+ const expression = expressionOrValue as string
137
+ if (this.expressionCache.has(expression)) {
138
+ return this.expressionCache.get(expression)
139
+ }
140
+ const { evaluator, errors } = evaluate(expression)
141
+ if (errors.length) {
142
+ console.error(`The following errors happened while evaluating the expression '${expression}':
143
+ ${errors.map((err) => `> ${err}`).join('\n')}`)
144
+ this.notifications.showNotification({
145
+ type: 'warning',
146
+ title: 'Technical error',
147
+ text: 'An error happened while evaluating an expression inside the editor configuration; open the developer console for more information',
148
+ })
149
+ }
150
+ this.expressionCache.set(
151
+ expression,
152
+ computed(() => evaluator({ globals: { record: this.recordSignal() } }))
153
+ )
154
+ return this.expressionCache.get(expression)
155
+ }
115
156
  }