geonetwork-ui 2.11.0-dev.80a8b0bfa → 2.11.0-dev.840dcc932

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 (105) hide show
  1. package/fesm2022/geonetwork-ui.mjs +2273 -1474
  2. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  3. package/index.d.ts +342 -136
  4. package/index.d.ts.map +1 -1
  5. package/package.json +7 -5
  6. package/src/libs/api/metadata-converter/src/lib/dcat-ap/dcat-ap.converter.ts +2 -0
  7. package/src/libs/api/metadata-converter/src/lib/fixtures/mel.records.dataset+featurecatalog.ts +395 -0
  8. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +35 -8
  9. package/src/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.ts +19 -0
  10. package/src/libs/api/metadata-converter/src/lib/iso19115-3/iso19115-3.converter.ts +17 -6
  11. package/src/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.ts +110 -0
  12. package/src/libs/api/metadata-converter/src/lib/iso19115-3/write-parts.ts +131 -0
  13. package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +9 -0
  14. package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +2 -1
  15. package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +134 -35
  16. package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +83 -35
  17. package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +11 -0
  18. package/src/libs/common/domain/src/lib/model/record/flags.model.ts +1 -0
  19. package/src/libs/common/domain/src/lib/model/record/index.ts +1 -0
  20. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +14 -18
  21. package/src/libs/common/domain/src/lib/model/search/aggregation.model.ts +2 -0
  22. package/src/libs/common/domain/src/lib/model/search/filter.model.ts +24 -2
  23. package/src/libs/common/domain/src/lib/platform.service.interface.ts +1 -0
  24. package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +20 -1
  25. package/src/libs/common/fixtures/src/lib/records.fixtures.ts +42 -62
  26. package/src/libs/feature/dataviz/src/lib/service/data.service.ts +32 -19
  27. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-constraints/form-field-constraints.component.ts +3 -5
  28. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.ts +2 -2
  29. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-record-flags/form-field-record-flag-component.html +18 -0
  30. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-record-flags/form-field-record-flag-component.ts +86 -0
  31. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +100 -88
  32. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +11 -4
  33. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.ts +18 -4
  34. package/src/libs/feature/editor/src/lib/expressions.ts +67 -0
  35. package/src/libs/feature/editor/src/lib/fields.config.ts +13 -0
  36. package/src/libs/feature/editor/src/lib/models/editor-config.model.ts +10 -4
  37. package/src/libs/feature/map/src/index.ts +0 -2
  38. package/src/libs/feature/map/src/lib/add-layer-from-file/add-layer-from-file.component.html +1 -0
  39. package/src/libs/feature/map/src/lib/feature-map.module.ts +1 -8
  40. package/src/libs/feature/notifications/src/lib/notifications.service.ts +6 -4
  41. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.ts +8 -3
  42. package/src/libs/feature/record/src/lib/state/mdview.effects.ts +3 -1
  43. package/src/libs/feature/record/src/lib/state/mdview.facade.ts +1 -1
  44. package/src/libs/feature/router/src/lib/default/state/query-params.utils.ts +1 -1
  45. package/src/libs/feature/search/src/index.ts +2 -0
  46. package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.html +18 -4
  47. package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.ts +80 -19
  48. package/src/libs/feature/search/src/lib/geocoding/geocoding.service.ts +105 -0
  49. package/src/libs/feature/search/src/lib/location-search/location-search.component.html +18 -0
  50. package/src/libs/feature/search/src/lib/location-search/location-search.component.ts +114 -0
  51. package/src/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.ts +19 -8
  52. package/src/libs/feature/search/src/lib/state/reducer.ts +1 -4
  53. package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +64 -4
  54. package/src/libs/feature/search/src/lib/utils/service/fields.ts +92 -9
  55. package/src/libs/ui/elements/src/lib/record-feature-catalog/feature-catalog-list/feature-catalog-list.component.html +3 -3
  56. package/src/libs/ui/elements/src/lib/record-feature-catalog/feature-catalog-list/feature-catalog-list.component.ts +2 -2
  57. package/src/libs/ui/elements/src/lib/service-capabilities/service-capabilities.component.ts +2 -1
  58. package/src/libs/ui/inputs/src/index.ts +2 -0
  59. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.css +0 -4
  60. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.html +12 -3
  61. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts +11 -3
  62. package/src/libs/ui/inputs/src/lib/date-adapter.providers.ts +30 -0
  63. package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.ts +3 -24
  64. package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.css +37 -3
  65. package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.html +135 -16
  66. package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.ts +166 -29
  67. package/src/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.ts +3 -7
  68. package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.css +1 -0
  69. package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.html +20 -7
  70. package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.ts +46 -5
  71. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +7 -4
  72. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.ts +12 -9
  73. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.model.ts +2 -2
  74. package/src/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.ts +2 -1
  75. package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.css +0 -0
  76. package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.html +117 -0
  77. package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.ts +244 -0
  78. package/src/libs/ui/layout/src/lib/block-list/block-list.component.css +1 -0
  79. package/src/libs/ui/map/src/lib/components/map-container/map-container.component.ts +6 -5
  80. package/src/libs/util/app-config/src/lib/app-config.ts +37 -0
  81. package/src/libs/util/app-config/src/lib/fixtures.ts +37 -1
  82. package/src/libs/util/app-config/src/lib/model.ts +25 -1
  83. package/src/libs/util/shared/src/index.ts +1 -0
  84. package/src/libs/util/shared/src/lib/autofocus.directive.ts +26 -0
  85. package/src/libs/util/shared/src/lib/links/link-utils.ts +19 -10
  86. package/src/libs/util/shared/src/lib/services/date.service.ts +3 -3
  87. package/src/libs/util/shared/src/lib/utils/file.ts +15 -0
  88. package/src/libs/util/shared/src/lib/utils/geojson.ts +24 -0
  89. package/src/libs/util/shared/src/lib/utils/index.ts +1 -0
  90. package/tailwind.base.css +5 -0
  91. package/translations/de.json +28 -4
  92. package/translations/en.json +27 -3
  93. package/translations/es.json +26 -2
  94. package/translations/fr.json +32 -8
  95. package/translations/it.json +26 -2
  96. package/translations/nl.json +26 -2
  97. package/translations/pt.json +26 -2
  98. package/translations/sk.json +26 -2
  99. package/src/libs/feature/map/src/lib/geocoding/geocoding.component.html +0 -41
  100. package/src/libs/feature/map/src/lib/geocoding/geocoding.component.ts +0 -112
  101. package/src/libs/feature/map/src/lib/geocoding.service.ts +0 -57
  102. package/src/libs/feature/map/src/lib/style/index.ts +0 -2
  103. package/src/libs/feature/map/src/lib/style/map-style.fixtures.ts +0 -47
  104. package/src/libs/feature/map/src/lib/style/map-style.service.ts +0 -108
  105. /package/src/libs/feature/{map/src/lib/geocoding/geocoding.component.css → editor/src/lib/components/record-form/form-field/form-field-record-flags/form-field-record-flag-component.css} +0 -0
