geonetwork-ui 2.6.0-dev.9d3ad45e2 → 2.6.0-dev.a225a5e7a

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 (118) hide show
  1. package/esm2022/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.mjs +36 -4
  2. package/esm2022/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.mjs +1 -1
  3. package/esm2022/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.mjs +8 -4
  4. package/esm2022/libs/api/metadata-converter/src/lib/iso19115-3/write-parts.mjs +5 -2
  5. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/read-parts.mjs +3 -3
  6. package/esm2022/libs/api/metadata-converter/src/lib/iso19139/write-parts.mjs +2 -2
  7. package/esm2022/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.mjs +3 -3
  8. package/esm2022/libs/api/repository/src/lib/gn4/gn4-repository.mjs +36 -6
  9. package/esm2022/libs/common/domain/src/lib/repository/records-repository.interface.mjs +1 -1
  10. package/esm2022/libs/feature/editor/src/lib/components/multilingual-panel/multilingual-panel.component.mjs +266 -11
  11. package/esm2022/libs/feature/record/src/lib/state/mdview.actions.mjs +3 -1
  12. package/esm2022/libs/feature/record/src/lib/state/mdview.effects.mjs +7 -1
  13. package/esm2022/libs/feature/record/src/lib/state/mdview.facade.mjs +3 -1
  14. package/esm2022/libs/feature/record/src/lib/state/mdview.reducer.mjs +7 -1
  15. package/esm2022/libs/feature/record/src/lib/state/mdview.selectors.mjs +3 -1
  16. package/esm2022/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.mjs +19 -34
  17. package/esm2022/libs/ui/elements/src/lib/internal-link-card-contact/internal-link-card-contact.component.mjs +59 -0
  18. package/esm2022/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.mjs +3 -3
  19. package/esm2022/libs/ui/layout/src/lib/interactive-table/interactive-table.component.mjs +3 -3
  20. package/esm2022/libs/ui/search/src/index.mjs +2 -1
  21. package/esm2022/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.mjs +19 -12
  22. package/esm2022/libs/ui/search/src/lib/results-table/results-table.component.mjs +27 -14
  23. package/esm2022/libs/ui/search/src/lib/ui-search.module.mjs +10 -4
  24. package/esm2022/libs/util/i18n/src/lib/i18n.constants.mjs +42 -1
  25. package/esm2022/libs/util/i18n/src/lib/language-codes.mjs +24 -2
  26. package/esm2022/translations/de.json +29 -4
  27. package/esm2022/translations/en.json +29 -4
  28. package/esm2022/translations/es.json +28 -3
  29. package/esm2022/translations/fr.json +29 -4
  30. package/esm2022/translations/it.json +28 -3
  31. package/esm2022/translations/nl.json +28 -3
  32. package/esm2022/translations/pt.json +28 -3
  33. package/fesm2022/geonetwork-ui.mjs +775 -150
  34. package/fesm2022/geonetwork-ui.mjs.map +1 -1
  35. package/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.d.ts.map +1 -1
  36. package/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.d.ts +0 -1
  37. package/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.d.ts.map +1 -1
  38. package/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.d.ts +1 -0
  39. package/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.d.ts.map +1 -1
  40. package/libs/api/metadata-converter/src/lib/iso19115-3/write-parts.d.ts.map +1 -1
  41. package/libs/api/metadata-converter/src/lib/iso19139/read-parts.d.ts.map +1 -1
  42. package/libs/api/metadata-converter/src/lib/iso19139/write-parts.d.ts.map +1 -1
  43. package/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.d.ts +1 -1
  44. package/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.d.ts.map +1 -1
  45. package/libs/api/repository/src/lib/gn4/gn4-repository.d.ts +7 -2
  46. package/libs/api/repository/src/lib/gn4/gn4-repository.d.ts.map +1 -1
  47. package/libs/common/domain/src/lib/repository/records-repository.interface.d.ts +3 -0
  48. package/libs/common/domain/src/lib/repository/records-repository.interface.d.ts.map +1 -1
  49. package/libs/feature/editor/src/lib/components/multilingual-panel/multilingual-panel.component.d.ts +48 -3
  50. package/libs/feature/editor/src/lib/components/multilingual-panel/multilingual-panel.component.d.ts.map +1 -1
  51. package/libs/feature/record/src/lib/state/mdview.actions.d.ts +10 -0
  52. package/libs/feature/record/src/lib/state/mdview.actions.d.ts.map +1 -1
  53. package/libs/feature/record/src/lib/state/mdview.effects.d.ts +6 -0
  54. package/libs/feature/record/src/lib/state/mdview.effects.d.ts.map +1 -1
  55. package/libs/feature/record/src/lib/state/mdview.facade.d.ts +2 -0
  56. package/libs/feature/record/src/lib/state/mdview.facade.d.ts.map +1 -1
  57. package/libs/feature/record/src/lib/state/mdview.reducer.d.ts +2 -0
  58. package/libs/feature/record/src/lib/state/mdview.reducer.d.ts.map +1 -1
  59. package/libs/feature/record/src/lib/state/mdview.selectors.d.ts +2 -0
  60. package/libs/feature/record/src/lib/state/mdview.selectors.d.ts.map +1 -1
  61. package/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.d.ts +3 -6
  62. package/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.d.ts.map +1 -1
  63. package/libs/ui/elements/src/lib/internal-link-card-contact/internal-link-card-contact.component.d.ts +14 -0
  64. package/libs/ui/elements/src/lib/internal-link-card-contact/internal-link-card-contact.component.d.ts.map +1 -0
  65. package/libs/ui/search/src/index.d.ts +1 -0
  66. package/libs/ui/search/src/index.d.ts.map +1 -1
  67. package/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.d.ts +3 -3
  68. package/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.d.ts.map +1 -1
  69. package/libs/ui/search/src/lib/results-table/results-table.component.d.ts +5 -1
  70. package/libs/ui/search/src/lib/results-table/results-table.component.d.ts.map +1 -1
  71. package/libs/ui/search/src/lib/ui-search.module.d.ts +2 -1
  72. package/libs/ui/search/src/lib/ui-search.module.d.ts.map +1 -1
  73. package/libs/util/i18n/src/lib/i18n.constants.d.ts +1 -0
  74. package/libs/util/i18n/src/lib/i18n.constants.d.ts.map +1 -1
  75. package/libs/util/i18n/src/lib/language-codes.d.ts +23 -1
  76. package/libs/util/i18n/src/lib/language-codes.d.ts.map +1 -1
  77. package/package.json +1 -1
  78. package/src/libs/api/metadata-converter/src/lib/fixtures/geocat-ch.records.ts +1 -1
  79. package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +54 -4
  80. package/src/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.ts +0 -1
  81. package/src/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.ts +13 -3
  82. package/src/libs/api/metadata-converter/src/lib/iso19115-3/write-parts.ts +5 -1
  83. package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +6 -3
  84. package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +4 -1
  85. package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +2 -2
  86. package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +57 -4
  87. package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +3 -0
  88. package/src/libs/feature/editor/src/lib/components/multilingual-panel/multilingual-panel.component.html +117 -11
  89. package/src/libs/feature/editor/src/lib/components/multilingual-panel/multilingual-panel.component.ts +316 -6
  90. package/src/libs/feature/record/src/lib/state/mdview.actions.ts +10 -0
  91. package/src/libs/feature/record/src/lib/state/mdview.effects.ts +22 -0
  92. package/src/libs/feature/record/src/lib/state/mdview.facade.ts +4 -0
  93. package/src/libs/feature/record/src/lib/state/mdview.reducer.ts +12 -0
  94. package/src/libs/feature/record/src/lib/state/mdview.selectors.ts +9 -0
  95. package/src/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.html +7 -75
  96. package/src/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.ts +20 -51
  97. package/src/libs/ui/elements/src/lib/internal-link-card-contact/internal-link-card-contact.component.html +69 -0
  98. package/src/libs/ui/elements/src/lib/internal-link-card-contact/internal-link-card-contact.component.ts +61 -0
  99. package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.html +1 -1
  100. package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.html +1 -0
  101. package/src/libs/ui/search/src/index.ts +1 -0
  102. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.html +11 -3
  103. package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.ts +14 -11
  104. package/src/libs/ui/search/src/lib/results-table/results-table.component.css +4 -0
  105. package/src/libs/ui/search/src/lib/results-table/results-table.component.html +27 -17
  106. package/src/libs/ui/search/src/lib/results-table/results-table.component.ts +20 -10
  107. package/src/libs/ui/search/src/lib/ui-search.module.ts +3 -0
  108. package/src/libs/util/i18n/src/lib/i18n.constants.ts +42 -0
  109. package/src/libs/util/i18n/src/lib/language-codes.ts +23 -1
  110. package/tailwind.base.css +1 -1
  111. package/translations/de.json +29 -4
  112. package/translations/en.json +29 -4
  113. package/translations/es.json +28 -3
  114. package/translations/fr.json +29 -4
  115. package/translations/it.json +28 -3
  116. package/translations/nl.json +28 -3
  117. package/translations/pt.json +28 -3
  118. package/translations/sk.json +28 -3
