geonetwork-ui 2.11.0-dev.96d0c6ce2 → 2.11.0-dev.a5c259d9e
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.
- package/fesm2022/geonetwork-ui.mjs +971 -585
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/index.d.ts +165 -59
- package/index.d.ts.map +1 -1
- package/package.json +11 -11
- package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +43 -13
- package/src/libs/api/metadata-converter/src/lib/gn4/types/elasticsearch.model.ts +13 -1
- package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +23 -3
- package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +40 -16
- package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +12 -0
- package/src/libs/common/domain/src/lib/model/search/sort-by.model.ts +1 -5
- package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +2 -2
- package/src/libs/feature/dataviz/src/lib/chart-view/chart-view.component.ts +3 -2
- package/src/libs/feature/dataviz/src/lib/geo-table-view/geo-table-view.component.ts +2 -1
- package/src/libs/feature/dataviz/src/lib/service/data.service.ts +22 -22
- package/src/libs/feature/map/src/lib/add-layer-from-file/add-layer-from-file.component.html +1 -0
- package/src/libs/feature/notifications/src/lib/notifications.service.ts +6 -4
- package/src/libs/feature/record/src/lib/state/mdview.actions.ts +4 -8
- package/src/libs/feature/record/src/lib/state/mdview.effects.ts +7 -16
- package/src/libs/feature/record/src/lib/state/mdview.facade.ts +1 -3
- package/src/libs/feature/record/src/lib/state/mdview.reducer.ts +4 -9
- package/src/libs/feature/record/src/lib/state/mdview.selectors.ts +2 -7
- package/src/libs/feature/router/src/lib/default/router.service.ts +1 -1
- package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.html +7 -0
- package/src/libs/feature/search/src/lib/filter-dropdown/filter-dropdown.component.ts +39 -0
- package/src/libs/feature/search/src/lib/sort-by/sort-by.component.ts +1 -1
- package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +3 -1
- package/src/libs/feature/search/src/lib/utils/service/fields.ts +16 -1
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.component.html +6 -7
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.component.ts +31 -19
- package/src/libs/ui/dataviz/src/lib/data-table/data-table.fixtures.ts +2 -2
- package/src/libs/ui/inputs/src/index.ts +1 -0
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.css +1 -0
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.html +20 -7
- package/src/libs/ui/inputs/src/lib/drag-and-drop-file-input/drag-and-drop-file-input.component.ts +46 -5
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.css +0 -0
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.html +117 -0
- package/src/libs/ui/inputs/src/lib/spatial-extent-dropdown/spatial-extent-dropdown.component.ts +191 -0
- package/src/libs/ui/map/src/lib/components/map-container/map-container.component.ts +4 -5
- package/src/libs/util/app-config/src/lib/app-config.ts +3 -0
- package/src/libs/util/app-config/src/lib/model.ts +1 -0
- package/src/libs/util/data-fetcher/src/lib/data-fetcher.ts +19 -22
- package/src/libs/util/data-fetcher/src/lib/engine/duckdb.ts +185 -0
- package/src/libs/util/data-fetcher/src/lib/engine/results.ts +63 -0
- package/src/libs/util/data-fetcher/src/lib/{sql-utils.ts → engine/sql-utils.ts} +28 -13
- package/src/libs/util/data-fetcher/src/lib/model.ts +2 -1
- package/src/libs/util/data-fetcher/src/lib/readers/base-file.ts +53 -38
- package/src/libs/util/data-fetcher/src/lib/readers/base.ts +11 -0
- package/src/libs/util/data-fetcher/src/lib/readers/csv.ts +9 -47
- package/src/libs/util/data-fetcher/src/lib/readers/excel.ts +27 -27
- package/src/libs/util/data-fetcher/src/lib/readers/geojson.ts +5 -24
- package/src/libs/util/data-fetcher/src/lib/readers/gml.ts +5 -49
- package/src/libs/util/data-fetcher/src/lib/readers/json.ts +5 -23
- package/src/libs/util/data-fetcher/src/lib/readers/wfs.ts +184 -128
- package/src/libs/util/data-fetcher/src/lib/utils.ts +0 -143
- package/src/libs/util/shared/src/lib/utils/file.ts +15 -0
- package/src/libs/util/shared/src/lib/utils/index.ts +1 -0
- package/tailwind.base.css +5 -0
- package/translations/de.json +11 -1
- package/translations/en.json +11 -1
- package/translations/es.json +10 -0
- package/translations/fr.json +11 -1
- package/translations/it.json +11 -1
- package/translations/nl.json +10 -0
- package/translations/pt.json +10 -0
- package/translations/sk.json +11 -1
- package/src/libs/util/data-fetcher/src/lib/readers/base-cache.ts +0 -14
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
AggregationsParams,
|
|
12
12
|
FieldFilter,
|
|
13
13
|
FieldFilters,
|
|
14
|
+
FieldSort,
|
|
14
15
|
FilterQuery,
|
|
15
16
|
FiltersAggregationParams,
|
|
16
17
|
SortByField,
|
|
@@ -141,6 +142,7 @@ export class ElasticsearchService {
|
|
|
141
142
|
values: uuids,
|
|
142
143
|
},
|
|
143
144
|
},
|
|
145
|
+
size: uuids.length,
|
|
144
146
|
}
|
|
145
147
|
}
|
|
146
148
|
|
|
@@ -199,9 +201,27 @@ export class ElasticsearchService {
|
|
|
199
201
|
}
|
|
200
202
|
|
|
201
203
|
private buildPayloadSort(sortBy: SortByField): SortParams {
|
|
202
|
-
if (sortBy ===
|
|
203
|
-
const fields = Array.isArray(sortBy[0])
|
|
204
|
-
|
|
204
|
+
if (!sortBy || sortBy.length === 0) return undefined
|
|
205
|
+
const fields = Array.isArray(sortBy[0])
|
|
206
|
+
? (sortBy as FieldSort[])
|
|
207
|
+
: [sortBy as FieldSort]
|
|
208
|
+
return fields.map((field) => {
|
|
209
|
+
// Sort by nested array of dates only works with the explicit syntax
|
|
210
|
+
if (field[1].endsWith('.date')) {
|
|
211
|
+
const nestedPath = field[1].slice(0, field[1].lastIndexOf('.date'))
|
|
212
|
+
return {
|
|
213
|
+
[field[1]]: {
|
|
214
|
+
order: field[0] as 'desc' | 'asc',
|
|
215
|
+
mode: field[0] === 'desc' ? 'max' : 'min',
|
|
216
|
+
missing: '_last',
|
|
217
|
+
nested: {
|
|
218
|
+
path: nestedPath,
|
|
219
|
+
},
|
|
220
|
+
},
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return { [field[1]]: field[0] }
|
|
224
|
+
})
|
|
205
225
|
}
|
|
206
226
|
|
|
207
227
|
private injectLangInQueryStringFields(
|
|
@@ -14,10 +14,13 @@ import {
|
|
|
14
14
|
} from '../../../../../../libs/api/metadata-converter/src'
|
|
15
15
|
import { PublicationVersionError } from '../../../../../../libs/common/domain/src/lib/model/error'
|
|
16
16
|
import {
|
|
17
|
+
AssociatedRecord,
|
|
17
18
|
CatalogRecord,
|
|
18
19
|
DatasetFeatureCatalog,
|
|
19
20
|
DatasetFeatureType,
|
|
20
21
|
LanguageCode,
|
|
22
|
+
LinkedRecord,
|
|
23
|
+
RecordRelation,
|
|
21
24
|
} from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
22
25
|
import {
|
|
23
26
|
Aggregations,
|
|
@@ -145,7 +148,7 @@ export class Gn4Repository implements RecordsRepositoryInterface {
|
|
|
145
148
|
return this.gn4SearchApi
|
|
146
149
|
.search(
|
|
147
150
|
'bucket',
|
|
148
|
-
['fcats', 'hassources'],
|
|
151
|
+
['fcats', 'hassources', 'siblings', 'associated'],
|
|
149
152
|
JSON.stringify(
|
|
150
153
|
this.gn4SearchHelper.getMetadataByIdsPayload([uniqueIdentifier])
|
|
151
154
|
)
|
|
@@ -249,22 +252,43 @@ export class Gn4Repository implements RecordsRepositoryInterface {
|
|
|
249
252
|
)
|
|
250
253
|
}
|
|
251
254
|
|
|
252
|
-
|
|
253
|
-
const
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
255
|
+
getLinkedRecords(record: CatalogRecord): Observable<LinkedRecord[]> {
|
|
256
|
+
const siblings = (record.extras?.['siblings'] ?? []) as AssociatedRecord[]
|
|
257
|
+
const relations: Array<[RecordRelation, string[]]> = [
|
|
258
|
+
['source', (record.extras?.['sourcesIdentifiers'] ?? []) as string[]],
|
|
259
|
+
['sourceOf', (record.extras?.['sourceOfIdentifiers'] ?? []) as string[]],
|
|
260
|
+
['sibling', siblings.map(({ uniqueIdentifier }) => uniqueIdentifier)],
|
|
261
|
+
[
|
|
262
|
+
'associated',
|
|
263
|
+
(record.extras?.['associatedIdentifiers'] ?? []) as string[],
|
|
264
|
+
],
|
|
265
|
+
]
|
|
266
|
+
const requested = relations.filter(
|
|
267
|
+
([, identifiers]) => identifiers.length > 0
|
|
268
|
+
)
|
|
269
|
+
if (requested.length === 0) {
|
|
270
|
+
return of([])
|
|
266
271
|
}
|
|
267
|
-
return
|
|
272
|
+
return forkJoin(
|
|
273
|
+
requested.map(([relation, identifiers]) =>
|
|
274
|
+
this.getMultipleRecords(identifiers).pipe(
|
|
275
|
+
map((records) =>
|
|
276
|
+
(records ?? []).map((record) => ({
|
|
277
|
+
record,
|
|
278
|
+
relation,
|
|
279
|
+
associationType:
|
|
280
|
+
relation === 'sibling'
|
|
281
|
+
? siblings.find(
|
|
282
|
+
({ uniqueIdentifier }) =>
|
|
283
|
+
uniqueIdentifier === record.uniqueIdentifier
|
|
284
|
+
)?.associationType
|
|
285
|
+
: undefined,
|
|
286
|
+
}))
|
|
287
|
+
),
|
|
288
|
+
catchError(() => of([]))
|
|
289
|
+
)
|
|
290
|
+
)
|
|
291
|
+
).pipe(map((groups) => groups.flat()))
|
|
268
292
|
}
|
|
269
293
|
|
|
270
294
|
aggregate(params: AggregationsParams): Observable<Aggregations> {
|
|
@@ -294,6 +294,18 @@ export interface AssociatedRecord {
|
|
|
294
294
|
associationType: AssociationType
|
|
295
295
|
}
|
|
296
296
|
|
|
297
|
+
/**
|
|
298
|
+
* Nature of the link between a record and another one; the association type is
|
|
299
|
+
* only known for 'sibling' relations, as it is held by the declaring record.
|
|
300
|
+
*/
|
|
301
|
+
export type RecordRelation = 'source' | 'sourceOf' | 'sibling' | 'associated'
|
|
302
|
+
|
|
303
|
+
export interface LinkedRecord {
|
|
304
|
+
record: CatalogRecord
|
|
305
|
+
relation: RecordRelation
|
|
306
|
+
associationType?: AssociationType
|
|
307
|
+
}
|
|
308
|
+
|
|
297
309
|
export interface DatasetRecord extends BaseRecord {
|
|
298
310
|
kind: 'dataset'
|
|
299
311
|
status: RecordStatus
|
|
@@ -6,9 +6,5 @@ export const SortByEnum: Record<string, SortByField> = {
|
|
|
6
6
|
RELEVANCY: ['desc', '_score'],
|
|
7
7
|
QUALITY_SCORE: ['desc', 'qualityScore'],
|
|
8
8
|
CHANGE_DATE: ['desc', 'changeDate'],
|
|
9
|
-
|
|
10
|
-
['desc', 'revisionDateForResource'],
|
|
11
|
-
['desc', 'publicationDateForResource'],
|
|
12
|
-
['desc', 'creationDateForResource'],
|
|
13
|
-
],
|
|
9
|
+
RESOURCE_DATE: ['desc', 'resourceDate.date'],
|
|
14
10
|
}
|
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
CatalogRecord,
|
|
11
11
|
DatasetFeatureCatalog,
|
|
12
12
|
LanguageCode,
|
|
13
|
+
LinkedRecord,
|
|
13
14
|
} from '../model/record'
|
|
14
15
|
|
|
15
16
|
export abstract class RecordsRepositoryInterface {
|
|
@@ -23,8 +24,7 @@ export abstract class RecordsRepositoryInterface {
|
|
|
23
24
|
abstract getSimilarRecords(
|
|
24
25
|
similarTo: CatalogRecord
|
|
25
26
|
): Observable<CatalogRecord[]>
|
|
26
|
-
abstract
|
|
27
|
-
abstract getSourceOf(record: CatalogRecord): Observable<CatalogRecord[]>
|
|
27
|
+
abstract getLinkedRecords(record: CatalogRecord): Observable<LinkedRecord[]>
|
|
28
28
|
abstract fuzzySearch(query: string): Observable<SearchResults>
|
|
29
29
|
abstract canDuplicate(record: CatalogRecord): boolean
|
|
30
30
|
abstract canDelete(record: CatalogRecord): Observable<boolean>
|
|
@@ -2,9 +2,9 @@ import {
|
|
|
2
2
|
ChangeDetectionStrategy,
|
|
3
3
|
ChangeDetectorRef,
|
|
4
4
|
Component,
|
|
5
|
+
inject,
|
|
5
6
|
Input,
|
|
6
7
|
Output,
|
|
7
|
-
inject,
|
|
8
8
|
} from '@angular/core'
|
|
9
9
|
import { marker } from '@biesbjerg/ngx-translate-extract-marker'
|
|
10
10
|
import {
|
|
@@ -183,7 +183,7 @@ export class ChartViewComponent {
|
|
|
183
183
|
yChoices$ = this.properties$.pipe(
|
|
184
184
|
map((properties) =>
|
|
185
185
|
properties
|
|
186
|
-
.filter((prop) => prop.type === 'number'
|
|
186
|
+
.filter((prop) => prop.type === 'number')
|
|
187
187
|
.map((prop) => ({ value: prop.name, label: prop.label || prop.name }))
|
|
188
188
|
),
|
|
189
189
|
tap((choices) => {
|
|
@@ -222,6 +222,7 @@ export class ChartViewComponent {
|
|
|
222
222
|
switchMap(([dataset, xProp, yProp, aggregation]) => {
|
|
223
223
|
const fieldAgg: FieldAggregation =
|
|
224
224
|
aggregation === 'count' ? ['count'] : [aggregation, yProp]
|
|
225
|
+
this.loading = true
|
|
225
226
|
return dataset
|
|
226
227
|
.groupBy(['distinct', xProp])
|
|
227
228
|
.aggregate(fieldAgg)
|
|
@@ -2,11 +2,11 @@ import {
|
|
|
2
2
|
ChangeDetectionStrategy,
|
|
3
3
|
ChangeDetectorRef,
|
|
4
4
|
Component,
|
|
5
|
+
inject,
|
|
5
6
|
Input,
|
|
6
7
|
OnDestroy,
|
|
7
8
|
OnInit,
|
|
8
9
|
ViewChild,
|
|
9
|
-
inject,
|
|
10
10
|
} from '@angular/core'
|
|
11
11
|
import {
|
|
12
12
|
DataTableComponent,
|
|
@@ -75,6 +75,7 @@ export class GeoTableViewComponent implements OnInit, OnDestroy {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
private async initMapContext(): Promise<MapContext> {
|
|
78
|
+
this.dataset.load()
|
|
78
79
|
this.dataset.selectAll()
|
|
79
80
|
return {
|
|
80
81
|
layers: [
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { inject, Injectable } from '@angular/core'
|
|
2
2
|
import { marker } from '@biesbjerg/ngx-translate-extract-marker'
|
|
3
3
|
import {
|
|
4
|
+
GetCollectionItemsOptions,
|
|
4
5
|
OgcApiCollectionInfo,
|
|
5
6
|
OgcApiEndpoint,
|
|
6
7
|
OgcApiRecord,
|
|
7
|
-
WfsEndpoint,
|
|
8
|
-
WfsVersion,
|
|
9
|
-
TmsEndpoint,
|
|
10
8
|
StacEndpoint,
|
|
11
|
-
GetCollectionItemsOptions,
|
|
12
9
|
StacItemsDocument,
|
|
10
|
+
TmsEndpoint,
|
|
11
|
+
WfsEndpoint,
|
|
12
|
+
WfsVersion,
|
|
13
13
|
} from '@camptocamp/ogc-client'
|
|
14
14
|
import {
|
|
15
15
|
BaseReader,
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
} from '../../../../../../libs/util/shared/src'
|
|
27
27
|
import type { FeatureCollection } from 'geojson'
|
|
28
28
|
import { from, Observable, throwError } from 'rxjs'
|
|
29
|
-
import { catchError, map, switchMap
|
|
29
|
+
import { catchError, map, switchMap } from 'rxjs/operators'
|
|
30
30
|
import {
|
|
31
31
|
DatasetOnlineResource,
|
|
32
32
|
DatasetServiceDistribution,
|
|
@@ -347,14 +347,11 @@ export class DataService {
|
|
|
347
347
|
if (link.type === 'service' && link.accessServiceProtocol === 'wfs') {
|
|
348
348
|
const wfsUrlEndpoint = this.proxy.getProxiedUrl(link.url.toString())
|
|
349
349
|
return from(
|
|
350
|
-
openDataset(
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
},
|
|
356
|
-
cacheActive
|
|
357
|
-
)
|
|
350
|
+
openDataset(wfsUrlEndpoint, {
|
|
351
|
+
typeHint: 'wfs',
|
|
352
|
+
wfsFeatureType: link.name,
|
|
353
|
+
enableCache: cacheActive,
|
|
354
|
+
})
|
|
358
355
|
)
|
|
359
356
|
} else if (link.type === 'download') {
|
|
360
357
|
const linkProxifiedUrl = this.proxy.getProxiedUrl(link.url.toString())
|
|
@@ -364,7 +361,10 @@ export class DataService {
|
|
|
364
361
|
? (format as SupportedType)
|
|
365
362
|
: undefined
|
|
366
363
|
return from(
|
|
367
|
-
openDataset(linkProxifiedUrl,
|
|
364
|
+
openDataset(linkProxifiedUrl, {
|
|
365
|
+
typeHint: supportedType,
|
|
366
|
+
enableCache: cacheActive,
|
|
367
|
+
})
|
|
368
368
|
).pipe()
|
|
369
369
|
} else if (
|
|
370
370
|
link.type === 'service' &&
|
|
@@ -374,7 +374,9 @@ export class DataService {
|
|
|
374
374
|
link.url.toString(),
|
|
375
375
|
'geojson'
|
|
376
376
|
)
|
|
377
|
-
return from(
|
|
377
|
+
return from(
|
|
378
|
+
openDataset(url, { typeHint: 'geojson', enableCache: cacheActive })
|
|
379
|
+
).pipe()
|
|
378
380
|
} else if (
|
|
379
381
|
link.type === 'service' &&
|
|
380
382
|
link.accessServiceProtocol === 'ogcFeatures'
|
|
@@ -395,12 +397,10 @@ export class DataService {
|
|
|
395
397
|
}
|
|
396
398
|
const urlWithoutLimit = new URL(geojsonUrl)
|
|
397
399
|
urlWithoutLimit.searchParams.delete('limit')
|
|
398
|
-
return openDataset(
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
cacheActive
|
|
403
|
-
)
|
|
400
|
+
return openDataset(urlWithoutLimit.toString(), {
|
|
401
|
+
typeHint: 'geojson',
|
|
402
|
+
enableCache: cacheActive,
|
|
403
|
+
})
|
|
404
404
|
})
|
|
405
405
|
)
|
|
406
406
|
}
|
|
@@ -18,10 +18,12 @@ export class NotificationsService {
|
|
|
18
18
|
error && console.error(error)
|
|
19
19
|
const id = Math.floor(Math.random() * 1000000)
|
|
20
20
|
this.notifications$.next([...this.notifications$.value, { ...content, id }])
|
|
21
|
-
if (typeof timeoutMs
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
21
|
+
if (typeof timeoutMs !== 'undefined') {
|
|
22
|
+
setTimeout(() => {
|
|
23
|
+
this.removeNotificationById(id)
|
|
24
|
+
}, timeoutMs)
|
|
25
|
+
}
|
|
26
|
+
return id
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
removeNotificationById(id: number) {
|
|
@@ -3,6 +3,7 @@ import { createAction, props } from '@ngrx/store'
|
|
|
3
3
|
import {
|
|
4
4
|
CatalogRecord,
|
|
5
5
|
DatasetFeatureCatalog,
|
|
6
|
+
LinkedRecord,
|
|
6
7
|
UserFeedback,
|
|
7
8
|
} from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
8
9
|
|
|
@@ -54,14 +55,9 @@ export const setRelated = createAction(
|
|
|
54
55
|
props<{ related: CatalogRecord[] }>()
|
|
55
56
|
)
|
|
56
57
|
|
|
57
|
-
export const
|
|
58
|
-
'[Metadata view] Set
|
|
59
|
-
props<{
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
export const setSourceOf = createAction(
|
|
63
|
-
'[Metadata view] Set has sources',
|
|
64
|
-
props<{ sourceOf: CatalogRecord[] }>()
|
|
58
|
+
export const setLinkedRecords = createAction(
|
|
59
|
+
'[Metadata view] Set associated records',
|
|
60
|
+
props<{ linkedRecords: LinkedRecord[] }>()
|
|
65
61
|
)
|
|
66
62
|
|
|
67
63
|
/*
|
|
@@ -70,25 +70,16 @@ export class MdViewEffects {
|
|
|
70
70
|
)
|
|
71
71
|
)
|
|
72
72
|
|
|
73
|
-
|
|
73
|
+
loadLinkedRecords$ = createEffect(() =>
|
|
74
74
|
this.actions$.pipe(
|
|
75
75
|
ofType(MdViewActions.loadFullMetadataSuccess),
|
|
76
|
-
switchMap(({ full }) => this.recordsRepository.
|
|
77
|
-
map((
|
|
78
|
-
return MdViewActions.
|
|
76
|
+
switchMap(({ full }) => this.recordsRepository.getLinkedRecords(full)),
|
|
77
|
+
map((linkedRecords) => {
|
|
78
|
+
return MdViewActions.setLinkedRecords({ linkedRecords })
|
|
79
79
|
}),
|
|
80
|
-
catchError(() =>
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
loadSourceOf$ = createEffect(() =>
|
|
85
|
-
this.actions$.pipe(
|
|
86
|
-
ofType(MdViewActions.loadFullMetadataSuccess),
|
|
87
|
-
switchMap(({ full }) => this.recordsRepository.getSourceOf(full)),
|
|
88
|
-
map((sourceOf) => {
|
|
89
|
-
return MdViewActions.setSourceOf({ sourceOf })
|
|
90
|
-
}),
|
|
91
|
-
catchError(() => of(MdViewActions.setSourceOf({ sourceOf: null })))
|
|
80
|
+
catchError(() =>
|
|
81
|
+
of(MdViewActions.setLinkedRecords({ linkedRecords: null }))
|
|
82
|
+
)
|
|
92
83
|
)
|
|
93
84
|
)
|
|
94
85
|
|
|
@@ -74,9 +74,7 @@ export class MdViewFacade {
|
|
|
74
74
|
|
|
75
75
|
related$ = this.store.pipe(select(MdViewSelectors.getRelated))
|
|
76
76
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
sourceOf$ = this.store.pipe(select(MdViewSelectors.getSourceOf))
|
|
77
|
+
linkedRecords$ = this.store.pipe(select(MdViewSelectors.getLinkedRecords))
|
|
80
78
|
|
|
81
79
|
chartConfig$ = this.store.pipe(select(MdViewSelectors.getChartConfig))
|
|
82
80
|
|
|
@@ -4,6 +4,7 @@ import { DatavizChartConfigModel } from '../../../../../../libs/common/domain/sr
|
|
|
4
4
|
import {
|
|
5
5
|
CatalogRecord,
|
|
6
6
|
DatasetFeatureCatalog,
|
|
7
|
+
LinkedRecord,
|
|
7
8
|
UserFeedback,
|
|
8
9
|
} from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
9
10
|
|
|
@@ -14,8 +15,7 @@ export interface MetadataViewState {
|
|
|
14
15
|
error: { notFound?: boolean; otherError?: string } | null
|
|
15
16
|
metadata?: Partial<CatalogRecord>
|
|
16
17
|
related?: CatalogRecord[]
|
|
17
|
-
|
|
18
|
-
sourceOf?: CatalogRecord[]
|
|
18
|
+
linkedRecords?: LinkedRecord[]
|
|
19
19
|
userFeedbacks?: UserFeedback[]
|
|
20
20
|
allUserFeedbacksLoading: boolean
|
|
21
21
|
addUserFeedbackLoading: boolean
|
|
@@ -77,14 +77,9 @@ const metadataViewReducer = createReducer(
|
|
|
77
77
|
related,
|
|
78
78
|
})),
|
|
79
79
|
|
|
80
|
-
on(MetadataViewActions.
|
|
80
|
+
on(MetadataViewActions.setLinkedRecords, (state, { linkedRecords }) => ({
|
|
81
81
|
...state,
|
|
82
|
-
|
|
83
|
-
})),
|
|
84
|
-
|
|
85
|
-
on(MetadataViewActions.setSourceOf, (state, { sourceOf }) => ({
|
|
86
|
-
...state,
|
|
87
|
-
sourceOf,
|
|
82
|
+
linkedRecords,
|
|
88
83
|
})),
|
|
89
84
|
|
|
90
85
|
/*
|
|
@@ -41,14 +41,9 @@ export const getRelated = createSelector(
|
|
|
41
41
|
(state: MetadataViewState) => state.related
|
|
42
42
|
)
|
|
43
43
|
|
|
44
|
-
export const
|
|
44
|
+
export const getLinkedRecords = createSelector(
|
|
45
45
|
getMdViewState,
|
|
46
|
-
(state: MetadataViewState) => state.
|
|
47
|
-
)
|
|
48
|
-
|
|
49
|
-
export const getSourceOf = createSelector(
|
|
50
|
-
getMdViewState,
|
|
51
|
-
(state: MetadataViewState) => state.sourceOf
|
|
46
|
+
(state: MetadataViewState) => state.linkedRecords
|
|
52
47
|
)
|
|
53
48
|
/*
|
|
54
49
|
Metadata selectors
|
|
@@ -6,6 +6,13 @@
|
|
|
6
6
|
(startDateChange)="onStartDateChange($event)"
|
|
7
7
|
(endDateChange)="onEndDateChange($event)"
|
|
8
8
|
></gn-ui-date-range-dropdown>
|
|
9
|
+
} @else if (fieldType === 'spatialExtent') {
|
|
10
|
+
<gn-ui-spatial-extent-dropdown
|
|
11
|
+
[title]="title"
|
|
12
|
+
[maxFileSizeMb]="spatialExtentMaxFileSize"
|
|
13
|
+
(bboxChange)="onBboxChange($event)"
|
|
14
|
+
(errorChange)="onSpatialExtentError($event)"
|
|
15
|
+
></gn-ui-spatial-extent-dropdown>
|
|
9
16
|
} @else {
|
|
10
17
|
<gn-ui-dropdown-multiselect
|
|
11
18
|
class="w-full"
|
|
@@ -9,6 +9,8 @@ import {
|
|
|
9
9
|
Choice,
|
|
10
10
|
DateRangeDropdownComponent,
|
|
11
11
|
DropdownMultiselectComponent,
|
|
12
|
+
SpatialExtentDropdownComponent,
|
|
13
|
+
SpatialExtentDropdownError,
|
|
12
14
|
} from '../../../../../../libs/ui/inputs/src'
|
|
13
15
|
import { Observable, of, switchMap } from 'rxjs'
|
|
14
16
|
import { catchError, filter, map, startWith } from 'rxjs/operators'
|
|
@@ -22,6 +24,10 @@ import {
|
|
|
22
24
|
} from '../utils/service/fields'
|
|
23
25
|
import { DateRange } from '../../../../../../libs/api/repository/src'
|
|
24
26
|
import { CommonModule } from '@angular/common'
|
|
27
|
+
import { BoundingBox } from '../../../../../../libs/util/shared/src'
|
|
28
|
+
import { NotificationsService } from '../../../../../../libs/feature/notifications/src'
|
|
29
|
+
import { TranslateService } from '@ngx-translate/core'
|
|
30
|
+
import { getOptionalSearchConfig } from '../../../../../../libs/util/app-config/src'
|
|
25
31
|
|
|
26
32
|
@Component({
|
|
27
33
|
selector: 'gn-ui-filter-dropdown',
|
|
@@ -33,16 +39,22 @@ import { CommonModule } from '@angular/common'
|
|
|
33
39
|
CommonModule,
|
|
34
40
|
DateRangeDropdownComponent,
|
|
35
41
|
DropdownMultiselectComponent,
|
|
42
|
+
SpatialExtentDropdownComponent,
|
|
36
43
|
],
|
|
37
44
|
})
|
|
38
45
|
export class FilterDropdownComponent implements OnInit {
|
|
39
46
|
private searchFacade = inject(SearchFacade)
|
|
40
47
|
private searchService = inject(SearchService)
|
|
41
48
|
private fieldsService = inject(FieldsService)
|
|
49
|
+
private notificationsService = inject(NotificationsService)
|
|
50
|
+
private translateService = inject(TranslateService)
|
|
42
51
|
|
|
43
52
|
@Input() fieldName: string
|
|
44
53
|
@Input() title: string
|
|
45
54
|
|
|
55
|
+
spatialExtentMaxFileSize =
|
|
56
|
+
getOptionalSearchConfig()?.SPATIAL_EXTENT_MAX_FILE_SIZE
|
|
57
|
+
|
|
46
58
|
fieldType: FieldType
|
|
47
59
|
dateRange: DateRange
|
|
48
60
|
choices$: Observable<Choice[]>
|
|
@@ -66,6 +78,33 @@ export class FilterDropdownComponent implements OnInit {
|
|
|
66
78
|
.subscribe((filters) => this.searchService.updateFilters(filters))
|
|
67
79
|
}
|
|
68
80
|
|
|
81
|
+
private spatialExtentErrorNotificationId: number | null = null
|
|
82
|
+
|
|
83
|
+
onBboxChange(bbox: BoundingBox | null) {
|
|
84
|
+
console.log(bbox)
|
|
85
|
+
this.clearSpatialExtentErrorNotification()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
onSpatialExtentError(error: SpatialExtentDropdownError) {
|
|
89
|
+
this.clearSpatialExtentErrorNotification()
|
|
90
|
+
this.spatialExtentErrorNotificationId =
|
|
91
|
+
this.notificationsService.showNotification({
|
|
92
|
+
type: 'error',
|
|
93
|
+
title: this.translateService.instant(
|
|
94
|
+
'search.filters.spatialExtent.error.title'
|
|
95
|
+
),
|
|
96
|
+
text: this.translateService.instant(error.key, error.params),
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private clearSpatialExtentErrorNotification() {
|
|
101
|
+
if (this.spatialExtentErrorNotificationId === null) return
|
|
102
|
+
this.notificationsService.removeNotificationById(
|
|
103
|
+
this.spatialExtentErrorNotificationId
|
|
104
|
+
)
|
|
105
|
+
this.spatialExtentErrorNotificationId = null
|
|
106
|
+
}
|
|
107
|
+
|
|
69
108
|
ngOnInit() {
|
|
70
109
|
this.fieldType = this.fieldsService.getFieldType(this.fieldName)
|
|
71
110
|
this.choices$ = this.fieldsService.getAvailableValues(this.fieldName).pipe(
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
TranslatedSearchField,
|
|
16
16
|
RecordKindField,
|
|
17
17
|
UserSearchField,
|
|
18
|
+
SpatialExtentSearchField,
|
|
18
19
|
} from './fields'
|
|
19
20
|
import { forkJoin, Observable, of } from 'rxjs'
|
|
20
21
|
import { map } from 'rxjs/operators'
|
|
@@ -42,7 +43,7 @@ marker('search.filters.producerOrg')
|
|
|
42
43
|
marker('search.filters.publisherOrg')
|
|
43
44
|
marker('search.filters.user')
|
|
44
45
|
marker('search.filters.changeDate')
|
|
45
|
-
|
|
46
|
+
marker('search.filters.spatialExtent')
|
|
46
47
|
@Injectable({
|
|
47
48
|
providedIn: 'root',
|
|
48
49
|
})
|
|
@@ -95,6 +96,7 @@ export class FieldsService {
|
|
|
95
96
|
user: new UserSearchField(this.injector),
|
|
96
97
|
changeDate: new DateRangeSearchField('changeDate', this.injector, 'desc'),
|
|
97
98
|
availableServices: new AvailableServicesField(this.injector),
|
|
99
|
+
spatialExtent: new SpatialExtentSearchField(this.injector),
|
|
98
100
|
} as Record<string, AbstractSearchField>
|
|
99
101
|
|
|
100
102
|
get supportedFields() {
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
import { formatUserInfo } from '../../../../../../../libs/util/shared/src'
|
|
24
24
|
import { PossibleResourceTypes } from '../../../../../../../libs/api/metadata-converter/src'
|
|
25
25
|
|
|
26
|
-
export type FieldType = 'values' | 'dateRange'
|
|
26
|
+
export type FieldType = 'values' | 'dateRange' | 'spatialExtent'
|
|
27
27
|
|
|
28
28
|
export type FieldValue = string | number
|
|
29
29
|
export interface FieldAvailableValue {
|
|
@@ -431,6 +431,21 @@ export class DateRangeSearchField extends SimpleSearchField {
|
|
|
431
431
|
}
|
|
432
432
|
}
|
|
433
433
|
|
|
434
|
+
export class SpatialExtentSearchField extends SimpleSearchField {
|
|
435
|
+
constructor(injector: Injector) {
|
|
436
|
+
super('spatialExtent', injector, 'asc')
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
getAvailableValues(): Observable<FieldAvailableValue[]> {
|
|
440
|
+
// TODO: return an array of spatial extents to show which ones are available in the dropdown
|
|
441
|
+
return of([])
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
getType(): FieldType {
|
|
445
|
+
return 'spatialExtent'
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
434
449
|
marker('search.filters.availableServices.view')
|
|
435
450
|
marker('search.filters.availableServices.download')
|
|
436
451
|
|