geonetwork-ui 2.10.3-dev.4154db07d → 2.11.0-dev.0559cfd9d

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 (128) hide show
  1. package/fesm2022/geonetwork-ui.mjs +2983 -2095
  2. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  3. package/index.d.ts +408 -180
  4. package/index.d.ts.map +1 -1
  5. package/package.json +10 -8
  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/generic.records.ts +5 -5
  8. package/src/libs/api/metadata-converter/src/lib/fixtures/mel.records.dataset+featurecatalog.ts +395 -0
  9. package/src/libs/api/metadata-converter/src/lib/fixtures/metawal.records.ts +5 -5
  10. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +78 -21
  11. package/src/libs/api/metadata-converter/src/lib/gn4/types/elasticsearch.model.ts +13 -1
  12. package/src/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.ts +19 -0
  13. package/src/libs/api/metadata-converter/src/lib/iso19115-3/iso19115-3.converter.ts +17 -6
  14. package/src/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.ts +110 -0
  15. package/src/libs/api/metadata-converter/src/lib/iso19115-3/write-parts.ts +133 -2
  16. package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +9 -0
  17. package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +11 -6
  18. package/src/libs/api/metadata-converter/src/lib/iso19139/utils/association-type.mapper.ts +12 -0
  19. package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +2 -2
  20. package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +157 -38
  21. package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +45 -50
  22. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +54 -20
  23. package/src/libs/common/domain/src/lib/model/search/aggregation.model.ts +2 -0
  24. package/src/libs/common/domain/src/lib/model/search/filter.model.ts +24 -2
  25. package/src/libs/common/domain/src/lib/model/search/sort-by.model.ts +1 -5
  26. package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +3 -3
  27. package/src/libs/common/fixtures/src/lib/records.fixtures.ts +42 -62
  28. package/src/libs/feature/dataviz/src/lib/chart-view/chart-view.component.ts +3 -2
  29. package/src/libs/feature/dataviz/src/lib/geo-table-view/geo-table-view.component.ts +2 -1
  30. package/src/libs/feature/dataviz/src/lib/service/data.service.ts +54 -41
  31. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-associated-records/form-field-associated-records.component.html +33 -0
  32. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-associated-records/form-field-associated-records.component.ts +94 -0
  33. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-temporal-extents/form-field-temporal-extents.component.html +16 -12
  34. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +6 -0
  35. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +6 -0
  36. package/src/libs/feature/editor/src/lib/fields.config.ts +5 -0
  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.actions.ts +4 -8
  43. package/src/libs/feature/record/src/lib/state/mdview.effects.ts +10 -17
  44. package/src/libs/feature/record/src/lib/state/mdview.facade.ts +2 -4
  45. package/src/libs/feature/record/src/lib/state/mdview.reducer.ts +4 -9
  46. package/src/libs/feature/record/src/lib/state/mdview.selectors.ts +2 -7
  47. package/src/libs/feature/router/src/lib/default/router.service.ts +1 -1
  48. package/src/libs/feature/router/src/lib/default/state/query-params.utils.ts +1 -1
  49. package/src/libs/feature/search/src/index.ts +2 -0
  50. package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.html +18 -4
  51. package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.ts +80 -19
  52. package/src/libs/feature/search/src/lib/geocoding/geocoding.service.ts +105 -0
  53. package/src/libs/feature/search/src/lib/location-search/location-search.component.html +18 -0
  54. package/src/libs/feature/search/src/lib/location-search/location-search.component.ts +114 -0
  55. package/src/libs/feature/search/src/lib/search-filters-summary-item/search-filters-summary-item.component.ts +19 -8
  56. package/src/libs/feature/search/src/lib/sort-by/sort-by.component.ts +1 -1
  57. package/src/libs/feature/search/src/lib/state/reducer.ts +1 -4
  58. package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +64 -4
  59. package/src/libs/feature/search/src/lib/utils/service/fields.ts +92 -9
  60. package/src/libs/ui/dataviz/src/lib/data-table/data-table.component.html +6 -7
  61. package/src/libs/ui/dataviz/src/lib/data-table/data-table.component.ts +31 -19
  62. package/src/libs/ui/dataviz/src/lib/data-table/data-table.fixtures.ts +2 -2
  63. package/src/libs/ui/elements/src/lib/record-feature-catalog/feature-catalog-list/feature-catalog-list.component.html +3 -3
  64. package/src/libs/ui/elements/src/lib/record-feature-catalog/feature-catalog-list/feature-catalog-list.component.ts +2 -2
  65. package/src/libs/ui/elements/src/lib/service-capabilities/service-capabilities.component.ts +2 -1
  66. package/src/libs/ui/inputs/src/index.ts +2 -0
  67. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.css +0 -4
  68. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.html +12 -3
  69. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts +11 -3
  70. package/src/libs/ui/inputs/src/lib/date-adapter.providers.ts +30 -0
  71. package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.ts +3 -24
  72. package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.css +37 -3
  73. package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.html +135 -16
  74. package/src/libs/ui/inputs/src/lib/date-range-dropdown/date-range-dropdown.component.ts +166 -29
  75. package/src/libs/ui/inputs/src/lib/date-range-picker/date-range-picker.component.ts +3 -7
  76. package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.css +1 -0
  77. package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.html +20 -7
  78. package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.ts +46 -5
  79. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +7 -4
  80. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.ts +12 -9
  81. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.model.ts +2 -2
  82. package/src/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.ts +2 -1
  83. package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.css +0 -0
  84. package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.html +117 -0
  85. package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.ts +244 -0
  86. package/src/libs/ui/layout/src/lib/block-list/block-list.component.css +1 -0
  87. package/src/libs/util/app-config/src/lib/app-config.ts +37 -0
  88. package/src/libs/util/app-config/src/lib/fixtures.ts +37 -1
  89. package/src/libs/util/app-config/src/lib/model.ts +25 -1
  90. package/src/libs/util/data-fetcher/src/lib/data-fetcher.ts +19 -22
  91. package/src/libs/util/data-fetcher/src/lib/engine/duckdb.ts +185 -0
  92. package/src/libs/util/data-fetcher/src/lib/engine/results.ts +63 -0
  93. package/src/libs/util/data-fetcher/src/lib/{sql-utils.ts → engine/sql-utils.ts} +28 -13
  94. package/src/libs/util/data-fetcher/src/lib/model.ts +2 -1
  95. package/src/libs/util/data-fetcher/src/lib/readers/base-file.ts +53 -38
  96. package/src/libs/util/data-fetcher/src/lib/readers/base.ts +11 -0
  97. package/src/libs/util/data-fetcher/src/lib/readers/csv.ts +9 -47
  98. package/src/libs/util/data-fetcher/src/lib/readers/excel.ts +27 -27
  99. package/src/libs/util/data-fetcher/src/lib/readers/geojson.ts +5 -24
  100. package/src/libs/util/data-fetcher/src/lib/readers/gml.ts +5 -49
  101. package/src/libs/util/data-fetcher/src/lib/readers/json.ts +5 -23
  102. package/src/libs/util/data-fetcher/src/lib/readers/wfs.ts +184 -128
  103. package/src/libs/util/data-fetcher/src/lib/utils.ts +0 -143
  104. package/src/libs/util/shared/src/index.ts +1 -0
  105. package/src/libs/util/shared/src/lib/autofocus.directive.ts +26 -0
  106. package/src/libs/util/shared/src/lib/gn-ui-version.ts +1 -1
  107. package/src/libs/util/shared/src/lib/links/link-utils.ts +19 -10
  108. package/src/libs/util/shared/src/lib/services/date.service.ts +3 -3
  109. package/src/libs/util/shared/src/lib/utils/file.ts +15 -0
  110. package/src/libs/util/shared/src/lib/utils/geojson.ts +24 -0
  111. package/src/libs/util/shared/src/lib/utils/index.ts +1 -0
  112. package/tailwind.base.css +5 -0
  113. package/translations/de.json +38 -5
  114. package/translations/en.json +37 -4
  115. package/translations/es.json +35 -2
  116. package/translations/fr.json +42 -9
  117. package/translations/it.json +36 -3
  118. package/translations/nl.json +35 -2
  119. package/translations/pt.json +35 -2
  120. package/translations/sk.json +36 -3
  121. package/src/libs/feature/map/src/lib/geocoding/geocoding.component.html +0 -41
  122. package/src/libs/feature/map/src/lib/geocoding/geocoding.component.ts +0 -112
  123. package/src/libs/feature/map/src/lib/geocoding.service.ts +0 -57
  124. package/src/libs/feature/map/src/lib/style/index.ts +0 -2
  125. package/src/libs/feature/map/src/lib/style/map-style.fixtures.ts +0 -47
  126. package/src/libs/feature/map/src/lib/style/map-style.service.ts +0 -108
  127. package/src/libs/util/data-fetcher/src/lib/readers/base-cache.ts +0 -14
  128. /package/src/libs/feature/{map/src/lib/geocoding/geocoding.component.css → editor/src/lib/components/record-form/form-field/form-field-associated-records/form-field-associated-records.component.css} +0 -0
