geonetwork-ui 2.6.0-dev.5b83cdcc5 → 2.6.0-dev.6681b0ab3

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 (99) hide show
  1. package/esm2022/libs/api/repository/src/lib/gn4/gn4-repository.mjs +5 -2
  2. package/esm2022/libs/common/domain/src/lib/model/record/metadata.model.mjs +1 -1
  3. package/esm2022/libs/feature/dataviz/src/lib/chart-view/chart-view.component.mjs +45 -9
  4. package/esm2022/libs/feature/dataviz/src/lib/geo-table-view/geo-table-view.component.mjs +2 -2
  5. package/esm2022/libs/feature/dataviz/src/lib/table-view/table-view.component.mjs +25 -6
  6. package/esm2022/libs/feature/record/src/lib/data-view/data-view.component.mjs +3 -3
  7. package/esm2022/libs/feature/record/src/lib/map-view/map-view.component.mjs +3 -3
  8. package/esm2022/libs/feature/record/src/lib/state/mdview.facade.mjs +7 -1
  9. package/esm2022/libs/ui/dataviz/src/lib/chart/chart.component.mjs +5 -3
  10. package/esm2022/libs/ui/dataviz/src/lib/data-table/data-table.component.mjs +11 -6
  11. package/esm2022/libs/ui/elements/src/index.mjs +2 -1
  12. package/esm2022/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.mjs +3 -3
  13. package/esm2022/libs/ui/elements/src/lib/metadata-info/metadata-info.component.mjs +3 -3
  14. package/esm2022/libs/ui/elements/src/lib/record-feature-catalog/feature-catalog-list/feature-catalog-list.component.mjs +51 -0
  15. package/esm2022/libs/ui/elements/src/lib/ui-elements.module.mjs +1 -1
  16. package/esm2022/libs/ui/inputs/src/index.mjs +2 -1
  17. package/esm2022/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.mjs +68 -0
  18. package/esm2022/libs/ui/layout/src/lib/expandable-panel/expandable-panel.component.mjs +34 -13
  19. package/esm2022/libs/ui/layout/src/lib/truncated-text/truncated-text.component.mjs +65 -14
  20. package/esm2022/libs/ui/map/src/lib/components/feature-detail/feature-detail.component.mjs +29 -4
  21. package/esm2022/libs/ui/map/src/lib/components/spatial-extent/spatial-extent.component.mjs +2 -1
  22. package/esm2022/libs/ui/search/src/lib/results-list/results-list.component.mjs +3 -3
  23. package/esm2022/translations/de.json +8 -0
  24. package/esm2022/translations/en.json +8 -0
  25. package/esm2022/translations/es.json +8 -0
  26. package/esm2022/translations/fr.json +8 -0
  27. package/esm2022/translations/it.json +8 -0
  28. package/esm2022/translations/nl.json +8 -0
  29. package/esm2022/translations/pt.json +8 -0
  30. package/fesm2022/geonetwork-ui.mjs +380 -55
  31. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  32. package/libs/api/repository/src/lib/gn4/gn4-repository.d.ts.map +1 -1
  33. package/libs/common/domain/src/lib/model/record/metadata.model.d.ts +3 -0
  34. package/libs/common/domain/src/lib/model/record/metadata.model.d.ts.map +1 -1
  35. package/libs/feature/dataviz/src/lib/chart-view/chart-view.component.d.ts +12 -8
  36. package/libs/feature/dataviz/src/lib/chart-view/chart-view.component.d.ts.map +1 -1
  37. package/libs/feature/dataviz/src/lib/table-view/table-view.component.d.ts +5 -2
  38. package/libs/feature/dataviz/src/lib/table-view/table-view.component.d.ts.map +1 -1
  39. package/libs/feature/record/src/lib/state/mdview.facade.d.ts +4 -0
  40. package/libs/feature/record/src/lib/state/mdview.facade.d.ts.map +1 -1
  41. package/libs/ui/dataviz/src/lib/chart/chart.component.d.ts +2 -1
  42. package/libs/ui/dataviz/src/lib/chart/chart.component.d.ts.map +1 -1
  43. package/libs/ui/dataviz/src/lib/data-table/data-table.component.d.ts +6 -1
  44. package/libs/ui/dataviz/src/lib/data-table/data-table.component.d.ts.map +1 -1
  45. package/libs/ui/elements/src/index.d.ts +1 -0
  46. package/libs/ui/elements/src/index.d.ts.map +1 -1
  47. package/libs/ui/elements/src/lib/record-feature-catalog/feature-catalog-list/feature-catalog-list.component.d.ts +16 -0
  48. package/libs/ui/elements/src/lib/record-feature-catalog/feature-catalog-list/feature-catalog-list.component.d.ts.map +1 -0
  49. package/libs/ui/elements/src/lib/ui-elements.module.d.ts.map +1 -1
  50. package/libs/ui/inputs/src/index.d.ts +1 -0
  51. package/libs/ui/inputs/src/index.d.ts.map +1 -1
  52. package/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.d.ts +17 -0
  53. package/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.d.ts.map +1 -0
  54. package/libs/ui/layout/src/lib/expandable-panel/expandable-panel.component.d.ts +15 -8
  55. package/libs/ui/layout/src/lib/expandable-panel/expandable-panel.component.d.ts.map +1 -1
  56. package/libs/ui/layout/src/lib/truncated-text/truncated-text.component.d.ts +15 -6
  57. package/libs/ui/layout/src/lib/truncated-text/truncated-text.component.d.ts.map +1 -1
  58. package/libs/ui/map/src/lib/components/feature-detail/feature-detail.component.d.ts +6 -2
  59. package/libs/ui/map/src/lib/components/feature-detail/feature-detail.component.d.ts.map +1 -1
  60. package/libs/ui/map/src/lib/components/spatial-extent/spatial-extent.component.d.ts.map +1 -1
  61. package/package.json +2 -2
  62. package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +4 -1
  63. package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +7 -2
  64. package/src/libs/feature/dataviz/src/lib/chart-view/chart-view.component.html +12 -9
  65. package/src/libs/feature/dataviz/src/lib/chart-view/chart-view.component.ts +54 -10
  66. package/src/libs/feature/dataviz/src/lib/table-view/table-view.component.html +1 -0
  67. package/src/libs/feature/dataviz/src/lib/table-view/table-view.component.ts +27 -1
  68. package/src/libs/feature/record/src/lib/data-view/data-view.component.html +2 -0
  69. package/src/libs/feature/record/src/lib/map-view/map-view.component.html +4 -1
  70. package/src/libs/feature/record/src/lib/state/mdview.facade.ts +7 -0
  71. package/src/libs/ui/dataviz/src/lib/chart/chart.component.ts +2 -1
  72. package/src/libs/ui/dataviz/src/lib/data-table/data-table.component.html +6 -3
  73. package/src/libs/ui/dataviz/src/lib/data-table/data-table.component.ts +5 -4
  74. package/src/libs/ui/elements/src/index.ts +1 -0
  75. package/src/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.html +6 -3
  76. package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +4 -0
  77. package/src/libs/ui/elements/src/lib/record-feature-catalog/feature-catalog-list/feature-catalog-list.component.html +48 -0
  78. package/src/libs/ui/elements/src/lib/record-feature-catalog/feature-catalog-list/feature-catalog-list.component.ts +52 -0
  79. package/src/libs/ui/elements/src/lib/ui-elements.module.ts +0 -1
  80. package/src/libs/ui/inputs/src/index.ts +1 -0
  81. package/src/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.css +0 -0
  82. package/src/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.html +43 -0
  83. package/src/libs/ui/inputs/src/lib/search-feature-catalog/search-feature-catalog.component.ts +77 -0
  84. package/src/libs/ui/layout/src/lib/expandable-panel/expandable-panel.component.html +24 -8
  85. package/src/libs/ui/layout/src/lib/expandable-panel/expandable-panel.component.ts +36 -10
  86. package/src/libs/ui/layout/src/lib/truncated-text/truncated-text.component.html +8 -10
  87. package/src/libs/ui/layout/src/lib/truncated-text/truncated-text.component.ts +75 -7
  88. package/src/libs/ui/map/src/lib/components/feature-detail/feature-detail.component.html +3 -3
  89. package/src/libs/ui/map/src/lib/components/feature-detail/feature-detail.component.ts +27 -3
  90. package/src/libs/ui/map/src/lib/components/spatial-extent/spatial-extent.component.ts +1 -0
  91. package/src/libs/ui/search/src/lib/results-list/results-list.component.html +1 -0
  92. package/translations/de.json +8 -0
  93. package/translations/en.json +8 -0
  94. package/translations/es.json +8 -0
  95. package/translations/fr.json +8 -0
  96. package/translations/it.json +8 -0
  97. package/translations/nl.json +8 -0
  98. package/translations/pt.json +8 -0
  99. package/translations/sk.json +8 -0
