geonetwork-ui 2.9.0-dev.fc19cbfed → 2.10.0-dev.02185426a

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 +2382 -6943
  2. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  3. package/index.d.ts +378 -196
  4. package/index.d.ts.map +1 -1
  5. package/material-styles.css +483 -0
  6. package/package.json +25 -35
  7. package/src/libs/api/metadata-converter/src/lib/dcat-ap/dcat-ap.converter.ts +9 -0
  8. package/src/libs/api/metadata-converter/src/lib/fixtures/eu.dcat-ap.records.ts +2 -0
  9. package/src/libs/api/metadata-converter/src/lib/fixtures/generic.records.ts +1 -0
  10. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.reuse+ongules.ts +7 -0
  11. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.reuse+roilaye.ts +1 -0
  12. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.ts +1 -0
  13. package/src/libs/api/metadata-converter/src/lib/fixtures/geocat-ch.records.ts +1 -0
  14. package/src/libs/api/metadata-converter/src/lib/fixtures/georhena.records.ts +1 -0
  15. package/src/libs/api/metadata-converter/src/lib/fixtures/metadata-for-i18n.records.ts +2 -0
  16. package/src/libs/api/metadata-converter/src/lib/fixtures/metawal.records.ts +1 -0
  17. package/src/libs/api/metadata-converter/src/lib/fixtures/opendataswiss.records.ts +1 -0
  18. package/src/libs/api/metadata-converter/src/lib/fixtures/sextant.records.ts +2 -0
  19. package/src/libs/api/metadata-converter/src/lib/fixtures/vlaanderen.dcat-ap.records.ts +1 -0
  20. package/src/libs/api/metadata-converter/src/lib/fixtures/wallonie.records.reuse.ts +8 -0
  21. package/src/libs/api/metadata-converter/src/lib/gn4/atomic-operations.ts +48 -7
  22. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.converter.ts +1 -0
  23. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +5 -4
  24. package/src/libs/api/metadata-converter/src/lib/gn4/metadata-url.service.ts +2 -2
  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 +7 -0
  27. package/src/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.ts +8 -0
  28. package/src/libs/api/metadata-converter/src/lib/iso19115-3/write-parts.ts +8 -0
  29. package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +11 -0
  30. package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +33 -0
  31. package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +33 -0
  32. package/src/libs/api/repository/src/lib/gn4/auth/auth.service.ts +8 -4
  33. package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +2 -2
  34. package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +72 -37
  35. package/src/libs/api/repository/src/lib/gn4/gn4.provider.ts +6 -1
  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 +48 -2
  39. package/src/libs/common/domain/src/index.ts +2 -0
  40. package/src/libs/common/domain/src/lib/model/index.ts +3 -0
  41. package/src/libs/common/domain/src/lib/model/record/index.ts +2 -2
  42. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +13 -5
  43. package/src/libs/common/domain/src/lib/model/search/index.ts +1 -1
  44. package/src/libs/common/domain/src/lib/model/thesaurus/thesaurus.model.ts +2 -1
  45. package/src/libs/common/domain/src/lib/model/user/group.model.ts +8 -0
  46. package/src/libs/common/domain/src/lib/model/user/index.ts +1 -0
  47. package/src/libs/common/domain/src/lib/platform.service.interface.ts +2 -0
  48. package/src/libs/common/fixtures/src/lib/records.fixtures.ts +56 -0
  49. package/src/libs/feature/catalog/src/lib/sources/sources.service.ts +5 -2
  50. package/src/libs/feature/dataviz/src/lib/chart-view/chart-view.component.html +3 -3
  51. package/src/libs/feature/editor/src/index.ts +2 -0
  52. package/src/libs/feature/editor/src/lib/+state/editor.actions.ts +10 -1
  53. package/src/libs/feature/editor/src/lib/+state/editor.effects.ts +26 -20
  54. package/src/libs/feature/editor/src/lib/+state/editor.facade.ts +15 -2
  55. package/src/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.ts +0 -2
  56. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.css +0 -0
  57. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.html +67 -0
  58. package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.ts +39 -0
  59. package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.html +5 -14
  60. package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.ts +3 -28
  61. package/src/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.html +18 -3
  62. package/src/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.ts +33 -40
  63. package/src/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.html +11 -6
  64. package/src/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.ts +18 -2
  65. package/src/libs/feature/editor/src/lib/components/record-form/form-field/field-focus.directive.ts +41 -0
  66. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-constraints-shortcuts/form-field-constraints-shortcuts.component.ts +33 -34
  67. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.html +13 -13
  68. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.ts +18 -4
  69. 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
  70. 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
  71. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-resources/form-field-online-resources.component.ts +1 -1
  72. 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
  73. 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
  74. 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
  75. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.ts +12 -5
  76. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-rich/form-field-rich.component.html +4 -4
  77. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-rich/form-field-rich.component.ts +7 -0
  78. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-topics/form-field-topics.component.ts +2 -2
  79. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-update-frequency/form-field-update-frequency.component.html +6 -2
  80. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-update-frequency/form-field-update-frequency.component.ts +3 -1
  81. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.css +3 -0
  82. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +21 -16
  83. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +14 -0
  84. package/src/libs/feature/editor/src/lib/components/record-form/form-field/index.ts +1 -0
  85. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.css +35 -0
  86. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.html +18 -5
  87. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.ts +118 -5
  88. package/src/libs/feature/editor/src/lib/expressions.ts +380 -8
  89. package/src/libs/feature/editor/src/lib/fields.config.ts +310 -336
  90. package/src/libs/feature/editor/src/lib/models/editor-config.model.ts +6 -3
  91. package/src/libs/feature/editor/src/lib/services/editor.service.ts +9 -13
  92. package/src/libs/feature/map/src/lib/utils/map-utils.service.ts +33 -0
  93. package/src/libs/feature/notify-reuse/src/index.ts +1 -0
  94. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.css +0 -0
  95. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.html +1 -0
  96. package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.ts +21 -0
  97. package/src/libs/feature/record/src/lib/gpf-api-dl/gpf-api-dl.component.ts +8 -4
  98. package/src/libs/feature/record/src/lib/map-view/map-view.component.ts +9 -1
  99. package/src/libs/feature/search/src/lib/results-table/results-table-container.component.ts +10 -5
  100. package/src/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.ts +2 -2
  101. package/src/libs/ui/dataviz/src/index.ts +0 -1
  102. package/src/libs/ui/elements/src/index.ts +3 -0
  103. package/src/libs/ui/elements/src/lib/contact-details/contact-details.component.html +88 -0
  104. package/src/libs/ui/elements/src/lib/contact-details/contact-details.component.ts +45 -0
  105. package/src/libs/ui/elements/src/lib/contact-pill/contact-pill.component.html +41 -0
  106. package/src/libs/ui/elements/src/lib/contact-pill/contact-pill.component.ts +70 -0
  107. package/src/libs/ui/elements/src/lib/downloads-list/downloads-list.component.ts +5 -3
  108. package/src/libs/ui/elements/src/lib/external-link-card/external-link-card.component.html +2 -1
  109. package/src/libs/ui/elements/src/lib/external-link-card/external-link-card.component.ts +2 -2
  110. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.css +13 -0
  111. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.html +13 -3
  112. package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.ts +43 -6
  113. package/src/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.html +1 -1
  114. package/src/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.ts +0 -1
  115. package/src/libs/ui/elements/src/lib/keyword-badge/keyword-badge.component.html +14 -0
  116. package/src/libs/ui/elements/src/lib/keyword-badge/keyword-badge.component.ts +85 -0
  117. package/src/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.ts +2 -2
  118. package/src/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.ts +2 -5
  119. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.css +3 -2
  120. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +26 -68
  121. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts +30 -10
  122. package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.html +4 -5
  123. package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.ts +4 -0
  124. package/src/libs/ui/inputs/src/lib/badge/badge.component.html +33 -26
  125. package/src/libs/ui/inputs/src/lib/badge/badge.component.ts +9 -0
  126. package/src/libs/ui/inputs/src/lib/button/button.component.ts +8 -0
  127. package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.ts +2 -2
  128. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +1 -1
  129. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.ts +3 -3
  130. package/src/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.ts +1 -1
  131. package/src/libs/ui/inputs/src/lib/url-input/url-input.component.html +2 -2
  132. package/src/libs/ui/inputs/src/lib/url-input/url-input.component.ts +6 -2
  133. package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.html +5 -5
  134. package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.ts +9 -1
  135. package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.html +1 -1
  136. package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.ts +3 -8
  137. package/src/libs/ui/layout/src/lib/sortable-list/sortable-list.component.html +7 -7
  138. package/src/libs/ui/layout/src/lib/sortable-list/sortable-list.component.ts +14 -1
  139. package/src/libs/ui/map/src/lib/components/map-container/map-container.component.ts +2 -1
  140. package/src/libs/ui/map/src/lib/components/spatial-extent/spatial-extent.component.ts +15 -64
  141. package/src/libs/ui/map/src/lib/map-utils.ts +48 -0
  142. package/src/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.ts +1 -1
  143. package/src/libs/ui/search/src/lib/record-preview-row/record-preview-row.component.html +0 -1
  144. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.html +12 -8
  145. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.ts +3 -1
  146. package/src/libs/ui/search/src/lib/results-table/results-table.component.html +1 -0
  147. package/src/libs/ui/widgets/src/lib/popover/popover.component.ts +7 -1
  148. package/src/libs/util/app-config/src/lib/app-config.ts +72 -9
  149. package/src/libs/util/app-config/src/lib/model.ts +8 -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 +28 -2
  166. package/translations/de.json +82 -48
  167. package/translations/en.json +87 -53
  168. package/translations/es.json +56 -22
  169. package/translations/fr.json +84 -50
  170. package/translations/it.json +82 -48
  171. package/translations/nl.json +55 -21
  172. package/translations/pt.json +56 -22
  173. package/translations/sk.json +56 -22
  174. package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.css +0 -3
