geonetwork-ui 2.2.0-dev.6f0f0ee9 → 2.2.0-dev.70a9dc16

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 (150) hide show
  1. package/esm2022/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.mjs +16 -4
  2. package/esm2022/libs/api/metadata-converter/src/lib/gn4/gn4.metadata.mapper.mjs +3 -1
  3. package/esm2022/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.mjs +1 -1
  4. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/converter.mjs +7 -2
  5. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/read-parts.mjs +4 -1
  6. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/write-parts.mjs +1 -1
  7. package/esm2022/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.mjs +2 -2
  8. package/esm2022/libs/common/domain/src/lib/model/record/metadata.model.mjs +1 -1
  9. package/esm2022/libs/feature/editor/src/index.mjs +6 -1
  10. package/esm2022/libs/feature/editor/src/lib/+state/editor.actions.mjs +8 -0
  11. package/esm2022/libs/feature/editor/src/lib/+state/editor.effects.mjs +26 -0
  12. package/esm2022/libs/feature/editor/src/lib/+state/editor.facade.mjs +30 -0
  13. package/esm2022/libs/feature/editor/src/lib/+state/editor.models.mjs +2 -0
  14. package/esm2022/libs/feature/editor/src/lib/+state/editor.reducer.mjs +42 -0
  15. package/esm2022/libs/feature/editor/src/lib/+state/editor.selectors.mjs +13 -0
  16. package/esm2022/libs/feature/editor/src/lib/expressions.mjs +13 -0
  17. package/esm2022/libs/feature/editor/src/lib/feature-editor.module.mjs +16 -4
  18. package/esm2022/libs/feature/editor/src/lib/fields.config.mjs +34 -0
  19. package/esm2022/libs/feature/editor/src/lib/models/fields.model.mjs +2 -0
  20. package/esm2022/libs/feature/editor/src/lib/record-form/record-form.component.mjs +15 -11
  21. package/esm2022/libs/feature/editor/src/lib/services/editor.service.mjs +22 -44
  22. package/esm2022/libs/feature/record/src/lib/data-view-permalink/data-view-permalink.component.mjs +13 -13
  23. package/esm2022/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.mjs +3 -3
  24. package/esm2022/libs/ui/elements/src/lib/max-lines/max-lines.component.mjs +5 -3
  25. package/esm2022/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.mjs +3 -3
  26. package/esm2022/libs/ui/elements/src/lib/metadata-info/metadata-info.component.mjs +17 -5
  27. package/esm2022/libs/ui/inputs/src/lib/button/button.component.mjs +9 -9
  28. package/esm2022/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.mjs +3 -3
  29. package/esm2022/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.mjs +3 -3
  30. package/esm2022/libs/ui/inputs/src/lib/form-field/form-field.component.mjs +4 -2
  31. package/esm2022/libs/ui/inputs/src/lib/form-field/form-field.model.mjs +1 -1
  32. package/esm2022/libs/ui/layout/src/lib/carousel/carousel.component.mjs +3 -3
  33. package/esm2022/libs/util/i18n/src/lib/i18n.constants.mjs +18 -1
  34. package/esm2022/libs/util/shared/src/lib/utils/index.mjs +2 -1
  35. package/esm2022/libs/util/shared/src/lib/utils/temporal-extent-union.mjs +32 -0
  36. package/esm2022/translations/de.json +35 -26
  37. package/esm2022/translations/en.json +27 -18
  38. package/esm2022/translations/es.json +24 -15
  39. package/esm2022/translations/fr.json +45 -36
  40. package/esm2022/translations/it.json +39 -30
  41. package/esm2022/translations/nl.json +25 -16
  42. package/esm2022/translations/pt.json +24 -15
  43. package/fesm2022/geonetwork-ui.mjs +575 -294
  44. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  45. package/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.d.ts.map +1 -1
  46. package/libs/api/metadata-converter/src/lib/gn4/gn4.metadata.mapper.d.ts.map +1 -1
  47. package/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.d.ts +1 -0
  48. package/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.d.ts.map +1 -1
  49. package/libs/api/metadata-converter/src/lib/iso19139/converter.d.ts.map +1 -1
  50. package/libs/api/metadata-converter/src/lib/iso19139/read-parts.d.ts +1 -0
  51. package/libs/api/metadata-converter/src/lib/iso19139/read-parts.d.ts.map +1 -1
  52. package/libs/api/metadata-converter/src/lib/iso19139/write-parts.d.ts.map +1 -1
  53. package/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.d.ts.map +1 -1
  54. package/libs/common/domain/src/lib/model/record/metadata.model.d.ts +3 -0
  55. package/libs/common/domain/src/lib/model/record/metadata.model.d.ts.map +1 -1
  56. package/libs/feature/editor/src/index.d.ts +5 -0
  57. package/libs/feature/editor/src/index.d.ts.map +1 -1
  58. package/libs/feature/editor/src/lib/+state/editor.actions.d.ts +23 -0
  59. package/libs/feature/editor/src/lib/+state/editor.actions.d.ts.map +1 -0
  60. package/libs/feature/editor/src/lib/+state/editor.effects.d.ts +15 -0
  61. package/libs/feature/editor/src/lib/+state/editor.effects.d.ts.map +1 -0
  62. package/libs/feature/editor/src/lib/+state/editor.facade.d.ts +19 -0
  63. package/libs/feature/editor/src/lib/+state/editor.facade.d.ts.map +1 -0
  64. package/libs/feature/editor/src/lib/+state/editor.models.d.ts +2 -0
  65. package/libs/feature/editor/src/lib/+state/editor.models.d.ts.map +1 -0
  66. package/libs/feature/editor/src/lib/+state/editor.reducer.d.ts +18 -0
  67. package/libs/feature/editor/src/lib/+state/editor.reducer.d.ts.map +1 -0
  68. package/libs/feature/editor/src/lib/+state/editor.selectors.d.ts +15 -0
  69. package/libs/feature/editor/src/lib/+state/editor.selectors.d.ts.map +1 -0
  70. package/libs/feature/editor/src/lib/expressions.d.ts +4 -0
  71. package/libs/feature/editor/src/lib/expressions.d.ts.map +1 -0
  72. package/libs/feature/editor/src/lib/feature-editor.module.d.ts +3 -1
  73. package/libs/feature/editor/src/lib/feature-editor.module.d.ts.map +1 -1
  74. package/libs/feature/editor/src/lib/fields.config.d.ts +3 -0
  75. package/libs/feature/editor/src/lib/fields.config.d.ts.map +1 -0
  76. package/libs/feature/editor/src/lib/models/fields.model.d.ts +15 -0
  77. package/libs/feature/editor/src/lib/models/fields.model.d.ts.map +1 -0
  78. package/libs/feature/editor/src/lib/record-form/record-form.component.d.ts +10 -5
  79. package/libs/feature/editor/src/lib/record-form/record-form.component.d.ts.map +1 -1
  80. package/libs/feature/editor/src/lib/services/editor.service.d.ts +3 -13
  81. package/libs/feature/editor/src/lib/services/editor.service.d.ts.map +1 -1
  82. package/libs/ui/elements/src/lib/max-lines/max-lines.component.d.ts.map +1 -1
  83. package/libs/ui/elements/src/lib/metadata-info/metadata-info.component.d.ts +6 -0
  84. package/libs/ui/elements/src/lib/metadata-info/metadata-info.component.d.ts.map +1 -1
  85. package/libs/ui/inputs/src/lib/button/button.component.d.ts.map +1 -1
  86. package/libs/ui/inputs/src/lib/form-field/form-field.component.d.ts.map +1 -1
  87. package/libs/ui/inputs/src/lib/form-field/form-field.model.d.ts +0 -1
  88. package/libs/ui/inputs/src/lib/form-field/form-field.model.d.ts.map +1 -1
  89. package/libs/util/i18n/src/lib/i18n.constants.d.ts +17 -0
  90. package/libs/util/i18n/src/lib/i18n.constants.d.ts.map +1 -1
  91. package/libs/util/shared/src/lib/utils/index.d.ts +1 -0
  92. package/libs/util/shared/src/lib/utils/index.d.ts.map +1 -1
  93. package/libs/util/shared/src/lib/utils/temporal-extent-union.d.ts +5 -0
  94. package/libs/util/shared/src/lib/utils/temporal-extent-union.d.ts.map +1 -0
  95. package/package.json +1 -1
  96. package/src/libs/api/metadata-converter/src/lib/fixtures/generic.records.ts +2 -0
  97. package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.ts +2 -0
  98. package/src/libs/api/metadata-converter/src/lib/fixtures/geocat-ch.records.ts +4 -0
  99. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +19 -3
  100. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.metadata.mapper.ts +2 -0
  101. package/src/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.ts +1 -0
  102. package/src/libs/api/metadata-converter/src/lib/iso19139/converter.ts +6 -0
  103. package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +5 -1
  104. package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +8 -2
  105. package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +1 -1
  106. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +4 -0
  107. package/src/libs/common/fixtures/src/lib/elasticsearch/full-response.ts +1 -1
  108. package/src/libs/common/fixtures/src/lib/records.fixtures.ts +2 -0
  109. package/src/libs/feature/editor/src/index.ts +5 -0
  110. package/src/libs/feature/editor/src/lib/+state/editor.actions.ts +24 -0
  111. package/src/libs/feature/editor/src/lib/+state/editor.effects.ts +49 -0
  112. package/src/libs/feature/editor/src/lib/+state/editor.facade.ts +30 -0
  113. package/src/libs/feature/editor/src/lib/+state/editor.models.ts +1 -0
  114. package/src/libs/feature/editor/src/lib/+state/editor.reducer.ts +68 -0
  115. package/src/libs/feature/editor/src/lib/+state/editor.selectors.ts +39 -0
  116. package/src/libs/feature/editor/src/lib/expressions.ts +16 -0
  117. package/src/libs/feature/editor/src/lib/feature-editor.module.ts +11 -0
  118. package/src/libs/feature/editor/src/lib/fields.config.ts +35 -0
  119. package/src/libs/feature/editor/src/lib/models/fields.model.ts +29 -0
  120. package/src/libs/feature/editor/src/lib/record-form/record-form.component.html +9 -7
  121. package/src/libs/feature/editor/src/lib/record-form/record-form.component.ts +11 -5
  122. package/src/libs/feature/editor/src/lib/services/editor.service.ts +34 -73
  123. package/src/libs/feature/record/src/lib/data-view-permalink/data-view-permalink.component.ts +12 -12
  124. package/src/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.html +1 -1
  125. package/src/libs/ui/elements/src/lib/max-lines/max-lines.component.html +5 -1
  126. package/src/libs/ui/elements/src/lib/max-lines/max-lines.component.ts +1 -0
  127. package/src/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.html +1 -1
  128. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +107 -19
  129. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts +14 -0
  130. package/src/libs/ui/inputs/src/lib/button/button.component.css +0 -5
  131. package/src/libs/ui/inputs/src/lib/button/button.component.html +0 -1
  132. package/src/libs/ui/inputs/src/lib/button/button.component.ts +6 -6
  133. package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +1 -1
  134. package/src/libs/ui/inputs/src/lib/dropdown-selector/dropdown-selector.component.html +3 -1
  135. package/src/libs/ui/inputs/src/lib/form-field/form-field.component.ts +3 -1
  136. package/src/libs/ui/inputs/src/lib/form-field/form-field.model.ts +0 -1
  137. package/src/libs/ui/layout/src/lib/carousel/carousel.component.html +1 -0
  138. package/src/libs/util/i18n/src/lib/i18n.constants.ts +18 -0
  139. package/src/libs/util/shared/src/lib/utils/index.ts +1 -0
  140. package/src/libs/util/shared/src/lib/utils/temporal-extent-union.ts +32 -0
  141. package/tailwind.base.config.js +0 -3
  142. package/tailwind.base.css +43 -14
  143. package/translations/de.json +35 -26
  144. package/translations/en.json +27 -18
  145. package/translations/es.json +24 -15
  146. package/translations/fr.json +45 -36
  147. package/translations/it.json +39 -30
  148. package/translations/nl.json +25 -16
  149. package/translations/pt.json +24 -15
  150. package/translations/sk.json +117 -108