@@ -2,9 +2,7 @@ import {
2
2
  ChangeDetectionStrategy,
3
3
  ChangeDetectorRef,
4
4
  Component,
5
- Inject,
6
5
  Input,
7
- Optional,
8
6
  Output,
9
7
  } from '@angular/core'
10
8
  import { marker } from '@biesbjerg/ngx-translate-extract-marker'
@@ -13,6 +11,7 @@ import {
13
11
  FetchError,
14
12
  FieldAggregation,
15
13
  getJsonDataItemsProxy,
14
+ PropertyInfo,
16
15
  } from '../../../../../../libs/util/data-fetcher/src'
17
16
  import {
18
17
  DropdownChoice,
@@ -30,7 +29,10 @@ import {
30
29
  } from 'rxjs/operators'
31
30
  import { DataService } from '../service/data.service'
32
31
  import { InputChartType } from '../../../../../../libs/common/domain/src/lib/model/dataviz/dataviz-configuration.model'
33
- import { DatasetOnlineResource } from '../../../../../../libs/common/domain/src/lib/model/record'
32
+ import {
33
+ DatasetFeatureCatalog,
34
+ DatasetOnlineResource,
35
+ } from '../../../../../../libs/common/domain/src/lib/model/record'
34
36
  import { TranslateModule, TranslateService } from '@ngx-translate/core'
35
37
  import { CommonModule } from '@angular/common'
36
38
  import { ChartComponent } from '../../../../../../libs/ui/dataviz/src'
@@ -67,9 +69,18 @@ marker('chart.aggregation.count')
67
69
  standalone: true,
68
70
  })
