geonetwork-ui 2.9.0-dev.f3376949a → 2.9.0-dev.fc19cbfed

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 (54) hide show
  1. package/fesm2022/geonetwork-ui.mjs +690 -563
  2. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  3. package/index.d.ts +253 -162
  4. package/index.d.ts.map +1 -1
  5. package/package.json +7 -8
  6. package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +11 -2
  7. package/src/libs/common/domain/src/index.ts +1 -0
  8. package/src/libs/feature/dataviz/src/lib/chart-view/chart-view.component.html +8 -10
  9. package/src/libs/feature/dataviz/src/lib/service/data.service.ts +33 -11
  10. package/src/libs/feature/editor/src/index.ts +8 -7
  11. package/src/libs/feature/editor/src/lib/+state/editor.actions.ts +6 -1
  12. package/src/libs/feature/editor/src/lib/+state/editor.facade.ts +5 -1
  13. package/src/libs/feature/editor/src/lib/+state/editor.reducer.ts +4 -0
  14. package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +2 -2
  15. package/src/libs/feature/record/src/lib/external-viewer-button/external-viewer-button.component.ts +2 -0
  16. package/src/libs/feature/record/src/lib/gpf-api-dl/gpf-api-dl.component.html +4 -4
  17. package/src/libs/feature/record/src/lib/gpf-api-dl/gpf-api-dl.component.ts +3 -4
  18. package/src/libs/feature/record/src/lib/map-view/map-view.component.html +10 -18
  19. package/src/libs/feature/record/src/lib/map-view/map-view.component.ts +96 -9
  20. package/src/libs/feature/record/src/lib/state/mdview.facade.ts +1 -1
  21. package/src/libs/feature/router/src/lib/default/router.module.ts +3 -3
  22. package/src/libs/feature/router/src/lib/default/router.service.ts +2 -2
  23. package/src/libs/feature/router/src/lib/default/services/router-search.service.ts +12 -3
  24. package/src/libs/feature/router/src/lib/default/state/router.effects.ts +34 -4
  25. package/src/libs/feature/search/src/lib/state/reducer.ts +3 -0
  26. package/src/libs/feature/search/src/lib/utils/service/fields.service.ts +9 -3
  27. package/src/libs/feature/search/src/lib/utils/service/fields.ts +5 -5
  28. package/src/libs/ui/elements/src/lib/api-card/api-card.component.html +2 -2
  29. package/src/libs/ui/elements/src/lib/application-banner/application-banner.component.ts +2 -3
  30. package/src/libs/ui/elements/src/lib/image-input/image-input.component.html +5 -2
  31. package/src/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.html +63 -56
  32. package/src/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.scss +5 -5
  33. package/src/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.html +11 -8
  34. package/src/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.ts +3 -3
  35. package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.html +1 -1
  36. package/src/libs/ui/elements/src/lib/service-capabilities/service-capabilities.component.html +1 -1
  37. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts +7 -5
  38. package/src/libs/ui/map/src/lib/map-utils.ts +1 -1
  39. package/src/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.html +19 -16
  40. package/src/libs/util/app-config/src/lib/app-config.ts +8 -2
  41. package/src/libs/util/app-config/src/lib/fixtures.ts +1 -0
  42. package/src/libs/util/app-config/src/lib/model.ts +1 -0
  43. package/src/libs/util/shared/src/lib/gn-ui-version.ts +5 -4
  44. package/src/libs/util/shared/src/lib/links/link-utils.ts +8 -4
  45. package/src/libs/util/shared/src/lib/services/theme.service.ts +9 -23
  46. package/tailwind.base.config.js +3 -2
  47. package/translations/de.json +5 -3
  48. package/translations/en.json +7 -5
  49. package/translations/es.json +2 -0
  50. package/translations/fr.json +7 -5
  51. package/translations/it.json +7 -5
  52. package/translations/nl.json +2 -0
  53. package/translations/pt.json +2 -0
  54. package/translations/sk.json +3 -1
@@ -1,12 +1,13 @@
1
+ export * from './lib/+state/editor.actions'
1
2
  export * from './lib/+state/editor.facade'
2
3
  export * from './lib/+state/editor.models'
3
- export * from './lib/+state/editor.selectors'
4
4
  export * from './lib/+state/editor.reducer'
5
- export * from './lib/+state/editor.actions'
6
- export * from './lib/feature-editor.module'
7
- export * from './lib/services/editor.service'
5
+ export * from './lib/+state/editor.selectors'
8
6
  export * from './lib/components/import-record/import-record.component'
