geonetwork-ui 2.7.0-dev.47822f9d1 → 2.7.0-dev.54999b5e2

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 (135) hide show
  1. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/utils/status.mapper.mjs +4 -1
  2. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/write-parts.mjs +5 -1
  3. package/esm2022/libs/api/repository/src/lib/gn4/elasticsearch/constant.mjs +4 -6
  4. package/esm2022/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.mjs +47 -3
  5. package/esm2022/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.mjs +18 -4
  6. package/esm2022/libs/common/domain/src/lib/model/record/metadata.model.mjs +5 -1
  7. package/esm2022/libs/feature/dataviz/src/lib/service/data.service.mjs +2 -2
  8. package/esm2022/libs/feature/editor/src/index.mjs +2 -1
  9. package/esm2022/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.mjs +80 -0
  10. package/esm2022/libs/feature/editor/src/lib/components/multilingual-panel/multilingual-panel.component.mjs +3 -3
  11. package/esm2022/libs/feature/editor/src/lib/fields.config.mjs +1 -3
  12. package/esm2022/libs/feature/map/src/lib/utils/map-utils.service.mjs +3 -5
  13. package/esm2022/libs/feature/record/src/lib/map-view/map-view.component.mjs +4 -2
  14. package/esm2022/libs/feature/search/src/lib/state/reducer.mjs +5 -2
  15. package/esm2022/libs/ui/elements/src/lib/downloads-list/downloads-list.component.mjs +5 -4
  16. package/esm2022/libs/ui/elements/src/lib/geo-data-badge/geo-data-badge.component.mjs +5 -4
  17. package/esm2022/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.mjs +4 -4
  18. package/esm2022/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.mjs +21 -43
  19. package/esm2022/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.mjs +5 -4
  20. package/esm2022/libs/util/app-config/src/lib/app-config.mjs +3 -1
  21. package/esm2022/libs/util/app-config/src/lib/model.mjs +1 -1
  22. package/esm2022/libs/util/shared/src/index.mjs +2 -2
  23. package/esm2022/libs/util/shared/src/lib/links/link-utils.mjs +4 -2
  24. package/esm2022/libs/util/shared/src/lib/record/index.mjs +3 -0
  25. package/esm2022/libs/util/shared/src/lib/record/quality-score.util.mjs +45 -0
  26. package/esm2022/libs/util/shared/src/lib/record/record.util.mjs +56 -0
  27. package/esm2022/libs/util/shared/src/lib/utils/geojson.mjs +58 -1
  28. package/esm2022/libs/util/shared/src/lib/utils/index.mjs +2 -1
  29. package/esm2022/libs/util/shared/src/lib/utils/mobile-screen.mjs +9 -0
  30. package/esm2022/translations/de.json +7 -0
  31. package/esm2022/translations/en.json +7 -0
  32. package/esm2022/translations/es.json +7 -0
  33. package/esm2022/translations/fr.json +8 -1
  34. package/esm2022/translations/it.json +7 -0
  35. package/esm2022/translations/nl.json +7 -0
  36. package/esm2022/translations/pt.json +7 -0
  37. package/esm2022/translations/sk.json +7 -0
  38. package/fesm2022/geonetwork-ui.mjs +2577 -2292
  39. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  40. package/libs/api/metadata-converter/src/lib/iso19139/utils/status.mapper.d.ts.map +1 -1
  41. package/libs/api/metadata-converter/src/lib/iso19139/write-parts.d.ts.map +1 -1
  42. package/libs/api/repository/src/lib/gn4/elasticsearch/constant.d.ts.map +1 -1
  43. package/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.d.ts +1 -1
  44. package/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.d.ts.map +1 -1
  45. package/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.d.ts +2 -0
  46. package/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.d.ts.map +1 -1
  47. package/libs/common/domain/src/lib/model/record/metadata.model.d.ts.map +1 -1
  48. package/libs/feature/dataviz/src/lib/service/data.service.d.ts.map +1 -1
  49. package/libs/feature/editor/src/index.d.ts +1 -0
  50. package/libs/feature/editor/src/index.d.ts.map +1 -1
  51. package/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.d.ts +19 -0
  52. package/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.d.ts.map +1 -0
  53. package/libs/feature/editor/src/lib/fields.config.d.ts.map +1 -1
  54. package/libs/feature/map/src/lib/utils/map-utils.service.d.ts +2 -2
  55. package/libs/feature/map/src/lib/utils/map-utils.service.d.ts.map +1 -1
  56. package/libs/feature/record/src/lib/map-view/map-view.component.d.ts.map +1 -1
  57. package/libs/feature/search/src/lib/state/reducer.d.ts.map +1 -1
  58. package/libs/ui/elements/src/lib/downloads-list/downloads-list.component.d.ts +1 -0
  59. package/libs/ui/elements/src/lib/downloads-list/downloads-list.component.d.ts.map +1 -1
  60. package/libs/ui/elements/src/lib/geo-data-badge/geo-data-badge.component.d.ts +1 -0
  61. package/libs/ui/elements/src/lib/geo-data-badge/geo-data-badge.component.d.ts.map +1 -1
  62. package/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.d.ts.map +1 -1
  63. package/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.d.ts +7 -6
  64. package/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.d.ts.map +1 -1
  65. package/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.d.ts +1 -0
  66. package/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.d.ts.map +1 -1
  67. package/libs/util/app-config/src/lib/app-config.d.ts.map +1 -1
  68. package/libs/util/app-config/src/lib/model.d.ts +1 -0
  69. package/libs/util/app-config/src/lib/model.d.ts.map +1 -1
  70. package/libs/util/shared/src/index.d.ts +1 -1
  71. package/libs/util/shared/src/index.d.ts.map +1 -1
  72. package/libs/util/shared/src/lib/links/link-utils.d.ts +1 -1
  73. package/libs/util/shared/src/lib/links/link-utils.d.ts.map +1 -1
  74. package/libs/util/shared/src/lib/record/index.d.ts +3 -0
  75. package/libs/util/shared/src/lib/record/index.d.ts.map +1 -0
  76. package/libs/util/shared/src/lib/record/quality-score.util.d.ts +13 -0
  77. package/libs/util/shared/src/lib/record/quality-score.util.d.ts.map +1 -0
  78. package/libs/util/shared/src/lib/record/record.util.d.ts +3 -0
  79. package/libs/util/shared/src/lib/record/record.util.d.ts.map +1 -0
  80. package/libs/util/shared/src/lib/utils/geojson.d.ts +7 -2
  81. package/libs/util/shared/src/lib/utils/geojson.d.ts.map +1 -1
  82. package/libs/util/shared/src/lib/utils/index.d.ts +1 -0
  83. package/libs/util/shared/src/lib/utils/index.d.ts.map +1 -1
  84. package/libs/util/shared/src/lib/utils/mobile-screen.d.ts +2 -0
  85. package/libs/util/shared/src/lib/utils/mobile-screen.d.ts.map +1 -0
  86. package/package.json +1 -1
  87. package/src/libs/api/metadata-converter/src/lib/dcat-ap/utils/status.mapper.ts +3 -0
  88. package/src/libs/api/metadata-converter/src/lib/iso19139/utils/status.mapper.ts +3 -0
  89. package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +4 -0
  90. package/src/libs/api/repository/src/lib/gn4/elasticsearch/constant.ts +3 -5
  91. package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +50 -3
  92. package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +19 -6
  93. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +4 -0
  94. package/src/libs/common/fixtures/src/lib/editor/editor.fixtures.ts +0 -3
  95. package/src/libs/feature/dataviz/src/lib/service/data.service.ts +3 -1
  96. package/src/libs/feature/editor/src/index.ts +1 -0
  97. package/src/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.css +0 -0
  98. package/src/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.html +37 -0
  99. package/src/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.ts +90 -0
  100. package/src/libs/feature/editor/src/lib/components/multilingual-panel/multilingual-panel.component.html +2 -1
  101. package/src/libs/feature/editor/src/lib/fields.config.ts +0 -2
  102. package/src/libs/feature/map/src/lib/utils/map-utils.service.ts +8 -8
  103. package/src/libs/feature/record/src/lib/map-view/map-view.component.ts +3 -1
  104. package/src/libs/feature/search/src/lib/state/reducer.ts +4 -1
  105. package/src/libs/ui/elements/src/lib/downloads-list/downloads-list.component.html +12 -11
  106. package/src/libs/ui/elements/src/lib/downloads-list/downloads-list.component.ts +7 -1
  107. package/src/libs/ui/elements/src/lib/geo-data-badge/geo-data-badge.component.html +4 -1
  108. package/src/libs/ui/elements/src/lib/geo-data-badge/geo-data-badge.component.ts +7 -1
  109. package/src/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.html +1 -0
  110. package/src/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.ts +5 -1
  111. package/src/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.html +1 -0
  112. package/src/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.ts +21 -54
  113. package/src/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.html +7 -1
  114. package/src/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.ts +3 -1
  115. package/src/libs/util/app-config/src/lib/app-config.ts +2 -0
  116. package/src/libs/util/app-config/src/lib/model.ts +1 -0
  117. package/src/libs/util/shared/src/index.ts +1 -1
  118. package/src/libs/util/shared/src/lib/links/link-utils.ts +2 -1
  119. package/src/libs/util/shared/src/lib/record/index.ts +2 -0
  120. package/src/libs/util/shared/src/lib/record/quality-score.util.ts +69 -0
  121. package/src/libs/util/shared/src/lib/{record.util.ts → record/record.util.ts} +1 -1
  122. package/src/libs/util/shared/src/lib/utils/geojson.ts +72 -2
  123. package/src/libs/util/shared/src/lib/utils/index.ts +1 -0
  124. package/src/libs/util/shared/src/lib/utils/mobile-screen.ts +14 -0
  125. package/translations/de.json +7 -0
  126. package/translations/en.json +7 -0
  127. package/translations/es.json +7 -0
  128. package/translations/fr.json +8 -1
  129. package/translations/it.json +7 -0
  130. package/translations/nl.json +7 -0
  131. package/translations/pt.json +7 -0
  132. package/translations/sk.json +7 -0
  133. package/esm2022/libs/util/shared/src/lib/record.util.mjs +0 -56
  134. package/libs/util/shared/src/lib/record.util.d.ts +0 -3
  135. package/libs/util/shared/src/lib/record.util.d.ts.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { Injectable, Injector } from '@angular/core'