@@ -1,15 +1,325 @@
1
- import { Component } from '@angular/core'
1
+ import {
2
+ ChangeDetectorRef,
3
+ Component,
4
+ ElementRef,
5
+ Input,
6
+ OnDestroy,
7
+ QueryList,
8
+ ViewChildren,
9
+ ViewContainerRef,
10
+ } from '@angular/core'
2
11
  import { CommonModule } from '@angular/common'
3
- import { CheckToggleComponent } from '../../../../../../../libs/ui/inputs/src'
4
- import { TranslateModule } from '@ngx-translate/core'
12
+ import { ButtonComponent, CheckToggleComponent } from '../../../../../../../libs/ui/inputs/src'
13
+ import { TranslateModule, TranslateService } from '@ngx-translate/core'
14
+ import { CatalogRecord } from '../../../../../../../libs/common/domain/src/lib/model/record'
15
+ import {
16
+ NgIconComponent,
17
+ provideIcons,
18
+ provideNgIconsConfig,
19
+ } from '@ng-icons/core'
20
+ import {
21
+ iconoirCheckCircle,
22
+ iconoirCircle,
23
+ iconoirSettings,
24
+ } from '@ng-icons/iconoir'
25
+ import { matMoreHorizOutline } from '@ng-icons/material-icons/outline'
26
+ import { EditorFacade } from '../../+state/editor.facade'
27
+ import { ConfirmationDialogComponent } from '../../../../../../../libs/ui/elements/src'
28
+ import { MatDialog } from '@angular/material/dialog'
29
+ import { RecordsRepositoryInterface } from '../../../../../../../libs/common/domain/src/lib/repository/records-repository.interface'
30
+ import { map, Subscription } from 'rxjs'
31
+ import { Overlay, OverlayRef } from '@angular/cdk/overlay'
32
+ import { TemplatePortal } from '@angular/cdk/portal'
33
+ import { ActionMenuComponent } from '../../../../../../../libs/ui/search/src'
34
+
35
+ const extraFlagMap: { [key: string]: string } = {
36
+ ar: 'arab',
37
+ en: 'gb',
38
+ ko: 'kr',
39
+ cs: 'cz',
40
+ zh: 'cn',
41
+ ca: 'es-ct',
42
+ rm: 'ch',
43
+ da: 'dk',
44
+ sv: 'se',
45
+ cy: 'gb-wls',
46
+ hy: 'am',
47
+ ka: 'ge',
48
+ uk: 'ua',
49
+ }
5
50
 
