geonetwork-ui 2.10.0-dev.88fb568d7 → 2.10.0-dev.89f0dfe6f

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 (90) hide show
  1. package/fesm2022/geonetwork-ui.mjs +520 -127
  2. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  3. package/index.d.ts +107 -29
  4. package/index.d.ts.map +1 -1
  5. package/package.json +2 -2
  6. package/src/libs/api/metadata-converter/src/lib/dcat-ap/dcat-ap.converter.ts +9 -0
  7. package/src/libs/api/metadata-converter/src/lib/fixtures/eu.dcat-ap.records.ts +2 -0
  8. package/src/libs/api/metadata-converter/src/lib/fixtures/generic.records.ts +1 -0
  9. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.reuse+ongules.ts +7 -0
  10. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.reuse+roilaye.ts +1 -0
  11. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.ts +1 -0
  12. package/src/libs/api/metadata-converter/src/lib/fixtures/geocat-ch.records.ts +1 -0
  13. package/src/libs/api/metadata-converter/src/lib/fixtures/georhena.records.ts +1 -0
  14. package/src/libs/api/metadata-converter/src/lib/fixtures/metadata-for-i18n.records.ts +2 -0
  15. package/src/libs/api/metadata-converter/src/lib/fixtures/metawal.records.ts +1 -0
  16. package/src/libs/api/metadata-converter/src/lib/fixtures/opendataswiss.records.ts +1 -0
  17. package/src/libs/api/metadata-converter/src/lib/fixtures/sextant.records.ts +2 -0
  18. package/src/libs/api/metadata-converter/src/lib/fixtures/vlaanderen.dcat-ap.records.ts +1 -0
  19. package/src/libs/api/metadata-converter/src/lib/fixtures/wallonie.records.reuse.ts +8 -0
  20. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.converter.ts +1 -0
  21. package/src/libs/api/metadata-converter/src/lib/iso19115-3/iso19115-3.converter.ts +7 -0
  22. package/src/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.ts +8 -0
  23. package/src/libs/api/metadata-converter/src/lib/iso19115-3/write-parts.ts +8 -0
  24. package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +11 -0
  25. package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +33 -0
  26. package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +33 -0
  27. package/src/libs/api/repository/src/lib/gn4/auth/auth.service.ts +4 -0
  28. package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +14 -2
  29. package/src/libs/api/repository/src/lib/gn4/gn4.provider.ts +6 -1
  30. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +11 -0
  31. package/src/libs/common/fixtures/src/lib/records.fixtures.ts +7 -0
  32. package/src/libs/feature/editor/src/lib/+state/editor.actions.ts +6 -0
  33. package/src/libs/feature/editor/src/lib/+state/editor.effects.ts +0 -1
  34. package/src/libs/feature/editor/src/lib/+state/editor.facade.ts +10 -1
  35. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.css +0 -0
  36. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.html +67 -0
  37. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.ts +39 -0
  38. package/src/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.html +18 -3
  39. package/src/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.ts +33 -40
  40. package/src/libs/feature/editor/src/lib/components/record-form/form-field/field-focus.directive.ts +38 -0
  41. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-constraints-shortcuts/form-field-constraints-shortcuts.component.ts +33 -34
  42. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.html +13 -13
  43. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.ts +18 -4
  44. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.html +8 -7
  45. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.ts +6 -6
  46. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-single-link-resource/form-field-online-single-link-resource.component.css +0 -0
  47. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-single-link-resource/form-field-online-single-link-resource.component.html +5 -0
  48. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-single-link-resource/form-field-online-single-link-resource.component.ts +89 -0
  49. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.css +37 -0
  50. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +1 -0
  51. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +5 -0
  52. package/src/libs/feature/editor/src/lib/components/record-form/form-field/index.ts +1 -0
  53. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.ts +59 -3
  54. package/src/libs/feature/editor/src/lib/models/editor-config.model.ts +4 -0
  55. package/src/libs/feature/notify-reuse/src/index.ts +1 -0
  56. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.css +0 -0
  57. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.html +1 -0
  58. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.ts +21 -0
  59. package/src/libs/ui/elements/src/index.ts +2 -0
  60. package/src/libs/ui/elements/src/lib/contact-details/contact-details.component.html +96 -0
  61. package/src/libs/ui/elements/src/lib/contact-details/contact-details.component.ts +45 -0
  62. package/src/libs/ui/elements/src/lib/contact-pill/contact-pill.component.html +37 -0
  63. package/src/libs/ui/elements/src/lib/contact-pill/contact-pill.component.ts +70 -0
  64. package/src/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.html +1 -1
  65. package/src/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.ts +0 -1
  66. package/src/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.ts +2 -5
  67. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.css +0 -4
  68. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +28 -67
  69. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts +30 -10
  70. package/src/libs/ui/inputs/src/lib/button/button.component.ts +4 -0
  71. package/src/libs/ui/inputs/src/lib/url-input/url-input.component.html +2 -2
  72. package/src/libs/ui/inputs/src/lib/url-input/url-input.component.ts +2 -1
  73. package/src/libs/ui/map/src/lib/components/map-container/map-container.component.ts +2 -1
  74. package/src/libs/ui/map/src/lib/components/spatial-extent/spatial-extent.component.ts +11 -10
  75. package/src/libs/ui/search/src/lib/record-preview-row/record-preview-row.component.html +0 -1
  76. package/src/libs/util/app-config/src/lib/app-config.ts +36 -0
  77. package/src/libs/util/app-config/src/lib/model.ts +4 -0
  78. package/src/libs/util/app-config/src/lib/parse-utils.ts +23 -1
  79. package/src/libs/util/shared/src/lib/record/quality-score.util.ts +33 -18
  80. package/src/libs/util/shared/src/lib/utils/index.ts +1 -0
  81. package/src/libs/util/shared/src/lib/utils/user-display.ts +32 -0
  82. package/tailwind.base.css +11 -2
  83. package/translations/de.json +10 -1
  84. package/translations/en.json +10 -1
  85. package/translations/es.json +10 -1
  86. package/translations/fr.json +10 -1
  87. package/translations/it.json +10 -1
  88. package/translations/nl.json +10 -1
  89. package/translations/pt.json +10 -1
  90. package/translations/sk.json +10 -1