2
- import { Geometry } from 'geojson'
2
+ import type { Geometry } from 'geojson'
3
3
  import {
4
4
  ES_QUERY_FIELDS_PRIORITY,
5
5
  ES_SOURCE_SUMMARY,
@@ -30,6 +30,9 @@ import { getLang3FromLang2 } from '../../../../../../../libs/util/i18n/src'
30
30
  import { formatDate, isDateRange } from './date-range.utils'
31
31
  import { CatalogRecord } from '../../../../../../../libs/common/domain/src/lib/model/record'
32
32
  import { TranslateService } from '@ngx-translate/core'
33
+ import { getGeometryBoundingBox } from '../../../../../../../libs/util/shared/src'
34
+ import { getLength as getGeodesicLength } from 'ol/sphere'
35
+ import { LineString } from 'ol/geom'
33
36
 
34
37
  export type DateRange = { start?: Date; end?: Date }
35
38
 
@@ -351,6 +354,11 @@ export class ElasticsearchService {
351
354
  })
352
355
  }
353
356
  if (geometry) {
357
+ // boosts applied using the filter geometry:
358
+ // * records completely within the geometry receive a boost of 5
359
+ // * records intersecting the geometry receive a boost of 2
360
+ // * records close to the geometry center receive a boost of 5 (based on the `location` field)
361
+ // * records on the outskirt of the geometry receive a boost of 2.5
354
362
  should.push(
355
363
  {
356
364
  geo_shape: {
@@ -358,7 +366,7 @@ export class ElasticsearchService {
358
366
  shape: geometry,
359
367
  relation: 'within',
360
368
  },
361
- boost: 10.0,
369
+ boost: 5.0,
362
370
  },
363
371
  },
364
372
  {
@@ -367,10 +375,49 @@ export class ElasticsearchService {
367
375
  shape: geometry,
368
376
  relation: 'intersects',
369
377
  },
370
- boost: 7.0,
378
+ boost: 2.0,
371
379
  },
372
380
  }
373
381
  )
