geonetwork-ui 2.10.0-dev.91673b3ba → 2.10.0-dev.981f59a75
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 +1536 -6196
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/index.d.ts +257 -182
- package/index.d.ts.map +1 -1
- package/package.json +25 -35
- package/src/libs/api/metadata-converter/src/lib/dcat-ap/dcat-ap.converter.ts +9 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/eu.dcat-ap.records.ts +2 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/generic.records.ts +1 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.reuse+ongules.ts +7 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.reuse+roilaye.ts +1 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.ts +1 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/geocat-ch.records.ts +1 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/georhena.records.ts +1 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/metadata-for-i18n.records.ts +2 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/metawal.records.ts +1 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/opendataswiss.records.ts +1 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/sextant.records.ts +2 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/vlaanderen.dcat-ap.records.ts +1 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/wallonie.records.reuse.ts +8 -0
- package/src/libs/api/metadata-converter/src/lib/gn4/atomic-operations.ts +48 -7
- package/src/libs/api/metadata-converter/src/lib/gn4/gn4.converter.ts +1 -0
- package/src/libs/api/metadata-converter/src/lib/gn4/gn4.field.mapper.ts +6 -5
- package/src/libs/api/metadata-converter/src/lib/gn4/metadata-url.service.ts +2 -2
- package/src/libs/api/metadata-converter/src/lib/gn4/types/metadata.model.ts +1 -0
- package/src/libs/api/metadata-converter/src/lib/iso19115-3/iso19115-3.converter.ts +7 -0
- package/src/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.ts +8 -0
- package/src/libs/api/metadata-converter/src/lib/iso19115-3/write-parts.ts +12 -0
- package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +14 -0
- package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +33 -0
- package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +36 -0
- package/src/libs/api/repository/src/lib/gn4/auth/auth.service.ts +8 -4
- package/src/libs/api/repository/src/lib/gn4/elasticsearch/elasticsearch.service.ts +2 -2
- package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +59 -37
- package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-groups.service.ts +1 -1
- package/src/libs/api/repository/src/lib/gn4/organizations/organizations-from-metadata.service.ts +1 -1
- package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +37 -2
- package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +13 -5
- package/src/libs/common/domain/src/lib/model/thesaurus/thesaurus.model.ts +2 -1
- package/src/libs/common/domain/src/lib/model/user/group.model.ts +8 -0
- package/src/libs/common/domain/src/lib/model/user/index.ts +1 -0
- package/src/libs/common/domain/src/lib/platform.service.interface.ts +2 -0
- package/src/libs/common/fixtures/src/lib/records.fixtures.ts +56 -0
- package/src/libs/feature/catalog/src/lib/sources/sources.service.ts +5 -2
- package/src/libs/feature/editor/src/lib/+state/editor.actions.ts +10 -1
- package/src/libs/feature/editor/src/lib/+state/editor.effects.ts +26 -19
- package/src/libs/feature/editor/src/lib/+state/editor.facade.ts +15 -2
- package/src/libs/feature/editor/src/lib/components/constraint-card/constraint-card.component.ts +0 -2
- package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.css +0 -0
- package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.html +68 -0
- package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.ts +39 -0
- package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.html +5 -14
- package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.ts +3 -28
- package/src/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.html +18 -3
- package/src/libs/feature/editor/src/lib/components/metadata-quality-panel/metadata-quality-panel.component.ts +33 -40
- package/src/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.html +5 -5
- package/src/libs/feature/editor/src/lib/components/online-service-resource-input/online-service-resource-input.component.ts +2 -1
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/field-focus.directive.ts +41 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-constraints-shortcuts/form-field-constraints-shortcuts.component.ts +33 -34
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.html +9 -2
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts/form-field-contacts.component.ts +12 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-resources/form-field-online-resources.component.ts +1 -1
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-single-link-resource/form-field-online-single-link-resource.component.css +0 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-single-link-resource/form-field-online-single-link-resource.component.html +5 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-online-single-link-resource/form-field-online-single-link-resource.component.ts +89 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.ts +12 -5
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-rich/form-field-rich.component.html +4 -4
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-rich/form-field-rich.component.ts +7 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-topics/form-field-topics.component.ts +2 -2
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-update-frequency/form-field-update-frequency.component.html +6 -2
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-update-frequency/form-field-update-frequency.component.ts +3 -1
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.css +3 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +21 -16
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +14 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/index.ts +1 -0
- package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.css +35 -0
- package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.html +18 -5
- package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.ts +118 -5
- package/src/libs/feature/editor/src/lib/expressions.ts +380 -8
- package/src/libs/feature/editor/src/lib/fields.config.ts +310 -336
- package/src/libs/feature/editor/src/lib/models/editor-config.model.ts +6 -3
- package/src/libs/feature/editor/src/lib/services/editor.service.ts +9 -13
- package/src/libs/feature/map/src/lib/utils/map-utils.service.ts +33 -0
- package/src/libs/feature/notify-reuse/src/index.ts +1 -0
- package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.css +0 -0
- package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.html +111 -0
- package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.ts +229 -0
- package/src/libs/feature/record/src/lib/map-view/map-view.component.ts +9 -1
- package/src/libs/feature/search/src/lib/results-table/results-table-container.component.ts +10 -5
- package/src/libs/ui/catalog/src/lib/language-switcher/language-switcher.component.ts +2 -2
- package/src/libs/ui/dataviz/src/index.ts +0 -1
- package/src/libs/ui/elements/src/index.ts +2 -0
- package/src/libs/ui/elements/src/lib/contact-details/contact-details.component.html +88 -0
- package/src/libs/ui/elements/src/lib/contact-details/contact-details.component.ts +45 -0
- package/src/libs/ui/elements/src/lib/contact-pill/contact-pill.component.html +29 -4
- package/src/libs/ui/elements/src/lib/contact-pill/contact-pill.component.ts +51 -7
- package/src/libs/ui/elements/src/lib/external-link-card/external-link-card.component.html +2 -1
- package/src/libs/ui/elements/src/lib/external-link-card/external-link-card.component.ts +2 -2
- package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.css +13 -0
- package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.html +13 -3
- package/src/libs/ui/elements/src/lib/image-overlay-preview/image-overlay-preview.component.ts +43 -6
- package/src/libs/ui/elements/src/lib/keyword-badge/keyword-badge.component.html +14 -0
- package/src/libs/ui/elements/src/lib/keyword-badge/keyword-badge.component.ts +85 -0
- package/src/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.ts +2 -2
- package/src/libs/ui/elements/src/lib/metadata-contact/metadata-contact.component.ts +2 -5
- package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.css +5 -0
- package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +7 -10
- package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.ts +2 -2
- package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.html +4 -5
- package/src/libs/ui/elements/src/lib/record-api-form/record-api-form.component.ts +4 -0
- package/src/libs/ui/inputs/src/lib/badge/badge.component.html +33 -26
- package/src/libs/ui/inputs/src/lib/badge/badge.component.ts +9 -0
- package/src/libs/ui/inputs/src/lib/button/button.component.ts +4 -0
- package/src/libs/ui/inputs/src/lib/date-picker/date-picker.component.ts +2 -2
- package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.html +1 -1
- package/src/libs/ui/inputs/src/lib/dropdown-multiselect/dropdown-multiselect.component.ts +3 -3
- package/src/libs/ui/inputs/src/lib/text-input/text-input.component.css +0 -3
- package/src/libs/ui/inputs/src/lib/text-input/text-input.component.html +11 -7
- package/src/libs/ui/inputs/src/lib/text-input/text-input.component.ts +56 -30
- package/src/libs/ui/inputs/src/lib/url-input/url-input.component.html +2 -2
- package/src/libs/ui/inputs/src/lib/url-input/url-input.component.ts +2 -1
- package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.html +5 -5
- package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.ts +9 -1
- package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.html +1 -1
- package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.ts +3 -8
- package/src/libs/ui/layout/src/lib/sortable-list/sortable-list.component.html +7 -7
- package/src/libs/ui/layout/src/lib/sortable-list/sortable-list.component.ts +14 -1
- package/src/libs/ui/map/src/lib/components/map-container/map-container.component.ts +2 -1
- package/src/libs/ui/map/src/lib/components/spatial-extent/spatial-extent.component.ts +15 -64
- package/src/libs/ui/map/src/lib/map-utils.ts +48 -0
- package/src/libs/ui/search/src/lib/record-preview-feed/record-preview-feed.component.ts +1 -1
- package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.html +12 -8
- package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.ts +3 -1
- package/src/libs/ui/search/src/lib/results-table/results-table.component.html +1 -0
- package/src/libs/ui/widgets/src/lib/popover/popover.component.ts +7 -1
- package/src/libs/ui/widgets/src/lib/spinning-loader/spinning-loader.component.html +1 -1
- package/src/libs/util/app-config/src/lib/app-config.ts +70 -9
- package/src/libs/util/app-config/src/lib/model.ts +7 -0
- package/src/libs/util/app-config/src/lib/parse-utils.ts +50 -1
- package/src/libs/util/data-fetcher/src/lib/readers/base-file.ts +1 -2
- package/src/libs/util/data-fetcher/src/lib/readers/wfs.ts +6 -3
- package/src/libs/util/i18n/src/index.ts +0 -1
- package/src/libs/util/i18n/src/lib/i18n.constants.ts +17 -11
- package/src/libs/util/i18n/src/lib/i18n.interceptor.ts +2 -2
- package/src/libs/util/i18n/src/lib/i18n.providers.ts +14 -17
- package/src/libs/util/i18n/src/lib/test.translate.loader.ts +48 -0
- package/src/libs/util/shared/src/lib/links/link-utils.ts +23 -1
- package/src/libs/util/shared/src/lib/record/quality-score.util.ts +33 -18
- package/src/libs/util/shared/src/lib/services/date.service.ts +3 -3
- package/src/libs/util/shared/src/lib/utils/geojson.ts +58 -1
- package/src/libs/util/shared/src/lib/utils/user-display.ts +9 -0
- package/style.css +0 -1
- package/tailwind.base.css +20 -2
- package/translations/de.json +93 -48
- package/translations/en.json +98 -53
- package/translations/es.json +67 -22
- package/translations/fr.json +95 -50
- package/translations/it.json +94 -49
- package/translations/nl.json +66 -21
- package/translations/pt.json +67 -22
- package/translations/sk.json +67 -22
- package/src/libs/feature/editor/src/lib/components/generic-keywords/generic-keywords.component.css +0 -3
|
@@ -19,9 +19,13 @@ export interface FormFieldConfig {
|
|
|
19
19
|
// This is used for instance to target only certain online resources in a field
|
|
20
20
|
type OnlineLinkResourceSpecifier = `onlineResourceType:link`
|
|
21
21
|
type DatasetDistributionsSpecifier = `onlineResourceType:!link`
|
|
22
|
+
// When set on the `contacts` field, contacts are rendered as editable detail
|
|
23
|
+
// fields (ContactDetailsFormComponent) instead of cards (ContactCardComponent)
|
|
24
|
+
type EditableContactDetailsSpecifier = `contact:editableDetails`
|
|
22
25
|
export type FieldModelSpecifier =
|
|
23
26
|
| OnlineLinkResourceSpecifier
|
|
24
27
|
| DatasetDistributionsSpecifier
|
|
28
|
+
| EditableContactDetailsSpecifier
|
|
25
29
|
|
|
26
30
|
export type FormFieldComponentName =
|
|
27
31
|
| 'form-field-constraints-shortcuts'
|
|
@@ -44,8 +48,7 @@ export interface EditorField extends EditorFieldIdentification {
|
|
|
44
48
|
gridColumnSpan?: number
|
|
45
49
|
|
|
46
50
|
// a hidden field won't show but can still be used to modify the record
|
|
47
|
-
|
|
48
|
-
hidden?: boolean
|
|
51
|
+
hidden?: boolean | EditorFieldExpression
|
|
49
52
|
|
|
50
53
|
// the result of this expression will replace the field value on save
|
|
51
54
|
onSaveProcess?: EditorFieldExpression
|
|
@@ -54,7 +57,7 @@ export interface EditorField extends EditorFieldIdentification {
|
|
|
54
57
|
export interface EditorSection {
|
|
55
58
|
labelKey?: string
|
|
56
59
|
descriptionKey?: string
|
|
57
|
-
hidden
|
|
60
|
+
hidden?: boolean | EditorFieldExpression
|
|
58
61
|
fields: EditorField[]
|
|
59
62
|
}
|
|
60
63
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { inject, Injectable } from '@angular/core'
|
|
2
|
+
import { Observable, switchMap } from 'rxjs'
|
|
3
3
|
import { map, tap } from 'rxjs/operators'
|
|
4
4
|
import { CatalogRecord } from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
5
5
|
import { EditorConfig } from '../models/'
|
|
@@ -16,7 +16,8 @@ export class EditorService {
|
|
|
16
16
|
saveRecord(
|
|
17
17
|
record: CatalogRecord,
|
|
18
18
|
recordSource: string,
|
|
19
|
-
fieldsConfig: EditorConfig
|
|
19
|
+
fieldsConfig: EditorConfig,
|
|
20
|
+
publish: boolean
|
|
20
21
|
): Observable<[CatalogRecord, string]> {
|
|
21
22
|
const savedRecord = { ...record }
|
|
22
23
|
|
|
@@ -27,22 +28,17 @@ export class EditorService {
|
|
|
27
28
|
// run onSave processes
|
|
28
29
|
for (const field of fields) {
|
|
29
30
|
if (field.onSaveProcess && field.model) {
|
|
30
|
-
const evaluator = evaluate(field.onSaveProcess)
|
|
31
|
+
const { evaluator } = evaluate(field.onSaveProcess)
|
|
31
32
|
savedRecord[field.model] = evaluator({
|
|
32
|
-
|
|
33
|
-
|
|
33
|
+
globals: {
|
|
34
|
+
record,
|
|
35
|
+
},
|
|
34
36
|
})
|
|
35
37
|
}
|
|
36
38
|
}
|
|
37
|
-
let publishToAll = true
|
|
38
|
-
// if the record is new, generate a new unique identifier and pass publishToAll as false
|
|
39
|
-
if (!record.uniqueIdentifier) {
|
|
40
|
-
savedRecord.uniqueIdentifier = null
|
|
41
|
-
publishToAll = false
|
|
42
|
-
}
|
|
43
39
|
|
|
44
40
|
return this.recordsRepository
|
|
45
|
-
.saveRecord(savedRecord, recordSource,
|
|
41
|
+
.saveRecord(savedRecord, recordSource, publish)
|
|
46
42
|
.pipe(
|
|
47
43
|
switchMap((uniqueIdentifier) =>
|
|
48
44
|
this.recordsRepository.openRecordForEdition(uniqueIdentifier)
|
|
@@ -2,6 +2,23 @@ import { Injectable } from '@angular/core'
|
|
|
2
2
|
import { extend } from 'ol/extent.js'
|
|
3
3
|
import { CatalogRecord } from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
4
4
|
import { BoundingBox, getGeometryBoundingBox } from '../../../../../../libs/util/shared/src'
|
|
5
|
+
import { MapContextLayer } from '@geospatial-sdk/core'
|
|
6
|
+
import {
|
|
7
|
+
createSpatialExtentLayer,
|
|
8
|
+
SpatialExtentLayerStyle,
|
|
9
|
+
} from '../../../../../../libs/ui/map/src'
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Style of the extent overlay drawn on top of the previewed data: a dashed
|
|
13
|
+
* black outline over a very light fill, so the extent stays readable without
|
|
14
|
+
* hiding the data underneath.
|
|
15
|
+
*/
|
|
16
|
+
const RECORD_EXTENT_OVERLAY_STYLE: SpatialExtentLayerStyle = {
|
|
17
|
+
'stroke-color': 'rgba(0, 0, 0, 0.6)',
|
|
18
|
+
'stroke-width': 2,
|
|
19
|
+
'stroke-line-dash': [8, 6],
|
|
20
|
+
'fill-color': 'rgba(0, 0, 0, 0.03)',
|
|
21
|
+
}
|
|
5
22
|
|
|
6
23
|
@Injectable({
|
|
7
24
|
providedIn: 'root',
|
|
@@ -26,4 +43,20 @@ export class MapUtilsService {
|
|
|
26
43
|
[Infinity, Infinity, -Infinity, -Infinity]
|
|
27
44
|
)
|
|
28
45
|
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Builds a non-interactive overlay layer drawing the spatial extent(s)
|
|
49
|
+
* declared in the record's metadata (bounding boxes and/or geometries).
|
|
50
|
+
* Returns null when the record has no usable spatial extent.
|
|
51
|
+
*
|
|
52
|
+
* This is purely for display and is independent from the map's initial view,
|
|
53
|
+
* which is derived separately (see {@link getRecordExtent}).
|
|
54
|
+
*/
|
|
55
|
+
getRecordExtentLayer(record: Partial<CatalogRecord>): MapContextLayer | null {
|
|
56
|
+
return createSpatialExtentLayer(record.spatialExtents ?? [], {
|
|
57
|
+
label: 'Spatial extent',
|
|
58
|
+
clickable: false,
|
|
59
|
+
style: RECORD_EXTENT_OVERLAY_STYLE,
|
|
60
|
+
})
|
|
61
|
+
}
|
|
29
62
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './lib/notify-reuse-form/notify-reuse-form.component'
|
package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.css
ADDED
|
File without changes
|
package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.html
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<gn-ui-button
|
|
2
|
+
type="outline"
|
|
3
|
+
extraClass="w-full flex flex-row justify-between"
|
|
4
|
+
[style.--gn-ui-button-rounded]="'10px'"
|
|
5
|
+
(buttonClick)="toggleOverlay()"
|
|
6
|
+
>
|
|
7
|
+
<span translate>service.metadata.reuse</span>
|
|
8
|
+
<ng-icon class="mr-[5px]" name="iconoirAppWindow"></ng-icon>
|
|
9
|
+
</gn-ui-button>
|
|
10
|
+
<ng-template #notifyReuseForm>
|
|
11
|
+
<div
|
|
12
|
+
class="w-[400px] max-w-[90vw] flex flex-col gap-3 bg-gray-50 rounded-l overflow-clip px-4 py-5 shadow-[0px_9px_12px_0px_rgba(9,30,66,0.15),0px_0px_1px_0px_rgba(9,30,66,0.31)]"
|
|
13
|
+
>
|
|
14
|
+
<div class="flex flex-col gap-3 pb-3">
|
|
15
|
+
<div class="flex items-start justify-between pb-3">
|
|
16
|
+
<p class="text-xl font-medium text-black" translate>
|
|
17
|
+
notify.reuse.form.title
|
|
18
|
+
</p>
|
|
19
|
+
<gn-ui-button
|
|
20
|
+
type="gray-light"
|
|
21
|
+
class="shrink-0"
|
|
22
|
+
(buttonClick)="closeOverlay()"
|
|
23
|
+
[style.--gn-ui-button-padding]="0"
|
|
24
|
+
[style.--gn-ui-button-width]="'21px'"
|
|
25
|
+
[style.--gn-ui-button-height]="'21px'"
|
|
26
|
+
aria-label="Close"
|
|
27
|
+
>
|
|
28
|
+
<ng-icon
|
|
29
|
+
class="text-[22px] !w-[21px] !h-[21px]"
|
|
30
|
+
name="matCloseOutline"
|
|
31
|
+
></ng-icon>
|
|
32
|
+
</gn-ui-button>
|
|
33
|
+
</div>
|
|
34
|
+
|
|
35
|
+
<div class="flex flex-col gap-[10px]">
|
|
36
|
+
<div class="flex flex-col gap-2">
|
|
37
|
+
<div class="flex items-center">
|
|
38
|
+
<label class="font-bold text-base text-main leading-6">
|
|
39
|
+
<span translate>notify.reuse.form.field.title</span>
|
|
40
|
+
<span class="text-red-500">*</span>
|
|
41
|
+
</label>
|
|
42
|
+
</div>
|
|
43
|
+
<gn-ui-text-input
|
|
44
|
+
#titleInput
|
|
45
|
+
[(value)]="title"
|
|
46
|
+
[placeholder]="
|
|
47
|
+
'notify.reuse.form.field.title.placeholder' | translate
|
|
48
|
+
"
|
|
49
|
+
[required]="true"
|
|
50
|
+
></gn-ui-text-input>
|
|
51
|
+
</div>
|
|
52
|
+
|
|
53
|
+
<div class="flex flex-col gap-2 pt-3">
|
|
54
|
+
<div class="flex items-center">
|
|
55
|
+
<label class="font-bold text-base text-main leading-6">
|
|
56
|
+
<span translate>notify.reuse.form.field.url</span>
|
|
57
|
+
<span class="text-red-500">*</span>
|
|
58
|
+
</label>
|
|
59
|
+
</div>
|
|
60
|
+
<gn-ui-text-input
|
|
61
|
+
#urlInput
|
|
62
|
+
[(value)]="url"
|
|
63
|
+
[type]="'url'"
|
|
64
|
+
[placeholder]="
|
|
65
|
+
'notify.reuse.form.field.url.placeholder' | translate
|
|
66
|
+
"
|
|
67
|
+
[required]="true"
|
|
68
|
+
></gn-ui-text-input>
|
|
69
|
+
</div>
|
|
70
|
+
|
|
71
|
+
<div class="flex flex-col gap-2 pt-3">
|
|
72
|
+
<div class="flex items-center">
|
|
73
|
+
<label class="font-bold text-base text-main leading-6">
|
|
74
|
+
<span translate>notify.reuse.form.field.email</span>
|
|
75
|
+
<span class="text-red-500">*</span>
|
|
76
|
+
</label>
|
|
77
|
+
</div>
|
|
78
|
+
<gn-ui-text-input
|
|
79
|
+
#emailInput
|
|
80
|
+
[(value)]="email"
|
|
81
|
+
[type]="'email'"
|
|
82
|
+
[placeholder]="
|
|
83
|
+
'notify.reuse.form.field.email.placeholder' | translate
|
|
84
|
+
"
|
|
85
|
+
[required]="true"
|
|
86
|
+
></gn-ui-text-input>
|
|
87
|
+
</div>
|
|
88
|
+
</div>
|
|
89
|
+
|
|
90
|
+
<p class="font-medium text-sm text-gray-700 leading-5" translate>
|
|
91
|
+
notify.reuse.form.required.hint
|
|
92
|
+
</p>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<div class="flex flex-col">
|
|
96
|
+
<gn-ui-button
|
|
97
|
+
type="primary"
|
|
98
|
+
extraClass="w-full h-8 justify-center gap-2"
|
|
99
|
+
[disabled]="!isFormValid() || loading()"
|
|
100
|
+
(buttonClick)="submit()"
|
|
101
|
+
>
|
|
102
|
+
@if (loading()) {
|
|
103
|
+
<mat-spinner [diameter]="16"></mat-spinner>
|
|
104
|
+
} @else {
|
|
105
|
+
<span translate>notify.reuse.form.submit</span>
|
|
106
|
+
<ng-icon name="iconoirPlusCircle" size="1.5em"></ng-icon>
|
|
107
|
+
}
|
|
108
|
+
</gn-ui-button>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
</ng-template>
|
package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.ts
ADDED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Overlay,
|
|
3
|
+
OverlayModule,
|
|
4
|
+
OverlayRef,
|
|
5
|
+
ScrollStrategyOptions,
|
|
6
|
+
} from '@angular/cdk/overlay'
|
|
7
|
+
import { TemplatePortal } from '@angular/cdk/portal'
|
|
8
|
+
import { CommonModule, LocationStrategy } from '@angular/common'
|
|
9
|
+
import {
|
|
10
|
+
ChangeDetectionStrategy,
|
|
11
|
+
Component,
|
|
12
|
+
computed,
|
|
13
|
+
inject,
|
|
14
|
+
InjectionToken,
|
|
15
|
+
input,
|
|
16
|
+
OnDestroy,
|
|
17
|
+
signal,
|
|
18
|
+
TemplateRef,
|
|
19
|
+
viewChild,
|
|
20
|
+
ViewContainerRef,
|
|
21
|
+
} from '@angular/core'
|
|
22
|
+
import { toSignal } from '@angular/core/rxjs-interop'
|
|
23
|
+
import { MatProgressSpinner } from '@angular/material/progress-spinner'
|
|
24
|
+
import { marker } from '@biesbjerg/ngx-translate-extract-marker'
|
|
25
|
+
import {
|
|
26
|
+
CatalogRecord,
|
|
27
|
+
Individual,
|
|
28
|
+
OnlineLinkResource,
|
|
29
|
+
ReuseRecord,
|
|
30
|
+
} from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
31
|
+
import { PlatformServiceInterface } from '../../../../../../libs/common/domain/src/lib/platform.service.interface'
|
|
32
|
+
import { RecordsRepositoryInterface } from '../../../../../../libs/common/domain/src/lib/repository/records-repository.interface'
|
|
33
|
+
import { NotificationsService } from '../../../../../../libs/feature/notifications/src'
|
|
34
|
+
import { ButtonComponent, TextInputComponent } from '../../../../../../libs/ui/inputs/src'
|
|
35
|
+
import { NgIcon, provideIcons, provideNgIconsConfig } from '@ng-icons/core'
|
|
36
|
+
import { iconoirAppWindow, iconoirPlusCircle } from '@ng-icons/iconoir'
|
|
37
|
+
import { matCloseOutline } from '@ng-icons/material-icons/outline'
|
|
38
|
+
import {
|
|
39
|
+
TranslateDirective,
|
|
40
|
+
TranslatePipe,
|
|
41
|
+
TranslateService,
|
|
42
|
+
} from '@ngx-translate/core'
|
|
43
|
+
|
|
44
|
+
marker('notify.reuse.form.error.title')
|
|
45
|
+
marker('notify.reuse.form.error.body')
|
|
46
|
+
|
|
47
|
+
export const REUSE_FORM_URL = new InjectionToken<string>('reuseFormUrl')
|
|
48
|
+
|
|
49
|
+
@Component({
|
|
50
|
+
selector: 'gn-ui-notify-reuse-form',
|
|
51
|
+
standalone: true,
|
|
52
|
+
imports: [
|
|
53
|
+
CommonModule,
|
|
54
|
+
OverlayModule,
|
|
55
|
+
TextInputComponent,
|
|
56
|
+
ButtonComponent,
|
|
57
|
+
TranslatePipe,
|
|
58
|
+
TranslateDirective,
|
|
59
|
+
NgIcon,
|
|
60
|
+
MatProgressSpinner,
|
|
61
|
+
],
|
|
62
|
+
templateUrl: './notify-reuse-form.component.html',
|
|
63
|
+
styleUrl: './notify-reuse-form.component.css',
|
|
64
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
65
|
+
viewProviders: [
|
|
66
|
+
provideIcons({
|
|
67
|
+
iconoirAppWindow,
|
|
68
|
+
iconoirPlusCircle,
|
|
69
|
+
matCloseOutline,
|
|
70
|
+
}),
|
|
71
|
+
provideNgIconsConfig({
|
|
72
|
+
size: '1.5em',
|
|
73
|
+
}),
|
|
74
|
+
],
|
|
75
|
+
})
|
|
76
|
+
export class NotifyReuseFormComponent implements OnDestroy {
|
|
77
|
+
private overlay = inject(Overlay)
|
|
78
|
+
private viewContainerRef = inject(ViewContainerRef)
|
|
79
|
+
private scrollStrategies = inject(ScrollStrategyOptions)
|
|
80
|
+
private overlayRef: OverlayRef | null = null
|
|
81
|
+
private recordsRepository = inject(RecordsRepositoryInterface)
|
|
82
|
+
private readonly translate = inject(TranslateService)
|
|
83
|
+
private readonly notificationsService = inject(NotificationsService)
|
|
84
|
+
private readonly platformServiceInterface = inject(PlatformServiceInterface)
|
|
85
|
+
reuseFormUrl = inject(REUSE_FORM_URL, { optional: true })
|
|
86
|
+
private locationStrategy = inject(LocationStrategy)
|
|
87
|
+
|
|
88
|
+
me = toSignal(this.platformServiceInterface.getMe())
|
|
89
|
+
initialEmail = computed(() => this.me()?.email)
|
|
90
|
+
templateRef = viewChild<TemplateRef<HTMLElement>>('notifyReuseForm')
|
|
91
|
+
record = input<Partial<CatalogRecord | null>>(null)
|
|
92
|
+
|
|
93
|
+
// TODO: use a form() signal once migrated to Angular 22
|
|
94
|
+
title = signal('')
|
|
95
|
+
url = signal('')
|
|
96
|
+
email = signal(this.initialEmail() ?? '')
|
|
97
|
+
|
|
98
|
+
titleInput = viewChild<TextInputComponent>('titleInput')
|
|
99
|
+
urlInput = viewChild<TextInputComponent>('urlInput')
|
|
100
|
+
emailInput = viewChild<TextInputComponent>('emailInput')
|
|
101
|
+
|
|
102
|
+
loading = signal(false)
|
|
103
|
+
|
|
104
|
+
isFormValid = computed(
|
|
105
|
+
() =>
|
|
106
|
+
this.titleInput().isValid() &&
|
|
107
|
+
this.urlInput().isValid() &&
|
|
108
|
+
this.emailInput().isValid()
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
clearInputs() {
|
|
112
|
+
this.title.set('')
|
|
113
|
+
this.url.set('')
|
|
114
|
+
this.email.set(this.initialEmail() ?? '')
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
toggleOverlay() {
|
|
118
|
+
if (this.overlayRef) {
|
|
119
|
+
this.closeOverlay()
|
|
120
|
+
} else {
|
|
121
|
+
this.openOverlay()
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
openOverlay() {
|
|
126
|
+
const positionStrategy = this.overlay
|
|
127
|
+
.position()
|
|
128
|
+
.global()
|
|
129
|
+
.centerVertically()
|
|
130
|
+
.right('16px')
|
|
131
|
+
|
|
132
|
+
this.overlayRef = this.overlay.create({
|
|
133
|
+
positionStrategy,
|
|
134
|
+
hasBackdrop: false,
|
|
135
|
+
backdropClass: 'cdk-overlay-transparent-backdrop',
|
|
136
|
+
scrollStrategy: this.scrollStrategies.noop(),
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
this.overlayRef.attach(
|
|
140
|
+
new TemplatePortal(this.templateRef(), this.viewContainerRef)
|
|
141
|
+
)
|
|
142
|
+
this.overlayRef.backdropClick().subscribe(() => this.closeOverlay())
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
closeOverlay() {
|
|
146
|
+
this.clearInputs()
|
|
147
|
+
this.overlayRef?.dispose()
|
|
148
|
+
this.overlayRef = null
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
ngOnDestroy() {
|
|
152
|
+
this.closeOverlay()
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
submit() {
|
|
156
|
+
if (!this.isFormValid()) return
|
|
157
|
+
const onlineResource: OnlineLinkResource = {
|
|
158
|
+
type: 'link',
|
|
159
|
+
url: new URL(this.url()),
|
|
160
|
+
name: this.url(),
|
|
161
|
+
description: this.translate.instant('notify.reuse.link.description'),
|
|
162
|
+
}
|
|
163
|
+
const contact: Individual = {
|
|
164
|
+
email: this.email(),
|
|
165
|
+
role: 'point_of_contact',
|
|
166
|
+
}
|
|
167
|
+
const reuseRecord: ReuseRecord = {
|
|
168
|
+
uniqueIdentifier: '',
|
|
169
|
+
kind: 'reuse',
|
|
170
|
+
title: this.title(),
|
|
171
|
+
abstract: '',
|
|
172
|
+
ownerOrganization: { name: '' },
|
|
173
|
+
contacts: [contact],
|
|
174
|
+
contactsForResource: [],
|
|
175
|
+
recordUpdated: new Date(),
|
|
176
|
+
topics: [],
|
|
177
|
+
keywords: [],
|
|
178
|
+
licenses: [],
|
|
179
|
+
legalConstraints: [],
|
|
180
|
+
securityConstraints: [],
|
|
181
|
+
otherConstraints: [],
|
|
182
|
+
overviews: [],
|
|
183
|
+
defaultLanguage: 'en',
|
|
184
|
+
otherLanguages: [],
|
|
185
|
+
lineage: '',
|
|
186
|
+
reuseType: 'application',
|
|
187
|
+
sourceRecords: [
|
|
188
|
+
{
|
|
189
|
+
uuid: this.record()?.uniqueIdentifier ?? '',
|
|
190
|
+
title: this.record()?.title ?? '',
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
onlineResources: [onlineResource],
|
|
194
|
+
spatialExtents: [],
|
|
195
|
+
temporalExtents: [],
|
|
196
|
+
}
|
|
197
|
+
this.loading.set(true)
|
|
198
|
+
this.recordsRepository.saveRecord(reuseRecord, undefined, false).subscribe({
|
|
199
|
+
next: (uniqueIdentifier) => {
|
|
200
|
+
this.loading.set(false)
|
|
201
|
+
this.closeOverlay()
|
|
202
|
+
const baseUrl = `${this.reuseFormUrl ?? ''}`.replace(/\/+$/, '')
|
|
203
|
+
// Normalisation du baseHref pour éviter les doubles /
|
|
204
|
+
const baseHrefClean = this.locationStrategy
|
|
205
|
+
.getBaseHref()
|
|
206
|
+
.replace(/^\/+|\/+$/g, '')
|
|
207
|
+
const prefix = baseHrefClean ? `/${baseHrefClean}` : ''
|
|
208
|
+
const rawRedirectUrl = `${window.location.origin}${prefix}/reuse/${uniqueIdentifier}`
|
|
209
|
+
const redirect_on_leave = encodeURIComponent(rawRedirectUrl)
|
|
210
|
+
window.open(
|
|
211
|
+
`${baseUrl}/edit/${uniqueIdentifier}?redirect_on_leave=${redirect_on_leave}`,
|
|
212
|
+
'_self'
|
|
213
|
+
)
|
|
214
|
+
},
|
|
215
|
+
error: (err) => {
|
|
216
|
+
this.loading.set(false)
|
|
217
|
+
this.notificationsService.showNotification(
|
|
218
|
+
{
|
|
219
|
+
type: 'error',
|
|
220
|
+
title: this.translate.instant('notify.reuse.form.error.title'),
|
|
221
|
+
text: this.translate.instant('notify.reuse.form.error.body'),
|
|
222
|
+
},
|
|
223
|
+
undefined,
|
|
224
|
+
err
|
|
225
|
+
)
|
|
226
|
+
},
|
|
227
|
+
})
|
|
228
|
+
}
|
|
229
|
+
}
|
|
@@ -431,11 +431,19 @@ export class MapViewComponent implements AfterViewInit {
|
|
|
431
431
|
}),
|
|
432
432
|
withLatestFrom(this.mdViewFacade.metadata$),
|
|
433
433
|
map(([context, metadata]) => {
|
|
434
|
-
|
|
434
|
+
// overlay the record's declared spatial extent on top of the data layers
|
|
435
|
+
const extentLayer = this.mapUtils.getRecordExtentLayer(metadata)
|
|
436
|
+
const layers = extentLayer
|
|
437
|
+
? [...context.layers, extentLayer]
|
|
438
|
+
: context.layers
|
|
439
|
+
// the view (initial zoom) is derived from the data layer or, as a
|
|
440
|
+
// fallback, from the record extent — independently from the overlay above
|
|
441
|
+
if (context.view) return { ...context, layers }
|
|
435
442
|
const extent = this.mapUtils.getRecordExtent(metadata)
|
|
436
443
|
const view = extent ? { extent } : null
|
|
437
444
|
return {
|
|
438
445
|
...context,
|
|
446
|
+
layers,
|
|
439
447
|
view,
|
|
440
448
|
}
|
|
441
449
|
}),
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Component,
|
|
3
3
|
EventEmitter,
|
|
4
|
+
inject,
|
|
4
5
|
Input,
|
|
5
6
|
OnDestroy,
|
|
6
7
|
Output,
|
|
7
|
-
inject,
|
|
8
8
|
} from '@angular/core'
|
|
9
9
|
import { CatalogRecord } from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
10
10
|
import { SearchFacade } from '../state/search.facade'
|
|
@@ -67,6 +67,7 @@ export class ResultsTableContainerComponent implements OnDestroy {
|
|
|
67
67
|
|
|
68
68
|
async handleDeleteRecord(item: unknown) {
|
|
69
69
|
const uniqueIdentifier = (item as CatalogRecord).uniqueIdentifier
|
|
70
|
+
const recordKind = (item as CatalogRecord).kind
|
|
70
71
|
this.subscription.add(
|
|
71
72
|
this.recordsRepository.deleteRecord(uniqueIdentifier).subscribe({
|
|
72
73
|
next: () => {
|
|
@@ -76,10 +77,12 @@ export class ResultsTableContainerComponent implements OnDestroy {
|
|
|
76
77
|
{
|
|
77
78
|
type: 'success',
|
|
78
79
|
title: this.translateService.instant(
|
|
79
|
-
'editor.record.deleteSuccess.title'
|
|
80
|
+
'editor.record.deleteSuccess.title',
|
|
81
|
+
{ recordKind }
|
|
80
82
|
),
|
|
81
83
|
text: `${this.translateService.instant(
|
|
82
|
-
'editor.record.deleteSuccess.body'
|
|
84
|
+
'editor.record.deleteSuccess.body',
|
|
85
|
+
{ recordKind }
|
|
83
86
|
)}`,
|
|
84
87
|
},
|
|
85
88
|
2500
|
|
@@ -90,10 +93,12 @@ export class ResultsTableContainerComponent implements OnDestroy {
|
|
|
90
93
|
{
|
|
91
94
|
type: 'error',
|
|
92
95
|
title: this.translateService.instant(
|
|
93
|
-
'editor.record.deleteError.title'
|
|
96
|
+
'editor.record.deleteError.title',
|
|
97
|
+
{ recordKind }
|
|
94
98
|
),
|
|
95
99
|
text: `${this.translateService.instant(
|
|
96
|
-
'editor.record.deleteError.body'
|
|
100
|
+
'editor.record.deleteError.body',
|
|
101
|
+
{ recordKind }
|
|
97
102
|
)} ${error}`,
|
|
98
103
|
closeMessage: this.translateService.instant(
|
|
99
104
|
'editor.record.deleteError.closeMessage'
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Component,
|
|
1
|
+
import { Component, inject, InjectionToken } from '@angular/core'
|
|
2
2
|
import { LANGUAGE_STORAGE_KEY } from '../../../../../../libs/util/i18n/src'
|
|
3
3
|
import { TranslateService } from '@ngx-translate/core'
|
|
4
4
|
import { DropdownSelectorComponent } from '../../../../../../libs/ui/inputs/src'
|
|
@@ -26,7 +26,7 @@ export class LanguageSwitcherComponent {
|
|
|
26
26
|
)
|
|
27
27
|
|
|
28
28
|
get currentLang() {
|
|
29
|
-
return this.translate.
|
|
29
|
+
return this.translate.getCurrentLang()
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
changeLanguage(value: unknown) {
|
|
@@ -11,6 +11,7 @@ export * from './lib/image-overlay-preview/image-overlay-preview.component'
|
|
|
11
11
|
export * from './lib/kind-badge/kind-badge.component'
|
|
12
12
|
export * from './lib/external-link-card/external-link-card.component'
|
|
13
13
|
export * from './lib/markdown-editor/markdown-editor.component'
|
|
14
|
+
export * from './lib/keyword-badge/keyword-badge.component'
|
|
14
15
|
export * from './lib/markdown-parser/markdown-parser.component'
|
|
15
16
|
export * from './lib/metadata-catalog/metadata-catalog.component'
|
|
16
17
|
export * from './lib/metadata-contact/metadata-contact.component'
|
|
@@ -19,6 +20,7 @@ export * from './lib/metadata-info/metadata-info.component'
|
|
|
19
20
|
export * from './lib/metadata-quality-item/metadata-quality-item.component'
|
|
20
21
|
export * from './lib/metadata-quality/metadata-quality.component'
|
|
21
22
|
export * from './lib/contact-pill/contact-pill.component'
|
|
23
|
+
export * from './lib/contact-details/contact-details.component'
|
|
22
24
|
export * from './lib/notification/notification.component'
|
|
23
25
|
export * from './lib/record-api-form/record-api-form.component'
|
|
24
26
|
export * from './lib/thumbnail/thumbnail.component'
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
<div
|
|
2
|
+
class="bg-gray-50 text-black rounded border border-gray-200 shadow-md p-4 flex flex-col gap-2 w-full"
|
|
3
|
+
data-test="contact-details"
|
|
4
|
+
>
|
|
5
|
+
@if (displayName) {
|
|
6
|
+
<div class="flex items-center gap-2.5">
|
|
7
|
+
@if (organization?.logoUrl?.href) {
|
|
8
|
+
<div
|
|
9
|
+
class="flex items-center justify-center rounded-lg bg-white w-14 h-14 shrink-0 overflow-hidden"
|
|
10
|
+
>
|
|
11
|
+
<gn-ui-thumbnail
|
|
12
|
+
class="relative h-full w-full"
|
|
13
|
+
[thumbnailUrl]="organization.logoUrl.href"
|
|
14
|
+
fit="contain"
|
|
15
|
+
></gn-ui-thumbnail>
|
|
16
|
+
</div>
|
|
17
|
+
}
|
|
18
|
+
<span
|
|
19
|
+
class="font-title text-xl leading-tight"
|
|
20
|
+
data-test="contact-details-name"
|
|
21
|
+
>{{ displayName }}</span
|
|
22
|
+
>
|
|
23
|
+
</div>
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@if (contact.email || contact.phone) {
|
|
27
|
+
<div
|
|
28
|
+
class="grid gap-1 items-start"
|
|
29
|
+
[class.grid-cols-2]="contact.email && contact.phone"
|
|
30
|
+
>
|
|
31
|
+
@if (contact.email) {
|
|
32
|
+
<div class="bg-gray-100 rounded-lg flex items-center gap-2 px-3 py-2">
|
|
33
|
+
<ng-icon
|
|
34
|
+
class="!w-5 !h-5 !text-[20px] shrink-0 opacity-30"
|
|
35
|
+
name="matMailOutline"
|
|
36
|
+
></ng-icon>
|
|
37
|
+
<a
|
|
38
|
+
[href]="'mailto:' + contact.email"
|
|
39
|
+
class="text-sm break-all hover:underline"
|
|
40
|
+
data-test="contact-details-email"
|
|
41
|
+
>{{ contact.email }}</a
|
|
42
|
+
>
|
|
43
|
+
</div>
|
|
44
|
+
}
|
|
45
|
+
@if (contact.phone) {
|
|
46
|
+
<div class="bg-gray-100 rounded-lg flex items-center gap-2 px-3 py-2">
|
|
47
|
+
<ng-icon
|
|
48
|
+
class="!w-5 !h-5 !text-[20px] shrink-0 opacity-30"
|
|
49
|
+
name="matCallOutline"
|
|
50
|
+
></ng-icon>
|
|
51
|
+
<span class="text-sm" data-test="contact-details-phone">{{
|
|
52
|
+
contact.phone
|
|
53
|
+
}}</span>
|
|
54
|
+
</div>
|
|
55
|
+
}
|
|
56
|
+
</div>
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
@if (addressLines.length) {
|
|
60
|
+
<div class="bg-gray-100 rounded-lg flex items-start gap-2 px-3 py-2">
|
|
61
|
+
<ng-icon
|
|
62
|
+
class="!w-5 !h-5 !text-[20px] shrink-0 mt-0.5 opacity-30"
|
|
63
|
+
name="matLocationOnOutline"
|
|
64
|
+
></ng-icon>
|
|
65
|
+
<div class="flex flex-col" data-test="contact-details-address">
|
|
66
|
+
@for (line of addressLines; track line) {
|
|
67
|
+
<p class="text-sm m-0">{{ line }}</p>
|
|
68
|
+
}
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
@if (organization?.website) {
|
|
74
|
+
<div class="bg-gray-100 rounded-lg flex items-center gap-2 px-3 py-2">
|
|
75
|
+
<ng-icon
|
|
76
|
+
class="!w-5 !h-5 !text-[20px] shrink-0 opacity-30"
|
|
77
|
+
name="matOpenInNew"
|
|
78
|
+
></ng-icon>
|
|
79
|
+
<a
|
|
80
|
+
[href]="organization.website.href"
|
|
81
|
+
target="_blank"
|
|
82
|
+
class="text-sm break-all hover:underline"
|
|
83
|
+
data-test="contact-details-website"
|
|
84
|
+
>{{ organization.website.href }}</a
|
|
85
|
+
>
|
|
86
|
+
</div>
|
|
87
|
+
}
|
|
88
|
+
</div>
|