9
- export * from './lib/components/record-form/record-form.component'
10
- export * from './lib/components/record-form/form-field'
11
- export * from './lib/components/multilingual-panel/multilingual-panel.component'
12
7
  export * from './lib/components/metadata-quality-panel/metadata-quality-panel.component'
8
+ export * from './lib/components/multilingual-panel/multilingual-panel.component'
9
+ export * from './lib/components/record-form/form-field'
10
+ export * from './lib/components/record-form/record-form.component'
11
+ export * from './lib/feature-editor.module'
12
+ export * from './lib/fields.config'
13
+ export * from './lib/services/editor.service'
@@ -4,7 +4,7 @@ import {
4
4
  LanguageCode,
5
5
  } from '../../../../../../libs/common/domain/src/lib/model/record'
6
6
  import { SaveRecordError } from './editor.models'
7
- import { EditorFieldIdentification } from '../models'
7
+ import { EditorConfig, EditorFieldIdentification } from '../models'
8
8
 
9
9
  export const openRecord = createAction(
10
10
  '[Editor] Open record',
@@ -39,6 +39,11 @@ export const draftSaveSuccess = createAction('[Editor] Draft save success')
39
39
 
40
40
  export const undoRecordDraft = createAction('[Editor] Undo record draft')
41
41
 
42
+ export const setEditorConfiguration = createAction(
43
+ '[Editor] Set editor configuration',
44
+ props<{ configuration: EditorConfig }>()
45
+ )
46
+
42
47
  export const setCurrentPage = createAction(
43
48
  '[Editor] Set current page',
44
49
  props<{ page: number }>()
@@ -8,7 +8,7 @@ import {
8
8
  } from '../../../../../../libs/common/domain/src/lib/model/record'
9
9
  import { filter } from 'rxjs'
10
10
  import { Actions, ofType } from '@ngrx/effects'
11
- import { EditorFieldIdentification } from '../models'
11
+ import { EditorConfig, EditorFieldIdentification } from '../models'
12
12
 
13
13
  @Injectable()
14
14
  export class EditorFacade {
@@ -69,6 +69,10 @@ export class EditorFacade {
69
69
  )
70
70
  }
71
71
 
72
+ setConfiguration(configuration: EditorConfig) {
73
+ this.store.dispatch(EditorActions.setEditorConfiguration({ configuration }))
74
+ }
75
+
72
76
  setCurrentPage(page: number) {
73
77
  this.store.dispatch(EditorActions.setCurrentPage({ page }))
74
78
  }
@@ -81,6 +81,10 @@ const reducer = createReducer(
81
81
  ...state,
82
82
  changedSinceSave: true,
83
83
  })),