382
+
383
+ // this will boost the results variably depending on their distance from the given geometry
384
+ // note: this takes into account the `location` field of a record; this is generally the center of all spatial extents
385
+ // combined, and thus the actual size/coverage of the record spatial extent isn't relevant here
386
+ const bbox = getGeometryBoundingBox(geometry)
387
+ const center = [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2]
388
+ const northToCenter = new LineString([
389
+ [center[0], bbox[3]],
390
+ center,
391
+ ]).transform('EPSG:4326', 'EPSG:3857')
392
+ const southToCenter = new LineString([
393
+ [center[0], bbox[1]],
394
+ center,
395
+ ]).transform('EPSG:4326', 'EPSG:3857')
396
+ const westToCenter = new LineString([
397
+ [bbox[0], center[1]],
398
+ center,
399
+ ]).transform('EPSG:4326', 'EPSG:3857')
400
+ const eastToCenter = new LineString([
401
+ [bbox[2], center[1]],
402
+ center,
403
+ ]).transform('EPSG:4326', 'EPSG:3857')
404
+ // cutoff distance is the distance from where the boost will only be half of the max value
405
+ // it is an average of the "size" of the bounding box in every direction, in meters
406
+ const cutoffDistance =
407
+ (getGeodesicLength(northToCenter) +
408
+ getGeodesicLength(southToCenter) +
409
+ getGeodesicLength(westToCenter) +
410
+ getGeodesicLength(eastToCenter)) /
411
+ 4
412
+
413
+ should.push({
414
+ distance_feature: {
415
+ field: 'location',
416
+ pivot: `${Math.round(cutoffDistance).toFixed(0)}m`,
417
+ origin: center,
418
+ boost: 5.0,
419
+ },
420
+ })
374
421
  }
375
422
 
376
423
  return {
@@ -46,7 +46,6 @@ import {
46
46
  } from 'rxjs'
47
47
  import { TranslateService } from '@ngx-translate/core'
48
48
  import { getLang3FromLang2 } from '../../../../../../../libs/util/i18n/src'
49
- import { DatavizConfigModel } from '../../../../../../../libs/common/domain/src/lib/model/dataviz/dataviz-configuration.model'
50
49
 
51
50
  const minApiVersion = '4.2.2'
52
51
 
@@ -56,6 +55,7 @@ export class Gn4PlatformService implements PlatformServiceInterface {
56
55
  private readonly me$: Observable<UserModel>
57
56
  private readonly users$: Observable<UserModel[]>
58
57
  private readonly isUserAnonymous$: Observable<boolean>
58
+ private readonly gnParseVersion = '4.2.5'
59
59
 
60
60
  private keyTranslations$ = this.toolsApiService
61
61
  .getTranslationsPackage1('gnui')
@@ -390,22 +390,35 @@ export class Gn4PlatformService implements PlatformServiceInterface {
390
390
  })
391
391
  )
