geonetwork-ui 2.10.0-dev.fc6515c0b → 2.11.0-dev.5c2e5295e

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 (161) hide show
  1. package/fesm2022/geonetwork-ui.mjs +1709 -6267
  2. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  3. package/index.d.ts +230 -172
  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/elasticsearch.model.ts +13 -1
  25. package/src/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.ts +1 -0
  26. package/src/libs/api/metadata-converter/src/lib/iso19115-3/iso19115-3.converter.ts +11 -0
  27. package/src/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.ts +21 -0
  28. package/src/libs/api/metadata-converter/src/lib/iso19115-3/write-parts.ts +52 -0
  29. package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +24 -1
  30. package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +77 -0
  31. package/src/libs/api/metadata-converter/src/lib/iso19139/utils/association-type.mapper.ts +12 -0
  32. package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +79 -0
  33. package/src/libs/api/repository/src/lib/gn4/auth/auth.service.ts +8 -4
  34. package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +24 -5
  35. package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +66 -40
  36. package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-groups.service.ts +1 -1
  37. package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-metadata.service.ts +1 -1
  38. package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +37 -2
  39. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +49 -5
  40. package/src/libs/common/domain/src/lib/model/search/sort-by.model.ts +1 -5
  41. package/src/libs/common/domain/src/lib/model/thesaurus/thesaurus.model.ts +2 -1
  42. package/src/libs/common/domain/src/lib/model/user/group.model.ts +8 -0
  43. package/src/libs/common/domain/src/lib/model/user/index.ts +1 -0
  44. package/src/libs/common/domain/src/lib/platform.service.interface.ts +2 -0
  45. package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +2 -1
  46. package/src/libs/common/fixtures/src/lib/records.fixtures.ts +62 -0
  47. package/src/libs/feature/catalog/src/lib/sources/sources.service.ts +5 -2
  48. package/src/libs/feature/editor/src/index.ts +1 -0
  49. package/src/libs/feature/editor/src/lib/+state/editor.actions.ts +4 -1
  50. package/src/libs/feature/editor/src/lib/+state/editor.effects.ts +26 -19
  51. package/src/libs/feature/editor/src/lib/+state/editor.facade.ts +5 -1
  52. package/src/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.ts +0 -2
  53. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.css +0 -0
  54. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.html +68 -0
  55. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.ts +41 -0
  56. package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.html +5 -14
  57. package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.ts +3 -28
  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 +4 -1
  61. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-associated-records/form-field-associated-records.component.css +0 -0
  62. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-associated-records/form-field-associated-records.component.html +33 -0
  63. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-associated-records/form-field-associated-records.component.ts +94 -0
  64. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-constraints-shortcuts/form-field-constraints-shortcuts.component.ts +7 -7
  65. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.html +9 -2
  66. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.ts +12 -0
  67. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-resources/form-field-online-resources.component.ts +1 -1
  68. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.ts +12 -5
  69. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-rich/form-field-rich.component.html +4 -4
  70. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-rich/form-field-rich.component.ts +7 -0
  71. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-temporal-extents/form-field-temporal-extents.component.html +16 -12
  72. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-topics/form-field-topics.component.ts +2 -2
  73. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-update-frequency/form-field-update-frequency.component.html +6 -2
  74. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-update-frequency/form-field-update-frequency.component.ts +3 -1
  75. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.css +0 -34
  76. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +34 -16
  77. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +17 -0
  78. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.css +35 -0
  79. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.html +18 -5
  80. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.ts +76 -19
  81. package/src/libs/feature/editor/src/lib/expressions.ts +380 -8
  82. package/src/libs/feature/editor/src/lib/fields.config.ts +359 -337
  83. package/src/libs/feature/editor/src/lib/models/editor-config.model.ts +10 -3
  84. package/src/libs/feature/editor/src/lib/services/editor.service.ts +9 -13
  85. package/src/libs/feature/map/src/lib/utils/map-utils.service.ts +33 -0
  86. package/src/libs/feature/notify-reuse/src/index.ts +1 -0
  87. package/src/libs/feature/notify-reuse/src/lib/edit-delete-reuse-buttons/edit-delete-reuse-buttons.component.css +0 -0
  88. package/src/libs/feature/notify-reuse/src/lib/edit-delete-reuse-buttons/edit-delete-reuse-buttons.component.html +23 -0
  89. package/src/libs/feature/notify-reuse/src/lib/edit-delete-reuse-buttons/edit-delete-reuse-buttons.component.ts +114 -0
  90. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.html +111 -1
  91. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.ts +219 -8
  92. package/src/libs/feature/notify-reuse/src/lib/utils/url.ts +16 -0
  93. package/src/libs/feature/record/src/lib/map-view/map-view.component.ts +9 -1
  94. package/src/libs/feature/router/src/lib/default/router.service.ts +1 -1
  95. package/src/libs/feature/search/src/lib/results-table/results-table-container.component.ts +10 -5
  96. package/src/libs/feature/search/src/lib/sort-by/sort-by.component.ts +1 -1
  97. package/src/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.ts +2 -2
  98. package/src/libs/ui/dataviz/src/index.ts +0 -1
  99. package/src/libs/ui/elements/src/index.ts +1 -0
  100. package/src/libs/ui/elements/src/lib/contact-details/contact-details.component.html +48 -56
  101. package/src/libs/ui/elements/src/lib/contact-pill/contact-pill.component.html +7 -3
  102. package/src/libs/ui/elements/src/lib/external-link-card/external-link-card.component.html +2 -1
  103. package/src/libs/ui/elements/src/lib/external-link-card/external-link-card.component.ts +2 -2
  104. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.css +13 -0
  105. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.html +13 -3
  106. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.ts +43 -6
  107. package/src/libs/ui/elements/src/lib/keyword-badge/keyword-badge.component.html +14 -0
  108. package/src/libs/ui/elements/src/lib/keyword-badge/keyword-badge.component.ts +85 -0
  109. package/src/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.ts +2 -2
  110. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.css +5 -0
  111. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +5 -8
  112. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts +2 -2
  113. package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.html +4 -5
  114. package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.ts +4 -0
  115. package/src/libs/ui/inputs/src/lib/badge/badge.component.html +33 -26
  116. package/src/libs/ui/inputs/src/lib/badge/badge.component.ts +9 -0
  117. package/src/libs/ui/inputs/src/lib/button/button.component.ts +4 -0
  118. package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.ts +2 -2
  119. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +1 -1
  120. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.ts +3 -3
  121. package/src/libs/ui/inputs/src/lib/text-input/text-input.component.css +0 -3
  122. package/src/libs/ui/inputs/src/lib/text-input/text-input.component.html +11 -7
  123. package/src/libs/ui/inputs/src/lib/text-input/text-input.component.ts +56 -30
  124. package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.html +5 -5
  125. package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.ts +9 -1
  126. package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.html +1 -1
  127. package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.ts +3 -8
  128. package/src/libs/ui/layout/src/lib/sortable-list/sortable-list.component.html +7 -7
  129. package/src/libs/ui/layout/src/lib/sortable-list/sortable-list.component.ts +14 -1
  130. package/src/libs/ui/map/src/lib/components/spatial-extent/spatial-extent.component.ts +4 -54
  131. package/src/libs/ui/map/src/lib/map-utils.ts +48 -0
  132. package/src/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.ts +1 -1
  133. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.html +12 -8
  134. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.ts +3 -1
  135. package/src/libs/ui/search/src/lib/results-table/results-table.component.html +1 -0
  136. package/src/libs/ui/widgets/src/lib/popover/popover.component.ts +7 -1
  137. package/src/libs/ui/widgets/src/lib/spinning-loader/spinning-loader.component.html +1 -1
  138. package/src/libs/util/app-config/src/lib/app-config.ts +35 -10
  139. package/src/libs/util/app-config/src/lib/model.ts +3 -0
  140. package/src/libs/util/app-config/src/lib/parse-utils.ts +27 -0
  141. package/src/libs/util/data-fetcher/src/lib/readers/base-file.ts +1 -2
  142. package/src/libs/util/data-fetcher/src/lib/readers/wfs.ts +6 -3
  143. package/src/libs/util/i18n/src/index.ts +0 -1
  144. package/src/libs/util/i18n/src/lib/i18n.constants.ts +17 -11
  145. package/src/libs/util/i18n/src/lib/i18n.interceptor.ts +2 -2
  146. package/src/libs/util/i18n/src/lib/i18n.providers.ts +14 -17
  147. package/src/libs/util/i18n/src/lib/test.translate.loader.ts +48 -0
  148. package/src/libs/util/shared/src/lib/links/link-utils.ts +23 -1
  149. package/src/libs/util/shared/src/lib/services/date.service.ts +3 -3
  150. package/src/libs/util/shared/src/lib/utils/geojson.ts +58 -1
  151. package/style.css +0 -1
  152. package/tailwind.base.css +15 -0
  153. package/translations/de.json +122 -50
  154. package/translations/en.json +127 -55
  155. package/translations/es.json +96 -24
  156. package/translations/fr.json +124 -52
  157. package/translations/it.json +123 -51
  158. package/translations/nl.json +95 -23
  159. package/translations/pt.json +96 -24
  160. package/translations/sk.json +96 -24
  161. package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.css +0 -3
