geonetwork-ui 2.7.0-dev.0d8b7bca9 → 2.7.0-dev.1feef407a

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 (123) 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/platform/gn4-platform.service.mjs +18 -4
  5. package/esm2022/libs/common/domain/src/lib/model/record/metadata.model.mjs +5 -1
  6. package/esm2022/libs/feature/dataviz/src/lib/service/data.service.mjs +2 -2
  7. package/esm2022/libs/feature/editor/src/index.mjs +2 -1
  8. package/esm2022/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.mjs +80 -0
  9. package/esm2022/libs/feature/editor/src/lib/components/multilingual-panel/multilingual-panel.component.mjs +3 -3
  10. package/esm2022/libs/feature/editor/src/lib/fields.config.mjs +1 -3
  11. package/esm2022/libs/feature/record/src/lib/map-view/map-view.component.mjs +4 -2
  12. package/esm2022/libs/feature/search/src/lib/state/reducer.mjs +5 -2
  13. package/esm2022/libs/ui/elements/src/lib/downloads-list/downloads-list.component.mjs +5 -4
  14. package/esm2022/libs/ui/elements/src/lib/geo-data-badge/geo-data-badge.component.mjs +5 -4
  15. package/esm2022/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.mjs +4 -4
  16. package/esm2022/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.mjs +21 -43
  17. package/esm2022/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.mjs +5 -4
  18. package/esm2022/libs/util/app-config/src/lib/app-config.mjs +3 -1
  19. package/esm2022/libs/util/app-config/src/lib/model.mjs +1 -1
  20. package/esm2022/libs/util/shared/src/index.mjs +2 -2
  21. package/esm2022/libs/util/shared/src/lib/links/link-utils.mjs +4 -2
  22. package/esm2022/libs/util/shared/src/lib/record/index.mjs +3 -0
  23. package/esm2022/libs/util/shared/src/lib/record/quality-score.util.mjs +45 -0
  24. package/esm2022/libs/util/shared/src/lib/record/record.util.mjs +56 -0
  25. package/esm2022/libs/util/shared/src/lib/utils/index.mjs +2 -1
  26. package/esm2022/libs/util/shared/src/lib/utils/mobile-screen.mjs +9 -0
  27. package/esm2022/translations/de.json +7 -0
  28. package/esm2022/translations/en.json +7 -0
  29. package/esm2022/translations/es.json +7 -0
  30. package/esm2022/translations/fr.json +8 -1
  31. package/esm2022/translations/it.json +7 -0
  32. package/esm2022/translations/nl.json +7 -0
  33. package/esm2022/translations/pt.json +7 -0
  34. package/esm2022/translations/sk.json +7 -0
  35. package/fesm2022/geonetwork-ui.mjs +259 -71
  36. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  37. package/libs/api/metadata-converter/src/lib/iso19139/utils/status.mapper.d.ts.map +1 -1
  38. package/libs/api/metadata-converter/src/lib/iso19139/write-parts.d.ts.map +1 -1
  39. package/libs/api/repository/src/lib/gn4/elasticsearch/constant.d.ts.map +1 -1
  40. package/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.d.ts +2 -0
  41. package/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.d.ts.map +1 -1
  42. package/libs/common/domain/src/lib/model/record/metadata.model.d.ts.map +1 -1
  43. package/libs/feature/dataviz/src/lib/service/data.service.d.ts.map +1 -1
  44. package/libs/feature/editor/src/index.d.ts +1 -0
  45. package/libs/feature/editor/src/index.d.ts.map +1 -1
  46. package/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.d.ts +19 -0
  47. package/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.d.ts.map +1 -0
  48. package/libs/feature/editor/src/lib/fields.config.d.ts.map +1 -1
  49. package/libs/feature/record/src/lib/map-view/map-view.component.d.ts.map +1 -1
  50. package/libs/feature/search/src/lib/state/reducer.d.ts.map +1 -1
  51. package/libs/ui/elements/src/lib/downloads-list/downloads-list.component.d.ts +1 -0
  52. package/libs/ui/elements/src/lib/downloads-list/downloads-list.component.d.ts.map +1 -1
  53. package/libs/ui/elements/src/lib/geo-data-badge/geo-data-badge.component.d.ts +1 -0
  54. package/libs/ui/elements/src/lib/geo-data-badge/geo-data-badge.component.d.ts.map +1 -1
  55. package/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.d.ts.map +1 -1
  56. package/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.d.ts +7 -6
  57. package/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.d.ts.map +1 -1
  58. package/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.d.ts +1 -0
  59. package/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.d.ts.map +1 -1
  60. package/libs/util/app-config/src/lib/app-config.d.ts.map +1 -1
  61. package/libs/util/app-config/src/lib/model.d.ts +1 -0
  62. package/libs/util/app-config/src/lib/model.d.ts.map +1 -1
  63. package/libs/util/shared/src/index.d.ts +1 -1
  64. package/libs/util/shared/src/index.d.ts.map +1 -1
  65. package/libs/util/shared/src/lib/links/link-utils.d.ts +1 -1
  66. package/libs/util/shared/src/lib/links/link-utils.d.ts.map +1 -1
  67. package/libs/util/shared/src/lib/record/index.d.ts +3 -0
  68. package/libs/util/shared/src/lib/record/index.d.ts.map +1 -0
  69. package/libs/util/shared/src/lib/record/quality-score.util.d.ts +13 -0
  70. package/libs/util/shared/src/lib/record/quality-score.util.d.ts.map +1 -0
  71. package/libs/util/shared/src/lib/record/record.util.d.ts +3 -0
  72. package/libs/util/shared/src/lib/record/record.util.d.ts.map +1 -0
  73. package/libs/util/shared/src/lib/utils/index.d.ts +1 -0
  74. package/libs/util/shared/src/lib/utils/index.d.ts.map +1 -1
  75. package/libs/util/shared/src/lib/utils/mobile-screen.d.ts +2 -0
  76. package/libs/util/shared/src/lib/utils/mobile-screen.d.ts.map +1 -0
  77. package/package.json +1 -1
  78. package/src/libs/api/metadata-converter/src/lib/dcat-ap/utils/status.mapper.ts +3 -0
  79. package/src/libs/api/metadata-converter/src/lib/iso19139/utils/status.mapper.ts +3 -0
  80. package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +4 -0
  81. package/src/libs/api/repository/src/lib/gn4/elasticsearch/constant.ts +3 -5
  82. package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +19 -6
  83. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +4 -0
  84. package/src/libs/common/fixtures/src/lib/editor/editor.fixtures.ts +0 -3
  85. package/src/libs/feature/dataviz/src/lib/service/data.service.ts +3 -1
  86. package/src/libs/feature/editor/src/index.ts +1 -0
  87. package/src/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.css +0 -0
  88. package/src/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.html +37 -0
  89. package/src/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.ts +90 -0
  90. package/src/libs/feature/editor/src/lib/components/multilingual-panel/multilingual-panel.component.html +2 -1
  91. package/src/libs/feature/editor/src/lib/fields.config.ts +0 -2
  92. package/src/libs/feature/record/src/lib/map-view/map-view.component.ts +3 -1
  93. package/src/libs/feature/search/src/lib/state/reducer.ts +4 -1
  94. package/src/libs/ui/elements/src/lib/downloads-list/downloads-list.component.html +12 -11
  95. package/src/libs/ui/elements/src/lib/downloads-list/downloads-list.component.ts +7 -1
  96. package/src/libs/ui/elements/src/lib/geo-data-badge/geo-data-badge.component.html +4 -1
  97. package/src/libs/ui/elements/src/lib/geo-data-badge/geo-data-badge.component.ts +7 -1
  98. package/src/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.html +1 -0
  99. package/src/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.ts +5 -1
  100. package/src/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.html +1 -0
  101. package/src/libs/ui/elements/src/lib/metadata-quality/metadata-quality.component.ts +21 -54
  102. package/src/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.html +7 -1
  103. package/src/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.ts +3 -1
  104. package/src/libs/util/app-config/src/lib/app-config.ts +2 -0
  105. package/src/libs/util/app-config/src/lib/model.ts +1 -0
  106. package/src/libs/util/shared/src/index.ts +1 -1
  107. package/src/libs/util/shared/src/lib/links/link-utils.ts +2 -1
  108. package/src/libs/util/shared/src/lib/record/index.ts +2 -0
  109. package/src/libs/util/shared/src/lib/record/quality-score.util.ts +69 -0
  110. package/src/libs/util/shared/src/lib/{record.util.ts → record/record.util.ts} +1 -1
  111. package/src/libs/util/shared/src/lib/utils/index.ts +1 -0
  112. package/src/libs/util/shared/src/lib/utils/mobile-screen.ts +14 -0
  113. package/translations/de.json +7 -0
  114. package/translations/en.json +7 -0
  115. package/translations/es.json +7 -0
  116. package/translations/fr.json +8 -1
  117. package/translations/it.json +7 -0
  118. package/translations/nl.json +7 -0
  119. package/translations/pt.json +7 -0
  120. package/translations/sk.json +7 -0
  121. package/esm2022/libs/util/shared/src/lib/record.util.mjs +0 -56
  122. package/libs/util/shared/src/lib/record.util.d.ts +0 -3
  123. package/libs/util/shared/src/lib/record.util.d.ts.map +0 -1