@@ -6,7 +6,7 @@
6
6
  type="url"
7
7
  [value]="inputValue"
8
8
  (input)="handleInput($event)"
9
- (keydown.enter)="handleUpload(input)"
9
+ (keydown.enter)="handleUpload(input, $event)"
10
10
  [placeholder]="placeholder"
11
11
  [attr.aria-label]="placeholder"
12
12
  [disabled]="disabled"
@@ -25,7 +25,7 @@
25
25
  extraClass="absolute inset-y-[var(--side-padding)] right-[var(--side-padding)]"
26
26
  type="primary"
27
27
  [disabled]="disabled || input.value === '' || !isValidUrl(input.value)"
28
- (buttonClick)="handleUpload(input)"
28
+ (buttonClick)="handleUpload(input, $event)"
29
29
  >
30
30
  <ng-content>
31
31
  <ng-icon name="iconoirArrowUp"></ng-icon>
@@ -62,7 +62,10 @@ export class UrlInputComponent implements OnChanges {
62
62
  inputValue = ''
63
63
 
64
64
  ngOnChanges(changes: SimpleChanges) {
65
- if (changes['resetUrlOnChange']) {
65
+ if (
66
+ changes['resetUrlOnChange'] &&
67
+ !changes['resetUrlOnChange'].firstChange
68
+ ) {
66
69
  this.inputValue = ''
67
70
  }
68
71
  }
@@ -78,7 +81,8 @@ export class UrlInputComponent implements OnChanges {
78
81
  this.valueChange.next(value)
79
82
  }
80
83
 
81
- handleUpload(element: HTMLInputElement) {
84
+ handleUpload(element: HTMLInputElement, event: Event) {
85
+ event.stopPropagation()
82
86
  const value = element.value
83
87
  if (!value || !this.isValidUrl(value)) return
84
88
  this.uploadClick.next(value)
@@ -3,14 +3,14 @@
3
3
  <span class="flex-none font-bold" translate="">{{ label }}</span>
4
4
  <div class="flex-1 flex justify-end items-center">
5
5
  <ng-content select="[form-field-interaction]"></ng-content>
6
- <span
6
+ <ng-icon
7
7
  *ngIf="hint"
8
- class="material-symbols-outlined m-2 gn-ui-icon-small"
8
+ name="matHelpOutline"
9
+ size="16"
10
+ class="m-2"
9
11
  [matTooltip]="hint"
10
12
  matTooltipPosition="above"
11
- >
12
- help
13
- </span>
13
+ ></ng-icon>
14
14
  </div>
15
15
  </div>
16
16
  <div class="flex-1 mt-2">
@@ -2,6 +2,8 @@ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
2
2
  import { MatTooltipModule } from '@angular/material/tooltip'
3
3
  import { CommonModule } from '@angular/common'
4
4
  import { TranslateDirective } from '@ngx-translate/core'
5
+ import { NgIconComponent, provideIcons } from '@ng-icons/core'
6
+ import { matHelpOutline } from '@ng-icons/material-icons/outline'
5
7
 
6
8
  @Component({
7
9
  selector: 'gn-ui-form-field-wrapper',
@@ -9,7 +11,13 @@ import { TranslateDirective } from '@ngx-translate/core'
9
11
  styleUrls: ['./form-field-wrapper.component.css'],
10
12
  changeDetection: ChangeDetectionStrategy.OnPush,
11
13
  standalone: true,
12
- imports: [MatTooltipModule, CommonModule, TranslateDirective],
14
+ imports: [
15
+ MatTooltipModule,
16
+ CommonModule,
17
+ TranslateDirective,
18
+ NgIconComponent,
19
+ ],
20
+ providers: [provideIcons({ matHelpOutline })],
13
21
  })
14
22
  export class FormFieldWrapperComponent {
15
23
  @Input() label?: string
@@ -37,7 +37,7 @@
37
37
  (click)="handleRowClick(item)"
38
38
  data-cy="table-row"
39
39
  [attr.data-cy-title]="item.title"
40
- [title]="getItemTitle(item) | translate"
40
+ [title]="getItemTitle(item) | translate: { recordKind: item['kind'] }"
41
41
  >
42
42
  <div
43
43
  class="relative h-0"
@@ -16,9 +16,6 @@ import { marker } from '@biesbjerg/ngx-translate-extract-marker'
16
16
  import { CatalogRecord } from '../../../../../../libs/common/domain/src/lib/model/record'
17
17
  import { Observable, of } from 'rxjs'
18
18
 
19
- marker('editor.record.lock.resourceType')
20
- marker('editor.record.lock.harvested')
21
- marker('editor.record.lock.owner')
22
19
  @Component({
23
20
  selector: 'gn-ui-interactive-table',
24
21
  templateUrl: './interactive-table.component.html',
@@ -53,12 +50,10 @@ export class InteractiveTableComponent {
53
50
 
54
51
  getItemTitle(item: CatalogRecord) {
55
52
  if (!this.isDraftPage) {
56
- if (item.kind !== 'dataset') {
57
- return 'editor.record.lock.resourceType'
58
- } else if (item.extras?.isHarvested) {
59
- return 'editor.record.lock.harvested'
53
+ if (item.extras?.isHarvested) {
54
+ return marker('editor.record.lock.harvested')
60
55
  } else if (!item.extras?.edit) {
61
- return 'editor.record.lock.owner'
56
+ return marker('editor.record.lock.owner')
62
57
  }
63
58
  }
64
59
  return ''
@@ -5,12 +5,12 @@
5
5
  >
6
6
  @for (item of items; track trackByFn(index); let index = $index) {
7
7
  <div class="sortable-box bg-white flex items-center" cdkDrag>
8
- <span
8
+ <ng-icon
9
9
  cdkDragHandle
10
- class="material-symbols-outlined mx-[10px] cursor-grab gn-ui-icon-medium flex-none"
11
- >
12
- drag_handle
13
- </span>
10
+ name="matDragHandleOutline"
11
+ size="20"
12
+ class="mx-[10px] cursor-grab flex-none"
13
+ ></ng-icon>
14
14
  <div class="flex-1">
15
15
  <ng-container
16
16
  *ngTemplateOutlet="
@@ -29,8 +29,8 @@
29
29
  class="ml-[8px] mr-[10px]"
30
30
  (buttonClick)="removeItem(index)"
31
31
  data-test="remove-item"
32
- ><span class="material-symbols-outlined gn-ui-icon-medium">close</span>
33
- </gn-ui-button>
32
+ ><ng-icon name="matCloseOutline" size="20"></ng-icon
33
+ ></gn-ui-button>
34
34
  </div>
35
35
  }
36
36
  </div>
@@ -15,6 +15,11 @@ import {
15
15
  TemplateRef,
16
16
  } from '@angular/core'
17
17
  import { ButtonComponent } from '../../../../../../libs/ui/inputs/src'
18
+ import { NgIconComponent, provideIcons } from '@ng-icons/core'
19
+ import {
20
+ matCloseOutline,
21
+ matDragHandleOutline,
22
+ } from '@ng-icons/material-icons/outline'
18
23
 
19
24
  @Component({
20
25
  selector: 'gn-ui-sortable-list',
@@ -22,7 +27,15 @@ import { ButtonComponent } from '../../../../../../libs/ui/inputs/src'
22
27
  styleUrls: ['sortable-list.component.css'],
23
28
  changeDetection: ChangeDetectionStrategy.OnPush,
24
29
  standalone: true,
25
- imports: [CommonModule, CdkDropList, CdkDrag, CdkDragHandle, ButtonComponent],
30
+ imports: [
31
+ CommonModule,
32
+ CdkDropList,
33
+ CdkDrag,
34
+ CdkDragHandle,
35
+ ButtonComponent,
36
+ NgIconComponent,
37
+ ],
38
+ providers: [provideIcons({ matDragHandleOutline, matCloseOutline })],
26
39
  })
27
40
  export class SortableListComponent {
28
41
  @Input() elementTemplate: TemplateRef<unknown>
@@ -204,11 +204,12 @@ export class MapContainerComponent implements AfterViewInit, OnChanges {
204
204
 
205
205
  async ngOnChanges(changes: SimpleChanges) {
206
206
  if ('context' in changes && !changes['context'].isFirstChange()) {
207
+ const olMap = await this.openlayersMap
207
208
  const diff = computeMapContextDiff(
208
209
  this.processContext(changes['context'].currentValue),
209
210
  this.processContext(changes['context'].previousValue)
210
211
  )
211
- await applyContextDiffToMap(this.olMap, diff)
212
+ await applyContextDiffToMap(olMap, diff)
212
213
 
213
214
  if (this._resolvedExtentChange && diff.viewChanges) {
214
215
  this._resolvedExtentChange.emit(this.calculateCurrentMapExtent())
@@ -1,18 +1,11 @@
1
- import { Component, Input } from '@angular/core'
1
+ import { ChangeDetectorRef, Component, inject, Input } from '@angular/core'
2
2
  import { CommonModule } from '@angular/common'
3
- import { Geometry } from 'geojson'
4
- import { GeoJSONFeatureCollection } from 'ol/format/GeoJSON.js'
5
- import GeoJSON from 'ol/format/GeoJSON.js'
6
- import { Polygon } from 'ol/geom.js'
7
- import {
8
- createViewFromLayer,
9
- MapContext,
10
- MapContextLayer,
11
- } from '@geospatial-sdk/core'
3
+ import { createViewFromLayer, MapContext } from '@geospatial-sdk/core'
12
4
  import { MapContainerComponent } from '../map-container/map-container.component'
13
- import { BehaviorSubject, Observable } from 'rxjs'
14
- import { switchMap } from 'rxjs/operators'
5
+ import { BehaviorSubject, from, Observable, of } from 'rxjs'
6
+ import { map, switchMap, tap } from 'rxjs/operators'
15
7
  import { DatasetSpatialExtent } from '../../../../../../../libs/common/domain/src/lib/model/record'
8
+ import { createSpatialExtentLayer } from '../../map-utils'
16
9
 
17
10
  @Component({
18
11
  selector: 'gn-ui-spatial-extent',
@@ -22,66 +15,24 @@ import { DatasetSpatialExtent } from '../../../../../../../libs/common/domain/sr
22
15
  styleUrl: './spatial-extent.component.css',
23
16
  })
24
17
  export class SpatialExtentComponent {
18
+ private _cdr = inject(ChangeDetectorRef)
19
+
25
20
  @Input() set spatialExtents(value: DatasetSpatialExtent[]) {
26
21
  this.spatialExtents$.next(value)
27
22
  }
28
23
  spatialExtents$ = new BehaviorSubject<DatasetSpatialExtent[]>([])
29
24
  mapContext$: Observable<MapContext> = this.spatialExtents$.pipe(
30
- switchMap(async (extents) => {
31
- if (extents.length === 0) {
32
- return null // null extent means default view
33
- }
34
- const featureCollection: GeoJSONFeatureCollection = {
35
- type: 'FeatureCollection',
36
- features: [],
37
- }
38
- extents.forEach((extent) => {
39
- if (extent.geometry) {
40
- featureCollection.features.push({
41
- type: 'Feature',
42
- properties: {},
43
- geometry: extent.geometry,
44
- })
45
- } else if (extent.bbox?.length >= 0) {
46
- featureCollection.features.push({
47
- type: 'Feature',
48
- properties: {},
49
- geometry: this.bboxCoordsToGeometry(extent.bbox),
50
- })
51
- }
52
- })
53
-
54
- const layer: MapContextLayer = {
55
- type: 'geojson',
56
- data: featureCollection,
57
- label: 'Spatial extents',
58
- style: {
59
- 'stroke-color': 'black',
60
- 'stroke-width': 2,
61
- 'fill-color': 'rgba(153, 153, 153, 0.3)',
62
- },
63
- }
64
- const view = await createViewFromLayer(layer)
65
- return {
66
- view,
67
- layers: [layer],
25
+ switchMap((extents) => {
26
+ const layer = createSpatialExtentLayer(extents)
27
+ if (!layer) {
28
+ return of(null)
68
29
  }
30
+ return from(createViewFromLayer(layer)).pipe(
31
+ map((view) => ({ view, layers: [layer] }) as MapContext),
32
+ tap(() => this._cdr.markForCheck())
33
+ )
69
34
  })
70
35
  )
71
36
 
72
37
  error = ''
73
-
74
- bboxCoordsToGeometry(bbox: [number, number, number, number]): Geometry {
75
- const geometry = new Polygon([
76
- [
77
- [bbox[0], bbox[1]],
78
- [bbox[0], bbox[3]],
79
- [bbox[2], bbox[3]],
80
- [bbox[2], bbox[1]],
81
- [bbox[0], bbox[1]],
82
- ],
83
- ])
84
-
85
- return new GeoJSON().writeGeometryObject(geometry)
86
- }
87
38
  }
@@ -12,6 +12,54 @@ import {
12
12
  MouseWheelZoom,
13
13
  } from 'ol/interaction.js'
14
14
  import MapBrowserEvent from 'ol/MapBrowserEvent.js'
15
+ import type { DatasetSpatialExtent } from '../../../../../libs/common/domain/src/lib/model/record'
16
+ import type {
17
+ MapContextLayer,
18
+ MapContextLayerGeojson,
19
+ } from '@geospatial-sdk/core'
20
+ import { spatialExtentsToFeatureCollection } from '../../../../../libs/util/shared/src'
21
+
22
+ export type SpatialExtentLayerStyle = NonNullable<
23
+ MapContextLayerGeojson['style']
24
+ >
25
+
26
+ /**
27
+ * Default style for a spatial-extent layer: a solid black outline over a
28
+ * translucent grey fill.
29
+ */
30
+ export const DEFAULT_SPATIAL_EXTENT_STYLE: SpatialExtentLayerStyle = {
31
+ 'stroke-color': 'black',
32
+ 'stroke-width': 2,
33
+ 'fill-color': 'rgba(153, 153, 153, 0.3)',
34
+ }
35
+
36
+ /**
37
+ * Builds a GeoJSON map layer drawing the given spatial extents: each extent is
38
+ * rendered from its own geometry or, when only a bounding box is available,
39
+ * from a polygon derived from that box.
40
+ *
41
+ * @returns the layer, or `null` when none of the extents can be represented.
42
+ */
43
+ export function createSpatialExtentLayer(
44
+ extents: DatasetSpatialExtent[],
45
+ overrides?: {
46
+ label?: string
47
+ clickable?: boolean
48
+ style?: SpatialExtentLayerStyle
49
+ }
50
+ ): MapContextLayer | null {
51
+ const data = spatialExtentsToFeatureCollection(extents)
52
+ if (data.features.length === 0) {
53
+ return null
54
+ }
55
+ return {
56
+ type: 'geojson',
57
+ data,
58
+ label: 'Spatial extents',
59
+ style: DEFAULT_SPATIAL_EXTENT_STYLE,
60
+ ...overrides,
61
+ }
62
+ }
15
63
 
16
64
  export function prioritizePageScroll(interactions: Collection<Interaction>) {
17
65
  interactions.clear()
@@ -52,7 +52,7 @@ export class RecordPreviewFeedComponent extends RecordPreviewComponent {
52
52
  protected elementRef: ElementRef
53
53
  private translate = inject(TranslateService)
54
54
 
55
- timeFormat = new Duration(this.translate.currentLang, {})
55
+ timeFormat = new Duration(this.translate.getCurrentLang(), {})
56
56
 
57
57
  constructor() {
58
58
  const elementRef = inject(ElementRef)
@@ -1,6 +1,5 @@
1
1
  <gn-ui-internal-link-card
2
2
  [linkHref]="linkHref"
3
- [linkTarget]="linkTarget"
4
3
  [record]="record"
5
4
  [favoriteTemplate]="favoriteTemplate"
6
5
  [metadataQualityDisplay]="metadataQualityDisplay"
@@ -52,10 +52,10 @@
52
52
  class="w-72 p-6 flex flex-col gap-3 mt-2 border border-gray-100 bg-white shadow-2xl rounded-2xl"
53
53
  >
54
54
  <span class="text-lg font-bold text-center">{{
55
- 'editor.record.delete.confirmation.title' | translate
55
+ 'editor.record.delete.confirmation.title' | translate: { recordKind }
56
56
  }}</span>
57
57
  <span class="text-center">{{
58
- 'editor.record.delete.confirmation.message' | translate
58
+ 'editor.record.delete.confirmation.message' | translate: { recordKind }
59
59
  }}</span>
60
60
  <div class="flex flex-row gap-8 justify-center">
61
61
  <gn-ui-button
@@ -65,14 +65,16 @@
65
65
  data-cy="confirm-button"
66
66
  [style.--gn-ui-button-width]="'100px'"
67
67
  >{{
68
- 'editor.record.delete.confirmation.confirmText' | translate
68
+ 'editor.record.delete.confirmation.confirmText'
69
+ | translate: { recordKind }
69
70
  }}</gn-ui-button
70
71
  >
71
72
  <gn-ui-button
72
73
  [style.--gn-ui-button-width]="'100px'"
73
74
  (buttonClick)="closeActionMenu.emit()"
74
75
  >{{
75
- 'editor.record.delete.confirmation.cancelText' | translate
76
+ 'editor.record.delete.confirmation.cancelText'
77
+ | translate: { recordKind }
76
78
  }}</gn-ui-button
77
79
  >
78
80
  </div>
@@ -84,10 +86,10 @@
84
86
  class="w-80 p-6 flex flex-col gap-3 mt-2 border border-gray-100 bg-white shadow-2xl rounded-2xl"
85
87
  >
86
88
  <span class="text-lg font-bold text-center">{{
87
- 'editor.record.undo.confirmation.title' | translate
89
+ 'editor.record.undo.confirmation.title' | translate: { recordKind }
88
90
  }}</span>
89
91
  <span class="text-center">{{
90
- 'editor.record.undo.confirmation.message' | translate
92
+ 'editor.record.undo.confirmation.message' | translate: { recordKind }
91
93
  }}</span>
92
94
  <div class="flex flex-row gap-8 justify-center">
93
95
  <gn-ui-button
@@ -97,14 +99,16 @@
97
99
  data-cy="confirm-button"
98
100
  [style.--gn-ui-button-width]="'120px'"
99
101
  >{{
100
- 'editor.record.undo.confirmation.confirmText' | translate
102
+ 'editor.record.undo.confirmation.confirmText'
103
+ | translate: { recordKind }
101
104
  }}</gn-ui-button
102
105
  >
103
106
  <gn-ui-button
104
107
  [style.--gn-ui-button-width]="'120px'"
105
108
  (buttonClick)="closeActionMenu.emit()"
106
109
  >{{
107
- 'editor.record.undo.confirmation.cancelText' | translate
110
+ 'editor.record.undo.confirmation.cancelText'
111
+ | translate: { recordKind }
108
112
  }}</gn-ui-button
109
113
  >
110
114
  </div>
@@ -2,15 +2,16 @@ import {
2
2
  ChangeDetectorRef,
3
3
  Component,
4
4
  EventEmitter,
5
+ inject,
5
6
  Input,
6
7
  Output,
7
8
  ViewChild,
8
- inject,
9
9
  } from '@angular/core'
10
10
  import { MatDialog, MatDialogModule } from '@angular/material/dialog'
11
11
  import { MatMenuModule, MatMenuTrigger } from '@angular/material/menu'
12
12
  import { ButtonComponent } from '../../../../../../../libs/ui/inputs/src'
13
13
  import { TranslateDirective, TranslatePipe } from '@ngx-translate/core'
14
+ import { RecordKind } from '../../../../../../../libs/common/domain/src/lib/model'
14
15
 
15
16
  type ActionMenuPage = 'mainMenu' | 'deleteMenu' | 'rollbackMenu'
16
17
 
@@ -35,6 +36,7 @@ export class ActionMenuComponent {
35
36
  @Input() isDuplicating: boolean
36
37
  @Input() canDelete = true
37
38
  @Input() page: 'draft' | 'main' | 'record'
39
+ @Input() recordKind: RecordKind
38
40
  @Output() duplicate = new EventEmitter<void>()
39
41
  @Output() delete = new EventEmitter<void>()
40
42
  @Output() closeActionMenu = new EventEmitter<void>()
@@ -172,6 +172,7 @@
172
172
  (delete)="handleDelete(item)"
173
173
  (closeActionMenu)="closeActionMenu()"
174
174
  (rollback)="handleRollback(item)"
175
+ [recordKind]="item.kind"
175
176
  >
176
177
  </gn-ui-action-menu>
177
178
  </ng-template>
@@ -28,7 +28,13 @@ export class PopoverComponent implements AfterViewInit, OnChanges, OnDestroy {
28
28
 
29
29
  @ViewChild('popoverContent', { static: false }) popoverContent: ElementRef
30
30
  @Input() content: string | TemplateRef<any>
31
- @Input() theme: 'light' | 'light-border' | 'translucent' | 'material' | ''
31
+ @Input() theme:
32
+ | 'light'
33
+ | 'light-border'
34
+ | 'translucent'
35
+ | 'material'
36
+ | 'dark'
37
+ | ''
32
38
 
33
39
  private tippyInstance: Instance
34
40
  private view: EmbeddedViewRef<any>
@@ -1,6 +1,8 @@
1
1
  import * as TOML from '@ltd/j-toml'
2
2
  import {
3
3
  checkMetadataLanguage,
4
+ checkNewRecordDefaultLanguage,
5
+ checkNewRecordStandard,
4
6
  parseConfigSection,
5
7
  parseMultiConfigSection,
6
8
  parseTranslationsConfigSection,
@@ -8,6 +10,7 @@ import {
8
10
  import {
9
11
  CustomTranslations,
10
12
  CustomTranslationsAllLanguages,
13
+ EditorConfig,
11
14
  GlobalConfig,
12
15
  LayerConfig,
13
16
  MapConfig,
@@ -15,10 +18,15 @@ import {
15
18
  SearchConfig,
16
19
  ThemeConfig,
17
20
  } from './model'
18
- import { TranslateCompiler, TranslateLoader } from '@ngx-translate/core'
21
+ import {
22
+ TranslateCompiler,
23
+ TranslateLoader,
24
+ TranslateModuleConfig,
25
+ } from '@ngx-translate/core'
19
26
  import { TranslateMessageFormatCompiler } from 'ngx-translate-messageformat-compiler'
20
- import { HttpClient } from '@angular/common/http'
21
27
  import { FileWithOverridesTranslateLoader } from './i18n/file-with-overrides.translate.loader'
28
+ import { TRANSLATE_HTTP_LOADER_CONFIG } from '@ngx-translate/http-loader'
29
+ import { HttpClient } from '@angular/common/http'
22
30
 
23
31
  const MISSING_CONFIG_ERROR = `Application configuration was not initialized correctly.
24
32
  This error might show up in case of an invalid/malformed configuration file.
@@ -51,6 +59,12 @@ export function getOptionalSearchConfig(): SearchConfig | null {
51
59
  return searchConfig
52
60
  }
53
61
 
62
+ let editorConfig: EditorConfig = null
63
+
64
+ export function getOptionalEditorConfig(): EditorConfig | null {
65
+ return editorConfig
66
+ }
67
+
54
68
  let metadataQualityConfig: MetadataQualityConfig = null
55
69
  export function getMetadataQualityConfig(): MetadataQualityConfig {
56
70
  return (
@@ -97,6 +111,7 @@ export function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
97
111
  ['geonetwork4_api_url'],
98
112
  [
99
113
  'datahub_url',
114
+ 'edit_url_template',
100
115
  'proxy_path',
101
116
  'metadata_language',
102
117
  'login_url',
@@ -122,6 +137,7 @@ export function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
122
137
  : ({
123
138
  GN4_API_URL: parsedGlobalSection.geonetwork4_api_url,
124
139
  DATAHUB_URL: parsedGlobalSection.datahub_url,
140
+ EDIT_URL_TEMPLATE: parsedGlobalSection.edit_url_template,
125
141
  PROXY_PATH: parsedGlobalSection.proxy_path,
126
142
  METADATA_LANGUAGE: parsedGlobalSection.metadata_language
127
143
  ? (
@@ -283,6 +299,45 @@ export function loadAppConfig(configUrl = 'assets/configuration/default.toml') {
283
299
  SORTABLE: parsedMetadataQualitySection.sortable,
284
300
  } as MetadataQualityConfig)
285
301
 
302
+ let parsedEditingSection = parseConfigSection(
303
+ parsed,
304
+ 'editing',
305
+ [],
306
+ ['new_record_default_language', 'new_record_standard'],
307
+ warnings,
308
+ errors
309
+ )
310
+ if (
311
+ parsedEditingSection !== null &&
312
+ parsedEditingSection.new_record_default_language !== undefined
313
+ ) {
314
+ parsedEditingSection = checkNewRecordDefaultLanguage(
315
+ parsedEditingSection,
316
+ warnings
317
+ )
318
+ }
319
+ if (
320
+ parsedEditingSection !== null &&
321
+ parsedEditingSection.new_record_standard !== undefined
322
+ ) {
323
+ parsedEditingSection = checkNewRecordStandard(
324
+ parsedEditingSection,
325
+ warnings
326
+ )
327
+ }
328
+ editorConfig =
329
+ parsedEditingSection === null
330
+ ? null
331
+ : ({
332
+ NEW_RECORD_DEFAULT_LANGUAGE:
333
+ parsedEditingSection.new_record_default_language as
334
+ | string
335
+ | undefined,
336
+ NEW_RECORD_STANDARD: parsedEditingSection.new_record_standard as
337
+ | EditorConfig['NEW_RECORD_STANDARD']
338
+ | undefined,
339
+ } as EditorConfig)
340
+
286
341
  customTranslations = parseTranslationsConfigSection(
287
342
  parsed,
288
343
  'translations'
@@ -307,19 +362,27 @@ export function isConfigLoaded() {
307
362
  export function _reset() {
308
363
  globalConfig = null
309
364
  themeConfig = null
365
+ editorConfig = null
310
366
  customTranslations = null
311
367
  }
312
368
 
313
- export const TRANSLATE_WITH_OVERRIDES_CONFIG = {
369
+ export const TRANSLATE_WITH_OVERRIDES_CONFIG: TranslateModuleConfig = {
314
370
  compiler: {
315
371
  provide: TranslateCompiler,
316
372
  useClass: TranslateMessageFormatCompiler,
317
373
  },
318
- loader: {
319
- provide: TranslateLoader,
320
- useFactory: function HttpLoaderFactory(http: HttpClient) {
321
- return new FileWithOverridesTranslateLoader(http, './assets/i18n/')
374
+ loader: [
375
+ {
376
+ provide: TRANSLATE_HTTP_LOADER_CONFIG,
377
+ useValue: {
378
+ prefix: './assets/i18n/',
379
+ suffix: '.json',
380
+ },
322
381
  },
323
- deps: [HttpClient],
324
- },
382
+ {
383
+ provide: TranslateLoader,
384
+ useClass: FileWithOverridesTranslateLoader,
385
+ deps: [HttpClient, TRANSLATE_HTTP_LOADER_CONFIG],
386
+ },
387
+ ],
325
388
  }
@@ -3,6 +3,7 @@ import { Geometry } from 'geojson'
3
3
  export interface GlobalConfig {
4
4
  GN4_API_URL: string
5
5
  DATAHUB_URL?: string
6
+ EDIT_URL_TEMPLATE?: string
6
7
  PROXY_PATH?: string
7
8
  METADATA_LANGUAGE?: string
8
9
  LOGIN_URL?: string
@@ -68,6 +69,13 @@ export interface MetadataQualityConfig {
68
69
  ENABLED: boolean
69
70
  }
70
71
 
72
+ export type NewRecordStandard = 'iso19139' | 'iso19115-3'
73
+
74
+ export interface EditorConfig {
75
+ NEW_RECORD_DEFAULT_LANGUAGE?: string
76
+ NEW_RECORD_STANDARD?: NewRecordStandard
77
+ }
78
+
71
79
  export type CustomTranslations = { [translationKey: string]: string }
72
80
  export type CustomTranslationsAllLanguages = {
73
81
  [lang: string]: CustomTranslations