@@ -7,23 +7,22 @@
7
7
  ></gn-ui-form-field-open-data>
8
8
  </ng-container> -->
9
9
  <div class="flex flex-col h-full">
10
- <ng-container *ngIf="withoutWrapper; else withGenericWrapper">
10
+ @if (withoutWrapper) {
11
11
  <ng-container *ngTemplateOutlet="fieldContent"></ng-container>
12
- </ng-container>
13
- <ng-template #withGenericWrapper>
12
+ } @else {
14
13
  <gn-ui-form-field-wrapper
15
14
  [label]="config.labelKey! | translate: { recordKind }"
16
15
  [hint]="config.hintKey! | translate: { recordKind }"
17
16
  >
18
17
  <ng-container *ngTemplateOutlet="fieldContent"></ng-container>
19
18
  </gn-ui-form-field-wrapper>
20
- </ng-template>
19
+ }
21
20
  </div>
22
21
 
23
22
  <ng-template #fieldContent>
24
- <ng-container [ngSwitch]="model">
25
- <ng-container *ngSwitchCase="'title'">
26
- <div class="flex flex-row flex-start items-center gap-3 mb-[12px]">
23
+ @switch (model) {
24
+ @case ('title') {
25
+ <div class="flex flex-row flex-start items-center gap-3">
27
26
  <textarea
28
27
  #titleInput
29
28
  cdkTextareaAutosize
@@ -45,158 +44,171 @@
45
44
  class="m-2 cursor-pointer"
46
45
  (click)="focusTitleInput()"
47
46
  ></ng-icon>
48
- <ng-icon
49
- *ngIf="config.hintKey"
50
- name="matHelpOutline"
51
- size="16"
52
- class="m-2"
53
- [matTooltip]="config.hintKey! | translate: { recordKind }"
54
- matTooltipPosition="above"
55
- ></ng-icon>
47
+ @if (config.hintKey) {
48
+ <ng-icon
49
+ name="matHelpOutline"
50
+ size="16"
51
+ class="m-2"
52
+ [matTooltip]="config.hintKey! | translate: { recordKind }"
53
+ matTooltipPosition="above"
54
+ ></ng-icon>
55
+ }
56
56
  </div>
57
57
  </div>
58
- </ng-container>
59
- <ng-container *ngSwitchCase="'abstract'">
58
+ }
59
+ @case ('abstract') {
60
60
  <gn-ui-form-field-rich
61
61
  [label]="config.labelKey! | translate: { recordKind }"
62
62
  [hint]="config.hintKey! | translate: { recordKind }"
63
63
  [value]="valueAsString"
64
64
  (valueChange)="valueChange.emit($event)"
65
65
  ></gn-ui-form-field-rich>
66
- </ng-container>
67
- <ng-container *ngSwitchCase="'overviews'">
66
+ }
67
+ @case ('overviews') {
68
68
  <gn-ui-form-field-overviews
69
69
  [metadataUuid]="uniqueIdentifier"
70
70
  [recordKind]="recordKind"
71
71
  [value]="valueAsOverviews"
72
72
  (valueChange)="valueChange.emit($event)"
73
73
  ></gn-ui-form-field-overviews>
74
- </ng-container>
75
- <ng-container *ngSwitchCase="'resourceIdentifiers'">
74
+ }
75
+ @case ('resourceIdentifiers') {
76
76
  <gn-ui-form-field-simple
77
77
  [type]="'text'"
78
78
  [value]="valueAsResourceIdentifierCode"
79
79
  (valueChange)="handleResourceIdentifierChange($event)"
80
80
  ></gn-ui-form-field-simple>
81
- </ng-container>
82
- <ng-container *ngSwitchCase="'resourceCreated'">
81
+ }
82
+ @case ('resourceCreated') {
83
83
  <gn-ui-form-field-date
84
84
  [value]="valueAsDate"
85
85
  (valueChange)="valueChange.emit($event)"
86
86
  ></gn-ui-form-field-date>
87
- </ng-container>
88
- <ng-container *ngSwitchCase="'resourceUpdated'">
87
+ }
88
+ @case ('resourceUpdated') {
89
89
  <gn-ui-form-field-date
90
90
  [value]="valueAsDate"
91
91
  (valueChange)="valueChange.emit($event)"
92
92
  ></gn-ui-form-field-date>
93
- </ng-container>
94
- <ng-container *ngSwitchCase="'updateFrequency'">
93
+ }
94
+ @case ('updateFrequency') {
95
95
  <gn-ui-form-field-update-frequency
96
96
  [value]="valueAsUpdateFrequency"
97
97
  (valueChange)="valueChange.emit($event)"
98
98
  [recordKind]="recordKind"
99
99
  ></gn-ui-form-field-update-frequency>
100
- </ng-container>
101
- <ng-container *ngSwitchCase="'associatedRecords'">
100
+ }
101
+ @case ('associatedRecords') {
102
102
  <gn-ui-form-field-associated-records
103
103
  [value]="valueAsAssociatedRecords"
104
104
  (valueChange)="valueChange.emit($event)"
105
105
  ></gn-ui-form-field-associated-records>
106
- </ng-container>
107
- <ng-container *ngSwitchCase="'temporalExtents'">
106
+ }
107
+ @case ('temporalExtents') {
108
108
  <gn-ui-form-field-temporal-extents
109
109
  [value]="valueAsTemporalExtents"
110
110
  (valueChange)="valueChange.emit($event)"
111
111
  ></gn-ui-form-field-temporal-extents>
112
- </ng-container>
113
- <ng-container *ngSwitchCase="'spatialExtents'">
112
+ }
113
+ @case ('spatialExtents') {
114
114
  <gn-ui-form-field-spatial-extent></gn-ui-form-field-spatial-extent>
115
- </ng-container>
116
- <ng-container *ngSwitchCase="'keywords'">
115
+ }
116
+ @case ('keywords') {
117
117
  <gn-ui-form-field-keywords
118
118
  [value]="valueAsKeywords"
119
119
  (valueChange)="valueChange.emit($event)"
120
120
  ></gn-ui-form-field-keywords>
121
- </ng-container>
122
- <ng-container *ngSwitchCase="'topics'">
121
+ }
122
+ @case ('topics') {
123
123
  <gn-ui-form-field-topics
124
124
  [value]="valueAsTopics"
125
125
  (valueChange)="valueChange.emit($event)"
126
126
  ></gn-ui-form-field-topics>
127
- </ng-container>
128
- <ng-container *ngSwitchCase="'licenses'">
127
+ }
128
+ @case ('licenses') {
129
129
  <gn-ui-form-field-license
130
130
  [label]="config.labelKey! | translate: { recordKind }"
131
131
  [recordLicences]="valueAsConstraints"
132
132
  (recordLicencesChange)="valueChange.emit($event)"
133
133
  ></gn-ui-form-field-license>
134
- </ng-container>
134
+ }
135
135
 
136
- <ng-container *ngSwitchCase="'legalConstraints'">
137
- <ng-container *ngTemplateOutlet="formFieldConstraints"></ng-container>
138
- </ng-container>
139
- <ng-container *ngSwitchCase="'securityConstraints'">
140
- <ng-container *ngTemplateOutlet="formFieldConstraints"></ng-container>
141
- </ng-container>
142
- <ng-container *ngSwitchCase="'otherConstraints'">
143
- <ng-container *ngTemplateOutlet="formFieldConstraints"></ng-container
144
- ></ng-container>
145
- <ng-template #formFieldConstraints>
136
+ <!-- TODO: merge these cases together when migrated to angular 21 -->
137
+ @case ('legalConstraints') {
138
+ <gn-ui-form-field-constraints
139
+ [label]="config.labelKey"
140
+ [value]="valueAsConstraints"
141
+ [constraintType]="model"
142
+ (valueChange)="valueChange.emit($event)"
143
+ ></gn-ui-form-field-constraints>
144
+ }
145
+ @case ('securityConstraints') {
146
+ <gn-ui-form-field-constraints
147
+ [label]="config.labelKey"
148
+ [value]="valueAsConstraints"
149
+ [constraintType]="model"
150
+ (valueChange)="valueChange.emit($event)"
151
+ ></gn-ui-form-field-constraints>
152
+ }
153
+ @case ('otherConstraints') {
146
154
  <gn-ui-form-field-constraints
147
155
  [label]="config.labelKey"
148
156
  [value]="valueAsConstraints"
149
157
  [constraintType]="model"
150
158
  (valueChange)="valueChange.emit($event)"
151
- ></gn-ui-form-field-constraints
152
- ></ng-template>
159
+ ></gn-ui-form-field-constraints>
160
+ }
153
161
 
154
- <ng-container *ngSwitchCase="'contactsForResource'">
162
+ @case ('contactsForResource') {
155
163
  <gn-ui-form-field-contacts-for-resource
156
164
  [value]="valueAsIndividuals"
157
165
  (valueChange)="valueChange.emit($event)"
158
166
  ></gn-ui-form-field-contacts-for-resource>
159
- </ng-container>
160
- <ng-container *ngSwitchCase="'contacts'">
167
+ }
168
+ @case ('contacts') {
161
169
  <gn-ui-form-field-contacts
162
170
  [value]="valueAsIndividuals"
163
171
  [modelSpecifier]="modelSpecifier"
164
172
  (valueChange)="valueChange.emit($event)"
165
173
  ></gn-ui-form-field-contacts>
166
- </ng-container>
167
- <ng-container *ngSwitchCase="'onlineResources'">
168
- <gn-ui-form-field-online-resources
169
- *ngIf="modelSpecifier === 'onlineResourceType:!link'"
170
- [metadataUuid]="uniqueIdentifier"
171
- [value]="valueAsOnlineResources"
172
- (valueChange)="valueChange.emit($event)"
173
- ></gn-ui-form-field-online-resources>
174
- </ng-container>
175
- <ng-container *ngSwitchCase="'onlineResources'">
176
- <gn-ui-form-field-online-link-resources
177
- *ngIf="modelSpecifier === 'onlineResourceType:link'"
178
- [metadataUuid]="uniqueIdentifier"
179
- [value]="valueAsOnlineResources"
180
- (valueChange)="valueChange.emit($event)"
181
- ></gn-ui-form-field-online-link-resources>
182
- </ng-container>
183
- <ng-container *ngSwitchCase="'onlineResources'">
184
- <gn-ui-form-field-online-single-link-resource
185
- *ngIf="modelSpecifier === 'onlineResourceType:singleLink'"
186
- [value]="valueAsOnlineResources"
187
- (valueChange)="valueChange.emit($event)"
188
- ></gn-ui-form-field-online-single-link-resource>
189
- </ng-container>
190
- </ng-container>
174
+ }
175
+ @case ('onlineResources') {
176
+ @if (modelSpecifier === 'onlineResourceType:!link') {
177
+ <gn-ui-form-field-online-resources
178
+ [metadataUuid]="uniqueIdentifier"
179
+ [value]="valueAsOnlineResources"
180
+ (valueChange)="valueChange.emit($event)"
181
+ ></gn-ui-form-field-online-resources>
182
+ } @else if (modelSpecifier === 'onlineResourceType:link') {
183
+ <gn-ui-form-field-online-link-resources
184
+ [metadataUuid]="uniqueIdentifier"
185
+ [value]="valueAsOnlineResources"
186
+ (valueChange)="valueChange.emit($event)"
187
+ ></gn-ui-form-field-online-link-resources>
188
+ } @else if (modelSpecifier === 'onlineResourceType:singleLink') {
189
+ <gn-ui-form-field-online-single-link-resource
190
+ [value]="valueAsOnlineResources"
191
+ (valueChange)="valueChange.emit($event)"
192
+ ></gn-ui-form-field-online-single-link-resource>
193
+ }
194
+ }
195
+ @case ('flags') {
196
+ <gn-ui-form-field-record-flag
197
+ [flagName]="recordFlag"
198
+ [labelKey]="config.labelKey"
199
+ [hintKey]="config.hintKey"
200
+ ></gn-ui-form-field-record-flag>
201
+ }
202
+ }
191
203
 
