geonetwork-ui 2.10.0-dev.e97ea6950 → 2.10.0-dev.f1c00d5fe

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 (174) hide show
  1. package/fesm2022/geonetwork-ui.mjs +2558 -6985
  2. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  3. package/index.d.ts +299 -189
  4. package/index.d.ts.map +1 -1
  5. package/package.json +25 -35
  6. package/src/libs/api/metadata-converter/src/lib/dcat-ap/dcat-ap.converter.ts +20 -0
  7. package/src/libs/api/metadata-converter/src/lib/fixtures/eu.dcat-ap.records.ts +4 -0
  8. package/src/libs/api/metadata-converter/src/lib/fixtures/generic.records.ts +23 -0
  9. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.reuse+ongules.ts +8 -0
  10. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.reuse+roilaye.ts +2 -0
  11. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.ts +2 -0
  12. package/src/libs/api/metadata-converter/src/lib/fixtures/geocat-ch.records.ts +2 -0
  13. package/src/libs/api/metadata-converter/src/lib/fixtures/georhena.records.ts +2 -0
  14. package/src/libs/api/metadata-converter/src/lib/fixtures/metadata-for-i18n.records.ts +4 -0
  15. package/src/libs/api/metadata-converter/src/lib/fixtures/metawal.records.ts +23 -0
  16. package/src/libs/api/metadata-converter/src/lib/fixtures/opendataswiss.records.ts +2 -0
  17. package/src/libs/api/metadata-converter/src/lib/fixtures/sextant.records.ts +4 -0
  18. package/src/libs/api/metadata-converter/src/lib/fixtures/vlaanderen.dcat-ap.records.ts +2 -0
  19. package/src/libs/api/metadata-converter/src/lib/fixtures/wallonie.records.reuse.ts +9 -0
  20. package/src/libs/api/metadata-converter/src/lib/gn4/atomic-operations.ts +48 -7
  21. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.converter.ts +2 -0
  22. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +6 -5
  23. package/src/libs/api/metadata-converter/src/lib/gn4/metadata-url.service.ts +2 -2
  24. package/src/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.ts +1 -0
  25. package/src/libs/api/metadata-converter/src/lib/iso19115-3/iso19115-3.converter.ts +11 -0
  26. package/src/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.ts +21 -0
  27. package/src/libs/api/metadata-converter/src/lib/iso19115-3/write-parts.ts +52 -0
  28. package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +24 -1
  29. package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +73 -0
  30. package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +79 -0
  31. package/src/libs/api/repository/src/lib/gn4/auth/auth.service.ts +8 -4
  32. package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +2 -2
  33. package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +79 -41
  34. package/src/libs/api/repository/src/lib/gn4/gn4.provider.ts +6 -1
  35. package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-groups.service.ts +1 -1
  36. package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-metadata.service.ts +1 -1
  37. package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +37 -2
  38. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +23 -5
  39. package/src/libs/common/domain/src/lib/model/thesaurus/thesaurus.model.ts +2 -1
  40. package/src/libs/common/domain/src/lib/model/user/group.model.ts +8 -0
  41. package/src/libs/common/domain/src/lib/model/user/index.ts +1 -0
  42. package/src/libs/common/domain/src/lib/platform.service.interface.ts +2 -0
  43. package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +2 -1
  44. package/src/libs/common/fixtures/src/lib/records.fixtures.ts +62 -0
  45. package/src/libs/feature/catalog/src/lib/sources/sources.service.ts +5 -2
  46. package/src/libs/feature/editor/src/index.ts +1 -0
  47. package/src/libs/feature/editor/src/lib/+state/editor.actions.ts +10 -1
  48. package/src/libs/feature/editor/src/lib/+state/editor.effects.ts +26 -20
  49. package/src/libs/feature/editor/src/lib/+state/editor.facade.ts +15 -2
  50. package/src/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.ts +0 -2
  51. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.css +0 -0
  52. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.html +68 -0
  53. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.ts +41 -0
  54. package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.html +5 -14
  55. package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.ts +3 -28
  56. package/src/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.html +18 -3
  57. package/src/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.ts +33 -40
  58. package/src/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.html +5 -5
  59. package/src/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.ts +2 -1
  60. package/src/libs/feature/editor/src/lib/components/record-form/form-field/field-focus.directive.ts +41 -0
  61. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-constraints-shortcuts/form-field-constraints-shortcuts.component.ts +33 -34
  62. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.html +13 -13
  63. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.ts +18 -4
  64. 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
  65. 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
  66. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-resources/form-field-online-resources.component.ts +1 -1
  67. 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
  68. 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
  69. 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
  70. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.ts +12 -5
  71. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-rich/form-field-rich.component.html +4 -4
  72. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-rich/form-field-rich.component.ts +7 -0
  73. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-topics/form-field-topics.component.ts +2 -2
  74. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-update-frequency/form-field-update-frequency.component.html +6 -2
  75. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-update-frequency/form-field-update-frequency.component.ts +3 -1
  76. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.css +3 -0
  77. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +28 -16
  78. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +16 -0
  79. package/src/libs/feature/editor/src/lib/components/record-form/form-field/index.ts +1 -0
  80. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.css +35 -0
  81. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.html +18 -5
  82. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.ts +118 -5
  83. package/src/libs/feature/editor/src/lib/expressions.ts +380 -8
  84. package/src/libs/feature/editor/src/lib/fields.config.ts +354 -337
  85. package/src/libs/feature/editor/src/lib/models/editor-config.model.ts +10 -3
  86. package/src/libs/feature/editor/src/lib/services/editor.service.ts +9 -13
  87. package/src/libs/feature/map/src/lib/utils/map-utils.service.ts +33 -0
  88. package/src/libs/feature/notify-reuse/src/index.ts +2 -0
  89. package/src/libs/feature/notify-reuse/src/lib/edit-delete-reuse-buttons/edit-delete-reuse-buttons.component.css +0 -0
  90. package/src/libs/feature/notify-reuse/src/lib/edit-delete-reuse-buttons/edit-delete-reuse-buttons.component.html +23 -0
  91. package/src/libs/feature/notify-reuse/src/lib/edit-delete-reuse-buttons/edit-delete-reuse-buttons.component.ts +114 -0
  92. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.css +0 -0
  93. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.html +111 -0
  94. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.ts +234 -0
  95. package/src/libs/feature/notify-reuse/src/lib/utils/url.ts +16 -0
  96. package/src/libs/feature/record/src/lib/map-view/map-view.component.ts +9 -1
  97. package/src/libs/feature/search/src/lib/results-table/results-table-container.component.ts +10 -5
  98. package/src/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.ts +2 -2
  99. package/src/libs/ui/dataviz/src/index.ts +0 -1
  100. package/src/libs/ui/elements/src/index.ts +3 -0
  101. package/src/libs/ui/elements/src/lib/contact-details/contact-details.component.html +88 -0
  102. package/src/libs/ui/elements/src/lib/contact-details/contact-details.component.ts +45 -0
  103. package/src/libs/ui/elements/src/lib/contact-pill/contact-pill.component.html +41 -0
  104. package/src/libs/ui/elements/src/lib/contact-pill/contact-pill.component.ts +70 -0
  105. package/src/libs/ui/elements/src/lib/external-link-card/external-link-card.component.html +2 -1
  106. package/src/libs/ui/elements/src/lib/external-link-card/external-link-card.component.ts +2 -2
  107. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.css +13 -0
  108. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.html +13 -3
  109. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.ts +43 -6
  110. package/src/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.html +1 -1
  111. package/src/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.ts +0 -1
  112. package/src/libs/ui/elements/src/lib/keyword-badge/keyword-badge.component.html +14 -0
  113. package/src/libs/ui/elements/src/lib/keyword-badge/keyword-badge.component.ts +85 -0
  114. package/src/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.ts +2 -2
  115. package/src/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.ts +2 -5
  116. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.css +3 -2
  117. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +26 -68
  118. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts +30 -10
  119. package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.html +4 -5
  120. package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.ts +4 -0
  121. package/src/libs/ui/inputs/src/lib/badge/badge.component.html +33 -26
  122. package/src/libs/ui/inputs/src/lib/badge/badge.component.ts +9 -0
  123. package/src/libs/ui/inputs/src/lib/button/button.component.ts +8 -0
  124. package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.ts +2 -2
  125. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +1 -1
  126. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.ts +3 -3
  127. package/src/libs/ui/inputs/src/lib/text-input/text-input.component.css +0 -3
  128. package/src/libs/ui/inputs/src/lib/text-input/text-input.component.html +11 -7
  129. package/src/libs/ui/inputs/src/lib/text-input/text-input.component.ts +56 -30
  130. package/src/libs/ui/inputs/src/lib/url-input/url-input.component.html +2 -2
  131. package/src/libs/ui/inputs/src/lib/url-input/url-input.component.ts +2 -1
  132. package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.html +5 -5
  133. package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.ts +9 -1
  134. package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.html +1 -1
  135. package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.ts +3 -8
  136. package/src/libs/ui/layout/src/lib/sortable-list/sortable-list.component.html +7 -7
  137. package/src/libs/ui/layout/src/lib/sortable-list/sortable-list.component.ts +14 -1
  138. package/src/libs/ui/map/src/lib/components/map-container/map-container.component.ts +2 -1
  139. package/src/libs/ui/map/src/lib/components/spatial-extent/spatial-extent.component.ts +15 -64
  140. package/src/libs/ui/map/src/lib/map-utils.ts +48 -0
  141. package/src/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.ts +1 -1
  142. package/src/libs/ui/search/src/lib/record-preview-row/record-preview-row.component.html +0 -1
  143. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.html +12 -8
  144. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.ts +3 -1
  145. package/src/libs/ui/search/src/lib/results-table/results-table.component.html +1 -0
  146. package/src/libs/ui/widgets/src/lib/popover/popover.component.ts +7 -1
  147. package/src/libs/ui/widgets/src/lib/spinning-loader/spinning-loader.component.html +1 -1
  148. package/src/libs/util/app-config/src/lib/app-config.ts +70 -9
  149. package/src/libs/util/app-config/src/lib/model.ts +7 -0
  150. package/src/libs/util/app-config/src/lib/parse-utils.ts +50 -1
  151. package/src/libs/util/data-fetcher/src/lib/readers/base-file.ts +1 -2
  152. package/src/libs/util/data-fetcher/src/lib/readers/wfs.ts +6 -3
  153. package/src/libs/util/i18n/src/index.ts +0 -1
  154. package/src/libs/util/i18n/src/lib/i18n.constants.ts +17 -11
  155. package/src/libs/util/i18n/src/lib/i18n.interceptor.ts +2 -2
  156. package/src/libs/util/i18n/src/lib/i18n.providers.ts +14 -17
  157. package/src/libs/util/i18n/src/lib/test.translate.loader.ts +48 -0
  158. package/src/libs/util/shared/src/lib/links/link-utils.ts +23 -1
  159. package/src/libs/util/shared/src/lib/record/quality-score.util.ts +33 -18
  160. package/src/libs/util/shared/src/lib/services/date.service.ts +3 -3
  161. package/src/libs/util/shared/src/lib/utils/geojson.ts +58 -1
  162. package/src/libs/util/shared/src/lib/utils/index.ts +1 -0
  163. package/src/libs/util/shared/src/lib/utils/user-display.ts +32 -0
  164. package/style.css +0 -1
  165. package/tailwind.base.css +26 -2
  166. package/translations/de.json +112 -50
  167. package/translations/en.json +117 -55
  168. package/translations/es.json +86 -24
  169. package/translations/fr.json +114 -52
  170. package/translations/it.json +113 -51
  171. package/translations/nl.json +85 -23
  172. package/translations/pt.json +86 -24
  173. package/translations/sk.json +86 -24
  174. package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.css +0 -3