392
392
  }
393
-
394
393
  getFileContent(url: URL | string): Observable<any> {
395
- return this.httpClient.get(url.toString(), { responseType: 'text' }).pipe(
396
- map((text) => {
394
+ return combineLatest([
395
+ this.httpClient.get(url.toString(), { responseType: 'text' }),
396
+ this.getApiVersion(),
397
+ ]).pipe(
398
+ map(([text, version]) => {
397
399
  const parsed = JSON.parse(text)
398
400
 
399
- if (typeof parsed === 'object') {
401
+ if (version > this.gnParseVersion) {
400
402
  return parsed
401
403
  }
402
404
 
403
- const decoded = atob(parsed)
405
+ const decoded = this.decodeBase64(parsed)
404
406
  return JSON.parse(decoded)
405
407
  })
406
408
  )
407
409
  }
408
410
 
411
+ decodeBase64(base64) {
412
+ const text = atob(base64)
413
+ const length = text.length
414
+ const bytes = new Uint8Array(length)
415
+ for (let i = 0; i < length; i++) {
416
+ bytes[i] = text.charCodeAt(i)
417
+ }
418
+ const decoder = new TextDecoder()
419
+ return decoder.decode(bytes)
420
+ }
421
+
409
422
  attachFileToRecord(
410
423
  recordUuid: string,
411
424
  file: File,
@@ -69,6 +69,8 @@ marker('domain.record.status.ongoing')
69
69
  marker('domain.record.status.under_development')
70
70
  marker('domain.record.status.deprecated')
71
71
  marker('domain.record.status.removed')
72
+ marker('domain.record.status.planned')
73
+ marker('domain.record.status.required')
72
74
 
73
75
  export const RecordStatusValues = [
74
76
  'completed',
@@ -76,6 +78,8 @@ export const RecordStatusValues = [
76
78
  'under_development',
77
79
  'deprecated',
78
80
  'removed',
81
+ 'planned',
82
+ 'required',
79
83
  ]
80
84
  export type RecordStatus = (typeof RecordStatusValues)[number]
81
85
 
@@ -127,9 +127,6 @@ export const editorFieldSpatialExtentsFixture = () => ({
127
127
  export const editorFieldKeywordsFixture = () => ({
128
128
  model: 'keywords',
129
129
  hidden: false,
130
- formFieldConfig: {
131
- labelKey: 'editor.record.form.field.keywords',
132
- },
133
130
  })
134
131
 
135
132
  export const editorFieldUniqueIdentifierFixture = () => ({
@@ -237,7 +237,9 @@ export class DataService {
237
237
  tmsLink: DatasetServiceDistribution,
238
238
  keepOriginalLink = false
239
239
  ): Promise<DatasetServiceDistribution[]> {
240
- const endpoint = new TmsEndpoint(tmsLink.url.toString())
240
+ const endpoint = new TmsEndpoint(
241
+ tmsLink.url.toString().replace(/\/?$/, `/${tmsLink.name}`)
242
+ )
241
243
  const tileMaps = await endpoint.allTileMaps.catch(() => {
242
244
  throw new Error(`ogc.unreachable.unknown`)
243
245
  })
@@ -9,3 +9,4 @@ export * from './lib/components/import-record/import-record.component'
9
9
  export * from './lib/components/record-form/record-form.component'
10
10
  export * from './lib/components/record-form/form-field'
11
11
  export * from './lib/components/multilingual-panel/multilingual-panel.component'
12
+ export * from './lib/components/metadata-quality-panel/metadata-quality-panel.component'
@@ -0,0 +1,37 @@
1
+ <div
2
+ class="flex flex-col h-full w-[302px] border-l border-gray-300 py-8 px-3 gap-3 overflow-auto"
3
+ style="background-color: #fafaf9"
4
+ >
5
+ <div class="flex flex-row px-2 justify-between">
6
+ <span class="text-3xl font-title text-black/80" translate
7
+ >editor.record.form.metadataQuality.title</span
8
+ >
9
+ </div>
10
+ <div
11
+ *ngFor="let properties of propertiesByPage; let i = index"
12
+ class="flex flex-col gap-2"
13
+ >
14
+ <gn-ui-button
15
+ *ngFor="let property of properties"
16
+ [extraClass]="getExtraClass(property.value)"
17
+ type="outline"
18
+ attr.data-cy="md-quality-btn-{{ property.label }}"
19
+ >
20
+ <span>{{ property.label | translate }}</span>
21
+ <div class="flex flex-row gap-2 items-center">
22
+ <ng-icon
23
+ *ngIf="property.value; else notChecked"
24
+ class="text-primary"
25
+ name="iconoirBadgeCheck"
26
+ ></ng-icon>
27
+ <ng-template #notChecked>
28
+ <ng-icon class="text-neutral-300" name="iconoirSystemShut"></ng-icon>
29
+ </ng-template>
30
+ </div>
31
+ </gn-ui-button>
32
+ <hr
33
+ *ngIf="i !== propertiesByPage.length - 1"
34
+ class="border-gray-300 w-11/12 mx-auto"
35
+ />
36
+ </div>
37
+ </div>
@@ -0,0 +1,90 @@
1
+ import { CommonModule } from '@angular/common'
2
+ import { Component, Input, OnChanges } from '@angular/core'
3
+ import { CatalogRecord } from '../../../../../../../libs/common/domain/src/lib/model/record'
4
+ import { ButtonComponent } from '../../../../../../../libs/ui/inputs/src'
5
+ import {
6
+ getAllKeysValidator,
7
+ getQualityValidators,
8
+ ValidatorMapperKeys,
9
+ } from '../../../../../../../libs/util/shared/src'
10
+ import {
11
+ NgIconComponent,
12
+ provideIcons,
13
+ provideNgIconsConfig,
14
+ } from '@ng-icons/core'
15
+ import { iconoirBadgeCheck, iconoirSystemShut } from '@ng-icons/iconoir'
16
+ import { TranslateDirective, TranslatePipe } from '@ngx-translate/core'
17
+ import { EditorConfig } from '../../models'
18
+ import { marker } from '@biesbjerg/ngx-translate-extract-marker'
19
+
20
+ //forced translations that are not available in fields.config.ts
21
+ marker('editor.record.form.field.keywords')
22
+ marker('editor.record.form.field.topics')
23
+ marker('editor.record.form.field.contacts')
24
+ marker('editor.record.form.field.organisation')
25
+ @Component({
26
+ selector: 'gn-ui-metadata-quality-panel',
27
+ standalone: true,
28
+ imports: [
29
+ CommonModule,
30
+ TranslateDirective,
31
+ TranslatePipe,
32
+ ButtonComponent,
33
+ NgIconComponent,
34
+ ],
35
+ providers: [
36
+ provideIcons({
37
+ iconoirSystemShut,
38
+ iconoirBadgeCheck,
39
+ }),
40
+ provideNgIconsConfig({
41
+ size: '1.25em',
42
+ }),
43
+ ],
44
+ templateUrl: './metadata-quality-panel.component.html',
45
+ styleUrl: './metadata-quality-panel.component.css',
46
+ })
47
+ export class MetadataQualityPanelComponent implements OnChanges {
48
+ propsToValidate: ValidatorMapperKeys[] = getAllKeysValidator()
49
+ propertiesByPage: { label: string; value: boolean }[][] = []
50
+ @Input() editorConfig: EditorConfig
51
+ @Input() record: CatalogRecord
52
+
53
+ ngOnChanges() {
54
+ if (this.editorConfig && this.record) {
55
+ const fieldsByPage = this.editorConfig.pages.map((page) =>
56
+ page.sections.flatMap((section) =>
57
+ section.fields
58
+ .filter((field) => this.propsToValidate.includes(field.model))
59
+ .map((field) => field.model as ValidatorMapperKeys)
60
+ )
61
+ )
62
+ // FIXME: temporarily add topics and organisation to the first and third page
63
+ // as long as they are not handled by the editor
64
+ if (fieldsByPage.length > 0) {
65
+ fieldsByPage[0].includes('topics') || fieldsByPage[0].push('topics')
66
+ fieldsByPage[2].includes('organisation') ||
67
+ fieldsByPage[2].push('organisation')
68
+ }
69
+ this.propertiesByPage = fieldsByPage
70
+ .map((fields) =>
71
+ getQualityValidators(
72
+ this.record,
73
+ fields as ValidatorMapperKeys[]
74
+ ).map(({ name, validator }) => ({
75
+ label: `editor.record.form.field.${name}`, // use same translations as in fields.config.ts
76
+ value: validator(),
77
+ }))
78
+ )
79
+ .filter((arr) => arr.length > 0)
80
+ }
81
+ }
82
+
83
+ getExtraClass(checked: boolean): string {
84
+ const baseClasses =
85
+ 'flex flex-row justify-between rounded mb-1 h-[34px] w-full focus:ring-0 hover:border-none border-none hover:text-black text-black cursor-default'
86
+ return checked
87
+ ? `${baseClasses} bg-neutral-100 hover:bg-neutral-100`
88
+ : `${baseClasses} bg-transparent hover:bg-transparent`
89
+ }
90
+ }
@@ -1,5 +1,6 @@
1
1
  <div
2
- class="flex flex-col h-full w-[302px] bg-neutral-100 border-l border-gray-300 py-8 px-3 gap-6 overflow-auto"
2
+ class="flex flex-col h-full w-[302px] border-l border-gray-300 py-8 px-3 gap-6 overflow-auto"
3
+ style="background-color: #fafaf9"
3
4
  >
4
5
  <div class="flex flex-row px-2 justify-between">
5
6
  <span class="text-3xl font-title text-black/80" translate
@@ -63,8 +63,6 @@ export const RECORD_KEYWORDS_FIELD: EditorField = {
63
63
  model: 'keywords',
64
64
  formFieldConfig: {},
65
65
  }
66
- // keeping track of the label to not lose existing translation
67
- marker('editor.record.form.field.keywords')
68
66
 
69
67
  export const RECORD_RESOURCE_CREATED_FIELD: EditorField = {
70
68
  model: 'resourceCreated',
@@ -1,25 +1,25 @@
1
1
  import { Injectable } from '@angular/core'
2
- import { extend, Extent } from 'ol/extent'
3
- import GeoJSON from 'ol/format/GeoJSON'
2
+ import { extend } from 'ol/extent'
4
3
  import { CatalogRecord } from '../../../../../../libs/common/domain/src/lib/model/record'
5
-
6
- const GEOJSON = new GeoJSON()
4
+ import { BoundingBox, getGeometryBoundingBox } from '../../../../../../libs/util/shared/src'
7
5
 
8
6
  @Injectable({
9
7
  providedIn: 'root',
10
8
  })
11
9
  export class MapUtilsService {
12
- getRecordExtent(record: Partial<CatalogRecord>): Extent {
10
+ getRecordExtent(record: Partial<CatalogRecord>): BoundingBox {
13
11
  if (!('spatialExtents' in record) || record.spatialExtents.length === 0) {
14
12
  return null
15
13
  }
16
14
  // extend all the spatial extents into an including bbox
17
15
  return record.spatialExtents.reduce(
18
16
  (prev, curr) => {
19
- if ('bbox' in curr) return extend(prev, curr.bbox)
17
+ if ('bbox' in curr) return extend(prev, curr.bbox) as BoundingBox
20
18
  else if ('geometry' in curr) {
21
- const geom = GEOJSON.readGeometry(curr.geometry)
22
- return extend(prev, geom.getExtent())
19
+ return extend(
20
+ prev,
21
+ getGeometryBoundingBox(curr.geometry)
22
+ ) as BoundingBox
23
23
  }
24
24
  return prev
25
25
  },
@@ -389,7 +389,9 @@ export class MapViewComponent implements AfterViewInit {
389
389
  ) {
390
390
  // FIXME: here we're assuming that the TMS serves vector tiles only; should be checked with ogc-client first
391
391
  return of({
392
- url: link.url.toString().replace(/\/?$/, '/{z}/{x}/{y}.pbf'),
392
+ url: link.url
393
+ .toString()
394
+ .replace(/\/?$/, `/${link.name}/{z}/{x}/{y}.pbf`),
393
395
  type: 'xyz',
394
396
  tileFormat: 'application/vnd.mapbox-vector-tile',
395
397
  name: link.name,
@@ -8,6 +8,7 @@ import {
8
8
  SortByField,
9
9
  } from '../../../../../../libs/common/domain/src/lib/model/search'
10
10
  import { DEFAULT_PAGE_SIZE, FIELDS_SUMMARY } from '../constants'
11
+ import { getOptionalSearchConfig } from '../../../../../../libs/util/app-config/src'
11
12
  import { CatalogRecord } from '../../../../../../libs/common/domain/src/lib/model/record'
12
13
 
13
14
  export const SEARCH_FEATURE_KEY = 'searchState'
@@ -55,7 +56,9 @@ export const initSearch = (): SearchStateSearch => {
55
56
  },
56
57
  params: {
57
58
  filters: {},
58
- pageSize: DEFAULT_PAGE_SIZE,
59
+ pageSize: getOptionalSearchConfig()?.LIMIT
60
+ ? getOptionalSearchConfig().LIMIT
61
+ : DEFAULT_PAGE_SIZE,
59
62
  currentPage: 0,
60
63
  favoritesOnly: false,
61
64
  useSpatialFilter: true,
@@ -13,8 +13,7 @@
13
13
  <span class="px-3">({{ linksCount }})</span>
14
14
  </div>
15
15
  <gn-ui-previous-next-buttons
16
- class="md:block hidden"
17
- *ngIf="_list?.pagesCount > 1"
16
+ *ngIf="(isMobile$ | async) === false && _list?.pagesCount > 1"
18
17
  [listComponent]="_list"
19
18
  ></gn-ui-previous-next-buttons>
20
19
  </div>
@@ -40,7 +39,7 @@
40
39
 
41
40
  <ng-container>
42
41
  <gn-ui-block-list
43
- class="md:block hidden"
42
+ *ngIf="(isMobile$ | async) === false"
44
43
  #blockList
45
44
  (listChanges)="updateList($event)"
46
45
  containerClass="gap-4 pt-5 pb-7"
@@ -57,12 +56,14 @@
57
56
  </gn-ui-block-list>
58
57
  </ng-container>
59
58
 
60
- <div class="mb-5 md:hidden block" *ngFor="let link of filteredLinks">
61
- <gn-ui-download-item
62
- size="M"
63
- [link]="link"
64
- [color]="getLinkColor(link)"
65
- [format]="getLinkFormat(link)"
66
- [isFromApi]="isFromApi(link)"
67
- ></gn-ui-download-item>
59
+ <div class="mb-5" *ngFor="let link of filteredLinks">
60
+ <ng-container *ngIf="(isMobile$ | async) === true">
61
+ <gn-ui-download-item
62
+ size="M"
63
+ [link]="link"
64
+ [color]="getLinkColor(link)"
65
+ [format]="getLinkFormat(link)"
66
+ [isFromApi]="isFromApi(link)"
67
+ ></gn-ui-download-item>
68
+ </ng-container>
68
69
  </div>
@@ -6,7 +6,11 @@ import {
6
6
  } from '@angular/core'
7
7
  import { TranslateDirective, TranslateService } from '@ngx-translate/core'
8
8
  import { marker } from '@biesbjerg/ngx-translate-extract-marker'
9
- import { getBadgeColor, getFileFormat } from '../../../../../../libs/util/shared/src'
9
+ import {
10
+ getBadgeColor,
11
+ getFileFormat,
12
+ getIsMobile,
13
+ } from '../../../../../../libs/util/shared/src'
10
14
  import { DatasetDownloadDistribution } from '../../../../../../libs/common/domain/src/lib/model/record'
11
15
  import { CommonModule } from '@angular/common'
12
16
  import { ButtonComponent } from '../../../../../../libs/ui/inputs/src'
@@ -50,6 +54,8 @@ export class DownloadsListComponent {
50
54
  return this.filteredLinks?.length || 0
51
55
  }
52
56
 
57
+ isMobile$ = getIsMobile()
58
+
53
59
  activeFilterFormats: FilterFormat[] = ['all']
54
60
 
55
61
  updateList($event: BlockListComponent) {
@@ -7,7 +7,10 @@
7
7
  class="shrink-0 text-[0.75em]"
8
8
  name="matLocationSearchingOutline"
9
9
  ></ng-icon>
10
- <span class="ml-1 hidden sm:inline-block shrink-0" *ngIf="showLabel" translate
10
+ <span
11
+ class="ml-1 inline-block shrink-0"
12
+ *ngIf="(isMobile$ | async) === false && showLabel"
13
+ translate
11
14
  >record.metadata.isGeographical</span
12
15
  >
13
16
  </div>
@@ -4,7 +4,11 @@ import { CatalogRecord } from '../../../../../../libs/common/domain/src/lib/mode
4
4
  import { CommonModule } from '@angular/common'
5
5
  import { TranslateDirective, TranslatePipe } from '@ngx-translate/core'
6
6
  import { NgIcon, provideIcons } from '@ng-icons/core'
7
- import { LinkClassifierService, LinkUsage } from '../../../../../../libs/util/shared/src'
7
+ import {
8
+ getIsMobile,
9
+ LinkClassifierService,
10
+ LinkUsage,
11
+ } from '../../../../../../libs/util/shared/src'
8
12
 
9
13
  @Component({
10
14
  selector: 'gn-ui-geo-data-badge',
@@ -24,6 +28,8 @@ export class GeoDataBadgeComponent {
24
28
  @Input() styling = 'default'
25
29
  @Input() record: CatalogRecord
26
30
 
31
+ isMobile$ = getIsMobile()
32
+
27
33
  isGeodata() {
28
34
  const links =
29
35
  'onlineResources' in this.record ? this.record.onlineResources : []
@@ -68,6 +68,7 @@
68
68
  [smaller]="true"
69
69
  [metadata]="record"
70
70
  [metadataQualityDisplay]="metadataQualityDisplay"
71
+ [popoverDisplay]="true"
71
72
  ></gn-ui-metadata-quality>
72
73
  </div>
73
74
  <div
@@ -21,7 +21,11 @@ import { KindBadgeComponent } from '../kind-badge/kind-badge.component'
21
21
  import { MarkdownParserComponent } from '../markdown-parser/markdown-parser.component'
22
22
  import { MetadataQualityComponent } from '../metadata-quality/metadata-quality.component'
23
23
  import { ThumbnailComponent } from '../thumbnail/thumbnail.component'
24
- import { removeWhitespace, stripHtml } from '../../../../../../libs/util/shared/src'
24
+ import {
25
+ removeWhitespace,
26
+ stripHtml,
27
+ type ValidatorMapperKeys,
28
+ } from '../../../../../../libs/util/shared/src'
25
29
 
26
30
  type CardSize = 'L' | 'M' | 'S' | 'XS'
27
31
 
@@ -10,6 +10,7 @@
10
10
  class="flex-grow"
11
11
  ></gn-ui-progress-bar>
12
12
  <gn-ui-popover
13
+ *ngIf="popoverDisplay"
13
14
  [content]="popoverItems"
14
15
  theme="light-border"
15
16
  [class]="smaller ? 'ml-2' : 'ml-2 mt-1'"
@@ -14,15 +14,16 @@ import {
14
14
  PopoverComponent,
15
15
  ProgressBarComponent,
16
16
  } from '../../../../../../libs/ui/widgets/src'
17
+ import {
18
+ getQualityValidators,
19
+ getAllKeysValidator,
20
+ type ValidatorMapperKeys,
21
+ } from '../../../../../../libs/util/shared/src'
17
22
  import { CommonModule } from '@angular/common'
18
23
  import { TranslateDirective } from '@ngx-translate/core'
19
24
  import { NgIcon, provideIcons, provideNgIconsConfig } from '@ng-icons/core'
20
25
  import { matInfoOutline } from '@ng-icons/material-icons/outline'
21
26
 
22
- type QualityChecks = {
23
- [key: string]: (metadata: Partial<CatalogRecord>) => boolean
24
- }
25
-
26
27
  @Component({
27
28
  selector: 'gn-ui-metadata-quality',
28
29
  templateUrl: './metadata-quality.component.html',
@@ -48,77 +49,43 @@ type QualityChecks = {
48
49
  ],
49
50
  })
50
51
  export class MetadataQualityComponent implements OnChanges {
51
- @Input() metadata: Partial<CatalogRecord>
52
+ @Input() metadata: CatalogRecord
52
53
  @Input() smaller = false
53
54
  @Input() metadataQualityDisplay: boolean
55
+ @Input() popoverDisplay = true
56
+ @Input() propsToValidate?: ValidatorMapperKeys[]
57
+ @Input() forceComputeScore = false // Instead of returning es' quality score
54
58
 
55
59
  items: MetadataQualityItem[] = []
56
60
 
57
61
  get qualityScore() {
58
- const qualityScore = this.metadata?.extras?.qualityScore
62
+ const qualityScore = !this.forceComputeScore
63
+ ? this.metadata?.extras?.qualityScore
64
+ : this.computedQualityScore
65
+
59
66
  return typeof qualityScore === 'number'
60
67
  ? qualityScore
61
- : this.calculatedQualityScore
68
+ : this.computedQualityScore
62
69
  }
63
70
 
64
- get calculatedQualityScore(): number {
71
+ get computedQualityScore(): number {
65
72
  return Math.round(
66
73
  (this.items.filter(({ value }) => value).length * 100) / this.items.length
67
74
  )
68
75
  }
69
76
 
70
- private add(name: string, value: boolean) {
71
- if (this.metadataQualityDisplay?.[name] !== false) {
72
- this.items.push({ name, value })
73
- }
74
- }
75
77
  hasGetCapabilities(url: string): boolean {
76
78
  return url.toLowerCase().includes('capabilities')
77
79
  }
78
80
 
79
- private readonly COMMON_CHECKS: QualityChecks = {
80
- title: (metadata) => !!metadata?.title,
81
- description: (metadata) => !!metadata?.abstract,
82
- keywords: (metadata) => (metadata?.keywords?.length ?? 0) > 0,
83
- legalConstraints: (metadata) =>
84
- (metadata?.legalConstraints?.length ?? 0) > 0,
85
- contact: (metadata) => !!metadata?.contacts?.[0]?.email,
86
- }
87
-
88
- private readonly SPECIFIC_CHECKS: Record<string, QualityChecks> = {
89
- dataset: {
90
- updateFrequency: (metadata) => !!metadata?.updateFrequency,
91
- topic: (metadata) => (metadata?.topics?.length ?? 0) > 0,
92
- organisation: (metadata) => !!metadata?.contacts?.[0]?.organization?.name,
93
- },
94
- service: {
95
- capabilities: (metadata) =>
96
- (metadata?.onlineResources ?? []).some((resource) =>
97
- this.hasGetCapabilities(resource?.url?.href ?? '')
98
- ),
99
- },
100
- reuse: {
101
- topic: (metadata) => (metadata?.topics?.length ?? 0) > 0,
102
- organisation: (metadata) => !!metadata?.contacts?.[0]?.organization?.name,
103
- source: (metadata) => !!metadata?.extras?.sourcesIdentifiers,
104
- },
105
- }
106
-
107
81
  initialize() {
108
- this.items = []
109
-
110
- Object.entries(this.COMMON_CHECKS).forEach(([name, check]) => {
111
- this.add(name, check(this.metadata))
112
- })
113
-
114
- const datasetType = this.metadata?.kind
115
- if (datasetType && this.SPECIFIC_CHECKS[datasetType]) {
116
- Object.entries(this.SPECIFIC_CHECKS[datasetType]).forEach(
117
- ([name, check]) => {
118
- this.add(name, check(this.metadata))
119
- }
120
- )
82
+ if (!this.propsToValidate) {
83
+ this.propsToValidate = getAllKeysValidator()
121
84
  }
85
+
86
+ this.items = getQualityValidators(this.metadata, this.propsToValidate).map(
87
+ ({ name, validator }) => ({ name, value: validator() })
88
+ )
122
89
  }
123
90
 
124
91
  ngOnChanges(changes: SimpleChanges): void {