84
+ on(EditorActions.setEditorConfiguration, (state, { configuration }) => ({
85
+ ...state,
86
+ editorConfig: configuration,
87
+ })),
84
88
  on(EditorActions.setCurrentPage, (state, { page }) => ({
85
89
  ...state,
86
90
  currentPage: page,
@@ -31,12 +31,12 @@
31
31
  cdkAutosizeMinRows="1"
32
32
  data-test="recordTitleInput"
33
33
  class="grow font-title text-3xl font-normal overflow-hidden text-black/80"
34
+ [value]="valueAsString"
34
35
  (change)="valueChange.emit($event.target.value)"
35
36
  [placeholder]="
36
37
  'editor.record.form.field.title.placeholder' | translate
37
38
  "
38
- >{{ valueAsString }}</textarea
39
- >
39
+ ></textarea>
40
40
  <div class="flex flex-row justify-between self-start mt-0.5">
41
41
  <span
42
42
  class="material-symbols-outlined gn-ui-icon-small m-2 cursor-pointer"
@@ -44,6 +44,7 @@ export class ExternalViewerButtonComponent {
44
44
  private openinNewTab = inject(EXTERNAL_VIEWER_OPEN_NEW_TAB)
45
45
 
46
46
  @Input() link: DatasetOnlineResource
47
+ @Input() mimeType = ''
47
48
  @Input() extraClass = ''
48
49
 
49
50
  get externalViewer() {
@@ -80,6 +81,7 @@ export class ExternalViewerButtonComponent {
80
81
  `${encodeURIComponent(this.link.url.toString())}`
81
82
  )
82
83
  .replace('${service_type}', `${this.supportedLinkLayerType}`)
84
+ .replace('${mime_type}', `${encodeURIComponent(this.mimeType)}`)
83
85
  window.open(url, this.openinNewTab ? '_blank' : '_self').focus()
84
86
  }
85
87
  }
@@ -12,7 +12,7 @@
12
12
  </h2>
13
13
  <button
14
14
  (click)="resetUrl()"
15
- class="bg-primary-opacity-50 inline-flex items-center justify-center px-4 py-2 text-white rounded hover:bg-primary transition"
15
+ class="bg-primary/50 inline-flex items-center justify-center px-4 py-2 text-white rounded hover:bg-primary transition"
16
16
  [attr.title]="'record.metadata.api.form.resetTooltip' | translate"
17
17
  [attr.aria-label]="'record.metadata.api.form.resetTooltip' | translate"
18
18
  >
@@ -97,7 +97,7 @@
97
97
  'px-4 py-2 text-white rounded transition ' +
98
98
  ((page$ | async) <= 1
99
99
  ? 'bg-gray-400 cursor-not-allowed '
100
- : 'bg-primary-opacity-50 hover:bg-primary')
100
+ : 'bg-primary/50 hover:bg-primary')
101
101
  "
102
102
  [attr.title]="'record.metadata.api.form.previousPageTooltip' | translate"
103
103
  [attr.aria-label]="
@@ -117,10 +117,10 @@
117
117
  <button
118
118
  (click)="moreResult()"
119
119
  [class]="
120
- 'bg-primary-opacity-50 px-4 py-2 text-white rounded transition ' +
120
+ 'bg-primary/50 px-4 py-2 text-white rounded transition ' +
121
121
  ((page$ | async) >= (pageMax$ | async)
122
122
  ? 'bg-gray-400 cursor-not-allowed '
123
- : 'bg-primary-opacity-50 hover:bg-primary')
123
+ : 'bg-primary/50 hover:bg-primary')
124
124
  "
125
125
  [attr.title]="'record.metadata.api.form.nextPageTooltip' | translate"
126
126
  [attr.aria-label]="'record.metadata.api.form.nextPageTooltip' | translate"
@@ -1,15 +1,14 @@
1
1
  import {
2
2
  ChangeDetectionStrategy,
3
3
  Component,
4
+ inject,
4
5
  Input,
5
6
  OnInit,
6
- inject,
7
7
  } from '@angular/core'
8
8
  import { DatasetServiceDistribution } from '../../../../../../libs/common/domain/src/lib/model/record'
9
9
  import { BehaviorSubject, combineLatest, map, mergeMap, Observable } from 'rxjs'
10
10
  import { HttpClient } from '@angular/common/http'
11
11
  import { Choice, DropdownSelectorComponent } from '../../../../../../libs/ui/inputs/src'
12
- import axios from 'axios'
13
12
  import { CommonModule } from '@angular/common'
14
13
  import { TranslateDirective, TranslatePipe } from '@ngx-translate/core'
15
14
  import { GpfApiDlListItemComponent } from '../gpf-api-dl-list-item/gpf-api-dl-list-item.component'
@@ -209,9 +208,9 @@ export class GpfApiDlComponent implements OnInit {
209
208
  let pageCount = 1
210
209
 
211
210
  while (choicesTest === undefined && pageCount > page) {
212
- const response = await axios.get(
211
+ const response = await fetch(
213
212
  this.url.concat(`&limit=200&page=${page}`)
214
- )
213
+ ).then((resp) => resp.json())
215
214
 
216
215
  choicesTest = response.data.entry.filter(
217
216
  (element) => element['id'] == this.apiBaseUrl
@@ -13,29 +13,21 @@
13
13
  ></gn-ui-dropdown-selector>
14
14
  }
15
15
 
16
- @if ((styleLinks$ | async)?.length > 0) {
17
- <gn-ui-dropdown-selector
18
- class="w-full md:flex-1 md:min-w-0"
19
- extraBtnClass="font-sans font-bold"
20
- [title]="'map.select.style' | translate"
21
- [choices]="styleDropdownChoices$ | async"
22
- [selected]="_styleFromConfig"
23
- (selectValue)="selectStyleToDisplay($event)"
24
- ></gn-ui-dropdown-selector>
25
- } @else {
26
- <gn-ui-dropdown-selector
27
- class="w-full md:flex-1 md:min-w-0 text-gray-400"
28
- extraBtnClass="font-sans font-bold text-gray-400"
29
- [title]="'map.select.style' | translate"
30
- [choices]="styleDropdownChoices$ | async"
31
- [disabled]="true"
32
- ></gn-ui-dropdown-selector>
33
- }
16
+ <gn-ui-dropdown-selector
17
+ class="w-full md:flex-1 md:min-w-0"
18
+ extraBtnClass="font-sans font-bold"
19
+ [title]="'map.select.style' | translate"
20
+ [choices]="styleDropdownChoices$ | async"
21
+ [selected]="selectedStyleId$ | async"
22
+ [disabled]="(styleDropdownChoices$ | async)?.length <= 1"
23
+ (selectValue)="selectStyleToDisplay($event)"
24
+ ></gn-ui-dropdown-selector>
34
25
 
35
26
  <div class="self-end md:ml-2">
36
27
  <gn-ui-external-viewer-button
37
28
  extraClass="w-[44px] h-[44px]"
38
29
  [link]="selectedLink$ | async"
30
+ [mimeType]="wmsMimeType$ | async"
39
31
  >
40
32
  </gn-ui-external-viewer-button>
41
33
  </div>
@@ -11,6 +11,7 @@ import {
11
11
  } from '@angular/core'
12
12
  import { MapUtilsService } from '../../../../../../libs/feature/map/src'
13
13
  import { getLinkId, getLinkLabel } from '../../../../../../libs/util/shared/src'
14
+ import { WmsEndpoint, LayerStyle } from '@camptocamp/ogc-client'
14
15
  import {
15
16
  BehaviorSubject,
16
17
  combineLatest,
@@ -40,6 +41,7 @@ import {
40
41
  createViewFromLayer,
41
42
  MapContext,
42
43
  MapContextLayer,
44
+ MapContextLayerWms,
43
45
  SourceLoadErrorEvent,
44
46
  } from '@geospatial-sdk/core'
45
47
  import {
@@ -77,6 +79,7 @@ marker('map.dropdown.placeholder')
77
79
  marker('wfs.feature.limit')
78
80
  marker('dataset.error.restrictedAccess')
79
81
  marker('map.select.style')
82
+ marker('map.style.default')
80
83
 
81
84
  @Component({
82
85
  selector: 'gn-ui-map-view',
@@ -228,7 +231,16 @@ export class MapViewComponent implements AfterViewInit {
228
231
  return compatibleLinks[0]
229
232
  }
230
233
  }
231
- })
234
+ }),
235
+ shareReplay(1)
236
+ )
237
+
238
+ isWmsStyleMode$ = this.selectedSourceLink$.pipe(
239
+ map(
240
+ (src) => src?.type === 'service' && src?.accessServiceProtocol === 'wms'
241
+ ),
242
+ distinctUntilChanged(),
243
+ shareReplay(1)
232
244
  )
233
245
 
234
246
  styleLinks$ = this.selectedSourceLink$.pipe(
@@ -262,6 +274,22 @@ export class MapViewComponent implements AfterViewInit {
262
274
  })
263
275
  )