@@ -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',
@@ -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 {
@@ -1,5 +1,8 @@
1
1
  <div
2
2
  class="flex items-center justify-between p-3 mt-8 bg-white rounded-lg border-b solid border-gray-300"
3
+ *ngIf="
4
+ (isMobile$ | async) === false || featureCatalog?.featureTypes?.length > 1
5
+ "
3
6
  >
4
7
  <div
5
8
  class="relative shrink-0"
@@ -17,7 +20,10 @@
17
20
  name="iconoirSearch"
18
21
  ></ng-icon>
19
22
  </div>
20
- <div class="text-sm px-1 ml-auto hidden sm:inline">
23
+ <div
24
+ *ngIf="(isMobile$ | async) === false"
25
+ class="text-sm px-1 ml-auto inline"
26
+ >
21
27
  <ng-container *ngIf="featureCatalog?.featureTypes?.length > 1">
22
28
  <span
23
29
  class="text-sm font-medium text-gray-900"
@@ -6,7 +6,7 @@ import { DatasetFeatureCatalog } from '../../../../../../libs/common/domain/src/
6
6
  import { FormsModule } from '@angular/forms'
7
7
  import { of } from 'rxjs'
8
8
  import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators'
9
- import { createFuzzyFilter } from '../../../../../../libs/util/shared/src'
9
+ import { createFuzzyFilter, getIsMobile } from '../../../../../../libs/util/shared/src'
10
10
  import { iconoirSearch } from '@ng-icons/iconoir'
11
11
 
12
12
  @Component({
@@ -39,6 +39,8 @@ export class SearchFeatureCatalogComponent {
39
39
  return this._featureCatalog
40
40
  }
41
41
 
42
+ isMobile$ = getIsMobile()
43
+
42
44
  @Output() filteredFeatureCatalogChange =
43
45
  new EventEmitter<DatasetFeatureCatalog>()
44
46
  filteredFeatureCatalog: DatasetFeatureCatalog
@@ -231,6 +231,7 @@ export function loadAppConfig() {
231
231
  'filter_geometry_url',
232
232
  'search_preset',
233
233
  'advanced_filters',
234
+ 'limit',
234
235
  ],
235
236
  warnings,
236
237
  errors
@@ -257,6 +258,7 @@ export function loadAppConfig() {
257
258
  filters: param.filters,
258
259
  })),
259
260
  ADVANCED_FILTERS: parsedSearchSection.advanced_filters,
261
+ LIMIT: parsedSearchSection.limit,
260
262
  } as SearchConfig)