@@ -1,59 +1,27 @@
1
1
  import { Inject, Injectable, Optional } from '@angular/core'
2
2
  import { toModel, toXml } from '../../../../../../libs/api/metadata-converter/src'
3
3
  import { Configuration } from '../../../../../../libs/data-access/gn4/src'
4
- import { BehaviorSubject, Observable } from 'rxjs'
5
- import { finalize, map, switchMap, take, tap } from 'rxjs/operators'
4
+ import { Observable } from 'rxjs'
5
+ import { map } from 'rxjs/operators'
6
6
  import { HttpClient } from '@angular/common/http'
7
- import { FormFieldConfig } from '../../../../../../libs/ui/inputs/src'
8
7
  import { CatalogRecord } from '../../../../../../libs/common/domain/src/lib/model/record'
9
-
10
- export interface FormField {
11
- config: FormFieldConfig
12
- value: string | number | boolean | unknown
13
- }
8
+ import { EditorFieldsConfig } from '../models/fields.model'
9
+ import { evaluate } from '../expressions'
14
10
 
15
11
  @Injectable({
16
12
  providedIn: 'root',
17
13
  })
18
14
  export class EditorService {
19
- private record$ = new BehaviorSubject<CatalogRecord | null>(null)
20
- private fieldsConfig: FormFieldConfig[] = [
21
- {
22
- model: 'title',
23
- labelKey: 'Metadata title',
24
- type: 'text',
25
- },
26
- {
27
- model: 'abstract',
28
- labelKey: 'Abstract',
29
- type: 'rich',
30
- },
31
- {
32
- model: 'uniqueIdentifier',
33
- labelKey: 'Unique identifier',
34
- type: 'text',
35
- locked: true,
36
- },
37
- ]
38
-
39
15
  private apiUrl = `${this.apiConfiguration?.basePath || '/geonetwork/srv/api'}`
40
16
 
41
- fields$: Observable<FormField[]> = this.record$.pipe(
42
- map((record) =>
43
- this.fieldsConfig.map((fieldConfig) => ({
44
- config: fieldConfig,
45
- value: record?.[fieldConfig.model] || null,
46
- }))
47
- )
48
- )
49
- saving$ = new BehaviorSubject(false)
50
-
51
17
  constructor(
52
18
  private http: HttpClient,
53
19
  @Optional()
54
20
  @Inject(Configuration)
55
21
  private apiConfiguration: Configuration
56
22
  ) {}
23
+
24
+ // TODO: use the catalog repository instead
57
25
  loadRecordByUuid(uuid: string): Observable<CatalogRecord> {
58
26
  return this.http
59
27
  .get(`${this.apiUrl}/records/${uuid}/formatters/xml`, {
@@ -65,43 +33,36 @@ export class EditorService {
65
33
  .pipe(map((response) => toModel(response.toString())))
66
34
  }
67
35
 
68
- saveCurrentRecord(): Observable<void> {
69
- return this.record$.pipe(
70
- take(1),
71
- tap((record) => {
72
- if (!record)
73
- throw new Error('Save record failed: no record currently open')
74
- this.saving$.next(true)
75
- }),
76
- switchMap((record) =>
77
- this.http.put(
78
- `${this.apiUrl}/records?metadataType=METADATA&uuidProcessing=OVERWRITE&transformWith=_none_&publishToAll=on`,
79
- toXml(record),
80
- {
81
- headers: {
82
- 'Content-Type': 'application/xml',
83
- },
84
- withCredentials: true,
85
- }
86
- )
87
- ),
88
- map(() => undefined),
89
- finalize(() => {
90
- this.saving$.next(false)
91
- })
92
- )
93
- }
36
+ // returns the record as it was when saved
37
+ saveRecord(
38
+ record: CatalogRecord,
39
+ fieldsConfig: EditorFieldsConfig
40
+ ): Observable<CatalogRecord> {
41
+ const savedRecord = { ...record }
94
42
 
95
- setCurrentRecord(record: CatalogRecord) {
96
- this.record$.next(record)
97
- }
43
+ // run onSave processes
44
+ for (const field of fieldsConfig) {
45
+ if (field.onSaveProcess && field.model) {
46
+ const evaluator = evaluate(field.onSaveProcess)
47
+ savedRecord[field.model] = evaluator({
48
+ config: field,
49
+ value: record[field.model],
50
+ })
51
+ }
52
+ }
98
53
 
99
- updateRecordField(fieldName: string, value: unknown) {
100
- this.record$
101
- .pipe(
102
- take(1),
103
- map((record) => ({ ...record, [fieldName]: value }))
54
+ // TODO: use the catalog repository instead
55
+ return this.http
56
+ .put(
57
+ `${this.apiUrl}/records?metadataType=METADATA&uuidProcessing=OVERWRITE&transformWith=_none_&publishToAll=on`,
58
+ toXml(savedRecord),
59
+ {
60
+ headers: {
61
+ 'Content-Type': 'application/xml',
62
+ },
63
+ withCredentials: true,
64
+ }
104
65
  )
105
- .subscribe((record) => this.record$.next(record))
66
+ .pipe(map(() => savedRecord))
106
67
  }
107
68
  }
@@ -29,18 +29,18 @@ export class DataViewPermalinkComponent {
29
29
  if (config) {
30
30
  const { aggregation, xProperty, yProperty, chartType } = config
31
31
  const url = new URL(`${this.wcEmbedderBaseUrl}`, window.location.origin)
32
- url.search = `?v=${this.version}
33
- &e=gn-dataset-view-chart
34
- &a=api-url=${this.config.basePath}
35
- &a=dataset-id=${metadata.uniqueIdentifier}
36
- &a=primary-color=%230f4395
37
- &a=secondary-color=%238bc832
38
- &a=main-color=%23555
39
- &a=background-color=%23fdfbff
40
- &a=aggregation=${aggregation}
41
- &a=x-property=${xProperty}
42
- &a=y-property=${yProperty}
43
- &a=chart-type=${chartType}`
32
+ url.searchParams.set('v', `${this.version}`)
33
+ url.searchParams.append('e', `gn-dataset-view-chart`)
34
+ url.searchParams.append('a', `api-url=${this.config.basePath}`)
35
+ url.searchParams.append('a', `dataset-id=${metadata.uniqueIdentifier}`)
36
+ url.searchParams.append('a', `primary-color=#0f4395`)
37
+ url.searchParams.append('a', `secondary-color=#8bc832`)
38
+ url.searchParams.append('a', `main-color=#555`)
39
+ url.searchParams.append('a', `background-color=#fdfbff`)
40
+ url.searchParams.append('a', `aggregation=${aggregation}`)
41
+ url.searchParams.append('a', `x-property=${xProperty}`)
42
+ url.searchParams.append('a', `y-property=${yProperty}`)
43
+ url.searchParams.append('a', `chart-type=${chartType}`)
44
44
  return url.toString()
45
45
  }
46
46
  return ''
@@ -5,6 +5,6 @@
5
5
  [selected]="currentLang"
6
6
  ariaName="languages"
7
7
  [showTitle]="false"
8
- [extraBtnClass]="'flex justify-items-center !pl-2 !py-1'"
8
+ extraBtnClass="bg-transparent flex justify-items-center !pl-2 !py-1"
9
9
  >
10
10
  </gn-ui-dropdown-selector>
@@ -1,10 +1,14 @@
1
1
  <div
2
2
  #container
3
- class="max-lines overflow-hidden transition-[max-height] duration-300"
3
+ class="max-lines overflow-hidden transition-[max-height] duration-300 relative"
4
4
  [ngClass]="isExpanded ? 'ease-in' : 'ease-out'"
5
5
  [style.maxHeight]="maxHeight"
6
6
  >
7
7
  <ng-content></ng-content>
8
+ <div
9
+ *ngIf="showToggleButton && !isExpanded"
10
+ class="absolute inset-x-0 bottom-0 bg-gradient-to-b from-transparent to-white h-3"
11
+ ></div>
8
12
  </div>
9
13
  <div
10
14
  *ngIf="showToggleButton"
@@ -78,6 +78,7 @@ export class MaxLinesComponent implements AfterViewInit, OnDestroy {
78
78
  }
79
79
 
80
80
  ngOnDestroy(): void {
81
+ if (!this.observer) return
81
82
  this.observer.unobserve(this.container.nativeElement.children[0])
82
83
  }
83
84
  }
@@ -1,5 +1,5 @@
1
1
  <div class="py-5 px-5 rounded bg-gray-100 text-black mb-6">
2
- <div class="grid gap-3">
2
+ <div class="grid gap-3 overflow-hidden">
3
3
  <div>
4
4
  <p class="text-sm font-medium" translate>record.metadata.contact</p>
5
5
  </div>
@@ -55,29 +55,82 @@
55
55
  </div>
56
56
  </gn-ui-expandable-panel>
57
57
  <gn-ui-expandable-panel
58
- class="metadata-origin"
59
58
  *ngIf="
60
59
  metadata.lineage ||
61
60
  metadata.recordUpdated ||
62
61
  metadata.updateFrequency ||
63
62
  metadata.status
64
63
  "
65
- [title]="'record.metadata.origin' | translate"
64
+ [title]="'record.metadata.details' | translate"
66
65
  >
67
- <p
68
- *ngIf="metadata.lineage"
69
- class="mb-5 pt-4 whitespace-pre-line break-words text-gray-900"
70
- gnUiLinkify
71
- >
72
- {{ metadata.lineage }}
73
- </p>
66
+ <div *ngIf="metadata.lineage" class="text-gray-900 flex flex-col mt-4 gap-2">
67
+ <p class="whitespace-pre-line break-words text-gray-900" gnUiLinkify>
68
+ {{ metadata.lineage }}
69
+ </p>
70
+ </div>
71
+ <div class="flex flex-row gap-6 mt-5 mb-8" *ngIf="resourceContact">
72
+ <div
73
+ *ngIf="resourceContact.organization?.logoUrl?.href"
74
+ class="flex items-center justify-center border-solid border border-gray-300 rounded-md bg-white h-32 overflow-hidden"
75
+ >
76
+ <gn-ui-thumbnail
77
+ class="relative h-full w-full"
78
+ [thumbnailUrl]="resourceContact.organization.logoUrl.href"
79
+ fit="contain"
80
+ ></gn-ui-thumbnail>
81
+ </div>
82
+ <div class="flex flex-col gap-1">
83
+ <p class="text-sm font-medium" translate>record.metadata.producer</p>
84
+ <div
85
+ class="text-primary font-title text-21 mr-2 cursor-pointer hover:underline"
86
+ data-cy="organization-name"
87
+ >
88
+ {{ resourceContact.organization?.name }}
89
+ </div>
90
+ <div *ngIf="resourceContact.organization?.website">
91
+ <a
92
+ [href]="resourceContact.organization.website"
93
+ target="_blank"
94
+ class="contact-website text-primary text-sm cursor-pointer hover:underline transition-all"
95
+ >{{ resourceContact.organization.website }}
96
+ <mat-icon
97
+ class="material-symbols-outlined !w-[12px] !h-[12px] !text-[12px] opacity-75 shrink-0"
98
+ >open_in_new</mat-icon
99
+ >
100
+ </a>
101
+ </div>
102
+ <div class="mt-4" *ngIf="resourceContact.email">
103
+ <div class="flex">
104
+ <mat-icon
105
+ class="material-symbols-outlined !w-5 !h-5 !text-[20px] opacity-75 shrink-0"
106
+ >
107
+ mail_outline</mat-icon
108
+ >
109
+ <a
110
+ *ngIf="resourceContact.email"
111
+ [href]="'mailto:' + resourceContact.email"
112
+ class="text-sm hover:underline ml-2"
113
+ target="_blank"
114
+ data-cy="contact-email"
115
+ >{{ resourceContact?.email }}</a
116
+ >
117
+ </div>
118
+ </div>
119
+ </div>
120
+ </div>
74
121
  <div
75
- class="py-4 px-6 rounded bg-gray-100 grid grid-cols-2 gap-y-[10px] gap-x-[20px] text-gray-700 info-grid"
122
+ class="py-6 px-6 rounded bg-gray-100 grid grid-cols-2 gap-y-6 gap-x-[20px] text-gray-700"
76
123
  >
77
- <div *ngIf="metadata.recordUpdated">
78
- <p class="text-sm" translate>record.metadata.updatedOn</p>
124
+ <div *ngIf="metadata.recordCreated">
125
+ <p class="text-sm" translate>record.metadata.creation</p>
79
126
  <p class="text-primary font-medium mt-1">
80
- {{ metadata.recordUpdated && metadata.recordUpdated.toLocaleString() }}
127
+ {{ metadata.recordCreated.toLocaleDateString() }}
128
+ </p>
129
+ </div>
130
+ <div *ngIf="metadata.recordPublished">
131
+ <p class="text-sm" translate>record.metadata.publication</p>
132
+ <p class="text-primary font-medium mt-1">
133
+ {{ metadata.recordPublished.toLocaleDateString() }}
81
134
  </p>
82
135
  </div>
83
136
  <div *ngIf="updateFrequency">
@@ -90,17 +143,52 @@
90
143
  {{ updateFrequency }}
91
144
  </p>
92
145
  </div>
93
- <div *ngIf="metadata.status">
94
- <p class="text-sm" translate>record.metadata.updateStatus</p>
95
- <p class="text-primary font-medium mt-1" translate>
96
- domain.record.status.{{ metadata.status }}
97
- </p>
146
+ <div *ngIf="metadata.languages">
147
+ <p class="text-sm mb-1" translate>record.metadata.languages</p>
148
+ <div class="flex flex-row gap-1 flex-wrap">
149
+ <p
150
+ class="text-primary font-medium"
151
+ translate
152
+ *ngFor="let language of metadata.languages"
153
+ >
154
+ language.{{ language }}
155
+ </p>
156
+ </div>
157
+ </div>
158
+ <div *ngIf="temporalExtent">
159
+ <p class="text-sm" translate>record.metadata.temporalExtent</p>
160
+ <div class="flex flex-row gap-1 mb-1 text-primary font-medium">
161
+ <p
162
+ *ngIf="temporalExtent.start && temporalExtent.end"
163
+ translate
164
+ [translateParams]="{
165
+ start: temporalExtent.start,
166
+ end: temporalExtent.end
167
+ }"
168
+ >
169
+ record.metadata.temporalExtent.fromDateToDate
170
+ </p>
171
+ <p
172
+ *ngIf="temporalExtent.start && !temporalExtent.end"
173
+ translate
174
+ [translateParams]="{ start: temporalExtent.start }"
175
+ >
176
+ record.metadata.temporalExtent.sinceDate
177
+ </p>
178
+ <p
179
+ *ngIf="!temporalExtent.start && temporalExtent.end"
180
+ translate
181
+ [translateParams]="{ end: temporalExtent.end }"
182
+ >
183
+ record.metadata.temporalExtent.untilDate
184
+ </p>
185
+ </div>
98
186
  </div>
99
187
  </div>
100
188
  </gn-ui-expandable-panel>
101
189
  <gn-ui-expandable-panel
102
190
  *ngIf="metadata.landingPage"
103
- [title]="'record.metadata.details' | translate"
191
+ [title]="'record.metadata.technical' | translate"
104
192
  >
105
193
  <div class="flex flex-col gap-4 mr-4 py-5 rounded text-gray-700">
106
194
  <div *ngIf="metadata.recordUpdated">
@@ -9,6 +9,7 @@ import {
9
9
  DatasetRecord,
10
10
  Keyword,
11
11
  } from '../../../../../../libs/common/domain/src/lib/model/record'
12
+ import { getTemporalRangeUnion } from '../../../../../../libs/util/shared/src'
12
13
 
13
14
  @Component({
14
15
  selector: 'gn-ui-metadata-info',
@@ -76,6 +77,19 @@ export class MetadataInfoComponent {
76
77
  }
77
78
  }
78
79
 
80
+ get temporalExtent(): { start: string; end: string } {
81
+ const temporalExtents = this.metadata.temporalExtents
82
+ return getTemporalRangeUnion(temporalExtents)
83
+ }
84
+
85
+ get shownOrganization() {
86
+ return this.metadata.ownerOrganization
87
+ }
88
+
89
+ get resourceContact() {
90
+ return this.metadata.contactsForResource?.[0]
91
+ }
92
+
79
93
  fieldReady(propName: string) {
80
94
  return !this.incomplete || propName in this.metadata
81
95
  }
@@ -1,5 +0,0 @@
1
- /* makes sure icons will not make the buttons grow vertically */
2
- :host ::ng-deep mat-icon.mat-icon {
3
- margin-top: -0.325em;
4
- margin-bottom: -0.325em;
5
- }
@@ -1,6 +1,5 @@
1
1
  <button
2
2
  type="button"
3
- class="flex flex-row items-center text-[1em] leading-none p-[1em] rounded-[0.25em] transition-all duration-100 focus:outline-none disabled:opacity-50 disabled:pointer-events-none"
4
3
  [class]="classList"
5
4
  [disabled]="disabled"
6
5
  (click)="handleClick($event)"
@@ -17,25 +17,25 @@ import { propagateToDocumentOnly } from '../../../../../../libs/util/shared/src'
17
17
  export class ButtonComponent {
18
18
  private btnClass: string
19
19
 
20
- // btn-classes are explicited to allow tailwind recognize them and add it in css.
21
20
  @Input() set type(
22
21
  value: 'primary' | 'secondary' | 'default' | 'outline' | 'light'
23
22
  ) {
23
+ // btn-classes are written in full to be picked up by tailwind
24
24
  switch (value) {
25
25
  case 'primary':
26
- this.btnClass = 'btn-primary'
26
+ this.btnClass = 'gn-ui-btn-primary'
27
27
  break
28
28
  case 'secondary':
29
- this.btnClass = 'btn-secondary'
29
+ this.btnClass = 'gn-ui-btn-secondary'
30
30
  break
31
31
  case 'outline':
32
- this.btnClass = 'btn-outline'
32
+ this.btnClass = 'gn-ui-btn-outline'
33
33
  break
34
34
  case 'light':
35
- this.btnClass = 'btn-light'
35
+ this.btnClass = 'gn-ui-btn-light'
36
36
  break
37
37
  default:
38
- this.btnClass = 'btn-default'
38
+ this.btnClass = 'gn-ui-btn-default'
39
39
  break
40
40
  }
41
41
  }
@@ -1,6 +1,6 @@
1
1
  <gn-ui-button
2
2
  type="outline"
3
- extraClass="w-full !p-[8px] !pl-[16px]"
3
+ extraClass="bg-background w-full !p-[8px] !pl-[16px]"
4
4
  [title]="title"
5
5
  [attr.aria-owns]="id"
6
6
  (buttonClick)="openOverlay()"
@@ -9,7 +9,9 @@
9
9
  <gn-ui-button
10
10
  type="outline"
11
11
  class="grow min-w-0"
12
- extraClass="!p-[8px] !pl-[16px] flex flex-row w-full {{ extraBtnClass }}"
12
+ extraClass="bg-background !p-[8px] !pl-[16px] flex flex-row w-full {{
13
+ extraBtnClass
14
+ }}"
13
15
  [title]="title"
14
16
  [attr.aria-owns]="id"
15
17
  (buttonClick)="openOverlay()"
@@ -17,7 +17,9 @@ import { Observable } from 'rxjs'
17
17
  export class FormFieldComponent {
18
18
  @Input() config: FormFieldConfig
19
19
  @Input() set value(v: unknown) {
20
- this.formControl.setValue(v)
20
+ this.formControl.setValue(v, {
21
+ emitEvent: false,
22
+ })
21
23
  }
22
24
  @Output() valueChange: Observable<unknown>
23
25
 
@@ -13,7 +13,6 @@ type BaseFormFieldType =
13
13
  type AllFormFieldType = BaseFormFieldType | 'object' | 'array'
14
14
 
15
15
  interface FormFieldConfigBase {
16
- model?: string
17
16
  type: AllFormFieldType
18
17
  labelKey: string
19
18
  hintKey?: string
@@ -4,6 +4,7 @@
4
4
  </div>
5
5
  </div>
6
6
  <div
7
+ *ngIf="steps.length > 1"
7
8
  class="absolute right-0 top-0 flex flex-row justify-center gap-[10px] p-1"
8
9
  [ngClass]="stepsContainerClass"
9
10
  >
@@ -40,6 +40,24 @@ export const LANG_3_TO_2_MAPPER = {
40
40
  slo: 'sk',
41
41
  }
42
42
 
43
+ export const LANGUAGE_NAMES = {
44
+ en: 'English',
45
+ nl: 'Nederlands',
46
+ fr: 'Français',
47
+ de: 'Deutsch',
48
+ ko: '한국어',
49
+ es: 'Español',
50
+ cs: 'Čeština',
51
+ ca: 'Català',
52
+ fi: 'Suomi',
53
+ is: 'Íslenska',
54
+ it: 'Italiano',
55
+ pt: 'Português',
56
+ ru: 'Русский',
57
+ zh: '中文',
58
+ sk: 'Slovenčina',
59
+ }
60
+
43
61
  // Caution: changing this can break language selection from third parties!
44
62
  export const LANGUAGE_STORAGE_KEY = 'geonetwork-ui-language'
45
63
 
@@ -6,3 +6,4 @@ export * from './sort-by'
6
6
  export * from './url'
7
7
  export * from './event'
8
8
  export * from './fuzzy-filter'
9
+ export * from './temporal-extent-union'
@@ -0,0 +1,32 @@
1
+ export function getTemporalRangeUnion(ranges) {
2
+ let earliestStartDate = Infinity
3
+ let latestEndDate = -Infinity
4
+ if (ranges.length) {
5
+ for (let i = 0; i < ranges.length; i++) {
6
+ const rangeStart = ranges[i].start
7
+ ? new Date(ranges[i].start).getTime()
8
+ : -Infinity
9
+ const rangeEnd = ranges[i].end
10
+ ? new Date(ranges[i].end).getTime()
11
+ : Infinity
12
+ if (rangeStart < earliestStartDate) {
13
+ earliestStartDate = rangeStart
14
+ }
15
+ if (rangeEnd > latestEndDate) {
16
+ latestEndDate = rangeEnd
17
+ }
18
+ }
19
+ return {
20
+ start:
21
+ earliestStartDate !== -Infinity
22
+ ? new Date(earliestStartDate).toLocaleDateString()
23
+ : undefined,
24
+ end:
25
+ latestEndDate !== Infinity
26
+ ? new Date(latestEndDate).toLocaleDateString()
27
+ : undefined,
28
+ }
29
+ } else {
30
+ return undefined
31
+ }
32
+ }
@@ -52,9 +52,6 @@ module.exports = {
52
52
  'var(--font-family-title, ui-serif, Georgia, Cambria, "Times New Roman", Times, serif)', // alias for serif
53
53
  mono: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
54
54
  },
55
- fontWeight: {
56
- title: '600',
57
- },
58
55
  fontSize: {
59
56
  13: '13px',
60
57
  21: [
package/tailwind.base.css CHANGED
@@ -2,49 +2,78 @@
2
2
  @tailwind components;
3
3
  @tailwind utilities;
4
4
 
5
+ /* PLEASE NOTE: all Tailwind components should be prefixed by `gn-ui` */
5
6
  @layer components {
7
+ /* TODO: add prefix */
6
8
  .card-shadow {
7
9
  @apply shadow-xl hover:shadow-xl-hover transition-shadow;
8
10
  }
9
11
 
10
- .btn {
11
- @apply flex flex-row items-center text-[1em] leading-none p-[1em] rounded-[0.25em] transition-all duration-100 focus:outline-none disabled:opacity-50;
12
+ /* BUTTON CLASSES */
13
+ /* these are used behind the scenes by the `gn-ui-button` component */
14
+
15
+ /* this is a base class, do not use it directly */
16
+ .gn-ui-btn {
17
+ --rounded: var(--gn-ui-button-rounded, 0.25em);
18
+ --padding: var(--gn-ui-button-padding, 1em);
19
+ --width: var(--gn-ui-button-width, auto);
20
+ --height: var(--gn-ui-button-height, auto);
21
+ --background: var(--gn-ui-button-background, --color-background);
22
+ --font-size: var(--gn-ui-button-font-size, 1em);
23
+ @apply flex flex-row items-center justify-center
24
+ text-[length:--font-size] leading-none
25
+ p-[--padding] rounded-[--rounded] w-[--width] h-[--height] overflow-hidden
26
+ bg-[--background] transition-all duration-100
27
+ focus:outline-none
28
+ disabled:opacity-50 disabled:pointer-events-none;
29
+ }
30
+ /* makes sure icons will not make the buttons grow vertically */
31
+ .gn-ui-btn mat-icon.mat-icon {
32
+ margin-top: -0.325em;
33
+ margin-bottom: -0.325em;
34
+ flex-shrink: 0;
35
+ }
36
+ /* makes spinners appear in the right color */
37
+ .gn-ui-btn mat-spinner circle {
38
+ stroke: currentColor !important;
12
39
  }
13
40
 
14
- .btn-default {
15
- @apply btn text-white
41
+ .gn-ui-btn-default {
42
+ @apply gn-ui-btn text-white
16
43
  bg-gray-700 hover:bg-gray-800 active:bg-gray-900
17
44
  border border-gray-700 focus:ring-4 focus:ring-gray-200;
18
45
  }
19
46
 
20
- .btn-primary {
21
- @apply btn text-white
47
+ .gn-ui-btn-primary {
48
+ @apply gn-ui-btn text-white
22
49
  bg-primary hover:bg-primary-darker focus:bg-primary-darker active:bg-primary-darkest
23
50
  border border-primary focus:ring-4 focus:ring-primary-lightest;
24
51
  }
25
52
 
26
- .btn-secondary {
27
- @apply btn text-white
53
+ .gn-ui-btn-secondary {
54
+ @apply gn-ui-btn text-white
28
55
  bg-secondary hover:bg-secondary-darker focus:bg-secondary-darker active:bg-secondary-darkest
29
56
  border border-secondary focus:ring-4 focus:ring-secondary-lightest;
30
57
  }
31
58
 
32
- .btn-outline {
33
- @apply btn text-main
34
- bg-transparent hover:text-primary-darker focus:text-primary-darker active:text-primary-black
59
+ .gn-ui-btn-outline {
60
+ @apply gn-ui-btn text-main
61
+ hover:text-primary-darker focus:text-primary-darker active:text-primary-black
35
62
  border border-gray-300 hover:border-primary-lighter focus:border-primary-lighter focus:ring-4 focus:ring-primary-lightest active:border-primary-darker;
36
63
  }
37
64
 
38
- .btn-light {
39
- @apply btn text-main
40
- bg-white hover:bg-gray-50 focus:bg-gray-50 active:bg-gray-100
65
+ .gn-ui-btn-light {
66
+ @apply gn-ui-btn text-main
67
+ hover:bg-gray-50 focus:bg-gray-50 active:bg-gray-100
41
68
  border border-white focus:ring-4 focus:ring-gray-300;
42
69
  }
43
70
 
71
+ /* TODO: add prefix */
44
72
  .badge-btn {
45
73
  @apply flex items-center justify-center px-4 py-1 text-white rounded backdrop-blur;
46
74
  }
47
75
 
76
+ /* TODO: add prefix */
48
77
  .card-icon {
49
78
  @apply text-primary opacity-50 group-hover:text-secondary group-hover:opacity-100 transition-colors transition-opacity;
50
79
  }