6
51
  @Component({
7
52
  selector: 'gn-ui-multilingual-panel',
8
53
  standalone: true,
9
- imports: [CommonModule, CheckToggleComponent, TranslateModule],
54
+ imports: [
55
+ CommonModule,
56
+ CheckToggleComponent,
57
+ TranslateModule,
58
+ ButtonComponent,
59
+ NgIconComponent,
60
+ ActionMenuComponent,
61
+ ],
62
+ providers: [
63
+ provideIcons({
64
+ iconoirSettings,
65
+ matMoreHorizOutline,
66
+ iconoirCheckCircle,
67
+ iconoirCircle,
68
+ }),
69
+ provideNgIconsConfig({
70
+ size: '1.25em',
71
+ }),
72
+ ],
10
73
  templateUrl: './multilingual-panel.component.html',
11
74
  styleUrl: './multilingual-panel.component.css',
12
75
  })
13
- export class MultilingualPanelComponent {
14
- translationsEnabled = false
76
+ export class MultilingualPanelComponent implements OnDestroy {
77
+ isMultilingual: boolean
78
+ _record: CatalogRecord
79
+ editTranslations: boolean
80
+ selectedLanguages = []
81
+ recordLanguages = []
82
+ formLanguage = ''
83
+ @Input() set record(value: CatalogRecord) {
84
+ this._record = value
85
+ this.isMultilingual = value.otherLanguages.length > 0
86
+ this.editTranslations = false
87
+ this.recordLanguages = [...value.otherLanguages, value.defaultLanguage]
88
+ this.selectedLanguages = this.recordLanguages
89
+ this.formLanguage = value.defaultLanguage
90
+ }
91
+ @ViewChildren('actionMenuButton', { read: ElementRef })
92
+ actionMenuButtons!: QueryList<ElementRef>
93
+ private overlayRef!: OverlayRef
94
+
95
+ isActionMenuOpen = false
96
+ subscription = new Subscription()
97
+
98
+ supportedLanguages$ = this.recordsRepository
99
+ .getApplicationLanguages()
100
+ .pipe(map((languages) => this.sortLanguages(languages)))
101
+
102
+ constructor(
103
+ public facade: EditorFacade,
104
+ public dialog: MatDialog,
105
+ private translateService: TranslateService,
106
+ private recordsRepository: RecordsRepositoryInterface,
107
+ private overlay: Overlay,
108
+ private viewContainerRef: ViewContainerRef,
109
+ private cdr: ChangeDetectorRef
110
+ ) {}
111
+
112
+ ngOnDestroy() {
113
+ this.subscription.unsubscribe()
114
+ }
115
+
116
+ sortLanguages(languages: string[]) {
117
+ return languages
118
+ .map((lang) => {
119
+ const label = this.translateService.instant('language.' + lang)
120
+ const isTranslated = label !== 'language.' + lang
121
+
122
+ return {
123
+ lang,
124
+ label,
125
+ isTranslated,
126
+ }
127
+ })
128
+ .sort((a, b) => {
129
+ if (a.isTranslated && !b.isTranslated) return -1
130
+ if (!a.isTranslated && b.isTranslated) return 1
131
+
132
+ return a.label.localeCompare(b.label)
133
+ })
134
+ .map((item) => item.lang)
135
+ }
136
+
137
+ toggleLanguageSelection() {
138
+ this.editTranslations = !this.editTranslations
139
+ }
140
+
141
+ getIconClass(lang: string) {
142
+ return extraFlagMap[lang]
143
+ ? `fi fi-${extraFlagMap[lang]} w-4 h-3`
144
+ : `fi fi-${lang} w-4 h-3`
145
+ }
146
+
147
+ switchMultilingual() {
148
+ if (this.isMultilingual && this.selectedLanguages.length > 1) {
149
+ this.confirmDeleteAction()
150
+ } else {
151
+ this.isMultilingual = true
152
+ this.editTranslations = true
153
+ }
154
+ }
155
+
156
+ getExtraClass(lang: string) {
157
+ const baseClass = 'h-[34px] w-full font-bold justify-start hover:bg-white'
158
+ if (this.selectedLanguages.includes(lang)) {
159
+ return `${baseClass} bg-white border border-black`
160
+ }
161
+ return baseClass
162
+ }
163
+
164
+ toggleLanguage(lang: string) {
165
+ if (this.selectedLanguages.includes(lang)) {
166
+ this.removeSelectedLanguage(lang)
167
+ } else {
168
+ this.selectedLanguages.push(lang)
169
+ }
170
+ }
171
+
172
+ removeSelectedLanguage(lang: string) {
173
+ this.selectedLanguages = this.selectedLanguages.filter(
174
+ (language) => language !== lang
175
+ )
176
+ }
177
+
178
+ validateTranslations() {
179
+ const equalLength =
180
+ this.selectedLanguages.length === this.recordLanguages.length
181
+ if (
182
+ this.selectedLanguages.length < this.recordLanguages.length ||
183
+ (equalLength && this.selectedLanguages !== this.recordLanguages)
184
+ ) {
185
+ this.confirmDeleteAction(this.selectedLanguages)
186
+ } else {
187
+ this.updateTranslations()
188
+ }
189
+ }
190
+
191
+ updateTranslations() {
192
+ this.facade.updateRecordField(
193
+ 'otherLanguages',
194
+ this.selectedLanguages.filter((lang) => lang !== this.formLanguage)
195
+ )
196
+ this.recordLanguages = this.selectedLanguages
197
+ this.editTranslations = false
198
+ }
199
+
200
+ switchFormLang(lang) {
201
+ // TO IMPLEMENT FURTHER
202
+ }
203
+
204
+ switchDefaultLang(lang: string) {
205
+ this.formLanguage = lang
206
+ this.facade.updateRecordField('defaultLanguage', lang)
207
+ this.facade.updateRecordField(
208
+ 'otherLanguages',
209
+ this.selectedLanguages.filter((lang) => lang !== this.formLanguage)
210
+ )
211
+ this.closeActionMenu()
212
+ }
213
+
214
+ confirmDeleteAction(lang?: string[] | string) {
215
+ const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
216
+ data: {
217
+ title: this.translateService.instant(
218
+ 'editor.record.multilingual.confirmation.title'
219
+ ),
220
+ message: this.translateService.instant(
221
+ 'editor.record.multilingual.confirmation.message'
222
+ ),
223
+ confirmText: this.translateService.instant(
224
+ 'editor.record.multilingual.confirmation.confirmText'
225
+ ),
226
+ cancelText: this.translateService.instant(
227
+ 'editor.record.multilingual.confirmation.cancelText'
228
+ ),
229
+ },
230
+ restoreFocus: true,
231
+ })
232
+ this.subscription.add(
233
+ dialogRef.afterClosed().subscribe((confirmed) => {
234
+ if (confirmed) {
235
+ if (lang) {
236
+ if (!Array.isArray(lang)) {
237
+ this.removeSelectedLanguage(lang)
238
+ this.closeActionMenu()
239
+ }
240
+ this.updateTranslations()
241
+ } else {
242
+ this.facade.updateRecordField('otherLanguages', [])
243
+ this.isMultilingual = false
244
+ this.selectedLanguages = []
245
+ }
246
+ } else {
247
+ this.isMultilingual = true
248
+ this.selectedLanguages = this.recordLanguages
249
+ }
250
+ this.editTranslations = false
251
+ })
252
+ )
253
+ }
254
+
255
+ isFirstUnsupported(index: number): boolean {
256
+ const langs = this.sortLanguages(this.recordLanguages)
257
+ return (
258
+ langs[index].length === 3 &&
259
+ langs.slice(0, index).every((lang) => lang.length !== 3)
260
+ )
261
+ }
262
+
263
+ isLangSupported(lang: string) {
264
+ return lang.length === 2
265
+ }
266
+
267
+ getToggleTitle(lang: string) {
268
+ if (lang === this._record.defaultLanguage) {
269
+ return this.translateService.instant(
270
+ 'editor.record.form.multilingual.forbidden'
271
+ )
272
+ }
273
+ return ''
274
+ }
275
+
276
+ openActionMenu(item: string, template) {
277
+ this.isActionMenuOpen = true
278
+ const index = this.sortLanguages(this.selectedLanguages).indexOf(item)
279
+ const buttonElement = this.actionMenuButtons.toArray()[index]
280
+
281
+ const positionStrategy = this.overlay
282
+ .position()
283
+ .flexibleConnectedTo(buttonElement)
284
+ .withFlexibleDimensions(true)
285
+ .withPush(true)
286
+ .withPositions([
287
+ {
288
+ originX: 'end',
289
+ originY: 'bottom',
290
+ overlayX: 'end',
291
+ overlayY: 'top',
292
+ },
293
+ {
294
+ originX: 'end',
295
+ originY: 'top',
296
+ overlayX: 'end',
297
+ overlayY: 'bottom',
298
+ },
299
+ ])
300
+
301
+ this.overlayRef = this.overlay.create({
302
+ hasBackdrop: true,
303
+ backdropClass: 'cdk-overlay-transparent-backdrop',
304
+ positionStrategy: positionStrategy,
305
+ scrollStrategy: this.overlay.scrollStrategies.reposition(),
306
+ })
307
+
308
+ const portal = new TemplatePortal(template, this.viewContainerRef)
309
+
310
+ this.overlayRef.attach(portal)
311
+ this.subscription.add(
312
+ this.overlayRef.backdropClick().subscribe(() => {
313
+ this.closeActionMenu()
314
+ })
315
+ )
316
+ }
317
+
318
+ closeActionMenu() {
319
+ if (this.overlayRef) {
320
+ this.isActionMenuOpen = false
321
+ this.overlayRef.dispose()
322
+ this.cdr.markForCheck()
323
+ }
324
+ }
15
325
  }