@@ -1,5 +1,6 @@
1
1
  import type { Observable } from 'rxjs'
2
2
  import type { UserModel } from './model/user/user.model'
3
+ import type { GroupModel } from './model/user/group.model'
3
4
  import type { Organization } from './model/record/organization.model'
4
5
  import { CatalogRecord, Keyword, UserFeedback } from './model/record'
5
6
  import { KeywordType } from './model/thesaurus'
@@ -28,6 +29,7 @@ export abstract class PlatformServiceInterface {
28
29
  abstract getMe(): Observable<UserModel>
29
30
  abstract isAnonymous(): Observable<boolean>
30
31
  abstract getUsers(): Observable<UserModel[]>
32
+ abstract getUserPermissionsByGroup(): Observable<GroupModel[]>
31
33
  abstract getUsersByOrganization(
32
34
  organisation: Organization
33
35
  ): Observable<UserModel[]>
@@ -39,7 +39,8 @@ export abstract class RecordsRepositoryInterface {
39
39
  * @returns Observable<[CatalogRecord, string, boolean] | null>
40
40
  */
41
41
  abstract openRecordForEdition(
42
- uniqueIdentifier: string
42
+ uniqueIdentifier: string,
43
+ disableDraft?: boolean
43
44
  ): Observable<[CatalogRecord, string, boolean] | null>
44
45
 
45
46
  /**
@@ -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'
@@ -29,7 +29,10 @@ export const markRecordAsChanged = createAction(
29
29
  '[Editor] Mark record as changed'
30
30
  )
31
31
 
32
- export const saveRecord = createAction('[Editor] Save record')
32
+ export const saveRecord = createAction(
33
+ '[Editor] Save record',
34
+ props<{ publish: boolean }>()
35
+ )
33
36
  export const saveRecordSuccess = createAction('[Editor] Save record success')
34
37
  export const saveRecordFailure = createAction(
35
38
  '[Editor] Save record failure',
@@ -28,30 +28,37 @@ export class EditorEffects {
28
28
  this.store.select(selectRecordSource),
29
29
  this.store.select(selectEditorConfig)
30
30
  ),
31
- switchMap(([, record, recordSource, fieldsConfig]) =>
32
- this.editorService.saveRecord(record, recordSource, fieldsConfig).pipe(
33
- switchMap(([savedRecord, savedRecordSource]) => {
34
- const actions: Action[] = [EditorActions.saveRecordSuccess()]
31
+ switchMap(([{ publish }, record, recordSource, fieldsConfig]) =>
32
+ this.editorService
33
+ .saveRecord(record, recordSource, fieldsConfig, publish)
34
+ .pipe(
35
+ switchMap(([savedRecord, savedRecordSource]) => {
36
+ const actions: Action[] = [EditorActions.saveRecordSuccess()]
37
+
38
+ if (publish) {
39
+ // saving without publishing doesn't unpublish
40
+ actions.push(EditorActions.isPublished({ isPublished: true }))
41
+ }
35
42
 
36
- if (!record?.uniqueIdentifier) {
37
- actions.push(
38
- EditorActions.openRecord({
39
- record: savedRecord,
40
- recordSource: savedRecordSource,
43
+ if (!record?.uniqueIdentifier) {
44
+ actions.push(
45
+ EditorActions.openRecord({
46
+ record: savedRecord,
47
+ recordSource: savedRecordSource,
48
+ })
49
+ )
50
+ }
51
+
52
+ return of(...actions)
53
+ }),
54
+ catchError((error) =>
55
+ of(
56
+ EditorActions.saveRecordFailure({
57
+ error,
41
58
  })
42
59
  )
43
- }
44
-
45
- return of(...actions)
46
- }),
47
- catchError((error) =>
48
- of(
49
- EditorActions.saveRecordFailure({
50
- error,
51
- })
52
60
  )
53
61
  )
54
- )
55
62
  )
56
63
  )
57
64
  )
@@ -54,7 +54,11 @@ export class EditorFacade {
54
54
  }
55
55
 
56
56
  saveRecord() {
57
- this.store.dispatch(EditorActions.saveRecord())
57
+ this.store.dispatch(EditorActions.saveRecord({ publish: false }))
58
+ }
59
+
60
+ saveAndPublishRecord() {
61
+ this.store.dispatch(EditorActions.saveRecord({ publish: true }))
58
62
  }
59
63
 
60
64
  undoRecordDraft() {
@@ -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
  }
@@ -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
  }),
@@ -10,7 +10,7 @@ export class FieldFocusDirective {
10
10
 
11
11
  private el = inject(ElementRef)
12
12
 
13
- public focusField() {
13
+ public focusField(focusInnerTarget = true) {
14
14
  setTimeout(() => {
15
15
  const host = this.el.nativeElement as HTMLElement
16
16
  const glowClass = this.gnUiFieldFocusGlowClass
@@ -25,6 +25,9 @@ export class FieldFocusDirective {
25
25
  )
26
26
 
27
27
  host.scrollIntoView({ behavior: 'smooth', block: 'start' })
28
+ if (!focusInnerTarget) {
29
+ return
30
+ }
28
31
  const target =
29
32
  host.querySelector<HTMLElement>(
30
33
  'input, textarea, select, [contenteditable="true"]'
@@ -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
+ }
@@ -131,14 +131,14 @@ export class FormFieldConstraintsShortcutsComponent implements OnDestroy {
131
131
  map((c) => c.length > 0),
132
132
  distinctUntilChanged()
133
133
  )
134
- combineLatest([
135
- isConstraintNotEmpty$,
136
- this.anyToggleActivated$,
137
- ]).subscribe(([isNotEmpty, anyToggleActivated]) => {
138
- const visible = isNotEmpty && !anyToggleActivated
139
- this.editorFacade.setFieldVisibility({ model }, visible)
140
- })
134
+ combineLatest([isConstraintNotEmpty$, this.anyToggleActivated$])
135
+ .pipe(takeUntil(this.onDestroy$))
136
+ .subscribe(([isNotEmpty, anyToggleActivated]) => {
137
+ const visible = isNotEmpty && !anyToggleActivated
138
+ this.editorFacade.setFieldVisibility({ model }, visible)
139
+ })
141
140
  }
141
+ hideEmptyConstraints(this.legalConstraints$, 'legalConstraints')
142
142
  hideEmptyConstraints(this.securityConstraints$, 'securityConstraints')
143
143
  hideEmptyConstraints(this.otherConstraints$, 'otherConstraints')
144
144
  })