69
71
  export class ChartViewComponent {
72
+ public featureCatalog$ = new BehaviorSubject<DatasetFeatureCatalog | null>(
73
+ null
74
+ )
75
+ @Input() set featureCatalog(value: DatasetFeatureCatalog) {
76
+ this.featureCatalog$.next(value)
77
+ }
70
78
  @Input() cacheActive = true
71
79
  @Input() set link(value: DatasetOnlineResource) {
72
80
  this.currentLink$.next(value)
81
+ if (value) {
82
+ this.aggregation$.next('sum')
83
+ }
73
84
  }
74
85
  private currentLink$ = new BehaviorSubject<DatasetOnlineResource>(null)
75
86
 
@@ -150,13 +161,8 @@ export class ChartViewComponent {
150
161
  }),
151
162
  shareReplay(1)
152
163
  )
153
- properties$ = this.dataset$.pipe(
154
- switchMap((dataset) =>
155
- dataset.properties.catch((error) => {
156
- this.handleError(error)
157
- return []
158
- })
159
- ),
164
+ properties$ = combineLatest([this.dataset$, this.featureCatalog$]).pipe(
165
+ switchMap(([dataset, catalog]) => this.setProperties(dataset, catalog)),
160
166
  shareReplay(1)
161
167
  )
162
168
  yChoices$ = this.properties$.pipe(
@@ -197,6 +203,7 @@ export class ChartViewComponent {
197
203
  this.yProperty$.pipe(filter((value) => value !== undefined)),
198
204
  this.aggregation$,
199
205
  ]).pipe(
206
+ filter(([_, x, y]) => !!x || !!y),
200
207
  switchMap(([dataset, xProp, yProp, aggregation]) => {
201
208
  const fieldAgg: FieldAggregation =
202
209
  aggregation === 'count' ? ['count'] : [aggregation, yProp]
@@ -217,6 +224,18 @@ export class ChartViewComponent {
217
224
  shareReplay(1)
218
225
  )
219
226
 
227
+ prettyLabel$ = combineLatest([
228
+ this.aggregation$,
229
+ this.properties$,
230
+ this.yProperty$,
231
+ ]).pipe(
232
+ map(([aggregation, properties, yProperty]) => {
233
+ if (aggregation === 'count') return 'count()'
234
+ const prop = properties.find((p) => p.name === yProperty)
235
+ return prop ? `${aggregation}(${prop.label})` : ''
236
+ })
237
+ )
238
+
220
239
  get labelProperty() {
221
240
  if (!this.xProperty$.value) return ''
222
241
  return `distinct(${this.xProperty$.value})`
@@ -235,6 +254,31 @@ export class ChartViewComponent {
235
254
  private translateService: TranslateService
236
255
  ) {}
237
256
 
257
+ setProperties(
258
+ dataset: BaseReader,
259
+ catalog: DatasetFeatureCatalog
260
+ ): Promise<PropertyInfo[]> {
261
+ return dataset.properties
262
+ .then((properties) => {
263
+ return properties.map((p) => {
264
+ if (catalog) {
265
+ const featureAttributes = catalog?.featureTypes[0]?.attributes ?? []
266
+ const matchingAttribute = featureAttributes.find(
267
+ (attr) => attr.name === p.label
268
+ )
269
+ if (matchingAttribute?.code) {
270
+ return { ...p, label: matchingAttribute.code }
271
+ }
272
+ return p
273
+ }
274
+ return p
275
+ })
276
+ })
277
+ .catch((error) => {
278
+ this.handleError(error)
279
+ return []
280
+ })
281
+ }
238
282
  handleError(error: FetchError | Error | string) {
239
283
  if (error instanceof FetchError) {
240
284
  this.error = this.translateService.instant(
@@ -4,6 +4,7 @@
4
4
  *ngIf="tableData$ | async as dataset"
5
5
  class="overflow-auto grow"
6
6
  [dataset]="dataset"
7
+ [featureAttributes]="featureAttributes"
7
8
  (selected)="onTableSelect($event)"
8
9
  ></gn-ui-data-table>
9
10
  <gn-ui-loading-mask
@@ -6,11 +6,15 @@ import {
6
6
  shareReplay,
7
7
  startWith,
8
8
  switchMap,
9
+ tap,
9
10
  } from 'rxjs/operators'
10
11
  import { BaseReader, FetchError } from '../../../../../../libs/util/data-fetcher/src'
11
12
  import { DataService } from '../service/data.service'
12
13
  import { DataTableComponent } from '../../../../../../libs/ui/dataviz/src'
13
- import { DatasetOnlineResource } from '../../../../../../libs/common/domain/src/lib/model/record'
14
+ import {
15
+ DatasetFeatureCatalog,
16
+ DatasetOnlineResource,
17
+ } from '../../../../../../libs/common/domain/src/lib/model/record'
14
18
  import { TranslateModule, TranslateService } from '@ngx-translate/core'
15
19
  import {
16
20
  LoadingMaskComponent,
@@ -33,6 +37,8 @@ import { CommonModule } from '@angular/common'
33
37
  standalone: true,
34
38
  })
35
39
  export class TableViewComponent {
40
+ featureAttributes = []
41
+ @Input() featureCatalog: DatasetFeatureCatalog
36
42
  @Input() cacheActive = true
37
43
  @Input() set link(value: DatasetOnlineResource) {
38
44
  this.currentLink$.next(value)
@@ -52,6 +58,7 @@ export class TableViewComponent {
52
58
  }
53
59
  this.loading = true
54
60
  return this.getDatasetReader(link).pipe(
61
+ tap((dataset: BaseReader) => this.setProperties(dataset)),
55
62
  catchError((error) => {
56
63
  this.handleError(error)
57
64
  return of(undefined)
@@ -96,4 +103,23 @@ export class TableViewComponent {
96
103
  }
97
104
  this.loading = false
98
105
  }
106
+
107
+ setProperties(dataset: BaseReader) {
108
+ dataset.properties.then((properties) => {
109
+ const updatedProperties = properties.map((p) => {
110
+ let label = p.name
111
+ if (this.featureCatalog) {
112
+ const attributes = this.featureCatalog.featureTypes[0].attributes
113
+ const matchingAttribute = attributes.find(
114
+ (attr) => attr.name === p.name
115
+ )
116
+ if (matchingAttribute && matchingAttribute.code) {
117
+ label = matchingAttribute.code
118
+ }
119
+ }
120
+ return { value: p.name, label }
121
+ })
122
+ this.featureAttributes = updatedProperties
123
+ })
124
+ }
99
125
  }
@@ -27,12 +27,14 @@
27
27
  *ngIf="mode === 'table'"
28
28
  [cacheActive]="cacheActive$ | async"
29
29
  [link]="selectedLink$ | async"
30
+ [featureCatalog]="mdViewFacade.featureCatalog$ | async"
30
31
  ></gn-ui-table-view>
31
32
  <gn-ui-chart-view
32
33
  *ngIf="mode === 'chart'"
33
34
  (chartConfig$)="setChartConfig($event)"
34
35
  [cacheActive]="cacheActive$ | async"
35
36
  [link]="selectedLink$ | async"
37
+ [featureCatalog]="mdViewFacade.featureCatalog$ | async"
36
38
  ></gn-ui-chart-view>
37
39
  </div>
38
40
  </ng-template>
@@ -55,7 +55,10 @@
55
55
  >
56
56
  <ng-icon name="matClose" class="align-middle text-sm"></ng-icon>
57
57
  </gn-ui-button>
58
- <gn-ui-feature-detail [feature]="selection"></gn-ui-feature-detail>
58
+ <gn-ui-feature-detail
59
+ [featureCatalog]="mdViewFacade.featureCatalog$ | async"
60
+ [feature]="selection"
61
+ ></gn-ui-feature-detail>
59
62
  </div>
60
63
 
61
64
  <div
@@ -216,4 +216,11 @@ export class MdViewFacade {
216
216
  loadUserFeedbacks(datasetUuid: string) {
217
217
  this.store.dispatch(MdViewActions.loadUserFeedbacks({ datasetUuid }))
218
218
  }
219
+
220
+ /**
221
+ * loadFeatureCatalog
222
+ */
223
+ loadFeatureCatalog(metadata: CatalogRecord) {
224
+ this.store.dispatch(MdViewActions.loadFeatureCatalog({ metadata }))
225
+ }
219
226
  }
@@ -57,6 +57,7 @@ export class ChartComponent implements OnChanges, AfterViewInit {
57
57
  this.dataRaw = value
58
58
  }
59
59
  @Input() labelProperty: string
60
+ @Input() prettyLabel: string
60
61
  @Input() valueProperty: string
61
62
  @Input() secondaryValueProperty: string
62
63
  @Input() type: InputChartType = 'bar'
@@ -95,7 +96,7 @@ export class ChartComponent implements OnChanges, AfterViewInit {
95
96
  labels: this.getDataProxy(this.labelProperty) as string[],
96
97
  datasets: [
97
98
  {
98
- label: this.valueProperty,
99
+ label: this.prettyLabel,
99
100
  data,
100
101
  },
101
102
  ],
@@ -8,21 +8,24 @@
8
8
  [matSortDisableClear]="true"
9
9
  *ngrxLet="properties$ as properties"
10
10
  >
11
- <ng-container *ngFor="let prop of properties" [matColumnDef]="prop">
11
+ <ng-container
12
+ *ngFor="let attr of _featureAttributes"
13
+ [matColumnDef]="attr.value"
14
+ >
12
15
  <th
13
16
  mat-header-cell
14
17
  *matHeaderCellDef
15
18
  mat-sort-header
16
19
  class="text-sm text-black bg-white"
17
20
  >
18
- {{ prop }}
21
+ {{ attr.label }}
19
22
  </th>
20
23
  <td
21
24
  mat-cell
22
25
  *matCellDef="let element"
23
26
  class="whitespace-nowrap pr-1 truncate"
24
27
  >
25
- {{ element[prop] }}
28
+ {{ element[attr.value] }}
26
29
  </td>
27
30
  </ng-container>
28
31
 
@@ -61,13 +61,14 @@ export interface TableItemModel {
61
61
  changeDetection: ChangeDetectionStrategy.OnPush,
62
62
  })
63
63
  export class DataTableComponent implements OnInit, AfterViewInit, OnChanges {
64
+ _featureAttributes = []
65
+ @Input() set featureAttributes(value: { value: string; label: string }[]) {
66
+ this._featureAttributes = value
67
+ this.properties$.next(value.map((attr) => attr.value))
68
+ }
64
69
  @Input() set dataset(value: BaseReader) {
65
- this.properties$.next(null)
66
70
  this.dataset_ = value
67
71
  this.dataset_.load()
68
- this.dataset_.properties.then((properties) =>
69
- this.properties$.next(properties.map((p) => p.name))
70
- )
71
72
  this.dataset_.info.then((info) => (this.count = info.itemsCount))
72
73
  }
73
74
  @Input() activeId: TableItemId
@@ -26,3 +26,4 @@ export * from './lib/user-preview/user-preview.component'
26
26
  export * from './lib/application-banner/application-banner.component'
27
27
  export * from './lib/internal-link-card/internal-link-card.component'
28
28
  export * from './lib/service-capabilities/service-capabilities.component'
29
+ export * from './lib/record-feature-catalog/feature-catalog-list/feature-catalog-list.component'
@@ -1,5 +1,8 @@
1
- <div class="py-5 px-5 rounded-lg bg-gray-100 text-black">
2
- <div class="grid grid-cols-1 gap-3 overflow-hidden">
1
+ <div
2
+ class="py-5 px-5 rounded bg-gray-100 text-black"
3
+ data-cy="metadata-organization"
4
+ >
5
+ <div class="grid gap-3 overflow-hidden">
3
6
  <div>
4
7
  <p class="text-base font-medium" translate>record.metadata.contact</p>
5
8
  </div>
@@ -18,7 +21,7 @@
18
21
  <div
19
22
  class="font-title text-21 mr-2 cursor-pointer hover:underline"
20
23
  (click)="onOrganizationClick()"
21
- data-cy="organization-name"
24
+ data-cy="organization-name-link"
22
25
  >
23
26
  {{ shownOrganization?.name }}
24
27
  </div>
@@ -46,6 +46,7 @@
46
46
  metadata.otherConstraints
47
47
  "
48
48
  [title]="'record.metadata.usage' | translate"
49
+ data-test="usage-panel"
49
50
  >
50
51
  <div class="flex flex-col gap-[10px] mr-4 py-[12px] rounded text-gray-900">
51
52
  <ng-container *ngFor="let license of licenses">
@@ -105,6 +106,7 @@
105
106
  (metadata.kind === 'dataset' && metadata.status)
106
107
  "
107
108
  [title]="'record.metadata.details' | translate"
109
+ data-test="details-panel"
108
110
  >
109
111
  <div *ngIf="metadata.lineage" class="text-gray-900 flex flex-col mt-4 gap-2">
110
112
  <p class="whitespace-pre-line break-words text-gray-900" gnUiLinkify>
@@ -231,6 +233,7 @@
231
233
  <gn-ui-expandable-panel
232
234
  *ngIf="metadata.kind !== 'dataset' && metadata.spatialExtents"
233
235
  [title]="'service.metadata.spatialExtent' | translate"
236
+ data-test="spatial-extent-panel"
234
237
  >
235
238
  <gn-ui-spatial-extent
236
239
  class="flex h-[271px] w-full rounded-lg border border-gray-100 mt-3 mb-6"
@@ -240,6 +243,7 @@
240
243
  <gn-ui-expandable-panel
241
244
  *ngIf="metadata.landingPage"
242
245
  [title]="'service.metadata.other' | translate"
246
+ data-test="other-panel"
243
247
  >
244
248
  <div class="flex flex-col gap-4 mr-4 py-5 rounded text-gray-700">
245
249
  <div *ngIf="metadata.recordUpdated">
@@ -0,0 +1,48 @@
1
+ <div class="flex flex-col gap-2 py-5 px-5 h-[562px] overflow-y-auto">
2
+ <div
3
+ *ngFor="let featureType of filteredFeatureCatalog?.featureTypes"
4
+ class="rounded shadow bg-white"
5
+ >
6
+ <gn-ui-expandable-panel
7
+ [collapsed]="filteredFeatureCatalog?.featureTypes?.length !== 1"
8
+ iconColor="black"
9
+ >
10
+ <ng-template #titleTemplate>
11
+ <div class="px-2">
12
+ <div class="text-lg font-bold">{{ featureType.name }}</div>
13
+ <div class="text-sm" *ngIf="featureType.definition">
14
+ {{ featureType.definition }}
15
+ </div>
16
+ </div>
17
+ </ng-template>
18
+ <div
19
+ class="grid gap-0"
20
+ [style.grid-template-columns]="gridTemplateColumns"
21
+ data-cy="feature-type-content"
22
+ >
23
+ <div
24
+ class="py-1 px-2 text-sm font-bold text-left border-t"
25
+ [class.border-l]="i > 0"
26
+ [class.border-gray-300]="i > 0"
27
+ *ngFor="let col of columns; let i = index"
28
+ data-test="column-label"
29
+ >
30
+ {{ col.label | translate }}
31
+ </div>
32
+ <ng-container *ngFor="let row of featureType.attributes">
33
+ <div
34
+ class="bg-white text-sm font-normal text-left border-t"
35
+ [class.border-l]="i > 0"
36
+ [class.border-gray-300]="i > 0"
37
+ *ngFor="let col of columns; let i = index"
38
+ >
39
+ <gn-ui-truncated-text
40
+ extraClass="py-3 px-2"
41
+ [text]="row[col.key]"
42
+ ></gn-ui-truncated-text>
43
+ </div>
44
+ </ng-container>
45
+ </div>
46
+ </gn-ui-expandable-panel>
47
+ </div>
48
+ </div>
@@ -0,0 +1,52 @@
1
+ import { Component, Input, OnInit } from '@angular/core'
2
+ import { CommonModule } from '@angular/common'
3
+ import { TranslateModule } from '@ngx-translate/core'
4
+ import { DatasetFeatureCatalog } from '../../../../../../../libs/common/domain/src/lib/model/record'
5
+ import {
6
+ ExpandablePanelComponent,
7
+ TruncatedTextComponent,
8
+ } from '../../../../../../../libs/ui/layout/src'
9
+
10
+ @Component({
11
+ selector: 'gn-ui-feature-catalog-list',
12
+ templateUrl: './feature-catalog-list.component.html',
13
+ standalone: true,
14
+ imports: [
15
+ CommonModule,
16
+ TranslateModule,
17
+ ExpandablePanelComponent,
18
+ TruncatedTextComponent,
19
+ ],
20
+ })
21
+ export class FeatureCatalogListComponent implements OnInit {
22
+ @Input() filteredFeatureCatalog: DatasetFeatureCatalog
23
+
24
+ columns = [
25
+ {
26
+ key: 'type',
27
+ label: 'feature.catalog.attribute.type',
28
+ width: '19%',
29
+ },
30
+ {
31
+ key: 'name',
32
+ label: 'feature.catalog.attribute.name',
33
+ width: '32%',
34
+ },
35
+ {
36
+ key: 'code',
37
+ label: 'feature.catalog.attribute.code',
38
+ width: '24%',
39
+ },
40
+ {
41
+ key: 'title',
42
+ label: 'feature.catalog.attribute.description',
43
+ width: '25%',
44
+ },
45
+ ]
46
+
47
+ gridTemplateColumns = ''
48
+
49
+ ngOnInit(): void {
50
+ this.gridTemplateColumns = this.columns.map((col) => col.width).join(' ')
51
+ }
52
+ }
@@ -23,7 +23,6 @@ import { UserPreviewComponent } from './user-preview/user-preview.component'
23
23
  import { ApplicationBannerComponent } from './application-banner/application-banner.component'
24
24
  import { InternalLinkCardComponent } from './internal-link-card/internal-link-card.component'
25
25
  import { ServiceCapabilitiesComponent } from './service-capabilities/service-capabilities.component'
26
-
27
26
  @NgModule({
28
27
  imports: [
29
28
  CommonModule,
@@ -24,3 +24,4 @@ export * from './lib/text-input/text-input.component'
24
24
  export * from './lib/ui-inputs.module'
25
25
  export * from './lib/url-input/url-input.component'
26
26
  export * from './lib/viewport-intersector/viewport-intersector.component'
27
+ export * from './lib/search-feature-catalog/search-feature-catalog.component'
@@ -0,0 +1,43 @@
1
+ <div
2
+ class="flex items-center justify-between p-3 mt-8 bg-white rounded-lg border-b solid border-gray-300"
3
+ >
4
+ <div
5
+ class="relative shrink-0"
6
+ *ngIf="featureCatalog?.featureTypes?.length > 1"
7
+ >
8
+ <input
9
+ type="text"
10
+ [placeholder]="'search.filter.into.feature.catalog' | translate"
11
+ class="w-[220px] h-8 pl-3 pr-10 py-2 border rounded-md"
12
+ [(ngModel)]="searchTerm"
13
+ (ngModelChange)="filterAction(searchTerm)"
14
+ />
15
+ <ng-icon
16
+ class="absolute right-2.5 top-1 w-6 h-6 text-black"
17
+ name="iconoirSearch"
18
+ ></ng-icon>
19
+ </div>
20
+ <div class="text-sm px-1 ml-auto hidden sm:inline">
21
+ <ng-container *ngIf="featureCatalog?.featureTypes?.length > 1">
22
+ <span
23
+ class="text-sm font-medium text-gray-900"
24
+ data-cy="total-objects-label"
25
+ translate
26
+ >record.feature.catalog.number.total.object</span
27
+ ><span
28
+ class="text-sm font-bold px-1 text-gray-900"
29
+ data-cy="total-objects"
30
+ >{{ totalObjects }}</span
31
+ >
32
+ <span class="px-1.5">|</span>
33
+ </ng-container>
34
+ <span
35
+ class="text-sm font-medium px-1 text-gray-900"
36
+ data-cy="total-attributes-label"
37
+ translate
38
+ >record.feature.catalog.number.total.attribute</span
39
+ ><span class="text-sm font-bold text-gray-900" data-cy="total-attributes">{{
40
+ totalAttributes
41
+ }}</span>
42
+ </div>
43
+ </div>
@@ -0,0 +1,77 @@
1
+ import { Component, Input, Output, EventEmitter } from '@angular/core'
2
+ import { CommonModule } from '@angular/common'
3
+ import { TranslateModule } from '@ngx-translate/core'
4
+ import { NgIcon, provideIcons, provideNgIconsConfig } from '@ng-icons/core'
5
+ import { DatasetFeatureCatalog } from '../../../../../../libs/common/domain/src/lib/model/record'
6
+ import { FormsModule } from '@angular/forms'
7
+ import { of } from 'rxjs'
8
+ import { debounceTime, distinctUntilChanged, tap } from 'rxjs/operators'
9
+ import { createFuzzyFilter } from '../../../../../../libs/util/shared/src'
10
+ import { iconoirSearch } from '@ng-icons/iconoir'
11
+
12
+ @Component({
13
+ selector: 'gn-ui-search-feature-catalog',
14
+ templateUrl: './search-feature-catalog.component.html',
15
+ styleUrls: ['./search-feature-catalog.component.css'],
16
+ standalone: true,
17
+ imports: [CommonModule, NgIcon, TranslateModule, FormsModule],
18
+ viewProviders: [
19
+ provideIcons({ iconoirSearch }),
20
+ provideNgIconsConfig({
21
+ size: '1.5rem',
22
+ }),
23
+ ],
24
+ })
25
+ export class SearchFeatureCatalogComponent {
26
+ private _featureCatalog: DatasetFeatureCatalog
27
+ searchTerm: any
28
+ @Input() set featureCatalog(value: DatasetFeatureCatalog) {
29
+ this._featureCatalog = value
30
+ this.filteredFeatureCatalog = value
31
+ }
32
+ get featureCatalog(): DatasetFeatureCatalog {
33
+ return this._featureCatalog
34
+ }
35
+
36
+ @Output() filteredFeatureCatalogChange =
37
+ new EventEmitter<DatasetFeatureCatalog>()
38
+ filteredFeatureCatalog: DatasetFeatureCatalog
39
+
40
+ filterAction = (searchTerm: string): void => {
41
+ of(searchTerm)
42
+ .pipe(
43
+ debounceTime(300),
44
+ distinctUntilChanged(),
45
+ tap(() => {
46
+ const filtered = (this.featureCatalog?.featureTypes || []).filter(
47
+ (featureType) => {
48
+ const fuzzyFilter = createFuzzyFilter(searchTerm)
49
+ return (
50
+ fuzzyFilter(featureType.name) ||
51
+ (featureType.definition && fuzzyFilter(featureType.definition))
52
+ )
53
+ }
54
+ )
55
+ this.filteredFeatureCatalog = {
56
+ ...this.featureCatalog,
57
+ featureTypes: filtered,
58
+ }
59
+ this.filteredFeatureCatalogChange.emit(this.filteredFeatureCatalog)
60
+ })
61
+ )
62
+ .subscribe()
63
+ }
64
+
65
+ get totalObjects(): number {
66
+ return this.filteredFeatureCatalog?.featureTypes?.length || 0
67
+ }
68
+
69
+ get totalAttributes(): number {
70
+ return (
71
+ this.filteredFeatureCatalog?.featureTypes?.reduce(
72
+ (total, featureType) => total + (featureType.attributes?.length || 0),
73
+ 0
74
+ ) || 0
75
+ )
76
+ }
77
+ }
@@ -1,22 +1,38 @@
1
1
  <div
2
- class="group flex align-middle title border-b border-gray-100 cursor-pointer pt-2.5"
2
+ class="group flex items-center justify-between title border-b border-gray-100 cursor-pointer"
3
3
  (click)="toggle()"
4
+ data-cy="expandable-panel-header"
4
5
  >
5
- <div class="grow font-medium text-black text-sm">
6
- {{ title }}
6
+ <div class="grow py-2.5">
7
+ <ng-container *ngIf="titleTemplate; else simpleTitle">
8
+ <div data-cy="expandable-panel-custom-title">
9
+ <ng-container *ngTemplateOutlet="titleTemplate"></ng-container>
10
+ </div>
11
+ </ng-container>
12
+ <ng-template #simpleTitle>
13
+ <div
14
+ class="font-medium text-black text-sm"
15
+ data-cy="expandable-panel-title"
16
+ >
17
+ {{ title }}
18
+ </div>
19
+ </ng-template>
7
20
  </div>
8
21
  <div
9
- class="w-8 opacity-25 text-primary group-hover:opacity-100 transition-opacity"
22
+ class="w-8 opacity-25 text-primary group-hover:opacity-100 transition-opacity flex items-center shrink-0"
10
23
  >
11
- <ng-icon name="matAdd" *ngIf="collapsed"></ng-icon>
12
- <ng-icon name="matRemove" *ngIf="!collapsed"></ng-icon>
24
+ <ng-icon
25
+ [name]="collapsed ? 'matAdd' : 'matRemove'"
26
+ [style.color]="iconColor"
27
+ ></ng-icon>
13
28
  </div>
14
29
  </div>
15
30
  <div
16
- class="content overflow-hidden transition-[max-height] duration-300"
31
+ class="content overflow-hidden transition-all duration-300"
17
32
  [ngClass]="collapsed ? 'ease-out' : 'ease-in'"
18
- [style.maxHeight]="maxHeight"
19
33
  #contentDiv
34
+ *ngIf="!collapsed"
35
+ data-cy="expandable-panel-content"
20
36
  >
21
37
  <ng-content></ng-content>
22
38
  </div>