@@ -54,6 +54,16 @@ export const setRelated = createAction(
54
54
  props<{ related: CatalogRecord[] }>()
55
55
  )
56
56
 
57
+ export const setSources = createAction(
58
+ '[Metadata view] Set sources',
59
+ props<{ sources: CatalogRecord[] }>()
60
+ )
61
+
62
+ export const setSourceOf = createAction(
63
+ '[Metadata view] Set has sources',
64
+ props<{ sourceOf: CatalogRecord[] }>()
65
+ )
66
+
57
67
  /*
58
68
  ChartConfig actions
59
69
  */
@@ -68,6 +68,28 @@ export class MdViewEffects {
68
68
  )
69
69
  )
70
70
 
71
+ loadSources$ = createEffect(() =>
72
+ this.actions$.pipe(
73
+ ofType(MdViewActions.loadFullMetadataSuccess),
74
+ switchMap(({ full }) => this.recordsRepository.getSources(full)),
75
+ map((sources) => {
76
+ return MdViewActions.setSources({ sources })
77
+ }),
78
+ catchError(() => of(MdViewActions.setSources({ sources: null })))
79
+ )
80
+ )
81
+
82
+ loadSourceOf$ = createEffect(() =>
83
+ this.actions$.pipe(
84
+ ofType(MdViewActions.loadFullMetadataSuccess),
85
+ switchMap(({ full }) => this.recordsRepository.getSourceOf(full)),
86
+ map((sourceOf) => {
87
+ return MdViewActions.setSourceOf({ sourceOf })
88
+ }),
89
+ catchError(() => of(MdViewActions.setSourceOf({ sourceOf: null })))
90
+ )
91
+ )
92
+
71
93
  /*
72
94
  UserFeedback effects
73
95
  */
