geonetwork-ui 2.4.0-dev.aa5f997e → 2.4.0-dev.ac57b75b

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 (137) hide show
  1. package/esm2022/libs/api/metadata-converter/src/index.mjs +1 -2
  2. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.mjs +5 -5
  3. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/read-parts.mjs +30 -2
  4. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/utils/geometry.mjs +31 -0
  5. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/write-parts.mjs +23 -1
  6. package/esm2022/libs/api/metadata-converter/src/lib/xml-utils.mjs +6 -1
  7. package/esm2022/libs/api/repository/src/lib/gn4/gn4-repository.mjs +21 -4
  8. package/esm2022/libs/common/domain/src/lib/model/record/metadata.model.mjs +1 -1
  9. package/esm2022/libs/common/domain/src/lib/repository/records-repository.interface.mjs +1 -1
  10. package/esm2022/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.mjs +138 -0
  11. package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.mjs +21 -0
  12. package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field-temporal-extents/form-field-temporal-extents.component.mjs +7 -6
  13. package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.mjs +19 -9
  14. package/esm2022/libs/feature/editor/src/lib/components/record-form/record-form.component.mjs +2 -1
  15. package/esm2022/libs/feature/editor/src/lib/fields.config.mjs +12 -2
  16. package/esm2022/libs/feature/map/src/lib/utils/map-utils.service.mjs +10 -5
  17. package/esm2022/libs/feature/search/src/lib/results-table/results-table-container.component.mjs +35 -7
  18. package/esm2022/libs/feature/search/src/lib/state/search.facade.mjs +6 -2
  19. package/esm2022/libs/ui/elements/src/index.mjs +2 -1
  20. package/esm2022/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.mjs +27 -0
  21. package/esm2022/libs/ui/elements/src/lib/markdown-editor/markdown-editor.component.mjs +4 -3
  22. package/esm2022/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.mjs +2 -2
  23. package/esm2022/libs/ui/elements/src/lib/metadata-info/metadata-info.component.mjs +3 -3
  24. package/esm2022/libs/ui/elements/src/lib/sortable-list/sortable-list.component.mjs +6 -3
  25. package/esm2022/libs/ui/inputs/src/lib/image-input/image-input.component.mjs +11 -5
  26. package/esm2022/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.mjs +3 -3
  27. package/esm2022/libs/ui/layout/src/lib/interactive-table/interactive-table.component.mjs +3 -3
  28. package/esm2022/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.mjs +49 -11
  29. package/esm2022/libs/ui/search/src/lib/results-table/results-table.component.mjs +18 -6
  30. package/esm2022/translations/de.json +16 -16
  31. package/esm2022/translations/en.json +9 -9
  32. package/esm2022/translations/es.json +9 -9
  33. package/esm2022/translations/fr.json +18 -18
  34. package/esm2022/translations/it.json +9 -9
  35. package/esm2022/translations/nl.json +9 -9
  36. package/esm2022/translations/pt.json +9 -9
  37. package/fesm2022/geonetwork-ui.mjs +627 -253
  38. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  39. package/libs/api/metadata-converter/src/index.d.ts +0 -1
  40. package/libs/api/metadata-converter/src/index.d.ts.map +1 -1
  41. package/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.d.ts.map +1 -1
  42. package/libs/api/metadata-converter/src/lib/iso19139/read-parts.d.ts +8 -1
  43. package/libs/api/metadata-converter/src/lib/iso19139/read-parts.d.ts.map +1 -1
  44. package/libs/api/metadata-converter/src/lib/iso19139/utils/geometry.d.ts +5 -0
  45. package/libs/api/metadata-converter/src/lib/iso19139/utils/geometry.d.ts.map +1 -0
  46. package/libs/api/metadata-converter/src/lib/iso19139/write-parts.d.ts +3 -1
  47. package/libs/api/metadata-converter/src/lib/iso19139/write-parts.d.ts.map +1 -1
  48. package/libs/api/metadata-converter/src/lib/xml-utils.d.ts +1 -0
  49. package/libs/api/metadata-converter/src/lib/xml-utils.d.ts.map +1 -1
  50. package/libs/api/repository/src/lib/gn4/gn4-repository.d.ts +6 -1
  51. package/libs/api/repository/src/lib/gn4/gn4-repository.d.ts.map +1 -1
  52. package/libs/common/domain/src/lib/model/record/metadata.model.d.ts +2 -1
  53. package/libs/common/domain/src/lib/model/record/metadata.model.d.ts.map +1 -1
  54. package/libs/common/domain/src/lib/repository/records-repository.interface.d.ts +8 -0
  55. package/libs/common/domain/src/lib/repository/records-repository.interface.d.ts.map +1 -1
  56. package/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.d.ts +27 -0
  57. package/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.d.ts.map +1 -0
  58. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.d.ts +11 -0
  59. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.d.ts.map +1 -0
  60. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-temporal-extents/form-field-temporal-extents.component.d.ts +3 -1
  61. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-temporal-extents/form-field-temporal-extents.component.d.ts.map +1 -1
  62. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.d.ts +5 -1
  63. package/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.d.ts.map +1 -1
  64. package/libs/feature/editor/src/lib/components/record-form/record-form.component.d.ts.map +1 -1
  65. package/libs/feature/editor/src/lib/fields.config.d.ts +1 -0
  66. package/libs/feature/editor/src/lib/fields.config.d.ts.map +1 -1
  67. package/libs/feature/map/src/lib/utils/map-utils.service.d.ts.map +1 -1
  68. package/libs/feature/search/src/lib/results-table/results-table-container.component.d.ts +11 -3
  69. package/libs/feature/search/src/lib/results-table/results-table-container.component.d.ts.map +1 -1
  70. package/libs/feature/search/src/lib/state/search.facade.d.ts +1 -0
  71. package/libs/feature/search/src/lib/state/search.facade.d.ts.map +1 -1
  72. package/libs/ui/elements/src/index.d.ts +1 -0
  73. package/libs/ui/elements/src/index.d.ts.map +1 -1
  74. package/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.d.ts +18 -0
  75. package/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.d.ts.map +1 -0
  76. package/libs/ui/elements/src/lib/downloads-list/downloads-list.component.d.ts +1 -1
  77. package/libs/ui/elements/src/lib/record-api-form/record-api-form.component.d.ts +1 -1
  78. package/libs/ui/elements/src/lib/sortable-list/sortable-list.component.d.ts +3 -2
  79. package/libs/ui/elements/src/lib/sortable-list/sortable-list.component.d.ts.map +1 -1
  80. package/libs/ui/inputs/src/lib/image-input/image-input.component.d.ts +4 -2
  81. package/libs/ui/inputs/src/lib/image-input/image-input.component.d.ts.map +1 -1
  82. package/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.d.ts +10 -1
  83. package/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.d.ts.map +1 -1
  84. package/libs/ui/search/src/lib/results-table/results-table.component.d.ts +6 -2
  85. package/libs/ui/search/src/lib/results-table/results-table.component.d.ts.map +1 -1
  86. package/package.json +1 -1
  87. package/src/libs/api/metadata-converter/src/index.ts +0 -1
  88. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.ts +5 -1
  89. package/src/libs/api/metadata-converter/src/lib/fixtures/geocat-ch.records.ts +37 -12
  90. package/src/libs/api/metadata-converter/src/lib/fixtures/metawal.records.ts +5 -1
  91. package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +4 -2
  92. package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +72 -2
  93. package/src/libs/api/metadata-converter/src/lib/iso19139/utils/geometry.ts +39 -0
  94. package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +70 -1
  95. package/src/libs/api/metadata-converter/src/lib/xml-utils.ts +8 -0
  96. package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +24 -4
  97. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +2 -1
  98. package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +10 -0
  99. package/src/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.html +3 -1
  100. package/src/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.ts +117 -21
  101. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.css +0 -0
  102. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.html +5 -0
  103. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.ts +22 -0
  104. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-temporal-extents/form-field-temporal-extents.component.ts +8 -7
  105. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +6 -0
  106. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +13 -1
  107. package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.ts +1 -0
  108. package/src/libs/feature/editor/src/lib/fields.config.ts +12 -1
  109. package/src/libs/feature/map/src/lib/utils/map-utils.service.ts +8 -4
  110. package/src/libs/feature/search/src/lib/results-table/results-table-container.component.html +2 -1
  111. package/src/libs/feature/search/src/lib/results-table/results-table-container.component.ts +40 -3
  112. package/src/libs/feature/search/src/lib/state/search.facade.ts +6 -0
  113. package/src/libs/ui/elements/src/index.ts +1 -0
  114. package/src/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.css +0 -0
  115. package/src/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.html +12 -0
  116. package/src/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.ts +37 -0
  117. package/src/libs/ui/elements/src/lib/markdown-editor/markdown-editor.component.html +4 -1
  118. package/src/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.css +2 -1
  119. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +12 -8
  120. package/src/libs/ui/elements/src/lib/sortable-list/sortable-list.component.html +3 -1
  121. package/src/libs/ui/elements/src/lib/sortable-list/sortable-list.component.ts +6 -2
  122. package/src/libs/ui/inputs/src/lib/image-input/image-input.component.html +1 -1
  123. package/src/libs/ui/inputs/src/lib/image-input/image-input.component.ts +7 -2
  124. package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.html +1 -1
  125. package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.html +1 -0
  126. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.html +10 -1
  127. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.ts +55 -3
  128. package/src/libs/ui/search/src/lib/results-table/results-table.component.html +7 -2
  129. package/src/libs/ui/search/src/lib/results-table/results-table.component.ts +9 -9
  130. package/translations/de.json +16 -16
  131. package/translations/en.json +9 -9
  132. package/translations/es.json +9 -9
  133. package/translations/fr.json +18 -18
  134. package/translations/it.json +9 -9
  135. package/translations/nl.json +9 -9
  136. package/translations/pt.json +9 -9
  137. package/translations/sk.json +9 -9