192
- <ng-container *ngIf="!model">
193
- <ng-container [ngSwitch]="componentName">
194
- <ng-container *ngSwitchCase="'form-field-constraints-shortcuts'">
204
+ @if (!model) {
205
+ @switch (componentName) {
206
+ @case ('form-field-constraints-shortcuts') {
195
207
  <gn-ui-form-field-constraints-shortcuts></gn-ui-form-field-constraints-shortcuts>
196
- </ng-container>
197
- <ng-container *ngSwitchCase="'form-field-spatial-toggle'">
208
+ }
209
+ @case ('form-field-spatial-toggle') {
198
210
  <gn-ui-form-field-spatial-toggle></gn-ui-form-field-spatial-toggle>
199
- </ng-container>
200
- </ng-container>
201
- </ng-container>
211
+ }
212
+ }
213
+ }
202
214
  </ng-template>
@@ -12,13 +12,13 @@ import {
12
12
  import { MatTooltipModule } from '@angular/material/tooltip'
13
13
  import {
14
14
  AssociatedRecord,
15
- CatalogRecordKeys,
16
15
  Constraint,
17
16
  DatasetTemporalExtent,
18
17
  GraphicOverview,
19
18
  Individual,
20
19
  Keyword,
21
20
  OnlineResource,
21
+ RecordFlag,
22
22
  RecordKind,
23
23
  UpdateFrequency,
24
24
  } from '../../../../../../../../libs/common/domain/src/lib/model/record'
@@ -36,9 +36,10 @@ import {
36
36
  FormFieldTemporalExtentsComponent,
37
37
  } from '.'
38
38
  import {
39
- FieldModelSpecifier,
40
39
  FormFieldComponentName,
41
40
  FormFieldConfig,
41
+ FormFieldModel,
42
+ FormFieldModelSpecifier,
42
43
  } from '../../../models'
43
44
  import { FormFieldAssociatedRecordsComponent } from './form-field-associated-records/form-field-associated-records.component'
44
45
  import { FormFieldContactsForResourceComponent } from './form-field-contacts-for-resource/form-field-contacts-for-resource.component'
@@ -57,6 +58,7 @@ import { FormFieldConstraintsComponent } from './form-field-constraints/form-fie
57
58
  import { TextFieldModule } from '@angular/cdk/text-field'
58
59
  import { FormFieldSpatialToggleComponent } from './form-field-spatial-toggle/form-field-spatial-toggle.component'
59
60
  import { FormFieldTopicsComponent } from './form-field-topics/form-field-topics.component'
61
+ import { FormFieldRecordFlagComponent } from './form-field-record-flags/form-field-record-flag-component'
60
62
 
61
63
  @Component({
62
64
  selector: 'gn-ui-form-field',
@@ -90,6 +92,7 @@ import { FormFieldTopicsComponent } from './form-field-topics/form-field-topics.
90
92
  FormFieldTopicsComponent,
91
93
  TextFieldModule,
92
94
  NgIconComponent,
95
+ FormFieldRecordFlagComponent,
93
96
  ],
94
97
  providers: [provideIcons({ matEditOutline, matHelpOutline })],
95
98
  hostDirectives: [FieldFocusDirective],
@@ -97,8 +100,8 @@ import { FormFieldTopicsComponent } from './form-field-topics/form-field-topics.
97
100
  export class FormFieldComponent {
98
101
  @Input() uniqueIdentifier: string
99
102
  @Input() recordKind: RecordKind
100
- @Input() model: CatalogRecordKeys
101
- @Input() modelSpecifier: FieldModelSpecifier
103
+ @Input() model: FormFieldModel
104
+ @Input() modelSpecifier: FormFieldModelSpecifier
102
105
  @Input() componentName: FormFieldComponentName
103
106
 
104
107
  @Input() config: FormFieldConfig
@@ -126,6 +129,7 @@ export class FormFieldComponent {
126
129
  this.model === 'legalConstraints' ||
127
130
  this.model === 'securityConstraints' ||
128
131
  this.model === 'otherConstraints' ||
132
+ this.model === 'flags' ||
129
133
  this.componentName === 'form-field-constraints-shortcuts'
130
134
  )
131
135
  }
@@ -172,6 +176,9 @@ export class FormFieldComponent {
172
176
  }>
173
177
  return identifiers?.[0]?.code || ''
174
178
  }
179
+ get recordFlag() {
180
+ return this.modelSpecifier?.replace(/^flags:/, '') as RecordFlag
181
+ }
175
182
 
176
183
  handleResourceIdentifierChange(code: string) {
177
184
  const identifiers = this.value as Array<{
@@ -11,7 +11,7 @@ import {
11
11
  viewChildren,
12
12
  } from '@angular/core'
13
13
  import { EditorFacade } from '../../+state/editor.facade'
14
- import { EditorFieldValue } from '../../models'
14
+ import { EditorFieldValue, FormFieldModel } from '../../models'
15
15
  import { FieldFocusDirective, FormFieldComponent } from './form-field'
16
16
  import { TranslateDirective } from '@ngx-translate/core'
17
17
  import {
@@ -24,6 +24,7 @@ import { switchMap } from 'rxjs/operators'
24
24
  import { toSignal } from '@angular/core/rxjs-interop'
25
25
  import { evaluate, isExpression } from '../../expressions'
26
26
  import { NotificationsService } from '../../../../../../../libs/feature/notifications/src'
27
+ import { PlatformServiceInterface } from '../../../../../../../libs/common/domain/src/lib/platform.service.interface'
27
28
 
28
29
  @Component({
29
30
  selector: 'gn-ui-record-form',
@@ -41,6 +42,7 @@ import { NotificationsService } from '../../../../../../../libs/feature/notifica
41
42
  export class RecordFormComponent implements OnInit, OnDestroy {
42
43
  facade = inject(EditorFacade)
43
44
  notifications = inject(NotificationsService)
45
+ platform = inject(PlatformServiceInterface)
44
46
  subscription = new Subscription()
45
47
 
46
48
  recordUniqueIdentifier$ = this.facade.record$.pipe(
@@ -90,8 +92,8 @@ export class RecordFormComponent implements OnInit, OnDestroy {
90
92
  this.subscription.unsubscribe()
91
93
  }
92
94
 
93
- handleFieldValueChange(model: CatalogRecordKeys, newValue: EditorFieldValue) {
94
- if (!model) {
95
+ handleFieldValueChange(model: FormFieldModel, newValue: EditorFieldValue) {
96
+ if (!model || model === 'flags') {
95
97
  return
96
98
  }
97
99
  this.facade.updateRecordField(model, newValue)
@@ -122,6 +124,8 @@ export class RecordFormComponent implements OnInit, OnDestroy {
122
124
  }
123
125
 
124
126
  private recordSignal = toSignal(this.facade.record$, { requireSync: true })
127
+ private userSignal = toSignal(this.platform.getMe())
128
+ private isAdminSignal = toSignal(this.platform.isAdministrator())
125
129
  private expressionCache = new Map<string, Signal<EditorFieldValue>>()
126
130
 
127
131
  // only compiles the expression into an evaluator the first time;
@@ -149,7 +153,17 @@ ${errors.map((err) => `> ${err}`).join('\n')}`)
149
153
  }
150
154
  this.expressionCache.set(
151
155
  expression,
152
- computed(() => evaluator({ globals: { record: this.recordSignal() } }))
156
+ computed(() =>
157
+ evaluator({
158
+ globals: {
159
+ record: this.recordSignal(),
160
+ user: {
161
+ ...this.userSignal(),
162
+ isAdmin: this.isAdminSignal(),
163
+ },
164
+ },
165
+ })
166
+ )
153
167
  )
154
168
  return this.expressionCache.get(expression)
155
169
  }
@@ -17,10 +17,14 @@ const TOKEN_OPERATOR_LOGICAL = ['==', '!=']
17
17
  const TOKEN_STRING_LITERAL = ["'", '"']
18
18
  const TOKEN_GROUP_START = '('
19
19
  const TOKEN_GROUP_END = ')'
20
+ const TOKEN_OPERATOR_BOOLEAN = ['&&', '||']
21
+ const TOKEN_OPERATOR_NOT = '!'
20
22
 
21
23
  // See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Operator_precedence
22
24
  const OPERATORS_BY_PRECEDENCE: string[][] = [
23
25
  [TOKEN_OPERATOR_TERNARY_FIRST, TOKEN_OPERATOR_TERNARY_SECOND],
26
+ TOKEN_OPERATOR_BOOLEAN,
27
+ [TOKEN_OPERATOR_NOT],
24
28
  TOKEN_OPERATOR_LOGICAL,
25
29
  ]
26
30
  const ALL_OPERATORS = OPERATORS_BY_PRECEDENCE.flat()
@@ -161,6 +165,18 @@ function evaluateInternal(
161
165
  continue
162
166
  }
163
167
 
168
+ // BOOLEAN LITERAL
169
+ const isBooleanLiteral =
170
+ readNext(expression, pos, 4) === 'true' ||
171
+ readNext(expression, pos, 5) === 'false'
172
+ if (isBooleanLiteral) {
173
+ const literal = readNext(expression, pos, 4) === 'true' ? 'true' : 'false'
174
+ pos += literal.length
175
+ const booleanLiteral = literal === 'true'
176
+ currentEvaluator = () => booleanLiteral
177
+ continue
178
+ }
179
+
164
180
  // SYMBOL (NAME)
165
181
  const isSymbol = /[a-zA-Z]/.test(readNext(expression, pos))
166
182
  if (isSymbol) {
@@ -291,6 +307,57 @@ function evaluateInternal(
291
307
  continue
292
308
  }
293
309
 
310
+ // BOOLEAN OPERATORS
311
+ const isBooleanOperator = TOKEN_OPERATOR_BOOLEAN.some(
312
+ (token) => readNext(expression, pos, token.length) === token
313
+ )
314
+ if (isBooleanOperator) {
315
+ const matchingOperator = TOKEN_OPERATOR_BOOLEAN.find(
316
+ (token) => readNext(expression, pos, token.length) === token
317
+ )
318
+ const isAnd = matchingOperator === '&&'
319
+ pos += matchingOperator.length
320
+ const leftEvaluator = currentEvaluator
321
+ const rightExpression = readUntilNextOperator(
322
+ expression,
323
+ pos,
324
+ matchingOperator
325
+ )
326
+ pos += rightExpression.length
327
+ const rightEvaluator = evaluateInternal(
328
+ rightExpression,
329
+ currentContext
330
+ ).evaluator
331
+
332
+ currentEvaluator = (context) => {
333
+ return isAnd
334
+ ? Boolean(leftEvaluator(context) && rightEvaluator(context))
335
+ : Boolean(leftEvaluator(context) || rightEvaluator(context))
336
+ }
337
+ continue
338
+ }
339
+
340
+ // PREFIX OPERATORS
341
+ const isNotOperator = readNext(expression, pos) === TOKEN_OPERATOR_NOT
342
+ if (isNotOperator) {
343
+ pos++
344
+ const operandExpression = readUntilNextOperator(
345
+ expression,
346
+ pos,
347
+ TOKEN_OPERATOR_NOT
348
+ )
349
+ pos += operandExpression.length
350
+ const operandEvaluator = evaluateInternal(
351
+ operandExpression,
352
+ currentContext
353
+ ).evaluator
354
+
355
+ currentEvaluator = (context) => {
356
+ return !operandEvaluator(context)
357
+ }
358
+ continue
359
+ }
360
+
294
361
  // TERNARY OPERATOR
295
362
  const isTernary = readNext(expression, pos) === TOKEN_OPERATOR_TERNARY_FIRST
296
363
  if (isTernary) {
@@ -37,6 +37,19 @@ export const DEFAULT_CONFIGURATION: EditorConfig = {
37
37
  labelKey: marker('editor.record.form.field.title'),
38
38
  },
39
39
  },
40
+ {
41
+ model: 'flags',
42
+ modelSpecifier: 'flags:IS_REFERENCE_DATASET',
43
+ formFieldConfig: {
44
+ labelKey: marker(
45
+ 'editor.record.form.field.flags.IS_REFERENCE_DATASET'
46
+ ),
47
+ hintKey: marker(
48
+ 'editor.record.form.field.hint.flags.IS_REFERENCE_DATASET'
49
+ ),
50
+ },
51
+ hidden: '${record.kind != "dataset" || !user.isAdmin}',
52
+ },
40
53
  {
41
54
  model: 'abstract',
42
55
  formFieldConfig: {
@@ -1,4 +1,7 @@
1
- import { CatalogRecordKeys } from '../../../../../../libs/common/domain/src/lib/model/record'
1
+ import {
2
+ CatalogRecordKeys,
3
+ RecordFlag,
4
+ } from '../../../../../../libs/common/domain/src/lib/model/record'
2
5
 
3
6
  // Expressions should be enclosed in `${}` to be recognized as such
4
7
  // eg. ${dateNow()}
@@ -15,6 +18,8 @@ export interface FormFieldConfig {
15
18
  invalidHintKey?: string
16
19
  }
17
20
 
21
+ export type FormFieldModel = CatalogRecordKeys | 'flags'
22
+
18
23
  // Specifiers let us use specific components
19
24
  // This is used for instance to target only certain online resources in a field
20
25
  type OnlineLinkResourceSpecifier = `onlineResourceType:link`
@@ -25,11 +30,12 @@ type OnlineSingleLinkResourceSpecifier = `onlineResourceType:singleLink`
25
30
  // When set on the `contacts` field, contacts are rendered as editable detail
26
31
  // fields (ContactDetailsFormComponent) instead of cards (ContactCardComponent)
27
32
  type EditableContactDetailsSpecifier = `contact:editableDetails`
28
- export type FieldModelSpecifier =
33
+ export type FormFieldModelSpecifier =
29
34
  | OnlineLinkResourceSpecifier
30
35
  | DatasetDistributionsSpecifier
31
36
  | OnlineSingleLinkResourceSpecifier
32
37
  | EditableContactDetailsSpecifier
38
+ | `flags:${RecordFlag}`
33
39
 
34
40
  export type FormFieldComponentName =
35
41
  | 'form-field-constraints-shortcuts'
@@ -37,8 +43,8 @@ export type FormFieldComponentName =
37
43
 
38
44
  export interface EditorFieldIdentification {
39
45
  // name of the target field in the record; will not change the record directly if not defined
40
- model?: CatalogRecordKeys
41
- modelSpecifier?: FieldModelSpecifier
46
+ model?: FormFieldModel
47
+ modelSpecifier?: FormFieldModelSpecifier
42
48
 
43
49
  // if no model is given, a component can be shown instead
44
50
  componentName?: FormFieldComponentName
@@ -6,8 +6,6 @@ export * from './lib/feature-map.module'
6
6
  export * from './lib/map-state-container/map-state-container.component'
7
7
  export * from './lib/constant'
8
8
  export * from './lib/utils'
9
- export * from './lib/style'
10
9
  export * from './lib/layers-panel/layers-panel.component'
11
10
  export * from './lib/add-layer-from-catalog/add-layer-from-catalog.component'
12
11
  export * from './lib/add-layer-from-catalog/add-layer-record-preview/add-layer-record-preview.component'
13
- export * from './lib/geocoding/geocoding.component'
@@ -5,6 +5,7 @@
5
5
  (fileChange)="handleFileChange($event)"
6
6
  [accept]="acceptedMimeType.join(',')"
7
7
  [placeholder]="'map.addFromFile.placeholder' | translate"
8
+ textClass="text-gray-900 pl-2 py-2"
8
9
  class="placeholder-grey"
9
10
  ></gn-ui-drag-and-drop-file-input>
10
11
  </div>
@@ -2,18 +2,11 @@ import { NgModule } from '@angular/core'
2
2
  import { StoreModule } from '@ngrx/store'
3
3
  import * as fromMap from './+state/map.reducer'
4
4
  import { MapFacade } from './+state/map.facade'
5
- import { GEOCODING_PROVIDER, GeocodingProvider } from './geocoding.service'
6
5
 
7
6
  @NgModule({
8
7
  imports: [
9
8
  StoreModule.forFeature(fromMap.MAP_FEATURE_KEY, fromMap.mapReducer),
10
9
  ],
11
- providers: [
12
- MapFacade,
13
- {
14
- provide: GEOCODING_PROVIDER,
15
- useValue: ['geonames', { maxRows: 5 }] as GeocodingProvider,
16
- },
17
- ],
10
+ providers: [MapFacade],
18
11
  })
19
12
  export class FeatureMapModule {}
@@ -18,10 +18,12 @@ export class NotificationsService {
18
18
  error && console.error(error)
19
19
  const id = Math.floor(Math.random() * 1000000)
20
20
  this.notifications$.next([...this.notifications$.value, { ...content, id }])
21
- if (typeof timeoutMs === 'undefined') return
22
- setTimeout(() => {
23
- this.removeNotificationById(id)
24
- }, timeoutMs)
21
+ if (typeof timeoutMs !== 'undefined') {
22
+ setTimeout(() => {
23
+ this.removeNotificationById(id)
24
+ }, timeoutMs)
25
+ }
26
+ return id
25
27
  }
26
28
 
27
29
  removeNotificationById(id: number) {