@@ -76,6 +76,10 @@ export class MdViewFacade {
76
76
 
77
77
  related$ = this.store.pipe(select(MdViewSelectors.getRelated))
78
78
 
79
+ sources$ = this.store.pipe(select(MdViewSelectors.getSources))
80
+
81
+ sourceOf$ = this.store.pipe(select(MdViewSelectors.getSourceOf))
82
+
79
83
  chartConfig$ = this.store.pipe(select(MdViewSelectors.getChartConfig))
80
84
 
81
85
  allLinks$ = this.metadata$.pipe(
@@ -14,6 +14,8 @@ export interface MetadataViewState {
14
14
  error: { notFound?: boolean; otherError?: string } | null
15
15
  metadata?: Partial<CatalogRecord>
16
16
  related?: CatalogRecord[]
17
+ sources?: CatalogRecord[]
18
+ sourceOf?: CatalogRecord[]
17
19
  userFeedbacks?: UserFeedback[]
18
20
  allUserFeedbacksLoading: boolean
19
21
  addUserFeedbackLoading: boolean
@@ -75,6 +77,16 @@ const metadataViewReducer = createReducer(
75
77
  related,
76
78
  })),
77
79
 
80
+ on(MetadataViewActions.setSources, (state, { sources }) => ({
81
+ ...state,
82
+ sources,
83
+ })),
84
+
85
+ on(MetadataViewActions.setSourceOf, (state, { sourceOf }) => ({
86
+ ...state,
87
+ sourceOf,
88
+ })),
89
+
78
90
  /*
79
91
  ChartConfig reducers
80
92
  */
@@ -41,6 +41,15 @@ export const getRelated = createSelector(
41
41
  (state: MetadataViewState) => state.related
42
42
  )
43
43
 
44
+ export const getSources = createSelector(
45
+ getMdViewState,
46
+ (state: MetadataViewState) => state.sources
47
+ )
48
+
49
+ export const getSourceOf = createSelector(
50
+ getMdViewState,
51
+ (state: MetadataViewState) => state.sourceOf
52
+ )
44
53
  /*
45
54
  Metadata selectors
46
55
  */
@@ -60,8 +60,9 @@
60
60
  </h4>
61
61
  </div>
62
62
  <div
63
- *ngIf="size === 'L'"
64
- class="mr-6 text-xs text-gray-900 line-clamp-2 overflow-hidden"
63
+ *ngIf="displayAbstract()"
64
+ class="mr-6 text-xs text-gray-900 overflow-hidden"
65
+ [ngClass]="getAbstractClass()"
65
66
  data-cy="recordAbstract"
66
67
  >
67
68
  <gn-ui-markdown-parser
@@ -71,80 +72,11 @@
71
72
  </div>
72
73
  </div>
73
74
 
74
- <div
75
- data-cy="recordOrg"
75
+ <gn-ui-internal-link-card-contact
76
76
  *ngIf="size !== 'XS' && record.ownerOrganization?.name"
77
- class="flex items-center justify-evenly bg-gray-50 rounded-lg h-[53px] px-2"
78
- >
79
- <div class="flex items-center flex-1 min-w-0">
80
- <div
81
- class="w-[45px] h-[45px] rounded-lg overflow-hidden shrink-0 mr-3"
82
- >
83
- <gn-ui-thumbnail
84
- [thumbnailUrl]="
85
- record.ownerOrganization?.logoUrl?.toString() || ''
86
- "
87
- [fit]="'contain'"
88
- class="w-full h-full rounded-lg"
89
- ></gn-ui-thumbnail>
90
- </div>
91
- <div *ngIf="organization?.name" class="flex-1 w-0 overflow-hidden">
92
- <div
93
- class="text-xs text-black font-normal leading-tight truncate"
94
- translate
95
- >
96
- record.card.metadata.contact
97
- </div>
98
- <div
99
- data-cy="recordOrgName"
100
- class="text-xl text-primary-black font-medium truncate"
101
- >
102
- {{ organization.name }}
103
- </div>
104
- </div>
105
- </div>
106
- <div *ngIf="size === 'L'" class="ml-2 flex space-x-2">
107
- <div *ngIf="organization?.website" class="flex">
108
- <button
109
- [title]="organization.website"
110
- class="w-[40px] h-[32px] flex items-center justify-center rounded-lg border border-[#D4D3D7] px-[8px] py-[4px] hover:bg-primary-lightest"
111
- (click)="openExternalUrl($event, organization.website)"
112
- >
113
- <ng-icon name="iconoirInternet"></ng-icon>
114
- </button>
115
- </div>
116
- <div *ngIf="contacts[0]?.email" class="flex">
117
- <button
118
- [title]="contacts[0].email"
119
- class="w-[40px] h-[32px] flex items-center justify-center rounded-lg border border-[#D4D3D7] px-[8px] py-[4px] hover:bg-primary-lightest"
120
- data-cy="contact-email"
121
- (click)="openMailto($event, contacts[0].email)"
122
- >
123
- <ng-icon name="matEmailOutline"></ng-icon>
124
- </button>
125
- </div>
126
- <div *ngIf="contacts[0]?.phone" class="flex">
127
- <button
128
- [title]="'Copy to clipboard'"
129
- class="w-[40px] h-[32px] flex items-center justify-center rounded-lg border border-[#D4D3D7] px-[8px] py-[4px] hover:bg-primary-lightest relative group"
130
- data-cy="contact-phone"
131
- (click)="copyToClipboard($event, contacts[0].phone)"
132
- >
133
- <ng-icon name="matPhoneOutline"></ng-icon>
134
- </button>
135
- </div>
136
- <div *ngIf="contacts[0]?.address" class="flex">
137
- <button
138
- [title]="'Copy to clipboard'"
139
- class="w-[40px] h-[32px] flex items-center justify-center rounded-lg border border-[#D4D3D7] px-[8px] py-[4px] hover:bg-primary-lightest relative group"
140
- data-cy="contact-phone"
141
- (click)="copyToClipboard($event, contacts[0].address)"
142
- >
143
- <ng-icon name="matLocationOnOutline"></ng-icon>
144
- </button>
145
- </div>
146
- </div>
147
- </div>
77
+ [record]="record"
78
+ [size]="size"
79
+ ></gn-ui-internal-link-card-contact>
148
80
  </div>
149
81
  </div>
150
82
  </div>
@@ -7,35 +7,20 @@ import {
7
7
  EventEmitter,
8
8
  ElementRef,
9
9
  } from '@angular/core'
10
- import {
11
- CatalogRecord,
12
- Organization,
13
- } from '../../../../../../libs/common/domain/src/lib/model/record'
10
+ import { CatalogRecord } from '../../../../../../libs/common/domain/src/lib/model/record'
14
11
  import { NgClass, NgIf, NgTemplateOutlet } from '@angular/common'
15
12
  import { GeoDataBadgeComponent } from '../geo-data-badge/geo-data-badge.component'
16
13
  import { KindBadgeComponent } from '../kind-badge/kind-badge.component'
17
14
  import { MarkdownParserComponent } from '../markdown-parser/markdown-parser.component'
18
15
  import { MetadataQualityComponent } from '../metadata-quality/metadata-quality.component'
19
- import { ThumbnailComponent } from '../thumbnail/thumbnail.component'
20
- import {
21
- propagateToDocumentOnly,
22
- removeWhitespace,
23
- stripHtml,
24
- } from '../../../../../../libs/util/shared/src'
25
- import {
26
- NgIconComponent,
27
- provideIcons,
28
- provideNgIconsConfig,
29
- } from '@ng-icons/core'
30
- import {
31
- matLocationSearchingOutline,
32
- matEmailOutline,
33
- matPhoneOutline,
34
- matLocationOnOutline,
35
- } from '@ng-icons/material-icons/outline'
16
+ import { InternalLinkCardContactComponent } from '../internal-link-card-contact/internal-link-card-contact.component'
17
+ import { removeWhitespace, stripHtml } from '../../../../../../libs/util/shared/src'
18
+ import { provideIcons, provideNgIconsConfig } from '@ng-icons/core'
19
+ import { matLocationSearchingOutline } from '@ng-icons/material-icons/outline'
36
20
  import { iconoirInternet } from '@ng-icons/iconoir'
37
21
  import { TranslateModule } from '@ngx-translate/core'
38
22
  import { fromEvent, Subscription } from 'rxjs'
23
+ import { ThumbnailComponent } from '../thumbnail/thumbnail.component'
39
24
 
40
25
  type CardSize = 'L' | 'M' | 'S' | 'XS'
41
26
 
@@ -45,22 +30,19 @@ type CardSize = 'L' | 'M' | 'S' | 'XS'
45
30
  imports: [
46
31
  NgClass,
47
32
  NgIf,
48
- ThumbnailComponent,
49
33
  MetadataQualityComponent,
50
34
  NgTemplateOutlet,
51
- NgIconComponent,
52
35
  TranslateModule,
53
36
  GeoDataBadgeComponent,
54
37
  KindBadgeComponent,
55
38
  MarkdownParserComponent,
39
+ InternalLinkCardContactComponent,
40
+ ThumbnailComponent,
56
41
  ],
57
42
  providers: [
58
43
  provideIcons({
59
44
  iconoirInternet,
60
45
  matLocationSearchingOutline,
61
- matEmailOutline,
62
- matPhoneOutline,
63
- matLocationOnOutline,
64
46
  }),
65
47
  provideNgIconsConfig({
66
48
  size: '1.2em',
@@ -125,35 +107,22 @@ export class InternalLinkCardComponent implements OnInit {
125
107
  )
126
108
  }
127
109
 
128
- get organization(): Organization {
129
- return this.record.ownerOrganization
130
- }
131
-
132
- get contacts() {
133
- return (
134
- (this.record.kind === 'dataset'
135
- ? this.record.contactsForResource
136
- : this.record.contacts) || []
137
- )
138
- }
139
-
140
110
  getTitleClass() {
141
111
  return this.titleClassMap[this._size]
142
112
  }
143
-
144
- openExternalUrl(event: Event, url: URL): void {
145
- event.stopPropagation()
146
- window.open(url, '_blank')
147
- }
148
-
149
- openMailto(event: Event, email: string): void {
150
- event.stopPropagation()
151
- window.open(`mailto:${email}`, '_blank')
113
+ getAbstractClass(): string {
114
+ const marginClass = ['S', 'XS'].includes(this.size) ? 'ml-2' : ''
115
+ const clampClass =
116
+ this.size === 'L' && !this.record.ownerOrganization?.name
117
+ ? 'line-clamp-6'
118
+ : 'line-clamp-2'
119
+ return `${clampClass} ${marginClass}`.trim()
152
120
  }
153
-
154
- copyToClipboard(event: Event, text: string): void {
155
- event.stopPropagation()
156
- navigator.clipboard.writeText(text)
121
+ displayAbstract(): boolean {
122
+ return (
123
+ this.size === 'L' ||
124
+ (['M', 'S'].includes(this.size) && !this.record.ownerOrganization?.name)
125
+ )
157
126
  }
158
127
 
159
128
  get shouldShowThumbnail(): boolean {