@@ -164,69 +164,49 @@ As such, **it is not very interesting at all.**`,
164
164
  extras: {
165
165
  isPublishedToAll: true,
166
166
  edit: true,
167
- featureTypes: [
168
- {
169
- attributeTable: [
170
- {
171
- code: 'OBJECTID',
172
- name: 'OBJECTID',
173
- link: '',
174
- definition: 'Object identifier',
175
- type: 'OID',
176
- },
177
- {
178
- code: 'NOM',
179
- name: 'Nom',
180
- link: '',
181
- definition: 'Nom de la rue',
182
- type: 'String (48)',
183
- values: [
184
- {
185
- code: 'Pomme',
186
- label: 'Les Pommiers',
187
- description: 'Sous les pommiers',
188
- },
189
- {
190
- code: 'Cotton',
191
- label: 'Rue Cotton',
192
- description: 'Rue Cotton',
193
- },
194
- { code: "Passage de l'échiquier" },
195
- { description: 'Rue du Charlieu' },
196
- ],
197
- },
198
- {
199
- code: 'RUE',
200
- name: 'Rue',
201
- link: '',
202
- definition: '',
203
- type: 'String (50)',
204
- },
205
- ],
206
- code: '',
207
- aliases: '',
208
- typeName: "Catalogue d'attributs N°1",
209
- definition: 'Définition du catalogue d attributs N°1',
210
- isAbstract: 'false',
211
- },
212
- {
213
- attributeTable: [
214
- {
215
- code: 'UniqueObject',
216
- name: 'unique object ',
217
- link: '',
218
- definition: 'this is the only object of this catalog',
219
- type: 'String (50)',
220
- },
221
- ],
222
- code: '',
223
- aliases: '',
224
- typeName: "Catalogue d'attributs N°2",
225
- definition: 'Définition du catalogue d attributs N°2',
226
- isAbstract: 'false',
227
- },
228
- ],
229
167
  },
168
+ featureTypeDescriptions: [
169
+ {
170
+ name: "Catalogue d'attributs N°1",
171
+ description: 'Définition du catalogue d attributs N°1',
172
+ attributes: [
173
+ {
174
+ name: 'OBJECTID',
175
+ description: 'Object identifier',
176
+ code: 'OBJECTID',
177
+ type: 'OID',
178
+ },
179
+ {
180
+ name: 'Nom',
181
+ description: 'Nom de la rue',
182
+ code: 'NOM',
183
+ type: 'String (48)',
184
+ values: [
185
+ { value: 'Pomme', label: 'Les Pommiers' },
186
+ { value: 'Cotton', label: 'Rue Cotton' },
187
+ { value: "Passage de l'échiquier" },
188
+ ],
189
+ },
190
+ {
191
+ name: 'Rue',
192
+ code: 'RUE',
193
+ type: 'String (50)',
194
+ },
195
+ ],
196
+ },
197
+ {
198
+ name: "Catalogue d'attributs N°2",
199
+ description: 'Définition du catalogue d attributs N°2',
200
+ attributes: [
201
+ {
202
+ name: 'unique object ',
203
+ description: 'this is the only object of this catalog',
204
+ code: 'UniqueObject',
205
+ type: 'String (50)',
206
+ },
207
+ ],
208
+ },
209
+ ],
230
210
  },
231
211
  {
232
212
  uniqueIdentifier: '7d002c4c-92ef-4b9f-a568-d732f740b99e',
@@ -2,9 +2,9 @@ import {
2
2
  ChangeDetectionStrategy,
3
3
  ChangeDetectorRef,
4
4
  Component,
5
+ inject,
5
6
  Input,
6
7
  Output,
7
- inject,
8
8
  } from '@angular/core'
9
9
  import { marker } from '@biesbjerg/ngx-translate-extract-marker'
10
10
  import {
@@ -183,7 +183,7 @@ export class ChartViewComponent {
183
183
  yChoices$ = this.properties$.pipe(
184
184
  map((properties) =>
185
185
  properties
186
- .filter((prop) => prop.type === 'number' || prop.type === 'date')
186
+ .filter((prop) => prop.type === 'number')
187
187
  .map((prop) => ({ value: prop.name, label: prop.label || prop.name }))
188
188
  ),
189
189
  tap((choices) => {
@@ -222,6 +222,7 @@ export class ChartViewComponent {
222
222
  switchMap(([dataset, xProp, yProp, aggregation]) => {
223
223
  const fieldAgg: FieldAggregation =
224
224
  aggregation === 'count' ? ['count'] : [aggregation, yProp]
225
+ this.loading = true
225
226
  return dataset
226
227
  .groupBy(['distinct', xProp])
227
228
  .aggregate(fieldAgg)
@@ -2,11 +2,11 @@ import {
2
2
  ChangeDetectionStrategy,
3
3
  ChangeDetectorRef,
4
4
  Component,
5
+ inject,
5
6
  Input,
6
7
  OnDestroy,
7
8
  OnInit,
8
9
  ViewChild,
9
- inject,
10
10
  } from '@angular/core'
11
11
  import {
12
12
  DataTableComponent,
@@ -75,6 +75,7 @@ export class GeoTableViewComponent implements OnInit, OnDestroy {
75
75
  }
76
76
 
77
77
  private async initMapContext(): Promise<MapContext> {
78
+ this.dataset.load()
78
79
  this.dataset.selectAll()
79
80
  return {
80
81
  layers: [
@@ -1,15 +1,15 @@
1
- import { Injectable, inject } from '@angular/core'
1
+ import { inject, Injectable } from '@angular/core'
2
2
  import { marker } from '@biesbjerg/ngx-translate-extract-marker'
3
3
  import {
4
+ GetCollectionItemsOptions,
4
5
  OgcApiCollectionInfo,
5
6
  OgcApiEndpoint,
6
7
  OgcApiRecord,
7
- WfsEndpoint,
8
- WfsVersion,
9
- TmsEndpoint,
10
8
  StacEndpoint,
11
- GetCollectionItemsOptions,
12
9
  StacItemsDocument,
10
+ TmsEndpoint,
11
+ WfsEndpoint,
12
+ WfsVersion,
13
13
  } from '@camptocamp/ogc-client'
14
14
  import {
15
15
  BaseReader,
@@ -26,7 +26,7 @@ import {
26
26
  } from '../../../../../../libs/util/shared/src'
27
27
  import type { FeatureCollection } from 'geojson'
28
28
  import { from, Observable, throwError } from 'rxjs'
29
- import { catchError, map, switchMap, tap } from 'rxjs/operators'
29
+ import { catchError, map, switchMap } from 'rxjs/operators'
30
30
  import {
31
31
  DatasetOnlineResource,
32
32
  DatasetServiceDistribution,
@@ -209,7 +209,8 @@ export class DataService {
209
209
  ogcApiLink: DatasetServiceDistribution
210
210
  ): Promise<DatasetOnlineResource[]> {
211
211
  const collectionInfo = await this.getDownloadUrlsFromOgcApi(
212
- ogcApiLink.url.href
212
+ ogcApiLink.url.href,
213
+ ogcApiLink.name
213
214
  )
214
215
 
215
216
  return Object.keys(collectionInfo.bulkDownloadLinks).map((mimeType) => {
@@ -229,31 +230,41 @@ export class DataService {
229
230
  })
230
231
  }
231
232
 
232
- async getDownloadUrlsFromOgcApi(url: string): Promise<OgcApiCollectionInfo> {
233
+ private async getTargetCollectionId(
234
+ endpoint: OgcApiEndpoint,
235
+ collectionId?: string
236
+ ): Promise<string | undefined> {
237
+ const collections = await endpoint.featureCollections
238
+ return collections.length === 1 && !collectionId
239
+ ? collections[0]
240
+ : collectionId
241
+ }
242
+
243
+ async getDownloadUrlsFromOgcApi(
244
+ url: string,
245
+ collectionId?: string
246
+ ): Promise<OgcApiCollectionInfo> {
233
247
  const endpoint = new OgcApiEndpoint(url)
234
- return await endpoint.featureCollections
235
- .then((collections) => {
236
- return endpoint.getCollectionInfo(collections[0])
237
- })
238
- .catch((error) => {
239
- throw new Error(`ogc.unreachable.unknown`)
240
- })
248
+ try {
249
+ const id = await this.getTargetCollectionId(endpoint, collectionId)
250
+ return await endpoint.getCollectionInfo(id)
251
+ } catch {
252
+ throw new Error('ogc.unreachable.unknown')
253
+ }
241
254
  }
242
255
 
243
256
  async getItemsFromOgcApi(
244
257
  url: string,
258
+ collectionId?: string,
245
259
  limit?: number
246
260
  ): Promise<OgcApiRecord[]> {
247
261
  const endpoint = new OgcApiEndpoint(url)
248
- return await endpoint.featureCollections
249
- .then((collections) => {
250
- return collections.length
251
- ? endpoint.getCollectionItems(collections[0], limit)
252
- : null
253
- })
254
- .catch(() => {
255
- throw new Error(`ogc.unreachable.unknown`)
256
- })
262
+ try {
263
+ const id = await this.getTargetCollectionId(endpoint, collectionId)
264
+ return await endpoint.getCollectionItems(id, limit)
265
+ } catch {
266
+ throw new Error('ogc.unreachable.unknown')
267
+ }
257
268
  }
258
269
 
259
270
  async getItemsFromStacApi(
@@ -347,14 +358,11 @@ export class DataService {
347
358
  if (link.type === 'service' && link.accessServiceProtocol === 'wfs') {
348
359
  const wfsUrlEndpoint = this.proxy.getProxiedUrl(link.url.toString())
349
360
  return from(
350
- openDataset(
351
- wfsUrlEndpoint,
352
- 'wfs',
353
- {
354
- wfsFeatureType: link.name,
355
- },
356
- cacheActive
357
- )
361
+ openDataset(wfsUrlEndpoint, {
362
+ typeHint: 'wfs',
363
+ wfsFeatureType: link.name,
364
+ enableCache: cacheActive,
365
+ })
358
366
  )
359
367
  } else if (link.type === 'download') {
360
368
  const linkProxifiedUrl = this.proxy.getProxiedUrl(link.url.toString())
@@ -364,7 +372,10 @@ export class DataService {
364
372
  ? (format as SupportedType)
365
373
  : undefined
366
374
  return from(
367
- openDataset(linkProxifiedUrl, supportedType, undefined, cacheActive)
375
+ openDataset(linkProxifiedUrl, {
376
+ typeHint: supportedType,
377
+ enableCache: cacheActive,
378
+ })
368
379
  ).pipe()
369
380
  } else if (
370
381
  link.type === 'service' &&
@@ -374,12 +385,16 @@ export class DataService {
374
385
  link.url.toString(),
375
386
  'geojson'
376
387
  )
377
- return from(openDataset(url, 'geojson', undefined, cacheActive)).pipe()
388
+ return from(
389
+ openDataset(url, { typeHint: 'geojson', enableCache: cacheActive })
390
+ ).pipe()
378
391
  } else if (
379
392
  link.type === 'service' &&
380
393
  link.accessServiceProtocol === 'ogcFeatures'
381
394
  ) {
382
- return from(this.getDownloadUrlsFromOgcApi(link.url.href)).pipe(
395
+ return from(
396
+ this.getDownloadUrlsFromOgcApi(link.url.href, link.name)
397
+ ).pipe(
383
398
  switchMap((collectionInfo) => {
384
399
  const isMimeTypeJson = (mimeType: string): boolean => {
385
400
  return mimeType.toLowerCase().indexOf('json') > -1
@@ -395,12 +410,10 @@ export class DataService {
395
410
  }
396
411
  const urlWithoutLimit = new URL(geojsonUrl)
397
412
  urlWithoutLimit.searchParams.delete('limit')
398
- return openDataset(
399
- urlWithoutLimit.toString(),
400
- 'geojson',
401
- undefined,
402
- cacheActive
403
- )
413
+ return openDataset(urlWithoutLimit.toString(), {
414
+ typeHint: 'geojson',
415
+ enableCache: cacheActive,
416
+ })
404
417
  })
405
418
  )
406
419
  }
@@ -0,0 +1,33 @@
1
+ <div class="flex flex-col gap-2">
2
+ <gn-ui-check-toggle
3
+ [label]="'editor.record.form.field.parentLink' | translate"
4
+ [value]="hasParentLink"
5
+ (toggled)="onToggle($event)"
6
+ data-cy="associated-record-toggle"
7
+ ></gn-ui-check-toggle>
8
+ @if (hasParentLink) {
9
+ <gn-ui-form-field-wrapper
10
+ [label]="'editor.record.form.field.parentIdentifier' | translate"
11
+ >
12
+ <gn-ui-text-input
13
+ [value]="uniqueIdentifier"
14
+ (valueChange)="onUniqueIdentifierChange($event)"
15
+ placeholder=""
16
+ data-cy="associated-record-identifier"
17
+ ></gn-ui-text-input>
18
+ </gn-ui-form-field-wrapper>
19
+ <gn-ui-form-field-wrapper
20
+ [label]="'editor.record.form.field.parentType' | translate"
21
+ >
22
+ <gn-ui-dropdown-selector
23
+ [title]="'editor.record.form.field.parentType' | translate"
24
+ [showTitle]="false"
25
+ [choices]="choices"
26
+ [selected]="selectedType"
27
+ (selectValue)="onTypeChange($event)"
28
+ [extraBtnClass]="'input-as-button gn-ui-text-input'"
29
+ data-cy="associated-record-type"
30
+ ></gn-ui-dropdown-selector>
31
+ </gn-ui-form-field-wrapper>
32
+ }
33
+ </div>
@@ -0,0 +1,94 @@
1
+ import {
2
+ ChangeDetectionStrategy,
3
+ Component,
4
+ EventEmitter,
5
+ Input,
6
+ Output,
7
+ } from '@angular/core'
8
+ import {
9
+ AssociatedRecord,
10
+ AssociationType,
11
+ associationTypeValues,
12
+ } from '../../../../../../../../../libs/common/domain/src/lib/model/record'
13
+ import {
14
+ CheckToggleComponent,
15
+ DropdownChoice,
16
+ DropdownSelectorComponent,
17
+ TextInputComponent,
18
+ } from '../../../../../../../../../libs/ui/inputs/src'
19
+ import { FormFieldWrapperComponent } from '../../../../../../../../../libs/ui/layout/src'
20
+ import { TranslatePipe } from '@ngx-translate/core'
21
+
22
+ @Component({
23
+ selector: 'gn-ui-form-field-associated-records',
24
+ templateUrl: './form-field-associated-records.component.html',
25
+ styleUrls: ['./form-field-associated-records.component.css'],
26
+ changeDetection: ChangeDetectionStrategy.OnPush,
27
+ standalone: true,
28
+ imports: [
29
+ CheckToggleComponent,
30
+ TextInputComponent,
31
+ DropdownSelectorComponent,
32
+ FormFieldWrapperComponent,
33
+ TranslatePipe,
34
+ ],
35
+ })
36
+ export class FormFieldAssociatedRecordsComponent {
37
+ @Input() value?: AssociatedRecord[]
38
+ @Output() valueChange = new EventEmitter<AssociatedRecord[]>()
39
+
40
+ private get list() {
41
+ return this.value ?? []
42
+ }
43
+ private get first() {
44
+ return this.list[0]
45
+ }
46
+ private get rest() {
47
+ return this.list.slice(1)
48
+ }
49
+
50
+ get hasParentLink() {
51
+ return this.list.length > 0
52
+ }
53
+ get uniqueIdentifier() {
54
+ return this.first?.uniqueIdentifier ?? ''
55
+ }
56
+ get selectedType() {
57
+ return this.first?.associationType
58
+ }
59
+ get choices(): DropdownChoice[] {
60
+ return associationTypeValues.map((value) => ({
61
+ value,
62
+ label: `domain.record.associationType.${value}`,
63
+ }))
64
+ }
65
+
66
+ onToggle(checked: boolean) {
67
+ this.valueChange.emit(
68
+ checked
69
+ ? [
70
+ {
71
+ uniqueIdentifier: '',
72
+ associationType: associationTypeValues[0],
73
+ },
74
+ ...this.rest,
75
+ ]
76
+ : this.rest
77
+ )
78
+ }
79
+
80
+ onUniqueIdentifierChange(uniqueIdentifier: string) {
81
+ this.valueChange.emit(
82
+ uniqueIdentifier
83
+ ? [{ ...this.first, uniqueIdentifier }, ...this.rest]
84
+ : this.rest
85
+ )
86
+ }
87
+
88
+ onTypeChange(associationType: DropdownChoice['value']) {
89
+ this.valueChange.emit([
90
+ { ...this.first, associationType: associationType as AssociationType },
91
+ ...this.rest,
92
+ ])
93
+ }
94
+ }
@@ -1,17 +1,21 @@
1
- <div class="flex gap-2 mb-2">
2
- @for (addOption of addOptions$ | async; track addOption) {
3
- <gn-ui-button type="gray" (buttonClick)="onAdd(addOption.eventName)">
4
- <ng-icon name="iconoirPlus" class="text-primary"></ng-icon> &nbsp;{{
5
- addOption.buttonLabel
6
- }}</gn-ui-button
7
- >
1
+ <div class="flex flex-col gap-2">
2
+ <div class="flex gap-2">
3
+ @for (addOption of addOptions$ | async; track addOption) {
4
+ <gn-ui-button type="gray" (buttonClick)="onAdd(addOption.eventName)">
5
+ <ng-icon name="iconoirPlus" class="text-primary"></ng-icon> &nbsp;{{
6
+ addOption.buttonLabel
7
+ }}</gn-ui-button
8
+ >
9
+ }
10
+ </div>
11
+ @if (extents.length) {
12
+ <gn-ui-sortable-list
13
+ [items]="extents"
14
+ (itemsOrderChange)="onItemsOrderChange($event)"
15
+ [elementTemplate]="template"
16
+ ></gn-ui-sortable-list>
8
17
  }
9
18
  </div>
10
- <gn-ui-sortable-list
11
- [items]="extents"
12
- (itemsOrderChange)="onItemsOrderChange($event)"
13
- [elementTemplate]="template"
14
- ></gn-ui-sortable-list>
15
19
  <ng-template #template let-extent let-index="index">
16
20
  @if (extent.end === undefined) {
17
21
  <div class="border rounded-lg px-4 pb-4">
@@ -98,6 +98,12 @@
98
98
  [recordKind]="recordKind"
99
99
  ></gn-ui-form-field-update-frequency>
100
100
  </ng-container>
101
+ <ng-container *ngSwitchCase="'associatedRecords'">
102
+ <gn-ui-form-field-associated-records
103
+ [value]="valueAsAssociatedRecords"
104
+ (valueChange)="valueChange.emit($event)"
105
+ ></gn-ui-form-field-associated-records>
106
+ </ng-container>
101
107
  <ng-container *ngSwitchCase="'temporalExtents'">
102
108
  <gn-ui-form-field-temporal-extents
103
109
  [value]="valueAsTemporalExtents"
@@ -11,6 +11,7 @@ import {
11
11
  } from '@angular/core'
12
12
  import { MatTooltipModule } from '@angular/material/tooltip'
13
13
  import {
14
+ AssociatedRecord,
14
15
  CatalogRecordKeys,
15
16
  Constraint,
16
17
  DatasetTemporalExtent,
@@ -39,6 +40,7 @@ import {
39
40
  FormFieldComponentName,
40
41
  FormFieldConfig,
41
42
  } from '../../../models'
43
+ import { FormFieldAssociatedRecordsComponent } from './form-field-associated-records/form-field-associated-records.component'
42
44
  import { FormFieldContactsForResourceComponent } from './form-field-contacts-for-resource/form-field-contacts-for-resource.component'
43
45
  import { FormFieldContactsComponent } from './form-field-contacts/form-field-contacts.component'
44
46
  import { FormFieldKeywordsComponent } from './form-field-keywords/form-field-keywords.component'
@@ -70,6 +72,7 @@ import { FormFieldTopicsComponent } from './form-field-topics/form-field-topics.
70
72
  FormFieldLicenseComponent,
71
73
  FormFieldDateComponent,
72
74
  FormFieldUpdateFrequencyComponent,
75
+ FormFieldAssociatedRecordsComponent,
73
76
  FormFieldTemporalExtentsComponent,
74
77
  FormFieldSimpleComponent,
75
78
  FormFieldRichComponent,
@@ -140,6 +143,9 @@ export class FormFieldComponent {
140
143
  get valueAsUpdateFrequency() {
141
144
  return this.value as UpdateFrequency
142
145
  }
146
+ get valueAsAssociatedRecords() {
147
+ return this.value as Array<AssociatedRecord>
148
+ }
143
149
  get valueAsTemporalExtents() {
144
150
  return this.value as Array<DatasetTemporalExtent>
145
151
  }
@@ -134,6 +134,11 @@ export const DEFAULT_CONFIGURATION: EditorConfig = {
134
134
  },
135
135
  hidden: '${record.kind == "service"}',
136
136
  },
137
+ {
138
+ model: 'associatedRecords',
139
+ formFieldConfig: {},
140
+ hidden: '${record.kind == "service"}',
141
+ },
137
142
  ],
138
143
  },
139
144
 
@@ -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) {
@@ -33,7 +33,6 @@ import { RecordsRepositoryInterface } from '../../../../../../libs/common/domain
33
33
  import { NOT_KNOWN_CONSTRAINT } from '../../../../../../libs/feature/editor/src'
34
34
  import { NotificationsService } from '../../../../../../libs/feature/notifications/src'
35
35
  import { ButtonComponent, TextInputComponent } from '../../../../../../libs/ui/inputs/src'
36
- import { getOptionalEditorConfig } from '../../../../../../libs/util/app-config/src'
37
36
  import { NgIcon, provideIcons, provideNgIconsConfig } from '@ng-icons/core'
38
37
  import { iconoirAppWindow, iconoirPlusCircle } from '@ng-icons/iconoir'
39
38
  import { matCloseOutline } from '@ng-icons/material-icons/outline'
@@ -48,6 +47,10 @@ marker('notify.reuse.form.error.title')
48
47
  marker('notify.reuse.form.error.body')
49
48
 
50
49
  export const REUSE_FORM_URL = new InjectionToken<string>('reuseFormUrl')
50
+ // language assigned to the created reuse record; falls back to the current UI language
51
+ export const NEW_RECORD_DEFAULT_LANGUAGE = new InjectionToken<string>(
52
+ 'newRecordDefaultLanguage'
53
+ )
51
54
 
52
55
  @Component({
53
56
  selector: 'gn-ui-notify-reuse-form',
@@ -86,6 +89,9 @@ export class NotifyReuseFormComponent implements OnDestroy {
86
89
  private readonly notificationsService = inject(NotificationsService)
87
90
  private readonly platformServiceInterface = inject(PlatformServiceInterface)
88
91
  reuseFormUrl = inject(REUSE_FORM_URL, { optional: true })
92
+ private newRecordDefaultLanguage = inject(NEW_RECORD_DEFAULT_LANGUAGE, {
93
+ optional: true,
94
+ })
89
95
  private locationStrategy = inject(LocationStrategy)
90
96
 
91
97
  me = toSignal(this.platformServiceInterface.getMe())
@@ -158,8 +164,7 @@ export class NotifyReuseFormComponent implements OnDestroy {
158
164
  submit() {
159
165
  if (!this.isFormValid()) return
160
166
  const defaultLang =
161
- getOptionalEditorConfig()?.NEW_RECORD_DEFAULT_LANGUAGE ??
162
- this.translate.currentLang
167
+ this.newRecordDefaultLanguage ?? this.translate.currentLang
163
168
  const onlineResource: OnlineLinkResource = {
164
169
  type: 'link',
165
170
  url: new URL(this.url()),
@@ -3,6 +3,7 @@ import { createAction, props } from '@ngrx/store'
3
3
  import {
4
4
  CatalogRecord,
5
5
  DatasetFeatureCatalog,
6
+ LinkedRecord,
6
7
  UserFeedback,
7
8
  } from '../../../../../../libs/common/domain/src/lib/model/record'
8
9
 
@@ -54,14 +55,9 @@ export const setRelated = createAction(
54
55
  props<{ related: CatalogRecord[] }>()
55
56
  )
56
57
 
57
- export const setSources = createAction(
58
- '[Metadata view] Set sources',
59
- props<{ sources: CatalogRecord[] }>()
60
- )
61
-
62
- export const setSourceOf = createAction(
63
- '[Metadata view] Set has sources',
64
- props<{ sourceOf: CatalogRecord[] }>()
58
+ export const setLinkedRecords = createAction(
59
+ '[Metadata view] Set associated records',
60
+ props<{ linkedRecords: LinkedRecord[] }>()
65
61
  )
66
62
 
67
63
  /*