@@ -9,6 +9,7 @@ import {
9
9
  from,
10
10
  Observable,
11
11
  of,
12
+ Subject,
12
13
  switchMap,
13
14
  throwError,
14
15
  } from 'rxjs'
@@ -24,7 +25,6 @@ import {
24
25
  } from '../../../../../../libs/common/domain/src/lib/model/search'
25
26
  import { catchError, map, tap } from 'rxjs/operators'
26
27
  import {
27
- BaseConverter,
28
28
  findConverterForDocument,
29
29
  Gn4Converter,
30
30
  Gn4SearchResults,
@@ -33,8 +33,13 @@ import {
33
33
  import { CatalogRecord } from '../../../../../../libs/common/domain/src/lib/model/record'
34
34
  import { HttpErrorResponse } from '@angular/common/http'
35
35
 
36
+ const TEMPORARY_ID_PREFIX = 'TEMP-ID-'
37
+
36
38
  @Injectable()
37
39
  export class Gn4Repository implements RecordsRepositoryInterface {
40
+ _draftsChanged = new Subject<void>()
41
+ draftsChanged$ = this._draftsChanged.asObservable()
42
+
38
43
  constructor(
39
44
  private gn4SearchApi: SearchApiService,
40
45
  private gn4SearchHelper: ElasticsearchService,
@@ -238,13 +243,14 @@ export class Gn4Repository implements RecordsRepositoryInterface {
238
243
  switchMap(async (recordAsXml) => {
239
244
  const converter = findConverterForDocument(recordAsXml)
240
245
  const record = await converter.readRecord(recordAsXml)
241
- record.uniqueIdentifier = `TEMP-ID-${Date.now()}`
246
+ record.uniqueIdentifier = `${TEMPORARY_ID_PREFIX}${Date.now()}`
242
247
  record.title = `${record.title} (Copy)`
243
248
  const xml = await converter.writeRecord(record, recordAsXml)
244
249
  window.localStorage.setItem(
245
250
  this.getLocalStorageKeyForRecord(record.uniqueIdentifier),
246
251
  xml
247
252
  )
253
+ this._draftsChanged.next()
248
254
  return [record, xml, false] as [CatalogRecord, string, false]
249
255
  })
250
256
  )
@@ -295,17 +301,26 @@ export class Gn4Repository implements RecordsRepositoryInterface {
295
301
  )
296
302
  }
297
303
 
304
+ deleteRecord(uniqueIdentifier: string): Observable<void> {
305
+ return this.gn4RecordsApi.deleteRecord(uniqueIdentifier)
306
+ }
307
+
308
+ generateTemporaryId(): string {
309
+ return `${TEMPORARY_ID_PREFIX}${Date.now()}`
310
+ }
311
+
298
312
  saveRecordAsDraft(
299
313
  record: CatalogRecord,
300
314
  referenceRecordSource?: string
301
315
  ): Observable<string> {
302
316
  return this.serializeRecordToXml(record, referenceRecordSource).pipe(
303
- tap((recordXml) =>
317
+ tap((recordXml) => {
304
318
  window.localStorage.setItem(
305
319
  this.getLocalStorageKeyForRecord(record.uniqueIdentifier),
306
320
  recordXml
307
321
  )
308
- )
322
+ this._draftsChanged.next()
323
+ })
309
324
  )
310
325
  }
311
326
 
@@ -313,6 +328,7 @@ export class Gn4Repository implements RecordsRepositoryInterface {
313
328
  window.localStorage.removeItem(
314
329
  this.getLocalStorageKeyForRecord(uniqueIdentifier)
315
330
  )
331
+ this._draftsChanged.next()
316
332
  }
317
333
 
318
334
  recordHasDraft(uniqueIdentifier: string): boolean {
@@ -323,6 +339,10 @@ export class Gn4Repository implements RecordsRepositoryInterface {
323
339
  )
324
340
  }
325
341
 
342
+ isRecordNotYetSaved(uniqueIdentifier: string): boolean {
343
+ return uniqueIdentifier.startsWith(TEMPORARY_ID_PREFIX)
344
+ }
345
+
326
346
  // generated by copilot
327
347
  getAllDrafts(): Observable<CatalogRecord[]> {
328
348
  const items = { ...window.localStorage }
@@ -159,7 +159,8 @@ export interface GraphicOverview {
159
159
  }
160
160
 
161
161
  export interface DatasetSpatialExtent {
162
- geometry: Geometry
162
+ bbox?: [number, number, number, number]
163
+ geometry?: Geometry
163
164
  description?: string
164
165
  }
165
166
 
@@ -52,6 +52,14 @@ export abstract class RecordsRepositoryInterface {
52
52
  referenceRecordSource?: string
53
53
  ): Observable<string>
54
54
 
55
+ /**
56
+ * @param uniqueIdentifier
57
+ * @returns Observable<void> Returns when record is deleted
58
+ */
59
+ abstract deleteRecord(uniqueIdentifier: string): Observable<void>
60
+
61
+ abstract generateTemporaryId(): string
62
+
55
63
  /**
56
64
  * @param record
57
65
  * @param referenceRecordSource
@@ -64,7 +72,9 @@ export abstract class RecordsRepositoryInterface {
64
72
 
65
73
  abstract clearRecordDraft(uniqueIdentifier: string): void
66
74
  abstract recordHasDraft(uniqueIdentifier: string): boolean
75
+ abstract isRecordNotYetSaved(uniqueIdentifier: string): boolean
67
76
 
68
77
  /** will return all pending drafts, both published and not published */
69
78
  abstract getAllDrafts(): Observable<CatalogRecord[]>
79
+ abstract draftsChanged$: Observable<void>
70
80
  }
@@ -1,8 +1,10 @@
1
1
  <gn-ui-image-input
2
2
  [maxSizeMB]="5"
3
3
  [previewUrl]="resourceUrl"
4
- [altText]="resourceFileName"
4
+ [altText]="resourceAltText"
5
+ [formControl]="formControl"
5
6
  (fileChange)="handleFileChange($event)"
6
7
  (urlChange)="handleUrlChange($event)"
8
+ (altTextChange)="handleAltTextChange($event)"
7
9
  (delete)="handleDelete()"
8
10
  ></gn-ui-image-input>
@@ -2,12 +2,24 @@ import {
2
2
  ChangeDetectionStrategy,
3
3
  ChangeDetectorRef,
4
4
  Component,
5
+ EventEmitter,
5
6
  Input,
7
+ OnChanges,
6
8
  OnInit,
9
+ Output,
10
+ SimpleChanges,
7
11
  } from '@angular/core'
8
12
  import { CommonModule } from '@angular/common'
9
13
  import { RecordsApiService } from '../../../../../../../libs/data-access/gn4/src'
10
14
  import { UiInputsModule } from '../../../../../../../libs/ui/inputs/src'
15
+ import { FormControl } from '@angular/forms'
16
+ import { GraphicOverview } from '../../../../../../../libs/common/domain/src/lib/model/record'
17
+
18
+ const extractFileNameFromUrl = (url: string): string => {
19
+ const pattern = new RegExp(`attachments/([^/?#]+)(?:[/?#]|$)`, 'i')
20
+ const match = url.match(pattern)
21
+ return match ? match[1] : ''
22
+ }
11
23
 
12
24
  @Component({
13
25
  selector: 'gn-ui-overview-upload',
@@ -17,11 +29,15 @@ import { UiInputsModule } from '../../../../../../../libs/ui/inputs/src'
17
29
  styleUrls: ['./overview-upload.component.css'],
18
30
  changeDetection: ChangeDetectionStrategy.OnPush,
19
31
  })
20
- export class OverviewUploadComponent implements OnInit {
32
+ export class OverviewUploadComponent implements OnInit, OnChanges {
21
33
  @Input() metadataUuid: string
34
+ @Input() formControl!: FormControl
35
+ @Output() overviewChange = new EventEmitter<GraphicOverview | null>()
36
+ @Output() altTextChange: EventEmitter<string> = new EventEmitter()
22
37
 
23
- resourceFileName: string
24
- resourceUrl: string
38
+ resourceAltText = '' // = ressourceFileName by default
39
+ resourceFileName = ''
40
+ resourceUrl: URL
25
41
 
26
42
  constructor(
27
43
  private recordsApiService: RecordsApiService,
@@ -29,42 +45,122 @@ export class OverviewUploadComponent implements OnInit {
29
45
  ) {}
30
46
 
31
47
  ngOnInit(): void {
32
- this.recordsApiService
33
- .getAllResources(this.metadataUuid)
34
- .subscribe((resources) => {
35
- this.resourceFileName = resources[0]?.filename
36
- this.resourceUrl = resources[0]?.url
48
+ this.recordsApiService.getAllResources(this.metadataUuid).subscribe({
49
+ next: (resources) => {
50
+ if (resources && resources.length > 0) {
51
+ this.resourceUrl = new URL(resources[0]?.url)
52
+ this.resourceAltText =
53
+ this.resourceAltText === ''
54
+ ? resources[0].filename
55
+ : this.resourceAltText
56
+ this.resourceFileName = extractFileNameFromUrl(resources[0]?.url)
57
+ } else {
58
+ this.resourceUrl = null
59
+ this.resourceAltText = ''
60
+ this.resourceFileName = ''
61
+ }
62
+
37
63
  this.cd.markForCheck()
38
- })
64
+ },
65
+ error: this.errorHandle,
66
+ })
39
67
  }
40
68
 
41
69
  handleFileChange(file: File) {
42
70
  this.recordsApiService
43
71
  .putResource(this.metadataUuid, file, 'public')
44
- .subscribe((resource) => {
45
- this.resourceFileName = resource.filename
46
- this.resourceUrl = resource.url
47
- this.cd.markForCheck()
72
+ .subscribe({
73
+ next: (resource) => {
74
+ this.resourceUrl = new URL(resource.url)
75
+ this.resourceAltText = resource.filename
76
+
77
+ this.overviewChange.emit({
78
+ url: new URL(resource.url),
79
+ description: resource.filename,
80
+ })
81
+
82
+ this.cd.markForCheck()
83
+ },
84
+ error: this.errorHandle,
48
85
  })
49
86
  }
50
87
 
51
88
  handleUrlChange(url: string) {
52
89
  this.recordsApiService
53
90
  .putResourceFromURL(this.metadataUuid, url, 'public')
54
- .subscribe((resource) => {
55
- this.resourceFileName = resource.filename
56
- this.resourceUrl = resource.url
57
- this.cd.markForCheck()
91
+ .subscribe({
92
+ next: (resource) => {
93
+ this.resourceUrl = new URL(resource.url)
94
+ this.resourceAltText = resource.filename
95
+
96
+ this.overviewChange.emit({
97
+ url: new URL(resource.url),
98
+ description: resource.filename,
99
+ })
100
+
101
+ this.cd.markForCheck()
102
+ },
103
+ error: this.errorHandle,
58
104
  })
59
105
  }
60
106
 
107
+ handleAltTextChange(newAltText: string) {
108
+ this.resourceAltText = newAltText
109
+
110
+ this.overviewChange.emit({
111
+ url: this.resourceUrl,
112
+ description: this.resourceAltText,
113
+ })
114
+ this.cd.markForCheck()
115
+ }
116
+
61
117
  handleDelete() {
62
118
  this.recordsApiService
63
119
  .delResource(this.metadataUuid, this.resourceFileName)
64
- .subscribe(() => {
65
- this.resourceFileName = null
66
- this.resourceUrl = null
67
- this.cd.markForCheck()
120
+ .subscribe({
121
+ next: () => {
122
+ this.resourceAltText = null
123
+ this.resourceUrl = null
124
+
125
+ this.overviewChange.emit(null)
126
+
127
+ this.cd.markForCheck()
128
+ },
129
+ error: this.errorHandle,
68
130
  })
69
131
  }
132
+
133
+ private errorHandle = (error: never) => {
134
+ console.error(error)
135
+
136
+ this.resourceUrl = null
137
+ this.resourceAltText = ''
138
+ this.resourceFileName = ''
139
+
140
+ this.overviewChange.emit(null)
141
+
142
+ this.cd.markForCheck()
143
+ }
144
+
145
+ ngOnChanges(changes: SimpleChanges): void {
146
+ const overviewChanges = changes['formControl']
147
+ if (
148
+ overviewChanges &&
149
+ overviewChanges.currentValue !== overviewChanges.previousValue
150
+ ) {
151
+ let overview: GraphicOverview
152
+ if (
153
+ overviewChanges.currentValue.value &&
154
+ overviewChanges.currentValue.value.length > 0
155
+ ) {
156
+ overview = overviewChanges.currentValue.value[0] as GraphicOverview
157
+ } else {
158
+ return
159
+ }
160
+ if (overview.description && overview.description !== '') {
161
+ this.resourceAltText = overview.description
162
+ this.cd.markForCheck()
163
+ }
164
+ }
165
+ }
70
166
  }
@@ -0,0 +1,5 @@
1
+ <gn-ui-overview-upload
2
+ [metadataUuid]="metadataUuid"
3
+ [formControl]="control"
4
+ (overviewChange)="handleOverviewChange($event)"
5
+ ></gn-ui-overview-upload>
@@ -0,0 +1,22 @@
1
+ import { CommonModule } from '@angular/common'
2
+ import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
3
+ import { OverviewUploadComponent } from '../../../overview-upload/overview-upload.component'
4
+ import { FormControl } from '@angular/forms'
5
+ import { GraphicOverview } from '../../../../../../../../../libs/common/domain/src/lib/model/record'
6
+
7
+ @Component({
8
+ selector: 'gn-ui-form-field-overviews',
9
+ templateUrl: './form-field-overviews.component.html',
10
+ styleUrls: ['./form-field-overviews.component.css'],
11
+ changeDetection: ChangeDetectionStrategy.OnPush,
12
+ standalone: true,
13
+ imports: [CommonModule, OverviewUploadComponent],
14
+ })
15
+ export class FormFieldOverviewsComponent {
16
+ @Input() metadataUuid: string
17
+ @Input() control!: FormControl
18
+
19
+ handleOverviewChange(overView: GraphicOverview | null) {
20
+ this.control.setValue(overView ? [overView] : [])
21
+ }
22
+ }
@@ -62,9 +62,9 @@ export class FormFieldTemporalExtentsComponent implements OnInit, OnDestroy {
62
62
  )
63
63
  }
64
64
 
65
- onElementsChange(elements: any) {
65
+ onElementsChange(elements: { inputs: Record<string, unknown> }[]) {
66
66
  this.array.clear({ emitEvent: false })
67
- elements.forEach((e: any, i: number) =>
67
+ elements.forEach((e, i) =>
68
68
  this.array.push(e.inputs.control, {
69
69
  emitEvent: i === elements.length - 1,
70
70
  })
@@ -95,7 +95,7 @@ export class FormFieldTemporalExtentsComponent implements OnInit, OnDestroy {
95
95
 
96
96
  private resetValueFromInput(value) {
97
97
  this.array.clear({ emitEvent: false })
98
- this.elements = []
98
+ let newElements = []
99
99
  value.forEach((v: any) => {
100
100
  if ('start' in v && 'end' in v) {
101
101
  const rangeControl = new FormControl({
@@ -103,8 +103,8 @@ export class FormFieldTemporalExtentsComponent implements OnInit, OnDestroy {
103
103
  end: v.end,
104
104
  })
105
105
  this.array.push(rangeControl, { emitEvent: false })
106
- this.elements = [
107
- ...this.elements,
106
+ newElements = [
107
+ ...newElements,
108
108
  {
109
109
  component: FormFieldTemporalExtentsRangeComponent,
110
110
  inputs: {
@@ -115,8 +115,8 @@ export class FormFieldTemporalExtentsComponent implements OnInit, OnDestroy {
115
115
  } else {
116
116
  const dateControl = new FormControl({ start: v.start })
117
117
  this.array.push(dateControl, { emitEvent: false })
118
- this.elements = [
119
- ...this.elements,
118
+ newElements = [
119
+ ...newElements,
120
120
  {
121
121
  component: FormFieldTemporalExtentsDateComponent,
122
122
  inputs: {
@@ -126,5 +126,6 @@ export class FormFieldTemporalExtentsComponent implements OnInit, OnDestroy {
126
126
  ]
127
127
  }
128
128
  })
129
+ this.elements = newElements
129
130
  }
130
131
  }
@@ -76,6 +76,12 @@
76
76
  <ng-container *ngIf="isSpatialExtentField">
77
77
  <gn-ui-form-field-spatial-extent></gn-ui-form-field-spatial-extent>
78
78
  </ng-container>
79
+ <ng-container *ngIf="isGraphicOverview">
80
+ <gn-ui-form-field-overviews
81
+ [control]="formControl"
82
+ [metadataUuid]="metadataUuid$ | async"
83
+ ></gn-ui-form-field-overviews>
84
+ </ng-container>
79
85
  <ng-container *ngIf="isKeywords">
80
86
  <gn-ui-form-field-keywords
81
87
  [control]="formControl"
@@ -28,6 +28,9 @@ import { FormFieldSpatialExtentComponent } from './form-field-spatial-extent/for
28
28
  import { FormFieldUpdateFrequencyComponent } from './form-field-update-frequency/form-field-update-frequency.component'
29
29
  import { CatalogRecordKeys } from '../../../../../../../../libs/common/domain/src/lib/model/record'
30
30
  import { FormFieldKeywordsComponent } from './form-field-keywords/form-field-keywords.component'
31
+ import { FormFieldOverviewsComponent } from './form-field-overviews/form-field-overviews.component'
32
+ import { map, take } from 'rxjs/operators'
33
+ import { EditorFacade } from '../../../+state/editor.facade'
31
34
  import { FormFieldConfig } from '../../../models'
32
35
 
33
36
  @Component({
@@ -55,6 +58,7 @@ import { FormFieldConfig } from '../../../models'
55
58
  FormFieldArrayComponent,
56
59
  FormFieldKeywordsComponent,
57
60
  TranslateModule,
61
+ FormFieldOverviewsComponent,
58
62
  ],
59
63
  })
60
64
  export class FormFieldComponent {
@@ -70,9 +74,14 @@ export class FormFieldComponent {
70
74
 
71
75
  @ViewChild('titleInput') titleInput: ElementRef
72
76
 
77
+ metadataUuid$ = this.facade.record$.pipe(
78
+ take(1),
79
+ map((record) => record.uniqueIdentifier)
80
+ )
81
+
73
82
  formControl = new FormControl()
74
83
 
75
- constructor() {
84
+ constructor(private facade: EditorFacade) {
76
85
  this.valueChange = this.formControl.valueChanges
77
86
  }
78
87
 
@@ -101,6 +110,9 @@ export class FormFieldComponent {
101
110
  get isSpatialExtentField() {
102
111
  return this.model === 'spatialExtents'
103
112
  }
113
+ get isGraphicOverview() {
114
+ return this.model === 'overviews'
115
+ }
104
116
  get isSimpleField() {
105
117
  return this.model === 'uniqueIdentifier' || this.model === 'recordUpdated'
106
118
  }
@@ -24,6 +24,7 @@ export class RecordFormComponent {
24
24
  if (!model) {
25
25
  return
26
26
  }
27
+ console.log(newValue)
27
28
  this.facade.updateRecordField(model, newValue)
28
29
  }
29
30
 
@@ -83,6 +83,13 @@ export const RECORD_ABSTRACT_FIELD: EditorField = {
83
83
  },
84
84
  }
85
85
 
86
+ export const RECORD_GRAPHICAL_OVERVIEW_FIELD: EditorField = {
87
+ model: 'overviews',
88
+ formFieldConfig: {
89
+ labelKey: marker('editor.record.form.field.overviews'),
90
+ },
91
+ }
92
+
86
93
  /************************************************************
87
94
  *************** SECTIONS *****************
88
95
  ************************************************************
@@ -90,7 +97,11 @@ export const RECORD_ABSTRACT_FIELD: EditorField = {
90
97
 
91
98
  export const TITLE_SECTION: EditorSection = {
92
99
  hidden: false,
93
- fields: [RECORD_TITLE_FIELD, RECORD_ABSTRACT_FIELD],
100
+ fields: [
101
+ RECORD_TITLE_FIELD,
102
+ RECORD_ABSTRACT_FIELD,
103
+ RECORD_GRAPHICAL_OVERVIEW_FIELD,
104
+ ],
94
105
  }
95
106
 
96
107
  export const ABOUT_SECTION: EditorSection = {
@@ -32,7 +32,7 @@ import { ProxyService } from '../../../../../../libs/util/shared/src'
32
32
  import { WmsEndpoint, WmtsEndpoint } from '@camptocamp/ogc-client'
33
33
  import { LONLAT_CRS_CODES } from '../constant/projections'
34
34
  import { fromEPSGCode, register } from 'ol/proj/proj4'
35
- import proj4 from 'proj4/dist/proj4'
35
+ import proj4 from 'proj4'
36
36
  import { defaults as defaultControls } from 'ol/control/defaults'
37
37
 
38
38
  const FEATURE_PROJECTION = 'EPSG:3857'
@@ -220,11 +220,15 @@ export class MapUtilsService {
220
220
  if (!('spatialExtents' in record) || record.spatialExtents.length === 0) {
221
221
  return null
222
222
  }
223
- // transform an array of geojson geometries into a bbox
223
+ // extend all the spatial extents into an including bbox
224
224
  const totalExtent = record.spatialExtents.reduce(
225
225
  (prev, curr) => {
226
- const geom = GEOJSON.readGeometry(curr.geometry)
227
- return extend(prev, geom.getExtent())
226
+ if ('bbox' in curr) return extend(prev, curr.bbox)
227
+ else if ('geometry' in curr) {
228
+ const geom = GEOJSON.readGeometry(curr.geometry)
229
+ return extend(prev, geom.getExtent())
230
+ }
231
+ return prev
228
232
  },
229
233
  [Infinity, Infinity, -Infinity, -Infinity]
230
234
  )
@@ -1,10 +1,11 @@
1
1
  <gn-ui-results-table
2
2
  [records]="records$ | async"
3
- [recordHasDraft]="hasDraft"
3
+ [hasDraft]="hasDraft"
4
4
  [selectedRecordsIdentifiers]="selectedRecords$ | async"
5
5
  [sortOrder]="sortBy$ | async"
6
6
  (recordClick)="handleRecordClick($event)"
7
7
  (duplicateRecord)="handleDuplicateRecord($event)"
8
+ (deleteRecord)="handleDeleteRecord($event)"
8
9
  (recordsSelectedChange)="handleRecordsSelectedChange($event[0], $event[1])"
9
10
  (sortByChange)="handleSortByChange($event[0], $event[1])"
10
11
  ></gn-ui-results-table>
@@ -1,4 +1,4 @@
1
- import { Component, EventEmitter, Output } from '@angular/core'
1
+ import { Component, EventEmitter, OnDestroy, Output } from '@angular/core'
2
2
  import { CatalogRecord } from '../../../../../../libs/common/domain/src/lib/model/record'
3
3
  import { SearchFacade } from '../state/search.facade'
4
4
  import { SelectionService } from '../../../../../../libs/api/repository/src'
@@ -6,6 +6,9 @@ import { SearchService } from '../utils/service/search.service'
6
6
  import { RecordsRepositoryInterface } from '../../../../../../libs/common/domain/src/lib/repository/records-repository.interface'
7
7
  import { ResultsTableComponent } from '../../../../../../libs/ui/search/src'
8
8
  import { CommonModule } from '@angular/common'
9
+ import { Subscription } from 'rxjs'
10
+ import { NotificationsService } from '../../../../../../libs/feature/notifications/src'
11
+ import { TranslateService } from '@ngx-translate/core'
9
12
 
10
13
  @Component({
11
14
  selector: 'gn-ui-results-table-container',
@@ -14,10 +17,12 @@ import { CommonModule } from '@angular/common'
14
17
  standalone: true,
15
18
  imports: [CommonModule, ResultsTableComponent],
16
19
  })
17
- export class ResultsTableContainerComponent {
20
+ export class ResultsTableContainerComponent implements OnDestroy {
18
21
  @Output() recordClick = new EventEmitter<CatalogRecord>()
19
22
  @Output() duplicateRecord = new EventEmitter<CatalogRecord>()
20
23
 
24
+ subscription = new Subscription()
25
+
21
26
  records$ = this.searchFacade.results$
22
27
  selectedRecords$ = this.selectionService.selectedRecordsIdentifiers$
23
28
  sortBy$ = this.searchFacade.sortBy$
@@ -29,7 +34,9 @@ export class ResultsTableContainerComponent {
29
34
  private searchFacade: SearchFacade,
30
35
  private searchService: SearchService,
31
36
  private selectionService: SelectionService,
32
- private recordsRepository: RecordsRepositoryInterface
37
+ private recordsRepository: RecordsRepositoryInterface,
38
+ private notificationsService: NotificationsService,
39
+ private translateService: TranslateService
33
40
  ) {}
34
41
 
35
42
  handleRecordClick(item: unknown) {
@@ -40,6 +47,32 @@ export class ResultsTableContainerComponent {
40
47
  this.duplicateRecord.emit(item as CatalogRecord)
41
48
  }
42
49
 
50
+ async handleDeleteRecord(item: unknown) {
51
+ const uniqueIdentifier = (item as CatalogRecord).uniqueIdentifier
52
+ this.subscription.add(
53
+ this.recordsRepository.deleteRecord(uniqueIdentifier).subscribe({
54
+ next: () => {
55
+ this.recordsRepository.clearRecordDraft(uniqueIdentifier)
56
+ this.searchFacade.requestNewResults()
57
+ },
58
+ error: (error) => {
59
+ this.notificationsService.showNotification({
60
+ type: 'error',
61
+ title: this.translateService.instant(
62
+ 'editor.record.deleteError.title'
63
+ ),
64
+ text: `${this.translateService.instant(
65
+ 'editor.record.deleteError.body'
66
+ )} ${error}`,
67
+ closeMessage: this.translateService.instant(
68
+ 'editor.record.deleteError.closeMessage'
69
+ ),
70
+ })
71
+ },
72
+ })
73
+ )
74
+ }
75
+
43
76
  handleSortByChange(col: string, order: 'asc' | 'desc') {
44
77
  this.searchService.setSortBy([order, col])
45
78
  }
@@ -51,4 +84,8 @@ export class ResultsTableContainerComponent {
51
84
  this.selectionService.selectRecords(records)
52
85
  }
53
86
  }
87
+
88
+ ngOnDestroy() {
89
+ this.subscription.unsubscribe()
90
+ }
54
91
  }
@@ -8,6 +8,7 @@ import {
8
8
  Paginate,
9
9
  RequestMoreOnAggregation,
10
10
  RequestMoreResults,
11
+ RequestNewResults,
11
12
  SetConfigAggregations,
12
13
  SetConfigFilters,
13
14
  SetConfigRequestFields,
@@ -151,6 +152,11 @@ export class SearchFacade {
151
152
  return this
152
153
  }
153
154
 
155
+ requestNewResults(): SearchFacade {
156
+ this.store.dispatch(new RequestNewResults(this.searchId))
157
+ return this
158
+ }
159
+
154
160
  requestMoreOnAggregation(key: string, increment: number): SearchFacade {
155
161
  this.store.dispatch(
156
162
  new RequestMoreOnAggregation(key, increment, this.searchId)
@@ -1,5 +1,6 @@
1
1
  export * from './lib/api-card/api-card.component'
2
2
  export * from './lib/avatar/avatar.component'
3
+ export * from './lib/confirmation-dialog/confirmation-dialog.component'
3
4
  export * from './lib/content-ghost/content-ghost.component'
4
5
  export * from './lib/download-item/download-item.component'
5
6
  export * from './lib/downloads-list/downloads-list.component'
@@ -0,0 +1,12 @@
1
+ <h1 mat-dialog-title>{{ data.title }}</h1>
2
+ <div mat-dialog-content>{{ data.message }}</div>
3
+ <div mat-dialog-actions>
4
+ <gn-ui-button (buttonClick)="onCancel()">{{ data.cancelText }}</gn-ui-button>
5
+ <gn-ui-button
6
+ (buttonClick)="onConfirm()"
7
+ cdkFocusInitial
8
+ class="ml-2"
9
+ data-cy="confirm-button"
10
+ >{{ data.confirmText }}</gn-ui-button
11
+ >
12
+ </div>