261
263
 
262
264
  const parsedMetadataQualitySection = parseConfigSection(
@@ -60,6 +60,7 @@ export interface SearchConfig {
60
60
  FILTER_GEOMETRY_DATA?: string
61
61
  SEARCH_PRESET?: SearchPreset[]
62
62
  ADVANCED_FILTERS?: []
63
+ LIMIT?: number
63
64
  }
64
65
 
65
66
  export interface MetadataQualityConfig {
@@ -3,4 +3,4 @@ export * from './lib/utils'
3
3
  export * from './lib/links'
4
4
  export * from './lib/image-fallback.directive'
5
5
  export * from './lib/gn-ui-version'
6
- export * from './lib/record.util'
6
+ export * from './lib/record'
@@ -23,8 +23,8 @@ export const FORMATS = {
23
23
  excel: {
24
24
  extensions: [
25
25
  'excel',
26
- 'xls',
27
26
  'xlsx',
27
+ 'xls',
28
28
  'ms-excel',
29
29
  'openxmlformats-officedocument',
30
30
  ],
@@ -190,6 +190,7 @@ export function isFormatInQueryParam(
190
190
  alias: string
191
191
  ): boolean {
192
192
  const queryParams = link.url.searchParams
193
+ if (!queryParams) return false
193
194
  for (const [key, value] of queryParams.entries()) {
194
195
  if (key === 'format' || key === 'f') {
195
196
  return value === alias
@@ -0,0 +1,2 @@
1
+ export * from './quality-score.util'
2
+ export * from './record.util'
@@ -0,0 +1,69 @@
1
+ import {
2
+ CatalogRecord,
3
+ RecordKind,
4
+ } from '../../../../../../libs/common/domain/src/lib/model/record'
5
+
6
+ type TValidatorMapper = {
7
+ [key: string]: (metadata: Partial<CatalogRecord>) => boolean
8
+ }
9
+
10
+ const ValidatorMapper: TValidatorMapper = {
11
+ title: (record) => !!record?.title,
12
+ abstract: (record) => !!record?.abstract,
13
+ keywords: (record) => (record?.keywords?.length ?? 0) > 0,
14
+ legalConstraints: (record) => (record?.legalConstraints?.length ?? 0) > 0,
15
+ contacts: (record) => !!record?.contacts?.[0]?.email,
16
+ updateFrequency: (record) => !!record?.updateFrequency,
17
+ topics: (record) => (record?.topics?.length ?? 0) > 0,
18
+ organisation: (record) => !!record?.contacts?.[0]?.organization?.name,
19
+ capabilities: (record) =>
20
+ record?.onlineResources?.some((resource) =>
21
+ resource?.url?.href.toLowerCase().includes('capabilities')
22
+ ),
23
+ source: (record) => !!record?.extras?.sourcesIdentifiers,
24
+ } as const
25
+
26
+ export type ValidatorMapperKeys = keyof typeof ValidatorMapper & string
27
+
28
+ export function getAllKeysValidator() {
29
+ return Object.keys(ValidatorMapper)
30
+ }
31
+
32
+ function getMappersFromKind(kind: RecordKind) {
33
+ let kindKeys = <ValidatorMapperKeys[]>[]
34
+ const commonsKeys = <ValidatorMapperKeys[]>[
35
+ 'title',
36
+ 'abstract',
37
+ 'keywords',
38
+ 'legalConstraints',
39
+ 'contacts',
40
+ ]
41
+
42
+ switch (kind) {
43
+ case 'service':
44
+ kindKeys = ['capabilities']
45
+ break
46
+ case 'reuse':
47
+ kindKeys = ['topics', 'organisation', 'source']
48
+ break
49
+ case 'dataset':
50
+ default:
51
+ kindKeys = ['updateFrequency', 'topics', 'organisation']
52
+ }
53
+
54
+ return [...commonsKeys, ...kindKeys]
55
+ }
56
+
57
+ export function getQualityValidators(
58
+ record: Partial<CatalogRecord>,
59
+ propsToValidate: ValidatorMapperKeys[]
60
+ ) {
61
+ const filteredProps = propsToValidate.filter((prop) =>
62
+ getMappersFromKind(record.kind).includes(prop)
63
+ )
64
+
65
+ return filteredProps.map((name) => ({
66
+ name,
67
+ validator: () => ValidatorMapper[name](record),
68
+ }))
69
+ }
@@ -10,7 +10,7 @@ import {
10
10
  OnlineLinkResource,
11
11
  Organization,
12
12
  ServiceEndpoint,
13
- } from '../../../../../libs/common/domain/src/lib/model/record'
13
+ } from '../../../../../../libs/common/domain/src/lib/model/record'
14
14
 
15
15
  type TranslatedRecordObjects =
16
16
  | BaseRecord
@@ -4,6 +4,7 @@ export * from './format-fields'
4
4
  export * from './fuzzy-filter'
5
5
  export * from './geojson'
6
6
  export * from './image-resize'
7
+ export * from './mobile-screen'
7
8
  export * from './no-duplicate-file-name'
8
9
  export * from './parse'
9
10
  export * from './remove-whitespace'
@@ -0,0 +1,14 @@
1
+ import { fromEvent, startWith, map, shareReplay } from 'rxjs'
2
+
3
+ /**
4
+ * This returns true when the screen size is under 768px, which is the mobile threshold.
5
+ */
6
+ const MOBILE_MAX_WIDTH = 768
7
+
8
+ export function getIsMobile() {
9
+ return fromEvent(window, 'resize').pipe(
10
+ startWith(window.innerWidth),
11
+ map(() => window.innerWidth < MOBILE_MAX_WIDTH),
12
+ shareReplay({ bufferSize: 1, refCount: true })
13
+ )
14
+ }