@@ -3,6 +3,7 @@ import {
3
3
  DatasetRecord,
4
4
  DatasetSpatialExtent,
5
5
  Keyword,
6
+ ReuseRecord,
6
7
  ServiceRecord,
7
8
  } from '../../../../../libs/common/domain/src/lib/model/record'
8
9
 
@@ -134,6 +135,8 @@ Cette section contient des *caractères internationaux* (ainsi que des "caractè
134
135
  lineage: `This record was edited manually to test the conversion processes
135
136
 
136
137
  As such, **it is not very interesting at all.**`,
138
+ sourceRecords: [],
139
+ associatedRecords: [],
137
140
  licenses: [
138
141
  {
139
142
  text: 'Licence ODbL mai 2013 (basée sur ODbL 1.0)',
@@ -310,6 +313,8 @@ Malgré l'attention portée à la création de ces données, il est rappelé que
310
313
  ],
311
314
  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
315
  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.`,
316
+ sourceRecords: [],
317
+ associatedRecords: [],
313
318
  legalConstraints: [],
314
319
  securityConstraints: [],
315
320
  otherConstraints: [],
@@ -361,6 +366,8 @@ export const simpleDatasetRecordFixture = (): DatasetRecord => ({
361
366
  overviews: [],
362
367
  spatialExtents: [],
363
368
  temporalExtents: [],
369
+ sourceRecords: [],
370
+ associatedRecords: [],
364
371
  onlineResources: [
365
372
  {
366
373
  type: 'download',
@@ -413,6 +420,8 @@ export const simpleDatasetRecordWithFcatsFixture = (): DatasetRecord => ({
413
420
  overviews: [],
414
421
  spatialExtents: [],
415
422
  temporalExtents: [],
423
+ sourceRecords: [],
424
+ associatedRecords: [],
416
425
  onlineResources: [],
417
426
  updateFrequency: { per: 'month', updatedTimes: 3 },
418
427
  translations: {},
@@ -978,6 +987,8 @@ export const multilingualDatasetFixture: () => DatasetRecord = () => ({
978
987
  title: 'English Title',
979
988
  abstract: 'English Abstract',
980
989
  lineage: 'English Lineage',
990
+ sourceRecords: [],
991
+ associatedRecords: [],
981
992
  translations: {
982
993
  title: { fr: 'Titre Français', de: 'Titel DE' },
983
994
  abstract: { fr: 'Résumé Français', de: 'Beschreibung DE' },
@@ -1022,3 +1033,54 @@ export const multilingualDatasetFixture: () => DatasetRecord = () => ({
1022
1033
  topics: [],
1023
1034
  recordUpdated: null,
1024
1035
  })
1036
+
1037
+ export const simpleReuseRecordFixture = (): ReuseRecord => ({
1038
+ abstract: `Sample reuse record`,
1039
+ kind: 'reuse',
1040
+ reuseType: 'application',
1041
+ recordUpdated: new Date('2023-03-17T07:38:08.875Z'),
1042
+ recordPublished: null,
1043
+ ownerOrganization: null,
1044
+ licenses: [],
1045
+ legalConstraints: [],
1046
+ securityConstraints: [],
1047
+ otherConstraints: [],
1048
+ contacts: [],
1049
+ contactsForResource: [],
1050
+ keywords: [],
1051
+ topics: [],
1052
+ spatialExtents: [],
1053
+ temporalExtents: [],
1054
+ lineage: 'bla',
1055
+ sourceRecords: [],
1056
+ associatedRecords: [],
1057
+ overviews: [],
1058
+ defaultLanguage: null,
1059
+ otherLanguages: [],
1060
+ title: 'Sites de gestion des déchets miniers - Service de visualisation WMS',
1061
+ onlineResources: [
1062
+ {
1063
+ name: 'Rapport de disponibilité du service WMS',
1064
+ description:
1065
+ 'Ce service de visualisation WMS permet de consulter la série de couches de données "Sites de gestion des déchets miniers - Série".',
1066
+ mimeType: 'text/html',
1067
+ type: 'link',
1068
+ url: new URL(
1069
+ 'https://geoservices.wallonie.be/rapportDisponibilite/wms/sites_de_gestion_des_dechets_miniers_srie.html'
1070
+ ),
1071
+ accessRestricted: false,
1072
+ },
1073
+ ],
1074
+ uniqueIdentifier: '00b22798-ec8e-4500-89e8-90eeeda45919',
1075
+ landingPage: new URL(
1076
+ 'http://localhost:4200/geonetwork/srv/fre/catalog.search#/metadata/00b22798-ec8e-4500-89e8-90eeeda45919'
1077
+ ),
1078
+ extras: {
1079
+ isPublishedToAll: true,
1080
+ id: '723',
1081
+ isHarvested: true,
1082
+ ownerInfo: 'admin|admin|admin|Administrator',
1083
+ edit: true,
1084
+ },
1085
+ recordCreated: new Date('2023-03-17T07:38:08.875Z'),
1086
+ })
@@ -1,4 +1,4 @@
1
- import { Injectable, inject } from '@angular/core'
1
+ import { inject, Injectable } from '@angular/core'
2
2
  import { SourcesApiService } from '../../../../../../libs/data-access/gn4/src'
3
3
  import { Observable } from 'rxjs'
4
4
  import { filter, map, shareReplay } from 'rxjs/operators'
@@ -21,7 +21,10 @@ export class SourcesService {
21
21
  return this.sources$.pipe(
22
22
  map((sources) => sources.filter((source) => source.uuid === uuid)[0]),
23
23
  filter((source) => !!source),
24
- map((source) => source.label[toLang3(this.translateService.currentLang)])
24
+ map(
25
+ (source) =>
26
+ source.label[toLang3(this.translateService.getCurrentLang())]
27
+ )
25
28
  )
26
29
  }
27
30
  }
@@ -3,6 +3,7 @@ export * from './lib/+state/editor.facade'
3
3
  export * from './lib/+state/editor.models'
4
4
  export * from './lib/+state/editor.reducer'
5
5
  export * from './lib/+state/editor.selectors'
6
+ export * from './lib/components/contact-details/contact-details-form.component'
6
7
  export * from './lib/components/import-record/import-record.component'
7
8
  export * from './lib/components/metadata-quality-panel/metadata-quality-panel.component'
8
9
  export * from './lib/components/multilingual-panel/multilingual-panel.component'
@@ -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'
@@ -28,7 +29,10 @@ export const markRecordAsChanged = createAction(
28
29
  '[Editor] Mark record as changed'
29
30
  )
30
31
 
31
- export const saveRecord = createAction('[Editor] Save record')
32
+ export const saveRecord = createAction(
33
+ '[Editor] Save record',
34
+ props<{ publish: boolean }>()
35
+ )
32
36
  export const saveRecordSuccess = createAction('[Editor] Save record success')
33
37
  export const saveRecordFailure = createAction(
34
38
  '[Editor] Save record failure',
@@ -73,3 +77,8 @@ export const canEditRecord = createAction(
73
77
  '[Editor] User can edit record',
74
78
  props<{ canEditRecord: boolean }>()
75
79
  )
80
+
81
+ export const setFocusedField = createAction(
82
+ '[Editor] Set focused field',
83
+ props<{ model: CatalogRecordKeys }>()
84
+ )
@@ -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)
@@ -29,30 +28,37 @@ export class EditorEffects {
29
28
  this.store.select(selectRecordSource),
30
29
  this.store.select(selectEditorConfig)
31
30
  ),
32
- switchMap(([, record, recordSource, fieldsConfig]) =>
33
- this.editorService.saveRecord(record, recordSource, fieldsConfig).pipe(
34
- switchMap(([savedRecord, savedRecordSource]) => {
35
- const actions: Action[] = [EditorActions.saveRecordSuccess()]
31
+ switchMap(([{ publish }, record, recordSource, fieldsConfig]) =>
32
+ this.editorService
33
+ .saveRecord(record, recordSource, fieldsConfig, publish)
34
+ .pipe(
35
+ switchMap(([savedRecord, savedRecordSource]) => {
36
+ const actions: Action[] = [EditorActions.saveRecordSuccess()]
36
37
 
37
- if (!record?.uniqueIdentifier) {
38
- actions.push(
39
- EditorActions.openRecord({
40
- record: savedRecord,
41
- recordSource: savedRecordSource,
38
+ if (publish) {
39
+ // saving without publishing doesn't unpublish
40
+ actions.push(EditorActions.isPublished({ isPublished: true }))
41
+ }
42
+
43
+ if (!record?.uniqueIdentifier) {
44
+ actions.push(
45
+ EditorActions.openRecord({
46
+ record: savedRecord,
47
+ recordSource: savedRecordSource,
48
+ })
49
+ )
50
+ }
51
+
52
+ return of(...actions)
53
+ }),
54
+ catchError((error) =>
55
+ of(
56
+ EditorActions.saveRecordFailure({
57
+ error,
42
58
  })
43
59
  )
44
- }
45
-
46
- return of(...actions)
47
- }),
48
- catchError((error) =>
49
- of(
50
- EditorActions.saveRecordFailure({
51
- error,
52
- })
53
60
  )
54
61
  )
55
- )
56
62
  )
57
63
  )
58
64
  )
@@ -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(
@@ -49,7 +54,11 @@ export class EditorFacade {
49
54
  }
50
55
 
51
56
  saveRecord() {
52
- this.store.dispatch(EditorActions.saveRecord())
57
+ this.store.dispatch(EditorActions.saveRecord({ publish: false }))
58
+ }
59
+
60
+ saveAndPublishRecord() {
61
+ this.store.dispatch(EditorActions.saveRecord({ publish: true }))
53
62
  }
54
63
 
55
64
  undoRecordDraft() {
@@ -77,6 +86,10 @@ export class EditorFacade {
77
86
  this.store.dispatch(EditorActions.setCurrentPage({ page }))
78
87
  }
79
88
 
89
+ setFocusedField(model: CatalogRecordKeys) {
90
+ this.store.dispatch(EditorActions.setFocusedField({ model }))
91
+ }
92
+
80
93
  setFieldVisibility(field: EditorFieldIdentification, visible: boolean) {
81
94
  this.store.dispatch(EditorActions.setFieldVisibility({ field, visible }))
82
95
  }
@@ -8,7 +8,6 @@ import {
8
8
 
9
9
  import { FormFieldRichComponent } from '../record-form/form-field/form-field-rich/form-field-rich.component'
10
10
  import { ButtonComponent, UrlInputComponent } from '../../../../../../../libs/ui/inputs/src'
11
- import { MatIconModule } from '@angular/material/icon'
12
11
  import { TranslateDirective, TranslatePipe } from '@ngx-translate/core'
13
12
  import { Constraint } from '../../../../../../../libs/common/domain/src/lib/model/record'
14
13
  import {
@@ -26,7 +25,6 @@ import { marker } from '@biesbjerg/ngx-translate-extract-marker'
26
25
  FormFieldRichComponent,
27
26
  UrlInputComponent,
28
27
  ButtonComponent,
29
- MatIconModule,
30
28
  TranslateDirective,
31
29
  TranslatePipe,
32
30
  NgIconComponent,
@@ -0,0 +1,68 @@
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)="handleChange({ lastName: $event })"
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)="handleChange({ firstName: $event })"
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
+ [type]="'email'"
44
+ [value]="contact.email ?? ''"
45
+ (valueChange)="handleChange({ email: $event })"
46
+ [placeholder]="
47
+ 'editor.record.form.field.contactDetails.email.placeholder' | translate
48
+ "
49
+ data-test="contactDetailsEmail"
50
+ ></gn-ui-text-input>
51
+ </div>
52
+ <div>
53
+ <h3 class="text-[16px] font-bold text-main mb-[12px]" translate>
54
+ editor.record.form.field.contactDetails.organization
55
+ </h3>
56
+ <gn-ui-text-input
57
+ class="block w-full"
58
+ extraClass="w-full"
59
+ [value]="contact.organization?.name ?? ''"
60
+ (valueChange)="handleOrganizationChange({ name: $event })"
61
+ [placeholder]="
62
+ 'editor.record.form.field.contactDetails.organization.placeholder'
63
+ | translate
64
+ "
65
+ data-test="contactDetailsOrganization"
66
+ ></gn-ui-text-input>
67
+ </div>
68
+ </div>
@@ -0,0 +1,41 @@
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
+ // emit a fresh contact on each change; never mutate the @Input
28
+ handleChange(change: Partial<Individual>) {
29
+ this.contactChange.emit({ ...this.contact, ...change })
30
+ }
31
+
32
+ handleOrganizationChange(change: Partial<Organization>) {
33
+ this.contactChange.emit({
34
+ ...this.contact,
35
+ organization: {
36
+ ...(this.contact.organization ?? ({} as Organization)),
37
+ ...change,
38
+ } as Organization,
39
+ })
40
+ }
41
+ }
@@ -11,20 +11,11 @@
11
11
  ></gn-ui-autocomplete>
12
12
  <div class="flex gap-2 flex-wrap">
13
13
  @for (keyword of keywords; track keyword) {
14
- <gn-ui-badge
15
- [removable]="true"
16
- (badgeRemoveClicked)="removeKeyword(keyword)"
17
- >{{ keyword.label }}
18
- @if (isPlaceWithoutExtent(keyword)) {
19
- <ng-icon
20
- [title]="
21
- 'editor.record.form.keywords.place.withoutExtent' | translate
22
- "
23
- class="badge-warning"
24
- name="matWarningAmberOutline"
25
- ></ng-icon>
26
- }
27
- </gn-ui-badge>
14
+ <gn-ui-keyword-badge
15
+ [keyword]="keyword"
16
+ [editable]="true"
17
+ (keywordRemove)="removeKeyword($event)"
18
+ ></gn-ui-keyword-badge>
28
19
  }
29
20
  </div>
30
21
  </div>
@@ -7,40 +7,20 @@ import {
7
7
  inject,
8
8
  } from '@angular/core'
9
9
  import { PlatformServiceInterface } from '../../../../../../../libs/common/domain/src/lib/platform.service.interface'
10
- import { AutocompleteComponent, BadgeComponent } from '../../../../../../../libs/ui/inputs/src'
10
+ import { AutocompleteComponent } from '../../../../../../../libs/ui/inputs/src'
11
+ import { KeywordBadgeComponent } from '../../../../../../../libs/ui/elements/src'
11
12
  import { map } from 'rxjs'
12
13
  import { Keyword } from '../../../../../../../libs/common/domain/src/lib/model/record'
13
14
  import { KeywordType } from '../../../../../../../libs/common/domain/src/lib/model/thesaurus'
14
- import { TranslatePipe } from '@ngx-translate/core'
15
- import {
16
- NgIconComponent,
17
- provideIcons,
18
- provideNgIconsConfig,
19
- } from '@ng-icons/core'
20
- import { matWarningAmberOutline } from '@ng-icons/material-icons/outline'
21
15
 
22
16
  type AutocompleteItem = { title: string; value: Keyword }
23
17
 
24
18
  @Component({
25
19
  selector: 'gn-ui-generic-keywords',
26
20
  templateUrl: './generic-keywords.component.html',
27
- styleUrls: ['./generic-keywords.component.css'],
28
21
  changeDetection: ChangeDetectionStrategy.OnPush,
29
22
  standalone: true,
30
- imports: [
31
- AutocompleteComponent,
32
- NgIconComponent,
33
- BadgeComponent,
34
- TranslatePipe,
35
- ],
36
- providers: [
37
- provideIcons({
38
- matWarningAmberOutline,
39
- }),
40
- provideNgIconsConfig({
41
- size: '1.5em',
42
- }),
43
- ],
23
+ imports: [AutocompleteComponent, KeywordBadgeComponent],
44
24
  })
45
25
  export class GenericKeywordsComponent {
46
26
  private platformService = inject(PlatformServiceInterface)
@@ -87,9 +67,4 @@ export class GenericKeywordsComponent {
87
67
  this.changedKeywords.emit(this.keywords)
88
68
  this.deletedKeyword.emit(keyword)
89
69
  }
90
-
91
- isPlaceWithoutExtent(keyword: Keyword): boolean {
92
- if (keyword.type !== 'place') return false
93
- return !keyword.bbox
94
- }
95
70
  }
@@ -7,11 +7,26 @@
7
7
  >editor.record.form.metadataQuality.title</span
8
8
  >
9
9
  </div>
10
- @for (properties of propertiesByPage; track properties; let i = $index) {
10
+ @for (
11
+ properties of (propertiesByPage$ | async) ?? [];
12
+ track properties;
13
+ let isLast = $last
14
+ ) {
11
15
  <div class="flex flex-col gap-2">
12
16
  @for (property of properties; track property) {
13
17
  <gn-ui-button
14
- [extraClass]="getExtraClass(property.value)"
18
+ style="
19
+ --gn-ui-button-justify: space-between;
20
+ --gn-ui-button-height: 34px;
21
+ --gn-ui-button-width: 100%;
22
+ --gn-ui-button-border-width: 0;
23
+ --gn-ui-button-color: black;
24
+ --gn-ui-button-background: transparent;
25
+ --gn-ui-button-bg-hover: #f3f4f6;
26
+ --gn-ui-button-disabled-opacity: 1;
27
+ "
28
+ [disabled]="property.value"
29
+ (buttonClick)="onCriterionClick(property)"
15
30
  type="outline"
16
31
  attr.data-cy="md-quality-btn-{{ property.label }}"
17
32
  >
@@ -28,7 +43,7 @@
28
43
  </div>
29
44
  </gn-ui-button>
30
45
  }
31
- @if (i !== propertiesByPage.length - 1) {
46
+ @if (!isLast) {
32
47
  <hr class="border-gray-300 w-11/12 mx-auto" />
33
48
  }
34
49
  </div>
@@ -1,5 +1,5 @@
1
- import { Component, Input, OnChanges } from '@angular/core'
2
- import { CatalogRecord } from '../../../../../../../libs/common/domain/src/lib/model/record'
1
+ import { Component, inject } from '@angular/core'
2
+ import { CatalogRecordKeys } from '../../../../../../../libs/common/domain/src/lib/model/record'
3
3
  import { ButtonComponent } from '../../../../../../../libs/ui/inputs/src'
4
4
  import {
5
5
  getAllKeysValidator,
@@ -13,8 +13,10 @@ import {
13
13
  } from '@ng-icons/core'
14
14
  import { iconoirBadgeCheck, iconoirSystemShut } from '@ng-icons/iconoir'
15
15
  import { TranslateDirective, TranslatePipe } from '@ngx-translate/core'
16
- import { EditorConfig } from '../../models'
17
16
  import { marker } from '@biesbjerg/ngx-translate-extract-marker'
17
+ import { EditorFacade } from '../../+state/editor.facade'
18
+ import { combineLatest, map } from 'rxjs'
19
+ import { AsyncPipe } from '@angular/common'
18
20
 
19
21
  //forced translations that are not available in fields.config.ts
20
22
  marker('editor.record.form.field.keywords')
@@ -29,6 +31,7 @@ marker('editor.record.form.field.organisation')
29
31
  TranslatePipe,
30
32
  ButtonComponent,
31
33
  NgIconComponent,
34
+ AsyncPipe,
32
35
  ],
33
36
  providers: [
34
37
  provideIcons({
@@ -42,47 +45,37 @@ marker('editor.record.form.field.organisation')
42
45
  templateUrl: './metadata-quality-panel.component.html',
43
46
  styleUrl: './metadata-quality-panel.component.css',
44
47
  })
45
- export class MetadataQualityPanelComponent implements OnChanges {
48
+ export class MetadataQualityPanelComponent {
49
+ facade = inject(EditorFacade)
46
50
  propsToValidate: ValidatorMapperKeys[] = getAllKeysValidator()
47
- propertiesByPage: { label: string; value: boolean }[][] = []
48
- @Input() editorConfig: EditorConfig
49
- @Input() record: CatalogRecord
50
51
 
51
- ngOnChanges() {
52
- if (this.editorConfig && this.record) {
53
- const fieldsByPage = this.editorConfig.pages.map((page) =>
54
- page.sections.flatMap((section) =>
55
- section.fields
56
- .filter((field) => this.propsToValidate.includes(field.model))
57
- .map((field) => field.model as ValidatorMapperKeys)
58
- )
59
- )
60
- // FIXME: temporarily add topics and organisation to the first and third page
61
- // as long as they are not handled by the editor
62
- if (fieldsByPage.length > 0) {
63
- fieldsByPage[0].includes('topics') || fieldsByPage[0].push('topics')
64
- fieldsByPage[2].includes('organisation') ||
65
- fieldsByPage[2].push('organisation')
66
- }
67
- this.propertiesByPage = fieldsByPage
68
- .map((fields) =>
69
- getQualityValidators(
70
- this.record,
71
- fields as ValidatorMapperKeys[]
72
- ).map(({ name, validator }) => ({
73
- label: `editor.record.form.field.${name}`, // use same translations as in fields.config.ts
74
- value: validator(),
75
- }))
52
+ propertiesByPage$ = combineLatest([
53
+ this.facade.editorConfig$,
54
+ this.facade.record$,
55
+ ]).pipe(
56
+ map(([editorConfig, record]) => {
57
+ if (!editorConfig || !record) return []
58
+ const validators = getQualityValidators(record, this.propsToValidate)
59
+ return editorConfig.pages
60
+ .map((page) =>
61
+ page.sections
62
+ .flatMap((section) => section.fields)
63
+ .flatMap(({ model }) =>
64
+ validators.filter((v) => (v.alias ?? v.name) === model)
65
+ )
66
+ .map(({ name, validator, alias }) => ({
67
+ label: `editor.record.form.field.${name}`,
68
+ value: validator(),
69
+ model: (alias ?? name) as CatalogRecordKeys,
70
+ }))
76
71
  )
77
72
  .filter((arr) => arr.length > 0)
78
- }
79
- }
73
+ })
74
+ )
80
75
 
81
- getExtraClass(checked: boolean): string {
82
- const baseClasses =
83
- 'flex flex-row justify-between rounded mb-1 h-[34px] w-full focus:ring-0 hover:border-none border-none hover:text-black text-black cursor-default'
84
- return checked
85
- ? `${baseClasses} bg-neutral-100 hover:bg-neutral-100`
86
- : `${baseClasses} bg-transparent hover:bg-transparent`
76
+ onCriterionClick(property: { value: boolean; model: CatalogRecordKeys }) {
77
+ if (!property.value) {
78
+ this.facade.setFocusedField(property.model)
79
+ }
87
80
  }
88
81
  }
@@ -3,13 +3,13 @@
3
3
  editor.record.form.field.onlineResource.edit.protocol
4
4
  </h3>
5
5
  @if (protocolHint) {
6
- <span
7
- class="material-symbols-outlined m-2 gn-ui-icon-small"
6
+ <ng-icon
7
+ name="matHelpOutline"
8
+ size="16"
9
+ class="m-2"
8
10
  [matTooltip]="protocolHint"
9
11
  matTooltipPosition="above"
10
- >
11
- help
12
- </span>
12
+ ></ng-icon>
13
13
  }
14
14
  </div>
15
15
  <div class="flex flex-row items-center gap-4 h-[48px]">
@@ -30,6 +30,7 @@ import {
30
30
  provideNgIconsConfig,
31
31
  } from '@ng-icons/core'
32
32
  import { iconoirRefresh } from '@ng-icons/iconoir'
33
+ import { matHelpOutline } from '@ng-icons/material-icons/outline'
33
34
  import { TranslateDirective, TranslatePipe } from '@ngx-translate/core'
34
35
  import {
35
36
  BehaviorSubject,
@@ -65,7 +66,7 @@ marker(
65
66
  UrlInputComponent,
66
67
  ],
67
68
  providers: [
68
- provideIcons({ iconoirRefresh }),
69
+ provideIcons({ iconoirRefresh, matHelpOutline }),
69
70
  provideNgIconsConfig({
70
71
  size: '1.5em',
71
72
  }),