@@ -82,6 +82,7 @@ export const SEXTANT_BATHYMETRY_DATASET_RECORD: DatasetRecord = {
82
82
  Le pas de la grille est de 25m.
83
83
  Produit interne Ifremer.`,
84
84
  lineage: '',
85
+ sourceRecords: [],
85
86
  ownerOrganization: null,
86
87
  recordUpdated: new Date('2020-06-03T22:34:05.000Z'),
87
88
  resourceUpdated: new Date('2020-06-03T22:34:05.000Z'),
@@ -322,6 +323,7 @@ export const SEXTANT_CURRENTS_DATASET_RECORD: CatalogRecord = {
322
323
  ],
323
324
  lineage:
324
325
  'Sallée, J.B.; Speer, K and Morrow, R. Southern Ocean fronts and their variability to climate modes, Journ. of Climate, 2008, Vol. 21(12), pp. 3020-3039',
326
+ sourceRecords: [],
325
327
  onlineResources: [
326
328
  {
327
329
  name: 'Digital Object Identifier (DOI)',
@@ -68,6 +68,7 @@ export const VLAANDEREN_DATASET_RECORD: DatasetRecord = {
68
68
  temporalExtents: [],
69
69
  topics: ['biodiversity'],
70
70
  lineage: '',
71
+ sourceRecords: [],
71
72
  recordUpdated: new Date('2024-09-19T01:15:09.732Z'),
72
73
  resourceUpdated: new Date('2021-04-14T11:15+02:00'),
73
74
  status: 'completed',
@@ -447,5 +447,13 @@ export const WALLONIE_REUSE_SPW_RECORD: ReuseRecord = {
447
447
  'https://metawal.wallonie.be/geonetwork/srv/api/records/83809bcd-1763-4d28-b820-2b9828083ba5'
448
448
  ),
449
449
  lineage: "L'application a été développée sur base de l'API GeoViewer",
450
+ sourceRecords: [
451
+ {
452
+ uuid: 'ee965118-2416-4d48-b07e-bbc696f002c2',
453
+ title:
454
+ 'SCoT (Schéma de cohérence territoriale) en région Hauts-de-France',
455
+ href: 'https://metawal.wallonie.be/geonetwork/srv/api/records/ee965118-2416-4d48-b07e-bbc696f002c2',
456
+ },
457
+ ],
450
458
  temporalExtents: [],
451
459
  }
@@ -24,6 +24,7 @@ export class Gn4Converter extends BaseConverter<Gn4Record> {
24
24
  kind: 'dataset',
25
25
  status: null,
26
26
  lineage: null,
27
+ sourceRecords: [],
27
28
  recordUpdated: null,
28
29
  recordPublished: null,
29
30
  ownerOrganization: null,
@@ -9,6 +9,7 @@ import {
9
9
  readKind,
10
10
  readLandingPage,
11
11
  readLineage,
12
+ readSourceRecords,
12
13
  readOnlineResources,
13
14
  readOtherLanguages,
14
15
  readOwnerOrganization,
@@ -25,6 +26,7 @@ import {
25
26
  writeKind,
26
27
  writeLandingPage,
27
28
  writeLineage,
29
+ writeSourceRecords,
28
30
  writeOnlineResources,
29
31
  writeOtherLanguages,
30
32
  writeRecordCreated,
@@ -53,6 +55,7 @@ export class Iso191153Converter extends Iso19139Converter {
53
55
  this.readers['ownerOrganization'] = readOwnerOrganization
54
56
  this.readers['landingPage'] = readLandingPage
55
57
  this.readers['lineage'] = readLineage
58
+ this.readers['sourceRecords'] = readSourceRecords
56
59
  this.readers['onlineResources'] = readOnlineResources
57
60
  this.readers['defaultLanguage'] = readDefaultLanguage
58
61
  this.readers['otherLanguages'] = readOtherLanguages
@@ -72,6 +75,7 @@ export class Iso191153Converter extends Iso19139Converter {
72
75
  this.writers['ownerOrganization'] = () => undefined // fixme: find a way to store this value properly
73
76
  this.writers['landingPage'] = writeLandingPage
74
77
  this.writers['lineage'] = writeLineage
78
+ this.writers['sourceRecords'] = writeSourceRecords
75
79
  this.writers['onlineResources'] = writeOnlineResources
76
80
  this.writers['status'] = writeStatus
77
81
  this.writers['spatialRepresentation'] = writeSpatialRepresentation
@@ -167,6 +171,9 @@ export class Iso191153Converter extends Iso19139Converter {
167
171
  'gmd:fileName': 'mcc:fileName',
168
172
  'gmd:fileDescription': 'mcc:fileDescription',
169
173
 
174
+ // lineage sources
175
+ 'gmd:source': 'mrl:source',
176
+
170
177
  // no more URL elements
171
178
  'gmd:URL': 'gco:CharacterString',
172
179
  })
@@ -21,6 +21,7 @@ import {
21
21
  extractCharacterString,
22
22
  extractDatasetOnlineResources,
23
23
  extractDateTime,
24
+ extractSourceRecords,
24
25
  extractLocalizedCharacterString,
25
26
  extractReuseOnlineResources,
26
27
  extractRole,
@@ -31,6 +32,7 @@ import {
31
32
  import {
32
33
  Individual,
33
34
  LanguageCode,
35
+ SourceRecord,
34
36
  OnlineResource,
35
37
  Organization,
36
38
  OrganizationTranslations,
@@ -291,6 +293,12 @@ export function readLineage(
291
293
  )(rootEl)
292
294
  }
293
295
 
296
+ export function readSourceRecords(rootEl: XmlElement): SourceRecord[] {
297
+ return extractSourceRecords(
298
+ pipe(findNestedElement('mdb:resourceLineage', 'mrl:LI_Lineage'))(rootEl)
299
+ )
300
+ }
301
+
294
302
  function extractDateInfo(
295
303
  type: 'creation' | 'revision' | 'publication'
296
304
  ): ChainableFunction<XmlElement, Date> {
@@ -34,6 +34,7 @@ import {
34
34
  } from '../function-utils'
35
35
  import {
36
36
  appendKeywords,
37
+ appendSourceRecords,
37
38
  appendOnlineResource,
38
39
  appendServiceOnlineResources,
39
40
  createDistributionInfo,
@@ -564,3 +565,10 @@ export function writeOtherLanguages(record: DatasetRecord, rootEl: XmlElement) {
564
565
  )
565
566
  )(rootEl)
566
567
  }
568
+
569
+ export function writeSourceRecords(record: DatasetRecord, rootEl: XmlElement) {
570
+ pipe(
571
+ findNestedChildOrCreate('mdb:resourceLineage', 'mrl:LI_Lineage'),
572
+ appendSourceRecords(record.sourceRecords)
573
+ )(rootEl)
574
+ }
@@ -30,6 +30,7 @@ import {
30
30
  readLegalConstraints,
31
31
  readLicenses,
32
32
  readLineage,
33
+ readSourceRecords,
33
34
  readOnlineResources,
34
35
  readOtherConstraints,
35
36
  readOtherLanguages,
@@ -62,6 +63,7 @@ import {
62
63
  writeLegalConstraints,
63
64
  writeLicenses,
64
65
  writeLineage,
66
+ writeSourceRecords,
65
67
  writeOnlineResources,
66
68
  writeOtherConstraints,
67
69
  writeRecordUpdated,
@@ -112,6 +114,7 @@ export class Iso19139Converter extends BaseConverter<string> {
112
114
  spatialRepresentation: readSpatialRepresentation,
113
115
  overviews: readOverviews,
114
116
  lineage: readLineage,
117
+ sourceRecords: readSourceRecords,
115
118
  onlineResources: readOnlineResources,
116
119
  temporalExtents: readTemporalExtents,
117
120
  spatialExtents: readSpatialExtents,
@@ -153,6 +156,7 @@ export class Iso19139Converter extends BaseConverter<string> {
153
156
  spatialRepresentation: writeSpatialRepresentation,
154
157
  overviews: writeGraphicOverviews,
155
158
  lineage: writeLineage,
159
+ sourceRecords: writeSourceRecords,
156
160
  onlineResources: writeOnlineResources,
157
161
  temporalExtents: writeTemporalExtents,
158
162
  spatialExtents: writeSpatialExtents,
@@ -295,6 +299,7 @@ export class Iso19139Converter extends BaseConverter<string> {
295
299
  )
296
300
  const temporalExtents = this.readers['temporalExtents'](rootEl, tr)
297
301
  const lineage = this.readers['lineage'](rootEl, tr)
302
+ const sourceRecords = this.readers['sourceRecords'](rootEl, tr)
298
303
  const updateFrequency = this.readers['updateFrequency'](rootEl, tr)
299
304
 
300
305
  return this.afterRecordRead({
@@ -302,6 +307,7 @@ export class Iso19139Converter extends BaseConverter<string> {
302
307
  kind,
303
308
  status,
304
309
  lineage,
310
+ ...(sourceRecords && { sourceRecords }),
305
311
  ...(spatialRepresentation && { spatialRepresentation }),
306
312
  temporalExtents,
307
313
  updateFrequency,
@@ -309,6 +315,7 @@ export class Iso19139Converter extends BaseConverter<string> {
309
315
  } as DatasetRecord)
310
316
  } else if (kind === 'reuse') {
311
317
  const lineage = this.readers['lineage'](rootEl, tr)
318
+ const sourceRecords = this.readers['sourceRecords'](rootEl, tr)
312
319
  const temporalExtents = this.readers['temporalExtents'](rootEl, tr)
313
320
  const reuseType = this.readers['reuseType'](rootEl, tr)
314
321
 
@@ -316,6 +323,7 @@ export class Iso19139Converter extends BaseConverter<string> {
316
323
  ...this.readBaseRecord(rootEl, tr),
317
324
  kind,
318
325
  lineage,
326
+ ...(sourceRecords && { sourceRecords }),
319
327
  temporalExtents,
320
328
  reuseType,
321
329
  } as ReuseRecord)
@@ -411,7 +419,10 @@ export class Iso19139Converter extends BaseConverter<string> {
411
419
  this.writers['spatialExtents'](record, rootEl)
412
420
  ;(fieldChanged('lineage') || fieldChanged('translations')) &&
413
421
  this.writers['lineage'](record, rootEl)
422
+ fieldChanged('sourceRecords') &&
423
+ this.writers['sourceRecords'](record, rootEl)
414
424
  }
425
+
415
426
  fieldChanged('otherLanguages') &&
416
427
  this.writers['otherLanguages'](record, rootEl)
417
428
 
@@ -8,6 +8,7 @@ import {
8
8
  Keyword,
9
9
  KeywordTranslations,
10
10
  LanguageCode,
11
+ SourceRecord,
11
12
  ModelTranslations,
12
13
  OnlineLinkResource,
13
14
  OnlineResource,
@@ -894,6 +895,38 @@ export function readLineage(
894
895
  )(rootEl)
895
896
  }
896
897
 
898
+ export function extractSourceRecords(liLineageEl: XmlElement): SourceRecord[] {
899
+ if (!liLineageEl) return []
900
+ return pipe(
901
+ findChildrenElement('gmd:source', false),
902
+ mapArray((el) => {
903
+ const uuid = readAttribute('uuidref')(el)
904
+ const title = readAttribute('xlink:title')(el)
905
+ const href = readAttribute('xlink:href')(el)
906
+ if (!uuid && !title && !href) return null
907
+ return {
908
+ ...(uuid ? { uuid } : {}),
909
+ ...(title ? { title } : {}),
910
+ ...(href ? { href } : {}),
911
+ } as SourceRecord
912
+ }),
913
+ filterArray((s): s is SourceRecord => s !== null)
914
+ )(liLineageEl)
915
+ }
916
+
917
+ export function readSourceRecords(rootEl: XmlElement): SourceRecord[] {
918
+ return extractSourceRecords(
919
+ pipe(
920
+ findNestedElement(
921
+ 'gmd:dataQualityInfo',
922
+ 'gmd:DQ_DataQuality',
923
+ 'gmd:lineage',
924
+ 'gmd:LI_Lineage'
925
+ )
926
+ )(rootEl)
927
+ )
928
+ }
929
+
897
930
  export function readUpdateFrequency(rootEl: XmlElement): UpdateFrequency {
898
931
  return pipe(
899
932
  findIdentification(),
@@ -8,6 +8,7 @@ import {
8
8
  Individual,
9
9
  Keyword,
10
10
  LanguageCode,
11
+ SourceRecord,
11
12
  RecordStatus,
12
13
  RecordTranslations,
13
14
  ReuseRecord,
@@ -1231,6 +1232,38 @@ export function writeLineage(record: DatasetRecord, rootEl: XmlElement) {
1231
1232
  )(rootEl)
1232
1233
  }
1233
1234
 
1235
+ export function appendSourceRecords(
1236
+ sources: SourceRecord[]
1237
+ ): ChainableFunction<XmlElement, XmlElement> {
1238
+ return pipe(
1239
+ removeChildrenByName('gmd:source'),
1240
+ appendChildren(
1241
+ ...sources
1242
+ .filter((source) => source.uuid || source.title || source.href)
1243
+ .map((source) =>
1244
+ pipe(
1245
+ createElement('gmd:source'),
1246
+ source.uuid ? writeAttribute('uuidref', source.uuid) : noop,
1247
+ source.title ? writeAttribute('xlink:title', source.title) : noop,
1248
+ source.href ? writeAttribute('xlink:href', source.href) : noop
1249
+ )
1250
+ )
1251
+ )
1252
+ )
1253
+ }
1254
+
1255
+ export function writeSourceRecords(record: DatasetRecord, rootEl: XmlElement) {
1256
+ pipe(
1257
+ findNestedChildOrCreate(
1258
+ 'gmd:dataQualityInfo',
1259
+ 'gmd:DQ_DataQuality',
1260
+ 'gmd:lineage',
1261
+ 'gmd:LI_Lineage'
1262
+ ),
1263
+ appendSourceRecords(record.sourceRecords)
1264
+ )(rootEl)
1265
+ }
1266
+
1234
1267
  export function getServiceEndpointProtocol(endpoint: ServiceEndpoint): string {
1235
1268
  switch (endpoint.accessServiceProtocol.toLowerCase()) {
1236
1269
  case 'wfs':
@@ -44,6 +44,10 @@ export class AuthService {
44
44
  window.location.href
45
45
  ).toString()
46
46
  )
47
+ .replace(
48
+ '${current_path}',
49
+ this.location.prepareExternalUrl(this.location.path())
50
+ )
47
51
  .replace('${lang2}', toLang2(this.translateService.currentLang))
48
52
  .replace('${lang3}', toLang3(this.translateService.currentLang))
49
53
  }
@@ -3,7 +3,7 @@ import {
3
3
  HttpErrorResponse,
4
4
  HttpHeaders,
5
5
  } from '@angular/common/http'
6
- import { Injectable, inject } from '@angular/core'
6
+ import { Injectable, InjectionToken, inject } from '@angular/core'
7
7
  import {
8
8
  assertValidXml,
9
9
  findConverterForDocument,
@@ -57,6 +57,10 @@ const TEMPORARY_ID_PREFIX = 'TEMP-ID-'
57
57
 
58
58
  export type RecordAsXml = string
59
59
 
60
+ export const DISABLE_DRAFT = new InjectionToken<boolean>('gnDisableDraft', {
61
+ factory: () => false,
62
+ })
63
+
60
64
  @Injectable()
61
65
  export class Gn4Repository implements RecordsRepositoryInterface {
62
66
  private httpClient = inject(HttpClient)
@@ -67,6 +71,7 @@ export class Gn4Repository implements RecordsRepositoryInterface {
67
71
  private platformService = inject(PlatformServiceInterface)
68
72
  private gn4LanguagesApi = inject(LanguagesApiService)
69
73
  private settingsService = inject(Gn4SettingsService)
74
+ private disableDraft = inject(DISABLE_DRAFT, { optional: true }) ?? false
70
75
 
71
76
  _draftsChanged = new Subject<void>()
72
77
  draftsChanged$ = this._draftsChanged.asObservable()
@@ -350,7 +355,9 @@ export class Gn4Repository implements RecordsRepositoryInterface {
350
355
  openRecordForEdition(
351
356
  uniqueIdentifier: string
352
357
  ): Observable<[CatalogRecord, string, boolean] | null> {
353
- const draft$ = of(this.getRecordFromLocalStorage(uniqueIdentifier))
358
+ const draft$ = this.disableDraft
359
+ ? of(null)
360
+ : of(this.getRecordFromLocalStorage(uniqueIdentifier))
354
361
  const recordAsXml$ = this.getRecordAsXml(uniqueIdentifier)
355
362
 
356
363
  return combineLatest([draft$, recordAsXml$]).pipe(
@@ -475,6 +482,7 @@ export class Gn4Repository implements RecordsRepositoryInterface {
475
482
  record: CatalogRecord,
476
483
  referenceRecordSource?: string
477
484
  ): Observable<string> {
485
+ if (this.disableDraft) return of('')
478
486
  return this.serializeRecordToXml(record, referenceRecordSource).pipe(
479
487
  tap((recordXml) => {
480
488
  this.saveRecordToLocalStorage(recordXml, record.uniqueIdentifier)
@@ -484,16 +492,19 @@ export class Gn4Repository implements RecordsRepositoryInterface {
484
492
  }
485
493
 
486
494
  clearRecordDraft(uniqueIdentifier: string): void {
495
+ if (this.disableDraft) return
487
496
  this.removeRecordFromLocalStorage(uniqueIdentifier)
488
497
  this._draftsChanged.next()
489
498
  }
490
499
 
491
500
  recordHasDraft(uniqueIdentifier: string): boolean {
501
+ if (this.disableDraft) return false
492
502
  return this.getRecordFromLocalStorage(uniqueIdentifier) !== null
493
503
  }
494
504
 
495
505
  // generated by copilot
496
506
  getAllDrafts(): Observable<CatalogRecord[]> {
507
+ if (this.disableDraft) return of([])
497
508
  const items = { ...window.localStorage }
498
509
  const drafts = Object.keys(items)
499
510
  .filter((key) => key.startsWith('geonetwork-ui-draft-'))
@@ -509,6 +520,7 @@ export class Gn4Repository implements RecordsRepositoryInterface {
509
520
  }
510
521
 
511
522
  getDraftsCount(): Observable<number> {
523
+ if (this.disableDraft) return of(0)
512
524
  const items = { ...window.localStorage }
513
525
  const draftCount = Object.keys(items)
514
526
  .filter((key) => key.startsWith('geonetwork-ui-draft-'))
@@ -6,7 +6,7 @@ import {
6
6
  } from './platform/gn4-platform.service'
7
7
  import { Gn4PlatformMapper } from './platform/gn4-platform.mapper'
8
8
  import { RecordsRepositoryInterface } from '../../../../../../libs/common/domain/src/lib/repository/records-repository.interface'
9
- import { Gn4Repository } from './gn4-repository'
9
+ import { Gn4Repository, DISABLE_DRAFT } from './gn4-repository'
10
10
  import { AvatarServiceInterface, GravatarService } from './auth'
11
11
  import { OrganizationsServiceInterface } from '../../../../../../libs/common/domain/src/lib/organizations.service.interface'
12
12
  import {
@@ -22,6 +22,7 @@ import { TranslateService } from '@ngx-translate/core'
22
22
 
23
23
  interface Gn4ProvideOptions {
24
24
  disableAuth?: boolean
25
+ disableDraft?: boolean
25
26
  }
26
27
 
27
28
  export function provideGn4(provideOptions?: Gn4ProvideOptions): Provider[] {
@@ -30,6 +31,10 @@ export function provideGn4(provideOptions?: Gn4ProvideOptions): Provider[] {
30
31
  provide: DISABLE_AUTH,
31
32
  useValue: provideOptions?.disableAuth,
32
33
  },
34
+ {
35
+ provide: DISABLE_DRAFT,
36
+ useValue: provideOptions?.disableDraft,
37
+ },
33
38
  {
34
39
  provide: PlatformServiceInterface,
35
40
  useClass: Gn4PlatformService,
@@ -254,10 +254,20 @@ export interface DatasetTemporalExtent {
254
254
  end?: Date
255
255
  }
256
256
 
257
+ /**
258
+ * Represents a source dataset referenced from a lineage entry.
259
+ */
260
+ export interface SourceRecord {
261
+ uuid?: string
262
+ title?: string
263
+ href?: string
264
+ }
265
+
257
266
  export interface DatasetRecord extends BaseRecord {
258
267
  kind: 'dataset'
259
268
  status: RecordStatus
260
269
  lineage: string // Explanation of the origin of this record (e.g: how, why)"
270
+ sourceRecords: Array<SourceRecord>
261
271
  onlineResources: Array<DatasetOnlineResource>
262
272
  spatialExtents: Array<DatasetSpatialExtent>
263
273
  temporalExtents: Array<DatasetTemporalExtent>
@@ -285,6 +295,7 @@ export interface ServiceRecord extends BaseRecord {
285
295
  export interface ReuseRecord extends BaseRecord {
286
296
  kind: 'reuse'
287
297
  lineage: string // Explanation of the origin of this record (e.g: how, why)"
298
+ sourceRecords: Array<SourceRecord>
288
299
  onlineResources: Array<DatasetOnlineResource>
289
300
  reuseType: ReuseType
290
301
  spatialExtents: Array<DatasetSpatialExtent>
@@ -134,6 +134,7 @@ Cette section contient des *caractères internationaux* (ainsi que des "caractè
134
134
  lineage: `This record was edited manually to test the conversion processes
135
135
 
136
136
  As such, **it is not very interesting at all.**`,
137
+ sourceRecords: [],
137
138
  licenses: [
138
139
  {
139
140
  text: 'Licence ODbL mai 2013 (basée sur ODbL 1.0)',
@@ -310,6 +311,7 @@ Malgré l'attention portée à la création de ces données, il est rappelé que
310
311
  ],
311
312
  lineage: `Document d’urbanisme numérisé conformément aux prescriptions nationales du CNIG par le Service d'Information Géographique de l'Agglomération de la Région de Compiègne.
312
313
  Ce lot de données produit en 2019, a été numérisé à partir du PCI Vecteur de 2019 et contrôlé par le Service d'Information Géographique de l'Agglomération de la Région de Compiègne.`,
314
+ sourceRecords: [],
313
315
  legalConstraints: [],
314
316
  securityConstraints: [],
315
317
  otherConstraints: [],
@@ -357,10 +359,12 @@ export const simpleDatasetRecordFixture = (): DatasetRecord => ({
357
359
  securityConstraints: [],
358
360
  otherConstraints: [],
359
361
  lineage: 'This record was edited manually to test the conversion processes',
362
+ sourceRecords: [],
360
363
  spatialRepresentation: 'grid',
361
364
  overviews: [],
362
365
  spatialExtents: [],
363
366
  temporalExtents: [],
367
+ sourceRecords: [],
364
368
  onlineResources: [
365
369
  {
366
370
  type: 'download',
@@ -413,6 +417,7 @@ export const simpleDatasetRecordWithFcatsFixture = (): DatasetRecord => ({
413
417
  overviews: [],
414
418
  spatialExtents: [],
415
419
  temporalExtents: [],
420
+ sourceRecords: [],
416
421
  onlineResources: [],
417
422
  updateFrequency: { per: 'month', updatedTimes: 3 },
418
423
  translations: {},
@@ -978,6 +983,7 @@ export const multilingualDatasetFixture: () => DatasetRecord = () => ({
978
983
  title: 'English Title',
979
984
  abstract: 'English Abstract',
980
985
  lineage: 'English Lineage',
986
+ sourceRecords: [],
981
987
  translations: {
982
988
  title: { fr: 'Titre Français', de: 'Titel DE' },
983
989
  abstract: { fr: 'Résumé Français', de: 'Beschreibung DE' },
@@ -994,6 +1000,7 @@ export const multilingualDatasetFixture: () => DatasetRecord = () => ({
994
1000
  },
995
1001
  },
996
1002
  ],
1003
+ sourceRecords: [],
997
1004
  onlineResources: [],
998
1005
  licenses: [],
999
1006
  legalConstraints: [],
@@ -1,6 +1,7 @@
1
1
  import { createAction, props } from '@ngrx/store'
2
2
  import {
3
3
  CatalogRecord,
4
+ CatalogRecordKeys,
4
5
  LanguageCode,
5
6
  } from '../../../../../../libs/common/domain/src/lib/model/record'
6
7
  import { SaveRecordError } from './editor.models'
@@ -73,3 +74,8 @@ export const canEditRecord = createAction(
73
74
  '[Editor] User can edit record',
74
75
  props<{ canEditRecord: boolean }>()
75
76
  )
77
+
78
+ export const setFocusedField = createAction(
79
+ '[Editor] Set focused field',
80
+ props<{ model: CatalogRecordKeys }>()
81
+ )
@@ -12,7 +12,6 @@ import {
12
12
  } from './editor.selectors'
13
13
  import { RecordsRepositoryInterface } from '../../../../../../libs/common/domain/src/lib/repository/records-repository.interface'
14
14
  import { PlatformServiceInterface } from '../../../../../../libs/common/domain/src/lib/platform.service.interface'
15
-
16
15
  @Injectable()
17
16
  export class EditorEffects {
18
17
  private actions$ = inject(Actions)
@@ -4,9 +4,10 @@ import * as EditorActions from './editor.actions'
4
4
  import * as EditorSelectors from './editor.selectors'
5
5
  import {
6
6
  CatalogRecord,
7
+ CatalogRecordKeys,
7
8
  LanguageCode,
8
9
  } from '../../../../../../libs/common/domain/src/lib/model/record'
9
- import { filter } from 'rxjs'
10
+ import { filter, map } from 'rxjs'
10
11
  import { Actions, ofType } from '@ngrx/effects'
11
12
  import { EditorConfig, EditorFieldIdentification } from '../models'
12
13
 
@@ -37,6 +38,10 @@ export class EditorFacade {
37
38
  )
38
39
  isPublished$ = this.store.pipe(select(EditorSelectors.selectIsPublished))
39
40
  canEditRecord$ = this.store.pipe(select(EditorSelectors.selectCanEditRecord))
41
+ focusedField$ = this.actions$.pipe(
42
+ ofType(EditorActions.setFocusedField),
43
+ map(({ model }) => model)
44
+ )
40
45
 
41
46
  openRecord(record: CatalogRecord, recordSource: string) {
42
47
  this.store.dispatch(
@@ -77,6 +82,10 @@ export class EditorFacade {
77
82
  this.store.dispatch(EditorActions.setCurrentPage({ page }))
78
83
  }
79
84
 
85
+ setFocusedField(model: CatalogRecordKeys) {
86
+ this.store.dispatch(EditorActions.setFocusedField({ model }))
87
+ }
88
+
80
89
  setFieldVisibility(field: EditorFieldIdentification, visible: boolean) {
81
90
  this.store.dispatch(EditorActions.setFieldVisibility({ field, visible }))
82
91
  }
@@ -0,0 +1,67 @@
1
+ <div class="flex flex-col gap-7">
2
+ <div class="grid grid-cols-2 gap-4">
3
+ <div class="min-w-0">
4
+ <h3 class="text-[16px] font-bold text-main mb-[12px]" translate>
5
+ editor.record.form.field.contactDetails.lastName
6
+ </h3>
7
+ <gn-ui-text-input
8
+ class="block w-full"
9
+ extraClass="w-full"
10
+ [(value)]="contact.lastName"
11
+ (valueChange)="emitContactChange()"
12
+ [placeholder]="
13
+ 'editor.record.form.field.contactDetails.lastName.placeholder'
14
+ | translate
15
+ "
16
+ data-test="contactDetailsLastName"
17
+ ></gn-ui-text-input>
18
+ </div>
19
+ <div class="min-w-0">
20
+ <h3 class="text-[16px] font-bold text-main mb-[12px]" translate>
21
+ editor.record.form.field.contactDetails.firstName
22
+ </h3>
23
+ <gn-ui-text-input
24
+ class="block w-full"
25
+ extraClass="w-full"
26
+ [(value)]="contact.firstName"
27
+ (valueChange)="emitContactChange()"
28
+ [placeholder]="
29
+ 'editor.record.form.field.contactDetails.firstName.placeholder'
30
+ | translate
31
+ "
32
+ data-test="contactDetailsFirstName"
33
+ ></gn-ui-text-input>
34
+ </div>
35
+ </div>
36
+ <div>
37
+ <h3 class="text-[16px] font-bold text-main mb-[12px]" translate>
38
+ editor.record.form.field.contactDetails.email
39
+ </h3>
40
+ <gn-ui-text-input
41
+ class="block w-full"
42
+ extraClass="w-full"
43
+ [value]="contact.organization?.email ?? ''"
44
+ (valueChange)="handleOrganizationChange({ email: $event })"
45
+ [placeholder]="
46
+ 'editor.record.form.field.contactDetails.email.placeholder' | translate
47
+ "
48
+ data-test="contactDetailsEmail"
49
+ ></gn-ui-text-input>
50
+ </div>
51
+ <div>
52
+ <h3 class="text-[16px] font-bold text-main mb-[12px]" translate>
53
+ editor.record.form.field.contactDetails.organization
54
+ </h3>
55
+ <gn-ui-text-input
56
+ class="block w-full"
57
+ extraClass="w-full"
58
+ [value]="contact.organization?.name ?? ''"
59
+ (valueChange)="handleOrganizationChange({ name: $event })"
60
+ [placeholder]="
61
+ 'editor.record.form.field.contactDetails.organization.placeholder'
62
+ | translate
63
+ "
64
+ data-test="contactDetailsOrganization"
65
+ ></gn-ui-text-input>
66
+ </div>
67
+ </div>
@@ -0,0 +1,39 @@
1
+ import {
2
+ ChangeDetectionStrategy,
3
+ Component,
4
+ EventEmitter,
5
+ Input,
6
+ Output,
7
+ } from '@angular/core'
8
+ import {
9
+ Individual,
10
+ Organization,
11
+ } from '../../../../../../../libs/common/domain/src/lib/model/record'
12
+ import { TextInputComponent } from '../../../../../../../libs/ui/inputs/src'
13
+ import { TranslateDirective, TranslatePipe } from '@ngx-translate/core'
14
+
15
+ @Component({
16
+ selector: 'gn-ui-contact-details-form',
17
+ templateUrl: './contact-details-form.component.html',
18
+ styleUrls: ['./contact-details-form.component.css'],
19
+ changeDetection: ChangeDetectionStrategy.OnPush,
20
+ standalone: true,
21
+ imports: [TextInputComponent, TranslateDirective, TranslatePipe],
22
+ })
23
+ export class ContactDetailsFormComponent {
24
+ @Input() contact: Individual
25
+ @Output() contactChange = new EventEmitter<Individual>()
26
+
27
+ emitContactChange() {
28
+ this.contactChange.emit(this.contact)
29
+ }
30
+
31
+ handleOrganizationChange(change: Partial<Organization>) {
32
+ this.contact.organization = {
33
+ ...(this.contact.organization ?? ({} as Organization)),
34
+ ...change,
35
+ } as Organization
36
+
37
+ this.emitContactChange()
38
+ }
39
+ }