geonetwork-ui 2.10.0-dev.896c4b637 → 2.10.0-dev.91673b3ba
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 +916 -834
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/index.d.ts +22 -7
- package/index.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +14 -2
- package/src/libs/api/repository/src/lib/gn4/gn4.provider.ts +6 -1
- package/src/libs/feature/editor/src/lib/+state/editor.effects.ts +0 -1
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.html +8 -15
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.ts +6 -4
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.html +8 -7
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.ts +6 -6
- package/src/libs/ui/elements/src/index.ts +1 -0
- package/src/libs/ui/elements/src/lib/contact-pill/contact-pill.component.html +16 -0
- package/src/libs/ui/elements/src/lib/contact-pill/contact-pill.component.ts +26 -0
- package/src/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.html +1 -1
- package/src/libs/ui/elements/src/lib/internal-link-card/internal-link-card.component.ts +0 -1
- package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.css +0 -4
- package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +27 -66
- package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts +30 -10
- package/src/libs/ui/inputs/src/lib/button/button.component.ts +4 -0
- package/src/libs/ui/search/src/lib/record-preview-row/record-preview-row.component.html +0 -1
- package/src/libs/util/shared/src/lib/utils/index.ts +1 -0
- package/src/libs/util/shared/src/lib/utils/user-display.ts +23 -0
- package/tailwind.base.css +6 -0
- package/translations/de.json +1 -0
- package/translations/en.json +1 -0
- package/translations/es.json +1 -0
- package/translations/fr.json +1 -0
- package/translations/it.json +1 -0
- package/translations/nl.json +1 -0
- package/translations/pt.json +1 -0
- package/translations/sk.json +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "geonetwork-ui",
|
|
3
|
-
"version": "2.10.0-dev.
|
|
3
|
+
"version": "2.10.0-dev.91673b3ba",
|
|
4
4
|
"engines": {
|
|
5
5
|
"node": ">=20"
|
|
6
6
|
},
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
49
|
"@biesbjerg/ngx-translate-extract-marker": "~1.0.0",
|
|
50
|
-
"@camptocamp/ogc-client": "1.3.1-dev.
|
|
50
|
+
"@camptocamp/ogc-client": "1.3.1-dev.afd9c26",
|
|
51
51
|
"@geospatial-sdk/core": "0.0.5-dev.61",
|
|
52
52
|
"@geospatial-sdk/geocoding": "0.0.5-dev.61",
|
|
53
53
|
"@geospatial-sdk/legend": "0.0.5-dev.61",
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
HttpErrorResponse,
|
|
4
4
|
HttpHeaders,
|
|
5
5
|
} from '@angular/common/http'
|
|
6
|
-
import { Injectable, inject } from '@angular/core'
|
|
6
|
+
import { Injectable, InjectionToken, inject } from '@angular/core'
|
|
7
7
|
import {
|
|
8
8
|
assertValidXml,
|
|
9
9
|
findConverterForDocument,
|
|
@@ -57,6 +57,10 @@ const TEMPORARY_ID_PREFIX = 'TEMP-ID-'
|
|
|
57
57
|
|
|
58
58
|
export type RecordAsXml = string
|
|
59
59
|
|
|
60
|
+
export const DISABLE_DRAFT = new InjectionToken<boolean>('gnDisableDraft', {
|
|
61
|
+
factory: () => false,
|
|
62
|
+
})
|
|
63
|
+
|
|
60
64
|
@Injectable()
|
|
61
65
|
export class Gn4Repository implements RecordsRepositoryInterface {
|
|
62
66
|
private httpClient = inject(HttpClient)
|
|
@@ -67,6 +71,7 @@ export class Gn4Repository implements RecordsRepositoryInterface {
|
|
|
67
71
|
private platformService = inject(PlatformServiceInterface)
|
|
68
72
|
private gn4LanguagesApi = inject(LanguagesApiService)
|
|
69
73
|
private settingsService = inject(Gn4SettingsService)
|
|
74
|
+
private disableDraft = inject(DISABLE_DRAFT, { optional: true }) ?? false
|
|
70
75
|
|
|
71
76
|
_draftsChanged = new Subject<void>()
|
|
72
77
|
draftsChanged$ = this._draftsChanged.asObservable()
|
|
@@ -350,7 +355,9 @@ export class Gn4Repository implements RecordsRepositoryInterface {
|
|
|
350
355
|
openRecordForEdition(
|
|
351
356
|
uniqueIdentifier: string
|
|
352
357
|
): Observable<[CatalogRecord, string, boolean] | null> {
|
|
353
|
-
const draft$ =
|
|
358
|
+
const draft$ = this.disableDraft
|
|
359
|
+
? of(null)
|
|
360
|
+
: of(this.getRecordFromLocalStorage(uniqueIdentifier))
|
|
354
361
|
const recordAsXml$ = this.getRecordAsXml(uniqueIdentifier)
|
|
355
362
|
|
|
356
363
|
return combineLatest([draft$, recordAsXml$]).pipe(
|
|
@@ -475,6 +482,7 @@ export class Gn4Repository implements RecordsRepositoryInterface {
|
|
|
475
482
|
record: CatalogRecord,
|
|
476
483
|
referenceRecordSource?: string
|
|
477
484
|
): Observable<string> {
|
|
485
|
+
if (this.disableDraft) return of('')
|
|
478
486
|
return this.serializeRecordToXml(record, referenceRecordSource).pipe(
|
|
479
487
|
tap((recordXml) => {
|
|
480
488
|
this.saveRecordToLocalStorage(recordXml, record.uniqueIdentifier)
|
|
@@ -484,16 +492,19 @@ export class Gn4Repository implements RecordsRepositoryInterface {
|
|
|
484
492
|
}
|
|
485
493
|
|
|
486
494
|
clearRecordDraft(uniqueIdentifier: string): void {
|
|
495
|
+
if (this.disableDraft) return
|
|
487
496
|
this.removeRecordFromLocalStorage(uniqueIdentifier)
|
|
488
497
|
this._draftsChanged.next()
|
|
489
498
|
}
|
|
490
499
|
|
|
491
500
|
recordHasDraft(uniqueIdentifier: string): boolean {
|
|
501
|
+
if (this.disableDraft) return false
|
|
492
502
|
return this.getRecordFromLocalStorage(uniqueIdentifier) !== null
|
|
493
503
|
}
|
|
494
504
|
|
|
495
505
|
// generated by copilot
|
|
496
506
|
getAllDrafts(): Observable<CatalogRecord[]> {
|
|
507
|
+
if (this.disableDraft) return of([])
|
|
497
508
|
const items = { ...window.localStorage }
|
|
498
509
|
const drafts = Object.keys(items)
|
|
499
510
|
.filter((key) => key.startsWith('geonetwork-ui-draft-'))
|
|
@@ -509,6 +520,7 @@ export class Gn4Repository implements RecordsRepositoryInterface {
|
|
|
509
520
|
}
|
|
510
521
|
|
|
511
522
|
getDraftsCount(): Observable<number> {
|
|
523
|
+
if (this.disableDraft) return of(0)
|
|
512
524
|
const items = { ...window.localStorage }
|
|
513
525
|
const draftCount = Object.keys(items)
|
|
514
526
|
.filter((key) => key.startsWith('geonetwork-ui-draft-'))
|
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from './platform/gn4-platform.service'
|
|
7
7
|
import { Gn4PlatformMapper } from './platform/gn4-platform.mapper'
|
|
8
8
|
import { RecordsRepositoryInterface } from '../../../../../../libs/common/domain/src/lib/repository/records-repository.interface'
|
|
9
|
-
import { Gn4Repository } from './gn4-repository'
|
|
9
|
+
import { Gn4Repository, DISABLE_DRAFT } from './gn4-repository'
|
|
10
10
|
import { AvatarServiceInterface, GravatarService } from './auth'
|
|
11
11
|
import { OrganizationsServiceInterface } from '../../../../../../libs/common/domain/src/lib/organizations.service.interface'
|
|
12
12
|
import {
|
|
@@ -22,6 +22,7 @@ import { TranslateService } from '@ngx-translate/core'
|
|
|
22
22
|
|
|
23
23
|
interface Gn4ProvideOptions {
|
|
24
24
|
disableAuth?: boolean
|
|
25
|
+
disableDraft?: boolean
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
export function provideGn4(provideOptions?: Gn4ProvideOptions): Provider[] {
|
|
@@ -30,6 +31,10 @@ export function provideGn4(provideOptions?: Gn4ProvideOptions): Provider[] {
|
|
|
30
31
|
provide: DISABLE_AUTH,
|
|
31
32
|
useValue: provideOptions?.disableAuth,
|
|
32
33
|
},
|
|
34
|
+
{
|
|
35
|
+
provide: DISABLE_DRAFT,
|
|
36
|
+
useValue: provideOptions?.disableDraft,
|
|
37
|
+
},
|
|
33
38
|
{
|
|
34
39
|
provide: PlatformServiceInterface,
|
|
35
40
|
useClass: Gn4PlatformService,
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
} from './editor.selectors'
|
|
13
13
|
import { RecordsRepositoryInterface } from '../../../../../../libs/common/domain/src/lib/repository/records-repository.interface'
|
|
14
14
|
import { PlatformServiceInterface } from '../../../../../../libs/common/domain/src/lib/platform.service.interface'
|
|
15
|
-
|
|
16
15
|
@Injectable()
|
|
17
16
|
export class EditorEffects {
|
|
18
17
|
private actions$ = inject(Actions)
|
|
@@ -10,21 +10,14 @@
|
|
|
10
10
|
</gn-ui-autocomplete>
|
|
11
11
|
|
|
12
12
|
@if (contacts.length > 0) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
<gn-ui-
|
|
20
|
-
|
|
21
|
-
(itemsOrderChange)="handleContactsChanged($event)"
|
|
22
|
-
[elementTemplate]="contactTemplate"
|
|
23
|
-
></gn-ui-sortable-list>
|
|
24
|
-
<ng-template #contactTemplate let-contact>
|
|
25
|
-
<gn-ui-contact-card [contact]="contact"></gn-ui-contact-card>
|
|
26
|
-
</ng-template>
|
|
27
|
-
}
|
|
13
|
+
<gn-ui-sortable-list
|
|
14
|
+
[items]="contacts"
|
|
15
|
+
(itemsOrderChange)="handleContactsChanged($event)"
|
|
16
|
+
[elementTemplate]="contactTemplate"
|
|
17
|
+
></gn-ui-sortable-list>
|
|
18
|
+
<ng-template #contactTemplate let-contact>
|
|
19
|
+
<gn-ui-contact-card [contact]="contact"></gn-ui-contact-card>
|
|
20
|
+
</ng-template>
|
|
28
21
|
} @else {
|
|
29
22
|
<div
|
|
30
23
|
class="p-4 text-sm border border-primary bg-primary-lightest rounded-lg"
|
|
@@ -28,7 +28,11 @@ import { UserModel } from '../../../../../../../../../libs/common/domain/src/lib
|
|
|
28
28
|
import { PlatformServiceInterface } from '../../../../../../../../../libs/common/domain/src/lib/platform.service.interface'
|
|
29
29
|
import { OrganizationsServiceInterface } from '../../../../../../../../../libs/common/domain/src/lib/organizations.service.interface'
|
|
30
30
|
import { ContactCardComponent } from '../../../contact-card/contact-card.component'
|
|
31
|
-
import {
|
|
31
|
+
import {
|
|
32
|
+
createFuzzyFilter,
|
|
33
|
+
getIndividualDisplayName,
|
|
34
|
+
toIndividual,
|
|
35
|
+
} from '../../../../../../../../../libs/util/shared/src'
|
|
32
36
|
import { map } from 'rxjs/operators'
|
|
33
37
|
import { SortableListComponent } from '../../../../../../../../../libs/ui/layout/src'
|
|
34
38
|
|
|
@@ -117,9 +121,7 @@ export class FormFieldContactsComponent implements OnDestroy, OnChanges {
|
|
|
117
121
|
* gn-ui-autocomplete
|
|
118
122
|
*/
|
|
119
123
|
displayWithFn: (user: UserModel) => string = (user) =>
|
|
120
|
-
|
|
121
|
-
user.organisation ? `(${user.organisation})` : ''
|
|
122
|
-
}`
|
|
124
|
+
getIndividualDisplayName(toIndividual(user))
|
|
123
125
|
|
|
124
126
|
/**
|
|
125
127
|
* gn-ui-autocomplete
|
|
@@ -8,6 +8,14 @@
|
|
|
8
8
|
</gn-ui-button>
|
|
9
9
|
}
|
|
10
10
|
</div>
|
|
11
|
+
@if (value.length === 0) {
|
|
12
|
+
<div
|
|
13
|
+
class="p-4 border border-primary bg-primary-lightest rounded-lg"
|
|
14
|
+
translate
|
|
15
|
+
>
|
|
16
|
+
editor.record.form.field.contactsForResource.noContact
|
|
17
|
+
</div>
|
|
18
|
+
}
|
|
11
19
|
@if (roleSectionsToDisplay && roleSectionsToDisplay.length > 0) {
|
|
12
20
|
<div class="mt-8" data-test="displayedRoles">
|
|
13
21
|
@for (
|
|
@@ -53,12 +61,5 @@
|
|
|
53
61
|
</div>
|
|
54
62
|
}
|
|
55
63
|
</div>
|
|
56
|
-
} @else {
|
|
57
|
-
<div
|
|
58
|
-
class="p-4 border border-primary bg-primary-lightest rounded-lg"
|
|
59
|
-
translate
|
|
60
|
-
>
|
|
61
|
-
editor.record.form.field.contactsForResource.noContact
|
|
62
|
-
</div>
|
|
63
64
|
}
|
|
64
65
|
</div>
|
|
@@ -23,7 +23,11 @@ import {
|
|
|
23
23
|
AutocompleteComponent,
|
|
24
24
|
ButtonComponent,
|
|
25
25
|
} from '../../../../../../../../../libs/ui/inputs/src'
|
|
26
|
-
import {
|
|
26
|
+
import {
|
|
27
|
+
createFuzzyFilter,
|
|
28
|
+
getIndividualDisplayName,
|
|
29
|
+
toIndividual,
|
|
30
|
+
} from '../../../../../../../../../libs/util/shared/src'
|
|
27
31
|
import { TranslateDirective, TranslatePipe } from '@ngx-translate/core'
|
|
28
32
|
import {
|
|
29
33
|
debounceTime,
|
|
@@ -169,11 +173,7 @@ export class FormFieldContactsForResourceComponent
|
|
|
169
173
|
* gn-ui-autocomplete
|
|
170
174
|
*/
|
|
171
175
|
displayWithFn: (user: UserModel) => string = (user) =>
|
|
172
|
-
user
|
|
173
|
-
? `${user.name} ${user.surname} ${
|
|
174
|
-
user.organisation ? `(${user.organisation})` : ''
|
|
175
|
-
}`
|
|
176
|
-
: ``
|
|
176
|
+
getIndividualDisplayName(toIndividual(user))
|
|
177
177
|
|
|
178
178
|
/**
|
|
179
179
|
* gn-ui-autocomplete
|
|
@@ -18,6 +18,7 @@ export * from './lib/metadata-doi/metadata-doi.component'
|
|
|
18
18
|
export * from './lib/metadata-info/metadata-info.component'
|
|
19
19
|
export * from './lib/metadata-quality-item/metadata-quality-item.component'
|
|
20
20
|
export * from './lib/metadata-quality/metadata-quality.component'
|
|
21
|
+
export * from './lib/contact-pill/contact-pill.component'
|
|
21
22
|
export * from './lib/notification/notification.component'
|
|
22
23
|
export * from './lib/record-api-form/record-api-form.component'
|
|
23
24
|
export * from './lib/thumbnail/thumbnail.component'
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<gn-ui-button
|
|
2
|
+
type="primary-light"
|
|
3
|
+
extraClass="group w-full min-h-12 gap-3 justify-between py-2 pl-5 pr-4 rounded"
|
|
4
|
+
data-test="contact-pill"
|
|
5
|
+
>
|
|
6
|
+
<span
|
|
7
|
+
class="font-title font-medium text-base leading-tight truncate group-hover:text-white"
|
|
8
|
+
[title]="displayName"
|
|
9
|
+
>{{ displayName }}</span
|
|
10
|
+
>
|
|
11
|
+
<div
|
|
12
|
+
class="gn-ui-card-icon items-center justify-center w-10 h-8 group-hover:border-white group-hover:text-white"
|
|
13
|
+
>
|
|
14
|
+
<ng-icon class="!w-6 !h-6 !text-[24px]" name="matInfoOutline"></ng-icon>
|
|
15
|
+
</div>
|
|
16
|
+
</gn-ui-button>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ChangeDetectionStrategy, Component, Input } from '@angular/core'
|
|
2
|
+
import { Individual } from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
3
|
+
import { NgIcon, provideIcons } from '@ng-icons/core'
|
|
4
|
+
import { matInfoOutline } from '@ng-icons/material-icons/outline'
|
|
5
|
+
import { ButtonComponent } from '../../../../../../libs/ui/inputs/src'
|
|
6
|
+
import { getIndividualDisplayName } from '../../../../../../libs/util/shared/src'
|
|
7
|
+
|
|
8
|
+
@Component({
|
|
9
|
+
selector: 'gn-ui-contact-pill',
|
|
10
|
+
templateUrl: './contact-pill.component.html',
|
|
11
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
12
|
+
standalone: true,
|
|
13
|
+
imports: [NgIcon, ButtonComponent],
|
|
14
|
+
viewProviders: [
|
|
15
|
+
provideIcons({
|
|
16
|
+
matInfoOutline,
|
|
17
|
+
}),
|
|
18
|
+
],
|
|
19
|
+
})
|
|
20
|
+
export class ContactPillComponent {
|
|
21
|
+
@Input() contact: Individual
|
|
22
|
+
|
|
23
|
+
get displayName(): string {
|
|
24
|
+
return getIndividualDisplayName(this.contact)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -58,7 +58,6 @@ export class InternalLinkCardComponent implements OnInit {
|
|
|
58
58
|
protected elementRef = inject(ElementRef)
|
|
59
59
|
|
|
60
60
|
@Input() record: CatalogRecord
|
|
61
|
-
@Input() linkTarget = '_blank'
|
|
62
61
|
@Input() linkHref: string = null
|
|
63
62
|
@Input() metadataQualityDisplay: boolean
|
|
64
63
|
@Input() favoriteTemplate: TemplateRef<{ $implicit: CatalogRecord }>
|
|
@@ -28,11 +28,12 @@
|
|
|
28
28
|
<gn-ui-max-lines [maxLines]="7">
|
|
29
29
|
<div class="metadata-info-keywords sm:pb-4 flex flex-wrap gap-2">
|
|
30
30
|
@for (keyword of metadata.keywords; track keyword) {
|
|
31
|
-
<gn-ui-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
31
|
+
<gn-ui-button
|
|
32
|
+
type="primary-light"
|
|
33
|
+
class="inline-block opacity-70"
|
|
34
|
+
extraClass="lowercase text-sm py-1 px-2"
|
|
35
|
+
(buttonClick)="onKeywordClick(keyword)"
|
|
36
|
+
>{{ keyword.label }}</gn-ui-button
|
|
36
37
|
>
|
|
37
38
|
}
|
|
38
39
|
</div>
|
|
@@ -105,9 +106,29 @@
|
|
|
105
106
|
</div>
|
|
106
107
|
</gn-ui-expandable-panel>
|
|
107
108
|
}
|
|
109
|
+
@if (metadata.contactsForResource?.length) {
|
|
110
|
+
<gn-ui-expandable-panel
|
|
111
|
+
[title]="'record.metadata.resource.contacts' | translate"
|
|
112
|
+
data-test="contacts-panel"
|
|
113
|
+
>
|
|
114
|
+
<div class="flex flex-col gap-1 pt-3 pb-4">
|
|
115
|
+
@for (group of contactGroups; track group.role) {
|
|
116
|
+
<div class="flex flex-col gap-1 rounded bg-gray-50 py-4 px-2">
|
|
117
|
+
<p class="text-xs font-normal text-black">
|
|
118
|
+
{{ group.roleLabel | translate }}
|
|
119
|
+
</p>
|
|
120
|
+
<div class="grid gap-0.5 grid-cols-1 md:grid-cols-2">
|
|
121
|
+
@for (contact of group.contacts; track contact.email) {
|
|
122
|
+
<gn-ui-contact-pill [contact]="contact"></gn-ui-contact-pill>
|
|
123
|
+
}
|
|
124
|
+
</div>
|
|
125
|
+
</div>
|
|
126
|
+
}
|
|
127
|
+
</div>
|
|
128
|
+
</gn-ui-expandable-panel>
|
|
129
|
+
}
|
|
108
130
|
@if (
|
|
109
131
|
(metadata.kind === 'dataset' && metadata.lineage) ||
|
|
110
|
-
resourceContact ||
|
|
111
132
|
metadata.resourceCreated ||
|
|
112
133
|
metadata.resourcePublished ||
|
|
113
134
|
metadata.resourceUpdated ||
|
|
@@ -129,66 +150,6 @@
|
|
|
129
150
|
</p>
|
|
130
151
|
</div>
|
|
131
152
|
}
|
|
132
|
-
@if (resourceContact) {
|
|
133
|
-
<div
|
|
134
|
-
class="flex flex-row gap-6 mt-5 mb-8 resource-contact"
|
|
135
|
-
data-test="details-panel-resource-contact"
|
|
136
|
-
>
|
|
137
|
-
@if (resourceContact.organization?.logoUrl?.href) {
|
|
138
|
-
<div
|
|
139
|
-
class="flex items-center justify-center border-solid border border-gray-300 rounded-md bg-white h-32 overflow-hidden"
|
|
140
|
-
>
|
|
141
|
-
<gn-ui-thumbnail
|
|
142
|
-
class="relative h-full w-full"
|
|
143
|
-
[thumbnailUrl]="resourceContact.organization.logoUrl.href"
|
|
144
|
-
fit="contain"
|
|
145
|
-
></gn-ui-thumbnail>
|
|
146
|
-
</div>
|
|
147
|
-
}
|
|
148
|
-
<div class="flex flex-col gap-1">
|
|
149
|
-
<p class="text-sm font-medium" translate>record.metadata.producer</p>
|
|
150
|
-
<div
|
|
151
|
-
class="text-primary font-title text-21 mr-2 cursor-pointer hover:underline"
|
|
152
|
-
data-cy="organization-name"
|
|
153
|
-
>
|
|
154
|
-
{{ resourceContact.organization?.name }}
|
|
155
|
-
</div>
|
|
156
|
-
@if (resourceContact.organization?.website) {
|
|
157
|
-
<div>
|
|
158
|
-
<a
|
|
159
|
-
[href]="resourceContact.organization.website"
|
|
160
|
-
target="_blank"
|
|
161
|
-
class="contact-website text-primary text-sm cursor-pointer hover:underline transition-all"
|
|
162
|
-
>{{ resourceContact.organization.website }}
|
|
163
|
-
<ng-icon
|
|
164
|
-
class="!w-[12px] !h-[12px] !text-[12px] opacity-75 shrink-0"
|
|
165
|
-
name="matOpenInNew"
|
|
166
|
-
></ng-icon>
|
|
167
|
-
</a>
|
|
168
|
-
</div>
|
|
169
|
-
}
|
|
170
|
-
@if (resourceContact.email) {
|
|
171
|
-
<div class="mt-4">
|
|
172
|
-
<div class="flex">
|
|
173
|
-
<ng-icon
|
|
174
|
-
class="!w-5 !h-5 !text-[20px] opacity-75 shrink-0"
|
|
175
|
-
name="matMailOutline"
|
|
176
|
-
></ng-icon>
|
|
177
|
-
@if (resourceContact.email) {
|
|
178
|
-
<a
|
|
179
|
-
[href]="'mailto:' + resourceContact.email"
|
|
180
|
-
class="text-sm hover:underline ml-2"
|
|
181
|
-
target="_blank"
|
|
182
|
-
data-cy="contact-email"
|
|
183
|
-
>{{ resourceContact?.email }}</a
|
|
184
|
-
>
|
|
185
|
-
}
|
|
186
|
-
</div>
|
|
187
|
-
</div>
|
|
188
|
-
}
|
|
189
|
-
</div>
|
|
190
|
-
</div>
|
|
191
|
-
}
|
|
192
153
|
<div
|
|
193
154
|
class="py-6 px-6 rounded bg-gray-100 grid grid-cols-2 gap-y-6 gap-x-[20px] text-gray-700"
|
|
194
155
|
>
|
|
@@ -8,7 +8,10 @@ import {
|
|
|
8
8
|
} from '@angular/core'
|
|
9
9
|
import {
|
|
10
10
|
CatalogRecord,
|
|
11
|
+
Individual,
|
|
11
12
|
Keyword,
|
|
13
|
+
Role,
|
|
14
|
+
RoleLabels,
|
|
12
15
|
} from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
13
16
|
import { DateService, getTemporalRangeUnion } from '../../../../../../libs/util/shared/src'
|
|
14
17
|
import { MarkdownParserComponent } from '../markdown-parser/markdown-parser.component'
|
|
@@ -19,17 +22,18 @@ import {
|
|
|
19
22
|
import { TranslateDirective, TranslatePipe } from '@ngx-translate/core'
|
|
20
23
|
import {
|
|
21
24
|
BadgeComponent,
|
|
25
|
+
ButtonComponent,
|
|
22
26
|
CopyTextButtonComponent,
|
|
23
27
|
} from '../../../../../../libs/ui/inputs/src'
|
|
24
28
|
import { ContentGhostComponent } from '../content-ghost/content-ghost.component'
|
|
25
29
|
import { NgIcon, provideIcons } from '@ng-icons/core'
|
|
26
30
|
import { matOpenInNew } from '@ng-icons/material-icons/baseline'
|
|
27
31
|
import { matMailOutline } from '@ng-icons/material-icons/outline'
|
|
28
|
-
import { ThumbnailComponent } from '../thumbnail/thumbnail.component'
|
|
29
32
|
import { GnUiLinkifyDirective } from './linkify.directive'
|
|
30
33
|
import { GnUiHumanizeDateDirective } from '../../../../../../libs/util/shared/src'
|
|
31
34
|
|
|
32
35
|
import { SpatialExtentComponent } from '../../../../../../libs/ui/map/src'
|
|
36
|
+
import { ContactPillComponent } from '../contact-pill/contact-pill.component'
|
|
33
37
|
|
|
34
38
|
@Component({
|
|
35
39
|
selector: 'gn-ui-metadata-info',
|
|
@@ -42,15 +46,16 @@ import { SpatialExtentComponent } from '../../../../../../libs/ui/map/src'
|
|
|
42
46
|
TranslatePipe,
|
|
43
47
|
MarkdownParserComponent,
|
|
44
48
|
ExpandablePanelComponent,
|
|
49
|
+
ButtonComponent,
|
|
45
50
|
BadgeComponent,
|
|
46
51
|
ContentGhostComponent,
|
|
47
|
-
ThumbnailComponent,
|
|
48
52
|
MaxLinesComponent,
|
|
49
53
|
CopyTextButtonComponent,
|
|
50
54
|
NgIcon,
|
|
51
55
|
GnUiLinkifyDirective,
|
|
52
56
|
GnUiHumanizeDateDirective,
|
|
53
57
|
SpatialExtentComponent,
|
|
58
|
+
ContactPillComponent,
|
|
54
59
|
],
|
|
55
60
|
viewProviders: [
|
|
56
61
|
provideIcons({
|
|
@@ -132,18 +137,33 @@ export class MetadataInfoComponent {
|
|
|
132
137
|
return getTemporalRangeUnion(temporalExtents, this.dateService)
|
|
133
138
|
}
|
|
134
139
|
|
|
135
|
-
get shownOrganization() {
|
|
136
|
-
return this.metadata.ownerOrganization
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
get resourceContact() {
|
|
140
|
-
return this.metadata.contactsForResource?.[0]
|
|
141
|
-
}
|
|
142
|
-
|
|
143
140
|
fieldReady(propName: string) {
|
|
144
141
|
return !this.incomplete || propName in this.metadata
|
|
145
142
|
}
|
|
146
143
|
|
|
144
|
+
get contactGroups(): {
|
|
145
|
+
role: Role
|
|
146
|
+
roleLabel: string
|
|
147
|
+
contacts: Individual[]
|
|
148
|
+
}[] {
|
|
149
|
+
const groups: { role: Role; roleLabel: string; contacts: Individual[] }[] =
|
|
150
|
+
[]
|
|
151
|
+
const indexByRole = new Map<Role, number>()
|
|
152
|
+
for (const contact of this.metadata.contactsForResource ?? []) {
|
|
153
|
+
if (indexByRole.has(contact.role)) {
|
|
154
|
+
groups[indexByRole.get(contact.role)].contacts.push(contact)
|
|
155
|
+
} else {
|
|
156
|
+
indexByRole.set(contact.role, groups.length)
|
|
157
|
+
groups.push({
|
|
158
|
+
role: contact.role,
|
|
159
|
+
roleLabel: RoleLabels.get(contact.role),
|
|
160
|
+
contacts: [contact],
|
|
161
|
+
})
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
return groups
|
|
165
|
+
}
|
|
166
|
+
|
|
147
167
|
onKeywordClick(keyword: Keyword) {
|
|
148
168
|
this.keyword.emit(keyword)
|
|
149
169
|
}
|
|
@@ -26,6 +26,7 @@ export class ButtonComponent {
|
|
|
26
26
|
| 'light'
|
|
27
27
|
| 'gray'
|
|
28
28
|
| 'black'
|
|
29
|
+
| 'primary-light'
|
|
29
30
|
) {
|
|
30
31
|
// btn-classes are written in full to be picked up by tailwind
|
|
31
32
|
switch (value) {
|
|
@@ -47,6 +48,9 @@ export class ButtonComponent {
|
|
|
47
48
|
case 'black':
|
|
48
49
|
this.btnClass = 'gn-ui-btn-black'
|
|
49
50
|
break
|
|
51
|
+
case 'primary-light':
|
|
52
|
+
this.btnClass = 'gn-ui-btn-primary-light'
|
|
53
|
+
break
|
|
50
54
|
case 'default':
|
|
51
55
|
default:
|
|
52
56
|
this.btnClass = 'gn-ui-btn-default'
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { UserModel } from '../../../../../../libs/common/domain/src/lib/model/user'
|
|
2
|
+
import { Individual } from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
3
|
+
|
|
4
|
+
export function getIndividualDisplayName(individual: Individual): string {
|
|
5
|
+
const nameParts = [individual.firstName, individual.lastName]
|
|
6
|
+
.filter(Boolean)
|
|
7
|
+
.join(' ')
|
|
8
|
+
const orgPart = individual.organization?.name
|
|
9
|
+
? ` (${individual.organization.name})`
|
|
10
|
+
: ''
|
|
11
|
+
if (nameParts) return `${nameParts}${orgPart}`
|
|
12
|
+
return individual.organization?.name ?? individual.email ?? ''
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function toIndividual(user: UserModel): Individual {
|
|
16
|
+
return {
|
|
17
|
+
firstName: user.name,
|
|
18
|
+
lastName: user.surname,
|
|
19
|
+
email: user.email,
|
|
20
|
+
role: 'unspecified',
|
|
21
|
+
organization: user.organisation ? { name: user.organisation } : undefined,
|
|
22
|
+
}
|
|
23
|
+
}
|
package/tailwind.base.css
CHANGED
|
@@ -134,6 +134,12 @@
|
|
|
134
134
|
bg-white border-white focus:ring-4 focus:ring-gray-300;
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
+
.gn-ui-btn-primary-light {
|
|
138
|
+
@apply gn-ui-btn text-primary-darkest
|
|
139
|
+
bg-primary-white hover:bg-primary focus:bg-primary hover:text-white focus:text-white active:bg-primary-darker
|
|
140
|
+
border-transparent;
|
|
141
|
+
}
|
|
142
|
+
|
|
137
143
|
.gn-ui-card-s {
|
|
138
144
|
@apply min-h-[64px] w-full xs:min-w-[260px] xs:max-w-[280px] sm:min-w-[300px] sm:max-w-[500px] md:min-w-[230px];
|
|
139
145
|
}
|
package/translations/de.json
CHANGED
|
@@ -520,6 +520,7 @@
|
|
|
520
520
|
"record.metadata.quality.updateFrequency.failed": "Aktualisierungsfrequenz nicht angegeben",
|
|
521
521
|
"record.metadata.quality.updateFrequency.success": "Aktualisierungsfrequenz angegeben",
|
|
522
522
|
"record.metadata.related": "Entdecken Sie den Katalog",
|
|
523
|
+
"record.metadata.resource.contacts": "Kontakte zur Ressource",
|
|
523
524
|
"record.metadata.resourceCreated": "Erstellt",
|
|
524
525
|
"record.metadata.resourcePublished": "Veröffentlicht",
|
|
525
526
|
"record.metadata.resourceUpdated": "Zuletzt aktualisiert",
|
package/translations/en.json
CHANGED
|
@@ -520,6 +520,7 @@
|
|
|
520
520
|
"record.metadata.quality.updateFrequency.failed": "Update frequency is not specified",
|
|
521
521
|
"record.metadata.quality.updateFrequency.success": "Update frequency is specified",
|
|
522
522
|
"record.metadata.related": "Explore the catalog",
|
|
523
|
+
"record.metadata.resource.contacts": "Contacts for the resource",
|
|
523
524
|
"record.metadata.resourceCreated": "Created",
|
|
524
525
|
"record.metadata.resourcePublished": "Published",
|
|
525
526
|
"record.metadata.resourceUpdated": "Last updated",
|
package/translations/es.json
CHANGED
|
@@ -520,6 +520,7 @@
|
|
|
520
520
|
"record.metadata.quality.updateFrequency.failed": "",
|
|
521
521
|
"record.metadata.quality.updateFrequency.success": "",
|
|
522
522
|
"record.metadata.related": "",
|
|
523
|
+
"record.metadata.resource.contacts": "",
|
|
523
524
|
"record.metadata.resourceCreated": "",
|
|
524
525
|
"record.metadata.resourcePublished": "",
|
|
525
526
|
"record.metadata.resourceUpdated": "",
|
package/translations/fr.json
CHANGED
|
@@ -520,6 +520,7 @@
|
|
|
520
520
|
"record.metadata.quality.updateFrequency.failed": "La fréquence de mise à jour n'est pas renseignée",
|
|
521
521
|
"record.metadata.quality.updateFrequency.success": "La fréquence de mise à jour est renseignée",
|
|
522
522
|
"record.metadata.related": "Explorez le catalogue",
|
|
523
|
+
"record.metadata.resource.contacts": "Contacts liés à la donnée",
|
|
523
524
|
"record.metadata.resourceCreated": "Créé",
|
|
524
525
|
"record.metadata.resourcePublished": "Publié",
|
|
525
526
|
"record.metadata.resourceUpdated": "Mis à jour",
|
package/translations/it.json
CHANGED
|
@@ -520,6 +520,7 @@
|
|
|
520
520
|
"record.metadata.quality.updateFrequency.failed": "La frequenza di aggiornamento non è specificata",
|
|
521
521
|
"record.metadata.quality.updateFrequency.success": "La frequenza di aggiornamento è specificata",
|
|
522
522
|
"record.metadata.related": "Vedi anche",
|
|
523
|
+
"record.metadata.resource.contacts": "Contatti per la risorsa",
|
|
523
524
|
"record.metadata.resourceCreated": "Creato",
|
|
524
525
|
"record.metadata.resourcePublished": "Pubblicato",
|
|
525
526
|
"record.metadata.resourceUpdated": "Ultimo aggiornamento",
|
package/translations/nl.json
CHANGED
|
@@ -520,6 +520,7 @@
|
|
|
520
520
|
"record.metadata.quality.updateFrequency.failed": "",
|
|
521
521
|
"record.metadata.quality.updateFrequency.success": "",
|
|
522
522
|
"record.metadata.related": "",
|
|
523
|
+
"record.metadata.resource.contacts": "",
|
|
523
524
|
"record.metadata.resourceCreated": "",
|
|
524
525
|
"record.metadata.resourcePublished": "",
|
|
525
526
|
"record.metadata.resourceUpdated": "",
|