264
276
  }
277
+ if (
278
+ src &&
279
+ src.type === 'service' &&
280
+ src.accessServiceProtocol === 'wms'
281
+ ) {
282
+ return from(new WmsEndpoint(src.url.toString()).isReady()).pipe(
283
+ map((endpoint) => {
284
+ const layer = endpoint.getLayerByName(src.name)
285
+ return layer?.styles || []
286
+ }),
287
+ catchError((error) => {
288
+ this.handleError(error)
289
+ return of([])
290
+ })
291
+ )
292
+ }
265
293
  return of([])
266
294
  }),
267
295
  tap((styles) => {
@@ -274,33 +302,83 @@ export class MapViewComponent implements AfterViewInit {
274
302
  shareReplay(1)
275
303
  )
276
304
 
277
- styleDropdownChoices$ = this.styleLinks$.pipe(
278
- map((links) =>
305
+ styleDropdownChoices$ = combineLatest([
306
+ this.styleLinks$,
307
+ this.isWmsStyleMode$,
308
+ ]).pipe(
309
+ map(([links, isWmsStyleMode]) =>
279
310
  links.length
280
311
  ? links.map((link, index) => ({
281
- label: getLinkLabel(link),
312
+ label: isWmsStyleMode
313
+ ? (link as LayerStyle).title || (link as LayerStyle).name
314
+ : getLinkLabel(link as DatasetOnlineResource),
282
315
  value: index,
283
316
  }))
284
317
  : [
285
318
  {
286
- label: '\u00A0\u00A0\u00A0\u00A0',
319
+ label: this.translateService.instant('map.style.default'),
287
320
  value: 0,
288
321
  },
289
322
  ]
290
323
  )
291
324
  )
292
325
 
326
+ selectedWmsStyleName$ = combineLatest([
327
+ this.styleLinks$,
328
+ this.isWmsStyleMode$,
329
+ this.selectedStyleId$.pipe(distinctUntilChanged()),
330
+ ]).pipe(
331
+ map(([styles, isWmsStyleMode, styleIdx]) =>
332
+ isWmsStyleMode && Array.isArray(styles)
333
+ ? (styles[styleIdx] as LayerStyle)?.name
334
+ : undefined
335
+ )
336
+ )
337
+
293
338
  selectedLink$ = combineLatest([
294
339
  this.selectedSourceLink$,
295
340
  this.styleLinks$,
296
341
  this.selectedStyleId$.pipe(distinctUntilChanged()),
342
+ this.isWmsStyleMode$,
297
343
  ]).pipe(
298
- map(([src, styles, styleIdx]) => (styles.length ? styles[styleIdx] : src)),
344
+ map(([src, styles, styleIdx, isWmsStyleMode]) =>
345
+ !isWmsStyleMode && styles.length ? styles[styleIdx] : src
346
+ ),
299
347
  shareReplay(1)
300
348
  )
301
349
 
302
- currentLayers$ = combineLatest([this.selectedLink$, this.excludeWfs$]).pipe(
303
- switchMap(([link, excludeWfs]) => {
350
+ wmsMimeType$ = this.selectedSourceLink$.pipe(
351
+ switchMap((link) => {
352
+ if (link?.type === 'service' && link?.accessServiceProtocol === 'wms') {
353
+ return from(
354
+ new WmsEndpoint(link.url.toString())
355
+ .isReady()
356
+ .then((endpoint) => {
357
+ return endpoint.describeLayer(link.name).then((description) => {
358
+ if (description) {
359
+ return description.owsType === 'wfs'
360
+ ? 'image/png'
361
+ : 'image/jpeg'
362
+ }
363
+ const layer = endpoint.getLayerByName(link.name)
364
+ return layer?.opaque ? 'image/jpeg' : 'image/png'
365
+ })
366
+ })
367
+ .catch(() => 'image/png')
368
+ )
369
+ }
370
+ return of('')
371
+ }),
372
+ shareReplay(1)
373
+ )
374
+
375
+ currentLayers$ = combineLatest([
376
+ this.selectedLink$,
377
+ this.excludeWfs$,
378
+ this.selectedWmsStyleName$,
379
+ this.wmsMimeType$,
380
+ ]).pipe(
381
+ switchMap(([link, excludeWfs, wmsStyleName, wmsMimeType]) => {
304
382
  if (!link) {
305
383
  return of([])
306
384
  }
@@ -315,6 +393,15 @@ export class MapViewComponent implements AfterViewInit {
315
393
  return of([])
316
394
  }
317
395
  return this.getLayerFromLink(link).pipe(
396
+ map((layer) =>
397
+ layer.type === 'wms'
398
+ ? {
399
+ ...layer,
400
+ ...(wmsStyleName && { style: wmsStyleName }),
401
+ ...(wmsMimeType && { format: wmsMimeType }),
402
+ }
403
+ : layer
404
+ ),
318
405
  map((layer) => [layer]),
319
406
  catchError((e) => {
320
407
  this.handleError(e)
@@ -394,7 +481,7 @@ export class MapViewComponent implements AfterViewInit {
394
481
  url: link.url.toString(),
395
482
  type: 'wms',
396
483
  name: link.name,
397
- })
484
+ } as MapContextLayerWms)
398
485
  } else if (
399
486
  link.type === 'service' &&
400
487
  link.accessServiceProtocol === 'tms'
@@ -164,7 +164,7 @@ export class MdViewFacade {
164
164
  link.accessServiceProtocol === 'ogcFeatures'
165
165
  ) {
166
166
  return from(
167
- this.dataService.getItemsFromOgcApi(link.url.href)
167
+ this.dataService.getItemsFromOgcApi(link.url.href, 1)
168
168
  ).pipe(
169
169
  map((collectionRecords: OgcApiRecord[]) => {
170
170
  return collectionRecords &&
@@ -1,6 +1,5 @@
1
- import { ModuleWithProviders, NgModule, inject } from '@angular/core'
1
+ import { inject, ModuleWithProviders, NgModule } from '@angular/core'
2
2
  import { RouteReuseStrategy } from '@angular/router'
3
- import { EffectsModule } from '@ngrx/effects'
4
3
  import {
5
4
  FullRouterStateSerializer,
6
5
  routerReducer,
@@ -11,8 +10,9 @@ import { ROUTER_STATE_KEY } from './constants'
11
10
  import { RouterService } from './router.service'
12
11
  import { SearchRouteReuseStrategy } from './SearchRouteReuseStrategy'
13
12
  import { RouterFacade } from './state/router.facade'
14
- import { RouterEffects } from './state/router.effects'
15
13
  import { ROUTER_CONFIG, RouterConfigModel } from './router.config'
14
+ import { RouterEffects } from './state/router.effects'
15
+ import { EffectsModule } from '@ngrx/effects'
16
16
 
17
17
  @NgModule({
18
18
  imports: [
@@ -1,10 +1,10 @@
1
- import { Injectable, inject } from '@angular/core'
1
+ import { inject, Injectable } from '@angular/core'
2
2
  import {
3
3
  ROUTER_ROUTE_DATASET,
4
4
  ROUTER_ROUTE_ORGANIZATION,
5
+ ROUTER_ROUTE_REUSE,
5
6
  ROUTER_ROUTE_SEARCH,
6
7
  ROUTER_ROUTE_SERVICE,
7
- ROUTER_ROUTE_REUSE,
8
8
  } from '.'
9
9
  import { Router, Routes } from '@angular/router'
10
10
  import { ROUTER_CONFIG, RouterConfigModel } from './router.config'
@@ -1,4 +1,4 @@
1
- import { Injectable, inject } from '@angular/core'
1
+ import { inject, Injectable } from '@angular/core'
2
2
  import {
3
3
  FieldsService,
4
4
  SearchFacade,
@@ -6,6 +6,7 @@ import {
6
6
  } from '../../../../../../../libs/feature/search/src'
7
7
  import {
8
8
  FieldFilters,
9
+ SortByEnum,
9
10
  SortByField,
10
11
  } from '../../../../../../../libs/common/domain/src/lib/model/search'
11
12
  import { ROUTE_PARAMS, SearchRouteParams } from '../constants'
@@ -31,10 +32,12 @@ export class RouterSearchService implements SearchServiceI {
31
32
  }
32
33
 
33
34
  async setFilters(newFilters: FieldFilters) {
34
- const sortBy = await firstValueFrom(this.searchFacade.sortBy$)
35
+ let sortBy = await firstValueFrom(this.searchFacade.sortBy$)
35
36
  const fieldSearchParams = await firstValueFrom(
36
37
  this.fieldsService.readFieldValuesFromFilters(newFilters)
37
38
  )
39
+ // apply relevancy sort if a full text criteria is given
40
+ sortBy = newFilters['any'] ? SortByEnum.RELEVANCY : sortBy
38
41
  this.facade.setSearch({
39
42
  ...fieldSearchParams,
40
43
  [ROUTE_PARAMS.SORT]: sortBy ? sortByToString(sortBy) : undefined,
@@ -46,9 +49,15 @@ export class RouterSearchService implements SearchServiceI {
46
49
  this.searchFacade.searchFilters$
47
50
  )
48
51
  const updatedFilters = { ...currentFilters, ...newFilters }
49
- const newParams = await firstValueFrom(
52
+ let newParams = await firstValueFrom(
50
53
  this.fieldsService.readFieldValuesFromFilters(updatedFilters)
51
54
  )
55
+ if (newFilters['any']) {
56
+ newParams = {
57
+ ...newParams,
58
+ [ROUTE_PARAMS.SORT]: sortByToString(SortByEnum.RELEVANCY),
59
+ }
60
+ }
52
61
  this.facade.updateSearch(newParams as SearchRouteParams)
53
62
  }
54
63
 
@@ -1,5 +1,5 @@
1
1
  import { Location } from '@angular/common'
2
- import { Injectable, inject } from '@angular/core'
2
+ import { inject, Injectable } from '@angular/core'
3
3
  import { ActivatedRouteSnapshot, Router } from '@angular/router'
4
4
  import { MdViewActions } from '../../../../../../../libs/feature/record/src'
5
5
  import {
@@ -9,15 +9,18 @@ import {
9
9
  SetFilters,
10
10
  SetSortBy,
11
11
  } from '../../../../../../../libs/feature/search/src'
12
- import { FieldFilters } from '../../../../../../../libs/common/domain/src/lib/model/search'
12
+ import {
13
+ FieldFilters,
14
+ SortByEnum,
15
+ } from '../../../../../../../libs/common/domain/src/lib/model/search'
13
16
  import { Actions, createEffect, ofType } from '@ngrx/effects'
14
17
  import { navigation } from '@ngrx/router-store/data-persistence'
15
18
  import { of, pairwise, startWith } from 'rxjs'
16
- import { map, mergeMap, tap } from 'rxjs/operators'
19
+ import { map, mergeMap, take, tap } from 'rxjs/operators'
17
20
  import * as RouterActions from './router.actions'
18
21
  import { RouterFacade } from './router.facade'
19
22
  import { ROUTE_PARAMS } from '../constants'
20
- import { sortByFromString } from '../../../../../../../libs/util/shared/src'
23
+ import { sortByFromString, sortByToString } from '../../../../../../../libs/util/shared/src'
21
24
  import { ROUTER_CONFIG, RouterConfigModel } from '../router.config'
22
25
  import { RouterService } from '../router.service'
23
26
 
@@ -103,6 +106,33 @@ export class RouterEffects {
103
106
  )
104
107
  )
105
108
 
109
+ /**
110
+ * This effect is needed because on the page load, the search params from the URL are
111
+ * directly applied to the underlying search facade; this means that it doesn't go
112
+ * through the RouterSearchService which makes sure that a relevancy sort is applied
113
+ * whenever there's a full text search criteria set.
114
+ */
115
+ applyInitialRelevancySort$ = createEffect(
116
+ () =>
117
+ this.facade.searchParams$.pipe(
118
+ take(1),
119
+ tap((filters) => {
120
+ const relevancySort = sortByToString(SortByEnum.RELEVANCY)
121
+ if (
122
+ filters['q'] &&
123
+ (!Array.isArray(filters['q']) || filters['q'].length > 0) &&
124
+ !filters[ROUTE_PARAMS.SORT]
125
+ ) {
126
+ this.facade.updateSearch({
127
+ ...filters,
128
+ [ROUTE_PARAMS.SORT]: relevancySort,
129
+ })
130
+ }
131
+ })
132
+ ),
133
+ { dispatch: false }
134
+ )
135
+
106
136
  /**
107
137
  * This effect will load the metadata when a navigation to
108
138
  * a metadata record happens
@@ -111,6 +111,8 @@ export function reducerSearch(
111
111
  },
112
112
  }
113
113
  }
114
+ // From router.effects
115
+ // From home - fuzzy-search - search.service
114
116
  case fromActions.SET_FILTERS: {
115
117
  return {
116
118
  ...state,
@@ -132,6 +134,7 @@ export function reducerSearch(
132
134
  },
133
135
  }
134
136
  }
137
+ // From results WC
135
138
  case fromActions.SET_SEARCH: {
136
139
  return {
137
140
  ...state,
@@ -138,9 +138,15 @@ export class FieldsService {
138
138
  )
139
139
  })
140
140
  return forkJoin(filtersByField$).pipe(
141
- map((filters) =>
142
- filters.reduce((prev, curr) => ({ ...prev, ...curr }), {})
143
- )
141
+ map((filters) => {
142
+ if (typeof filters === 'string') {
143
+ return filters
144
+ }
145
+ return (filters as FieldFilters[]).reduce(
146
+ (prev, curr) => ({ ...prev, ...curr }),
147
+ {}
148
+ )
149
+ })
144
150
  )
145
151
  }
146
152
 
@@ -36,7 +36,7 @@ export abstract class AbstractSearchField {
36
36
  abstract getAvailableValues(): Observable<FieldAvailableValue[] | DateRange[]>
37
37
  abstract getFiltersForValues(
38
38
  values: FieldValue[] | DateRange[]
39
- ): Observable<FieldFilters>
39
+ ): Observable<FieldFilters | string>
40
40
  abstract getValuesForFilter(
41
41
  filters: FieldFilters
42
42
  ): Observable<FieldValue[] | FieldValue | DateRange>
@@ -89,7 +89,7 @@ export class SimpleSearchField implements AbstractSearchField {
89
89
  }
90
90
  getFiltersForValues(
91
91
  values: FieldValue[] | DateRange[]
92
- ): Observable<FieldFilters> {
92
+ ): Observable<FieldFilters | string> {
93
93
  // FieldValue[]
94
94
  if (this.getType() === 'values') {
95
95
  return of({
@@ -538,7 +538,7 @@ export class RecordKindField extends SimpleSearchField {
538
538
  ])
539
539
  }
540
540
 
541
- getFiltersForValues(values: FieldValue[]): Observable<FieldFilters> {
541
+ getFiltersForValues(values: FieldValue[]): Observable<FieldFilters | string> {
542
542
  const filters: FieldFilters = {
543
543
  [this.esFieldName]: values.reduce((acc, value) => {
544
544
  if (value === '') return { ...acc, [value]: true }
@@ -552,8 +552,8 @@ export class RecordKindField extends SimpleSearchField {
552
552
 
553
553
  const presentationFormFilter = {}
554
554
  if (values.includes('reuse') && !values.includes('dataset')) {
555
- presentationFormFilter['mapDigital'] = true
556
- presentationFormFilter['mapHardcopy'] = true
555
+ filters['gn-ui-crossFieldFilter'] =
556
+ `(resourceType:("dataset" OR "document") AND cl_presentationForm.key:("mapDigital" OR "mapHardcopy")) OR resourceType:("application" OR "interactiveMap" OR "map" OR "map/static" OR "map/interactive" OR "map-interactive" OR "map-static" OR "mapDigital" OR "mapHardcopy" OR "staticMap")`
557
557
  } else if (values.includes('dataset') && !values.includes('reuse')) {
558
558
  presentationFormFilter['mapDigital'] = false
559
559
  presentationFormFilter['mapHardcopy'] = false
@@ -64,7 +64,7 @@
64
64
  <div class="flex flex-row gap-2.5 items-center pt-1">
65
65
  @if (link.accessServiceProtocol !== 'GPFDL') {
66
66
  <span
67
- class="bg-primary-opacity-50 uppercase inline-flex items-center justify-center px-2 py-1 text-13 font-medium leading-none text-white rounded text-primary-lightest group-hover:bg-primary transition-colors"
67
+ class="bg-primary/50 uppercase inline-flex items-center justify-center px-2 py-1 text-13 font-medium leading-none text-white rounded text-primary-lightest group-hover:bg-primary transition-colors"
68
68
  [ngClass]="{
69
69
  '!bg-primary': currentlyActive,
70
70
  }"
@@ -76,7 +76,7 @@
76
76
  }
77
77
  @if (link.accessServiceProtocol === 'GPFDL') {
78
78
  <span
79
- class="bg-primary-opacity-50 uppercase inline-flex items-center justify-center px-2 py-1 text-13 font-medium leading-none text-white rounded text-primary-lightest group-hover:bg-primary transition-colors"
79
+ class="bg-primary/50 uppercase inline-flex items-center justify-center px-2 py-1 text-13 font-medium leading-none text-white rounded text-primary-lightest group-hover:bg-primary transition-colors"
80
80
  [ngClass]="{
81
81
  '!bg-primary': currentlyActive,
82
82
  }"
@@ -45,13 +45,12 @@ export class ApplicationBannerComponent {
45
45
  this.icon = 'matWarning'
46
46
  break
47
47
  case 'light':
48
- this.msgClass =
49
- 'bg-primary-opacity-10 border-primary-lightest text-black'
48
+ this.msgClass = 'bg-primary/10 border-primary-lightest text-black'
50
49
  this.icon = 'matInfoOutline'
51
50
  break
52
51
  case 'secondary':
53
52
  default:
54
- this.msgClass = 'bg-primary-opacity-50 border-primary-darker text-black'
53
+ this.msgClass = 'bg-primary/50 border-primary-darker text-black'
55
54
  this.icon = 'matWarningAmberOutline'
56
55
  break
57
56
  }
@@ -1,6 +1,9 @@
1
1
  @if (previewUrl) {
2
- <div class="w-80 h-full flex flex-col gap-2">
3
- <gn-ui-image-overlay-preview class="h-48" [imageUrl]="previewUrl">
2
+ <div class="w-[314px] h-full flex flex-col gap-2">
3
+ <gn-ui-image-overlay-preview
4
+ class="w-[314px] h-[314px]"
5
+ [imageUrl]="previewUrl"
6
+ >
4
7
  </gn-ui-image-overlay-preview>
5
8
  @if (showAltTextInput) {
6
9
  <gn-ui-text-input