geonetwork-ui 2.4.0-dev.aa5f997e → 2.4.0-dev.b0bcda82
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/esm2022/libs/api/metadata-converter/src/index.mjs +1 -2
- package/esm2022/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.mjs +5 -5
- package/esm2022/libs/api/metadata-converter/src/lib/iso19139/read-parts.mjs +30 -2
- package/esm2022/libs/api/metadata-converter/src/lib/iso19139/utils/geometry.mjs +31 -0
- package/esm2022/libs/api/metadata-converter/src/lib/iso19139/write-parts.mjs +23 -1
- package/esm2022/libs/api/metadata-converter/src/lib/xml-utils.mjs +10 -3
- package/esm2022/libs/api/repository/src/lib/gn4/gn4-repository.mjs +21 -4
- package/esm2022/libs/common/domain/src/lib/model/record/contact.model.mjs +28 -1
- package/esm2022/libs/common/domain/src/lib/model/record/metadata.model.mjs +1 -1
- package/esm2022/libs/common/domain/src/lib/repository/records-repository.interface.mjs +1 -1
- package/esm2022/libs/feature/editor/src/lib/components/contact-card/contact-card.component.mjs +29 -0
- package/esm2022/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.mjs +131 -0
- package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.mjs +170 -0
- package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field-keywords/form-field-keywords.component.mjs +3 -3
- package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field-open-data/form-field-open-data.component.mjs +47 -0
- package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.mjs +21 -0
- package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field-temporal-extents/form-field-temporal-extents.component.mjs +7 -6
- package/esm2022/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.mjs +33 -9
- package/esm2022/libs/feature/editor/src/lib/components/record-form/record-form.component.mjs +3 -3
- package/esm2022/libs/feature/editor/src/lib/fields.config.mjs +30 -3
- package/esm2022/libs/feature/map/src/lib/utils/map-utils.service.mjs +10 -5
- package/esm2022/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.mjs +3 -3
- package/esm2022/libs/feature/search/src/lib/results-table/results-table-container.component.mjs +40 -7
- package/esm2022/libs/feature/search/src/lib/state/search.facade.mjs +6 -2
- package/esm2022/libs/ui/elements/src/index.mjs +2 -1
- package/esm2022/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.mjs +27 -0
- package/esm2022/libs/ui/elements/src/lib/markdown-editor/markdown-editor.component.mjs +4 -3
- package/esm2022/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.mjs +2 -2
- package/esm2022/libs/ui/elements/src/lib/metadata-info/metadata-info.component.mjs +3 -3
- package/esm2022/libs/ui/elements/src/lib/sortable-list/sortable-list.component.mjs +7 -3
- package/esm2022/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.mjs +19 -5
- package/esm2022/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.mjs +3 -3
- package/esm2022/libs/ui/layout/src/lib/interactive-table/interactive-table.component.mjs +3 -3
- package/esm2022/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.mjs +49 -11
- package/esm2022/libs/ui/search/src/lib/results-table/results-table.component.mjs +18 -6
- package/esm2022/translations/de.json +41 -16
- package/esm2022/translations/en.json +34 -9
- package/esm2022/translations/es.json +34 -9
- package/esm2022/translations/fr.json +44 -19
- package/esm2022/translations/it.json +34 -9
- package/esm2022/translations/nl.json +34 -9
- package/esm2022/translations/pt.json +34 -9
- package/fesm2022/geonetwork-ui.mjs +1134 -263
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/libs/api/metadata-converter/src/index.d.ts +0 -1
- package/libs/api/metadata-converter/src/index.d.ts.map +1 -1
- package/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.d.ts.map +1 -1
- package/libs/api/metadata-converter/src/lib/iso19139/read-parts.d.ts +8 -1
- package/libs/api/metadata-converter/src/lib/iso19139/read-parts.d.ts.map +1 -1
- package/libs/api/metadata-converter/src/lib/iso19139/utils/geometry.d.ts +5 -0
- package/libs/api/metadata-converter/src/lib/iso19139/utils/geometry.d.ts.map +1 -0
- package/libs/api/metadata-converter/src/lib/iso19139/write-parts.d.ts +3 -1
- package/libs/api/metadata-converter/src/lib/iso19139/write-parts.d.ts.map +1 -1
- package/libs/api/metadata-converter/src/lib/xml-utils.d.ts +1 -0
- package/libs/api/metadata-converter/src/lib/xml-utils.d.ts.map +1 -1
- package/libs/api/repository/src/lib/gn4/gn4-repository.d.ts +6 -1
- package/libs/api/repository/src/lib/gn4/gn4-repository.d.ts.map +1 -1
- package/libs/common/domain/src/lib/model/record/contact.model.d.ts +1 -0
- package/libs/common/domain/src/lib/model/record/contact.model.d.ts.map +1 -1
- package/libs/common/domain/src/lib/model/record/metadata.model.d.ts +2 -1
- package/libs/common/domain/src/lib/model/record/metadata.model.d.ts.map +1 -1
- package/libs/common/domain/src/lib/repository/records-repository.interface.d.ts +8 -0
- package/libs/common/domain/src/lib/repository/records-repository.interface.d.ts.map +1 -1
- package/libs/feature/editor/src/lib/components/contact-card/contact-card.component.d.ts +12 -0
- package/libs/feature/editor/src/lib/components/contact-card/contact-card.component.d.ts.map +1 -0
- package/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.d.ts +27 -0
- package/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.d.ts.map +1 -0
- package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.d.ts +47 -0
- package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.d.ts.map +1 -0
- package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-open-data/form-field-open-data.component.d.ts +17 -0
- package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-open-data/form-field-open-data.component.d.ts.map +1 -0
- package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.d.ts +11 -0
- package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.d.ts.map +1 -0
- package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-temporal-extents/form-field-temporal-extents.component.d.ts +3 -1
- package/libs/feature/editor/src/lib/components/record-form/form-field/form-field-temporal-extents/form-field-temporal-extents.component.d.ts.map +1 -1
- package/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.d.ts +9 -1
- package/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.d.ts.map +1 -1
- package/libs/feature/editor/src/lib/fields.config.d.ts +7 -0
- package/libs/feature/editor/src/lib/fields.config.d.ts.map +1 -1
- package/libs/feature/map/src/lib/utils/map-utils.service.d.ts.map +1 -1
- package/libs/feature/search/src/lib/results-table/results-table-container.component.d.ts +11 -3
- package/libs/feature/search/src/lib/results-table/results-table-container.component.d.ts.map +1 -1
- package/libs/feature/search/src/lib/state/search.facade.d.ts +1 -0
- package/libs/feature/search/src/lib/state/search.facade.d.ts.map +1 -1
- package/libs/ui/elements/src/index.d.ts +1 -0
- package/libs/ui/elements/src/index.d.ts.map +1 -1
- package/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.d.ts +18 -0
- package/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.d.ts.map +1 -0
- package/libs/ui/elements/src/lib/downloads-list/downloads-list.component.d.ts +1 -1
- package/libs/ui/elements/src/lib/record-api-form/record-api-form.component.d.ts +1 -1
- package/libs/ui/elements/src/lib/sortable-list/sortable-list.component.d.ts +4 -4
- package/libs/ui/elements/src/lib/sortable-list/sortable-list.component.d.ts.map +1 -1
- package/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.d.ts +9 -1
- package/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.d.ts.map +1 -1
- package/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.d.ts +10 -1
- package/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.d.ts.map +1 -1
- package/libs/ui/search/src/lib/results-table/results-table.component.d.ts +6 -2
- package/libs/ui/search/src/lib/results-table/results-table.component.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/libs/api/metadata-converter/src/index.ts +0 -1
- package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.ts +5 -1
- package/src/libs/api/metadata-converter/src/lib/fixtures/geocat-ch.records.ts +37 -12
- package/src/libs/api/metadata-converter/src/lib/fixtures/metawal.records.ts +5 -1
- package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +4 -2
- package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +72 -2
- package/src/libs/api/metadata-converter/src/lib/iso19139/utils/geometry.ts +39 -0
- package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +70 -1
- package/src/libs/api/metadata-converter/src/lib/xml-utils.ts +13 -5
- package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +24 -4
- package/src/libs/common/domain/src/lib/model/record/contact.model.ts +28 -0
- package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +2 -1
- package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +10 -0
- package/src/libs/feature/editor/src/lib/components/contact-card/contact-card.component.css +0 -0
- package/src/libs/feature/editor/src/lib/components/contact-card/contact-card.component.html +25 -0
- package/src/libs/feature/editor/src/lib/components/contact-card/contact-card.component.ts +30 -0
- package/src/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.html +2 -1
- package/src/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.ts +110 -19
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.css +0 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.html +76 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.ts +271 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-keywords/form-field-keywords.component.html +1 -1
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-open-data/form-field-open-data.component.css +0 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-open-data/form-field-open-data.component.html +6 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-open-data/form-field-open-data.component.ts +59 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.css +0 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.html +5 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-overviews/form-field-overviews.component.ts +22 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field-temporal-extents/form-field-temporal-extents.component.ts +8 -7
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +18 -1
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +30 -1
- package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.html +1 -1
- package/src/libs/feature/editor/src/lib/fields.config.ts +32 -2
- package/src/libs/feature/map/src/lib/utils/map-utils.service.ts +8 -4
- package/src/libs/feature/search/src/lib/fuzzy-search/fuzzy-search.component.html +1 -1
- package/src/libs/feature/search/src/lib/results-table/results-table-container.component.html +2 -1
- package/src/libs/feature/search/src/lib/results-table/results-table-container.component.ts +52 -3
- package/src/libs/feature/search/src/lib/state/search.facade.ts +6 -0
- package/src/libs/ui/elements/src/index.ts +1 -0
- package/src/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.css +0 -0
- package/src/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.html +12 -0
- package/src/libs/ui/elements/src/lib/confirmation-dialog/confirmation-dialog.component.ts +37 -0
- package/src/libs/ui/elements/src/lib/markdown-editor/markdown-editor.component.html +4 -1
- package/src/libs/ui/elements/src/lib/markdown-parser/markdown-parser.component.css +2 -1
- package/src/libs/ui/elements/src/lib/metadata-info/metadata-info.component.html +12 -8
- package/src/libs/ui/elements/src/lib/sortable-list/sortable-list.component.html +3 -1
- package/src/libs/ui/elements/src/lib/sortable-list/sortable-list.component.ts +8 -4
- package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts +15 -3
- package/src/libs/ui/layout/src/lib/form-field-wrapper/form-field-wrapper.component.html +1 -1
- package/src/libs/ui/layout/src/lib/interactive-table/interactive-table.component.html +1 -0
- package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.html +10 -1
- package/src/libs/ui/search/src/lib/results-table/action-menu/action-menu.component.ts +55 -3
- package/src/libs/ui/search/src/lib/results-table/results-table.component.html +7 -2
- package/src/libs/ui/search/src/lib/results-table/results-table.component.ts +9 -9
- package/translations/de.json +41 -16
- package/translations/en.json +34 -9
- package/translations/es.json +34 -9
- package/translations/fr.json +44 -19
- package/translations/it.json +34 -9
- package/translations/nl.json +34 -9
- package/translations/pt.json +34 -9
- package/translations/sk.json +34 -9
|
@@ -14,7 +14,9 @@ import {
|
|
|
14
14
|
UpdateFrequencyCode,
|
|
15
15
|
UpdateFrequencyCustom,
|
|
16
16
|
} from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
17
|
+
import { ThesaurusModel } from '../../../../../../libs/common/domain/src/lib/model/thesaurus'
|
|
17
18
|
import format from 'date-fns/format'
|
|
19
|
+
import { Geometry } from 'geojson'
|
|
18
20
|
import {
|
|
19
21
|
ChainableFunction,
|
|
20
22
|
fallback,
|
|
@@ -44,8 +46,8 @@ import {
|
|
|
44
46
|
setTextContent,
|
|
45
47
|
} from '../xml-utils'
|
|
46
48
|
import { readKind } from './read-parts'
|
|
49
|
+
import { writeGeometry } from './utils/geometry'
|
|
47
50
|
import { namePartsToFull } from './utils/individual-name'
|
|
48
|
-
import { ThesaurusModel } from '../../../../../../libs/common/domain/src/lib/model/thesaurus'
|
|
49
51
|
|
|
50
52
|
export function writeCharacterString(
|
|
51
53
|
text: string
|
|
@@ -101,6 +103,14 @@ export function writeDate(
|
|
|
101
103
|
)
|
|
102
104
|
}
|
|
103
105
|
|
|
106
|
+
export function writeDecimal(
|
|
107
|
+
decimal: number
|
|
108
|
+
): ChainableFunction<XmlElement, XmlElement> {
|
|
109
|
+
return tap(
|
|
110
|
+
pipe(findChildOrCreate('gco:Decimal'), setTextContent(decimal.toString()))
|
|
111
|
+
)
|
|
112
|
+
}
|
|
113
|
+
|
|
104
114
|
export function getProgressCode(status: RecordStatus): string {
|
|
105
115
|
switch (status) {
|
|
106
116
|
case 'completed':
|
|
@@ -1184,3 +1194,62 @@ export function writeTemporalExtents(
|
|
|
1184
1194
|
)
|
|
1185
1195
|
)(rootEl)
|
|
1186
1196
|
}
|
|
1197
|
+
|
|
1198
|
+
export function writeSpatialExtents(record: DatasetRecord, rootEl: XmlElement) {
|
|
1199
|
+
const appendBoundingPolygon = (geometry?: Geometry) => {
|
|
1200
|
+
if (!geometry) return null
|
|
1201
|
+
return pipe(
|
|
1202
|
+
createElement('gmd:EX_BoundingPolygon'),
|
|
1203
|
+
appendChildren(
|
|
1204
|
+
pipe(
|
|
1205
|
+
createElement('gmd:polygon'),
|
|
1206
|
+
appendChildren(() => writeGeometry(geometry))
|
|
1207
|
+
)
|
|
1208
|
+
)
|
|
1209
|
+
)
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
const appendGeographicBoundingBox = (
|
|
1213
|
+
bbox?: [number, number, number, number]
|
|
1214
|
+
) => {
|
|
1215
|
+
if (!bbox) return null
|
|
1216
|
+
return pipe(
|
|
1217
|
+
createElement('gmd:EX_GeographicBoundingBox'),
|
|
1218
|
+
appendChildren(
|
|
1219
|
+
pipe(createElement('gmd:westBoundLongitude'), writeDecimal(bbox[0])),
|
|
1220
|
+
pipe(createElement('gmd:eastBoundLongitude'), writeDecimal(bbox[2])),
|
|
1221
|
+
pipe(createElement('gmd:southBoundLatitude'), writeDecimal(bbox[1])),
|
|
1222
|
+
pipe(createElement('gmd:northBoundLatitude'), writeDecimal(bbox[3]))
|
|
1223
|
+
)
|
|
1224
|
+
)
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
const appendGeographicDescription = (description?: string) => {
|
|
1228
|
+
if (!description) return null
|
|
1229
|
+
return pipe(
|
|
1230
|
+
createElement('gmd:EX_GeographicDescription'),
|
|
1231
|
+
createChild('gmd:geographicIdentifier'),
|
|
1232
|
+
createChild('gmd:MD_Identifier'),
|
|
1233
|
+
createChild('gmd:code'),
|
|
1234
|
+
writeCharacterString(description)
|
|
1235
|
+
)
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1238
|
+
pipe(
|
|
1239
|
+
findOrCreateIdentification(),
|
|
1240
|
+
findNestedChildOrCreate('gmd:extent', 'gmd:EX_Extent'),
|
|
1241
|
+
removeChildrenByName('gmd:geographicElement'),
|
|
1242
|
+
appendChildren(
|
|
1243
|
+
...record.spatialExtents.map((extent) =>
|
|
1244
|
+
pipe(
|
|
1245
|
+
createElement('gmd:geographicElement'),
|
|
1246
|
+
appendChildren(
|
|
1247
|
+
appendBoundingPolygon(extent.geometry),
|
|
1248
|
+
appendGeographicBoundingBox(extent.bbox),
|
|
1249
|
+
appendGeographicDescription(extent.description)
|
|
1250
|
+
)
|
|
1251
|
+
)
|
|
1252
|
+
)
|
|
1253
|
+
)
|
|
1254
|
+
)(rootEl)
|
|
1255
|
+
}
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
XmlText,
|
|
7
7
|
} from '@rgrove/parse-xml'
|
|
8
8
|
import { ChainableFunction, fallback } from './function-utils'
|
|
9
|
+
|
|
9
10
|
export { XmlDocument, XmlElement } from '@rgrove/parse-xml'
|
|
10
11
|
|
|
11
12
|
export class XmlParseError extends Error {
|
|
@@ -115,6 +116,10 @@ export function allChildrenElement(element: XmlElement): Array<XmlElement> {
|
|
|
115
116
|
] as Array<XmlElement>
|
|
116
117
|
}
|
|
117
118
|
|
|
119
|
+
export function firstChildElement(element: XmlElement): XmlElement {
|
|
120
|
+
return allChildrenElement(element)[0] ?? null
|
|
121
|
+
}
|
|
122
|
+
|
|
118
123
|
/**
|
|
119
124
|
* Will return all matching elements nested according to the given
|
|
120
125
|
* names (similar to a path), starting form the input element;
|
|
@@ -175,11 +180,11 @@ export function findParent(
|
|
|
175
180
|
|
|
176
181
|
export function readText(): ChainableFunction<XmlElement, string> {
|
|
177
182
|
return (el) => {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
return textNode ? textNode.text :
|
|
183
|
+
if (!el) return null
|
|
184
|
+
const textNode = Array.isArray(el.children)
|
|
185
|
+
? (el.children.find((node) => node.type === 'text') as XmlText)
|
|
186
|
+
: null
|
|
187
|
+
return textNode ? textNode.text : ''
|
|
183
188
|
}
|
|
184
189
|
}
|
|
185
190
|
|
|
@@ -228,6 +233,7 @@ export function xmlToString(
|
|
|
228
233
|
${padding}<${el.name}${attrs}/>
|
|
229
234
|
${parentPadding}`
|
|
230
235
|
}
|
|
236
|
+
|
|
231
237
|
return `
|
|
232
238
|
${padding}<${el.name}${attrs}>${children}</${el.name}>
|
|
233
239
|
${parentPadding}`
|
|
@@ -307,11 +313,13 @@ function getTreeRoot(element: XmlElement): XmlElement {
|
|
|
307
313
|
|
|
308
314
|
// stays on the parent element
|
|
309
315
|
// if the given elements are part of a subtree, will add the root of subtree
|
|
316
|
+
// will filter out falsy elements
|
|
310
317
|
export function appendChildren(
|
|
311
318
|
...childrenFns: Array<ChainableFunction<void, XmlElement>>
|
|
312
319
|
): ChainableFunction<XmlElement, XmlElement> {
|
|
313
320
|
return (element) => {
|
|
314
321
|
if (!element) return null
|
|
322
|
+
childrenFns = childrenFns.filter((fn) => fn)
|
|
315
323
|
element.children.push(...childrenFns.map((fn) => fn()).map(getTreeRoot))
|
|
316
324
|
element.children.forEach((el) => (el.parent = element))
|
|
317
325
|
return element
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
from,
|
|
10
10
|
Observable,
|
|
11
11
|
of,
|
|
12
|
+
Subject,
|
|
12
13
|
switchMap,
|
|
13
14
|
throwError,
|
|
14
15
|
} from 'rxjs'
|
|
@@ -24,7 +25,6 @@ import {
|
|
|
24
25
|
} from '../../../../../../libs/common/domain/src/lib/model/search'
|
|
25
26
|
import { catchError, map, tap } from 'rxjs/operators'
|
|
26
27
|
import {
|
|
27
|
-
BaseConverter,
|
|
28
28
|
findConverterForDocument,
|
|
29
29
|
Gn4Converter,
|
|
30
30
|
Gn4SearchResults,
|
|
@@ -33,8 +33,13 @@ import {
|
|
|
33
33
|
import { CatalogRecord } from '../../../../../../libs/common/domain/src/lib/model/record'
|
|
34
34
|
import { HttpErrorResponse } from '@angular/common/http'
|
|
35
35
|
|
|
36
|
+
const TEMPORARY_ID_PREFIX = 'TEMP-ID-'
|
|
37
|
+
|
|
36
38
|
@Injectable()
|
|
37
39
|
export class Gn4Repository implements RecordsRepositoryInterface {
|
|
40
|
+
_draftsChanged = new Subject<void>()
|
|
41
|
+
draftsChanged$ = this._draftsChanged.asObservable()
|
|
42
|
+
|
|
38
43
|
constructor(
|
|
39
44
|
private gn4SearchApi: SearchApiService,
|
|
40
45
|
private gn4SearchHelper: ElasticsearchService,
|
|
@@ -238,13 +243,14 @@ export class Gn4Repository implements RecordsRepositoryInterface {
|
|
|
238
243
|
switchMap(async (recordAsXml) => {
|
|
239
244
|
const converter = findConverterForDocument(recordAsXml)
|
|
240
245
|
const record = await converter.readRecord(recordAsXml)
|
|
241
|
-
record.uniqueIdentifier =
|
|
246
|
+
record.uniqueIdentifier = `${TEMPORARY_ID_PREFIX}${Date.now()}`
|
|
242
247
|
record.title = `${record.title} (Copy)`
|
|
243
248
|
const xml = await converter.writeRecord(record, recordAsXml)
|
|
244
249
|
window.localStorage.setItem(
|
|
245
250
|
this.getLocalStorageKeyForRecord(record.uniqueIdentifier),
|
|
246
251
|
xml
|
|
247
252
|
)
|
|
253
|
+
this._draftsChanged.next()
|
|
248
254
|
return [record, xml, false] as [CatalogRecord, string, false]
|
|
249
255
|
})
|
|
250
256
|
)
|
|
@@ -295,17 +301,26 @@ export class Gn4Repository implements RecordsRepositoryInterface {
|
|
|
295
301
|
)
|
|
296
302
|
}
|
|
297
303
|
|
|
304
|
+
deleteRecord(uniqueIdentifier: string): Observable<void> {
|
|
305
|
+
return this.gn4RecordsApi.deleteRecord(uniqueIdentifier)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
generateTemporaryId(): string {
|
|
309
|
+
return `${TEMPORARY_ID_PREFIX}${Date.now()}`
|
|
310
|
+
}
|
|
311
|
+
|
|
298
312
|
saveRecordAsDraft(
|
|
299
313
|
record: CatalogRecord,
|
|
300
314
|
referenceRecordSource?: string
|
|
301
315
|
): Observable<string> {
|
|
302
316
|
return this.serializeRecordToXml(record, referenceRecordSource).pipe(
|
|
303
|
-
tap((recordXml) =>
|
|
317
|
+
tap((recordXml) => {
|
|
304
318
|
window.localStorage.setItem(
|
|
305
319
|
this.getLocalStorageKeyForRecord(record.uniqueIdentifier),
|
|
306
320
|
recordXml
|
|
307
321
|
)
|
|
308
|
-
|
|
322
|
+
this._draftsChanged.next()
|
|
323
|
+
})
|
|
309
324
|
)
|
|
310
325
|
}
|
|
311
326
|
|
|
@@ -313,6 +328,7 @@ export class Gn4Repository implements RecordsRepositoryInterface {
|
|
|
313
328
|
window.localStorage.removeItem(
|
|
314
329
|
this.getLocalStorageKeyForRecord(uniqueIdentifier)
|
|
315
330
|
)
|
|
331
|
+
this._draftsChanged.next()
|
|
316
332
|
}
|
|
317
333
|
|
|
318
334
|
recordHasDraft(uniqueIdentifier: string): boolean {
|
|
@@ -323,6 +339,10 @@ export class Gn4Repository implements RecordsRepositoryInterface {
|
|
|
323
339
|
)
|
|
324
340
|
}
|
|
325
341
|
|
|
342
|
+
isRecordNotYetSaved(uniqueIdentifier: string): boolean {
|
|
343
|
+
return uniqueIdentifier.startsWith(TEMPORARY_ID_PREFIX)
|
|
344
|
+
}
|
|
345
|
+
|
|
326
346
|
// generated by copilot
|
|
327
347
|
getAllDrafts(): Observable<CatalogRecord[]> {
|
|
328
348
|
const items = { ...window.localStorage }
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Organization } from './organization.model'
|
|
2
|
+
import { marker } from '@biesbjerg/ngx-translate-extract-marker'
|
|
2
3
|
|
|
3
4
|
export const RoleValues = [
|
|
4
5
|
'unspecified',
|
|
@@ -24,6 +25,33 @@ export const RoleValues = [
|
|
|
24
25
|
'user', // Party who uses the resource
|
|
25
26
|
]
|
|
26
27
|
|
|
28
|
+
export const RoleLabels = new Map<Role, string>([
|
|
29
|
+
['unspecified', marker('domain.contact.role.unspecified')],
|
|
30
|
+
['other', marker('domain.contact.role.other')],
|
|
31
|
+
['author', marker('domain.contact.role.author')],
|
|
32
|
+
['collaborator', marker('domain.contact.role.collaborator')],
|
|
33
|
+
['contributor', marker('domain.contact.role.contributor')],
|
|
34
|
+
['custodian', marker('domain.contact.role.custodian')],
|
|
35
|
+
['distributor', marker('domain.contact.role.distributor')],
|
|
36
|
+
['editor', marker('domain.contact.role.editor')],
|
|
37
|
+
['funder', marker('domain.contact.role.funder')],
|
|
38
|
+
['mediator', marker('domain.contact.role.mediator')],
|
|
39
|
+
['originator', marker('domain.contact.role.originator')],
|
|
40
|
+
['owner', marker('domain.contact.role.owner')],
|
|
41
|
+
['point_of_contact', marker('domain.contact.role.point_of_contact')],
|
|
42
|
+
[
|
|
43
|
+
'principal_investigator',
|
|
44
|
+
marker('domain.contact.role.principal_investigator'),
|
|
45
|
+
],
|
|
46
|
+
['processor', marker('domain.contact.role.processor')],
|
|
47
|
+
['publisher', marker('domain.contact.role.publisher')],
|
|
48
|
+
['resource_provider', marker('domain.contact.role.resource_provider')],
|
|
49
|
+
['rights_holder', marker('domain.contact.role.rights_holder')],
|
|
50
|
+
['sponsor', marker('domain.contact.role.sponsor')],
|
|
51
|
+
['stakeholder', marker('domain.contact.role.stakeholder')],
|
|
52
|
+
['user', marker('domain.contact.role.user')],
|
|
53
|
+
])
|
|
54
|
+
|
|
27
55
|
export type Role = typeof RoleValues[number]
|
|
28
56
|
|
|
29
57
|
export interface Individual {
|
|
@@ -52,6 +52,14 @@ export abstract class RecordsRepositoryInterface {
|
|
|
52
52
|
referenceRecordSource?: string
|
|
53
53
|
): Observable<string>
|
|
54
54
|
|
|
55
|
+
/**
|
|
56
|
+
* @param uniqueIdentifier
|
|
57
|
+
* @returns Observable<void> Returns when record is deleted
|
|
58
|
+
*/
|
|
59
|
+
abstract deleteRecord(uniqueIdentifier: string): Observable<void>
|
|
60
|
+
|
|
61
|
+
abstract generateTemporaryId(): string
|
|
62
|
+
|
|
55
63
|
/**
|
|
56
64
|
* @param record
|
|
57
65
|
* @param referenceRecordSource
|
|
@@ -64,7 +72,9 @@ export abstract class RecordsRepositoryInterface {
|
|
|
64
72
|
|
|
65
73
|
abstract clearRecordDraft(uniqueIdentifier: string): void
|
|
66
74
|
abstract recordHasDraft(uniqueIdentifier: string): boolean
|
|
75
|
+
abstract isRecordNotYetSaved(uniqueIdentifier: string): boolean
|
|
67
76
|
|
|
68
77
|
/** will return all pending drafts, both published and not published */
|
|
69
78
|
abstract getAllDrafts(): Observable<CatalogRecord[]>
|
|
79
|
+
abstract draftsChanged$: Observable<void>
|
|
70
80
|
}
|
|
File without changes
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<div class="flex flex-row gap-4 items-center">
|
|
2
|
+
<div class="flex flex-row border border-gray-200 rounded-xl p-4 gap-4 w-full">
|
|
3
|
+
<gn-ui-thumbnail
|
|
4
|
+
class="w-[56px] h-[56px] rounded-[4px]"
|
|
5
|
+
[thumbnailUrl]="contact.organization.logoUrl?.href"
|
|
6
|
+
[fit]="'contain'"
|
|
7
|
+
></gn-ui-thumbnail>
|
|
8
|
+
<div class="flex flex-col w-full">
|
|
9
|
+
<div class="flex flex-row justify-between">
|
|
10
|
+
<span class="flex flex-wrap font-bold w-full"
|
|
11
|
+
>{{ contact.firstName }} {{ contact.lastName }}</span
|
|
12
|
+
>
|
|
13
|
+
</div>
|
|
14
|
+
<div>{{ contact.email }}</div>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
17
|
+
<gn-ui-button
|
|
18
|
+
*ngIf="removable"
|
|
19
|
+
data-test="removeContactButton"
|
|
20
|
+
type="light"
|
|
21
|
+
extraClass="w-[20px] h-[20px] flex items-center justify-center"
|
|
22
|
+
(buttonClick)="removeContact(contact)"
|
|
23
|
+
><span class="material-symbols-outlined"> close </span>
|
|
24
|
+
</gn-ui-button>
|
|
25
|
+
</div>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChangeDetectionStrategy,
|
|
3
|
+
Component,
|
|
4
|
+
EventEmitter,
|
|
5
|
+
Input,
|
|
6
|
+
Output,
|
|
7
|
+
} from '@angular/core'
|
|
8
|
+
import { Individual } from '../../../../../../../libs/common/domain/src/lib/model/record'
|
|
9
|
+
import { MatIconModule } from '@angular/material/icon'
|
|
10
|
+
import { CommonModule } from '@angular/common'
|
|
11
|
+
import { ButtonComponent } from '../../../../../../../libs/ui/inputs/src'
|
|
12
|
+
import { ThumbnailComponent } from '../../../../../../../libs/ui/elements/src'
|
|
13
|
+
|
|
14
|
+
@Component({
|
|
15
|
+
selector: 'gn-ui-contact-card',
|
|
16
|
+
templateUrl: './contact-card.component.html',
|
|
17
|
+
styleUrls: ['./contact-card.component.css'],
|
|
18
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
19
|
+
standalone: true,
|
|
20
|
+
imports: [CommonModule, MatIconModule, ButtonComponent, ThumbnailComponent],
|
|
21
|
+
})
|
|
22
|
+
export class ContactCardComponent {
|
|
23
|
+
@Input() contact: Individual
|
|
24
|
+
@Input() removable = true
|
|
25
|
+
@Output() contactRemoved = new EventEmitter<Individual>()
|
|
26
|
+
|
|
27
|
+
removeContact(contact: Individual) {
|
|
28
|
+
this.contactRemoved.emit(contact)
|
|
29
|
+
}
|
|
30
|
+
}
|
package/src/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.html
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
<gn-ui-image-input
|
|
2
2
|
[maxSizeMB]="5"
|
|
3
3
|
[previewUrl]="resourceUrl"
|
|
4
|
-
[altText]="
|
|
4
|
+
[altText]="resourceAltText"
|
|
5
5
|
(fileChange)="handleFileChange($event)"
|
|
6
6
|
(urlChange)="handleUrlChange($event)"
|
|
7
|
+
(altTextChange)="handleAltTextChange($event)"
|
|
7
8
|
(delete)="handleDelete()"
|
|
8
9
|
></gn-ui-image-input>
|
package/src/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.ts
CHANGED
|
@@ -2,12 +2,18 @@ import {
|
|
|
2
2
|
ChangeDetectionStrategy,
|
|
3
3
|
ChangeDetectorRef,
|
|
4
4
|
Component,
|
|
5
|
+
EventEmitter,
|
|
5
6
|
Input,
|
|
7
|
+
OnChanges,
|
|
6
8
|
OnInit,
|
|
9
|
+
Output,
|
|
10
|
+
SimpleChanges,
|
|
7
11
|
} from '@angular/core'
|
|
8
12
|
import { CommonModule } from '@angular/common'
|
|
9
13
|
import { RecordsApiService } from '../../../../../../../libs/data-access/gn4/src'
|
|
10
14
|
import { UiInputsModule } from '../../../../../../../libs/ui/inputs/src'
|
|
15
|
+
import { FormControl } from '@angular/forms'
|
|
16
|
+
import { GraphicOverview } from '../../../../../../../libs/common/domain/src/lib/model/record'
|
|
11
17
|
|
|
12
18
|
@Component({
|
|
13
19
|
selector: 'gn-ui-overview-upload',
|
|
@@ -17,9 +23,13 @@ import { UiInputsModule } from '../../../../../../../libs/ui/inputs/src'
|
|
|
17
23
|
styleUrls: ['./overview-upload.component.css'],
|
|
18
24
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
19
25
|
})
|
|
20
|
-
export class OverviewUploadComponent implements OnInit {
|
|
26
|
+
export class OverviewUploadComponent implements OnInit, OnChanges {
|
|
21
27
|
@Input() metadataUuid: string
|
|
28
|
+
@Input() formControl!: FormControl
|
|
29
|
+
@Output() overviewChange = new EventEmitter<GraphicOverview | null>()
|
|
30
|
+
@Output() altTextChange: EventEmitter<string> = new EventEmitter()
|
|
22
31
|
|
|
32
|
+
resourceAltText: string
|
|
23
33
|
resourceFileName: string
|
|
24
34
|
resourceUrl: string
|
|
25
35
|
|
|
@@ -29,42 +39,123 @@ export class OverviewUploadComponent implements OnInit {
|
|
|
29
39
|
) {}
|
|
30
40
|
|
|
31
41
|
ngOnInit(): void {
|
|
32
|
-
this.recordsApiService
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
42
|
+
this.recordsApiService.getAllResources(this.metadataUuid).subscribe({
|
|
43
|
+
next: (resources) => {
|
|
44
|
+
if (resources && resources.length > 0) {
|
|
45
|
+
this.resourceUrl = resources[0].url
|
|
46
|
+
this.resourceFileName = resources[0].filename
|
|
47
|
+
if (!this.resourceAltText) {
|
|
48
|
+
this.resourceAltText = this.resourceFileName
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
this.resourceUrl = ''
|
|
52
|
+
this.resourceAltText = ''
|
|
53
|
+
this.resourceFileName = ''
|
|
54
|
+
}
|
|
55
|
+
|
|
37
56
|
this.cd.markForCheck()
|
|
38
|
-
}
|
|
57
|
+
},
|
|
58
|
+
error: this.errorHandle,
|
|
59
|
+
})
|
|
39
60
|
}
|
|
40
61
|
|
|
41
62
|
handleFileChange(file: File) {
|
|
42
63
|
this.recordsApiService
|
|
43
64
|
.putResource(this.metadataUuid, file, 'public')
|
|
44
|
-
.subscribe(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
65
|
+
.subscribe({
|
|
66
|
+
next: (resource) => {
|
|
67
|
+
this.resourceUrl = resource.url
|
|
68
|
+
this.resourceAltText = resource.filename
|
|
69
|
+
|
|
70
|
+
this.overviewChange.emit({
|
|
71
|
+
url: new URL(resource.url),
|
|
72
|
+
description: resource.filename,
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
this.cd.markForCheck()
|
|
76
|
+
},
|
|
77
|
+
error: this.errorHandle,
|
|
48
78
|
})
|
|
49
79
|
}
|
|
50
80
|
|
|
51
81
|
handleUrlChange(url: string) {
|
|
52
82
|
this.recordsApiService
|
|
53
83
|
.putResourceFromURL(this.metadataUuid, url, 'public')
|
|
54
|
-
.subscribe(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
84
|
+
.subscribe({
|
|
85
|
+
next: (resource) => {
|
|
86
|
+
this.resourceUrl = resource.url
|
|
87
|
+
this.resourceAltText = resource.filename
|
|
88
|
+
|
|
89
|
+
this.overviewChange.emit({
|
|
90
|
+
url: new URL(resource.url),
|
|
91
|
+
description: resource.filename,
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
this.cd.markForCheck()
|
|
95
|
+
},
|
|
96
|
+
error: this.errorHandle,
|
|
58
97
|
})
|
|
59
98
|
}
|
|
60
99
|
|
|
100
|
+
handleAltTextChange(newAltText: string) {
|
|
101
|
+
this.resourceAltText = newAltText
|
|
102
|
+
|
|
103
|
+
this.overviewChange.emit({
|
|
104
|
+
url: new URL(this.resourceUrl),
|
|
105
|
+
description: this.resourceAltText,
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
this.cd.markForCheck()
|
|
109
|
+
}
|
|
110
|
+
|
|
61
111
|
handleDelete() {
|
|
112
|
+
//this.formControl.markAsDirty()
|
|
62
113
|
this.recordsApiService
|
|
63
114
|
.delResource(this.metadataUuid, this.resourceFileName)
|
|
64
|
-
.subscribe(
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
115
|
+
.subscribe({
|
|
116
|
+
next: () => {
|
|
117
|
+
this.resourceAltText = ''
|
|
118
|
+
this.resourceUrl = ''
|
|
119
|
+
|
|
120
|
+
this.overviewChange.emit(null)
|
|
121
|
+
|
|
122
|
+
this.cd.markForCheck()
|
|
123
|
+
},
|
|
124
|
+
error: this.errorHandle,
|
|
68
125
|
})
|
|
69
126
|
}
|
|
127
|
+
|
|
128
|
+
private errorHandle = (error: never) => {
|
|
129
|
+
console.error(error)
|
|
130
|
+
|
|
131
|
+
this.resourceUrl = ''
|
|
132
|
+
this.resourceAltText = ''
|
|
133
|
+
this.resourceFileName = ''
|
|
134
|
+
|
|
135
|
+
this.overviewChange.emit(null)
|
|
136
|
+
|
|
137
|
+
this.cd.markForCheck()
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
ngOnChanges(changes: SimpleChanges): void {
|
|
141
|
+
const overviewChanges = changes['formControl']
|
|
142
|
+
if (
|
|
143
|
+
overviewChanges &&
|
|
144
|
+
overviewChanges.currentValue !== overviewChanges.previousValue
|
|
145
|
+
) {
|
|
146
|
+
let overview: GraphicOverview
|
|
147
|
+
if (
|
|
148
|
+
overviewChanges.currentValue.value &&
|
|
149
|
+
overviewChanges.currentValue.value.length > 0
|
|
150
|
+
) {
|
|
151
|
+
overview = overviewChanges.currentValue.value[0] as GraphicOverview
|
|
152
|
+
} else {
|
|
153
|
+
return
|
|
154
|
+
}
|
|
155
|
+
if (overview.description) {
|
|
156
|
+
this.resourceAltText = overview.description
|
|
157
|
+
this.cd.markForCheck()
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
70
161
|
}
|
|
File without changes
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<div class="flex flex-col gap-3">
|
|
2
|
+
<div class="flex flex-row flex-wrap gap-2" data-test="rolesToPick">
|
|
3
|
+
<ng-container *ngFor="let role of rolesToPick">
|
|
4
|
+
<gn-ui-button
|
|
5
|
+
extraClass="px-2 py-1.5"
|
|
6
|
+
(buttonClick)="addRoleToDisplay(role)"
|
|
7
|
+
>
|
|
8
|
+
<div class="flex flex-row gap-1 items-center">
|
|
9
|
+
<span class="text-primary text-[20px] leading-[0] font-bold pb-[5px]"
|
|
10
|
+
>₊</span
|
|
11
|
+
>
|
|
12
|
+
<span class="font-bold" translate>{{ roleToLabel(role) }}</span>
|
|
13
|
+
</div>
|
|
14
|
+
</gn-ui-button>
|
|
15
|
+
</ng-container>
|
|
16
|
+
</div>
|
|
17
|
+
<div
|
|
18
|
+
class="mt-8"
|
|
19
|
+
*ngIf="
|
|
20
|
+
roleSectionsToDisplay && roleSectionsToDisplay.length > 0;
|
|
21
|
+
else noContact
|
|
22
|
+
"
|
|
23
|
+
data-test="displayedRoles"
|
|
24
|
+
>
|
|
25
|
+
<div
|
|
26
|
+
*ngFor="
|
|
27
|
+
let role of roleSectionsToDisplay;
|
|
28
|
+
let index = index;
|
|
29
|
+
let isLast = last
|
|
30
|
+
"
|
|
31
|
+
class="flex flex-col gap-4"
|
|
32
|
+
>
|
|
33
|
+
<div class="flex flex-row justify-between">
|
|
34
|
+
<span class="font-bold text-base" translate>{{
|
|
35
|
+
roleToLabel(role)
|
|
36
|
+
}}</span>
|
|
37
|
+
</div>
|
|
38
|
+
|
|
39
|
+
<gn-ui-autocomplete
|
|
40
|
+
[placeholder]="'Choose a contact'"
|
|
41
|
+
[action]="autoCompleteAction"
|
|
42
|
+
(itemSelected)="addContact($event, role)"
|
|
43
|
+
[displayWithFn]="displayWithFn"
|
|
44
|
+
[minCharacterCount]="1"
|
|
45
|
+
[clearOnSelection]="true"
|
|
46
|
+
>
|
|
47
|
+
</gn-ui-autocomplete>
|
|
48
|
+
|
|
49
|
+
<ng-container *ngIf="contactsForRessourceByRole.get(role) as contacts">
|
|
50
|
+
<ng-container *ngIf="contacts.length > 1">
|
|
51
|
+
<gn-ui-sortable-list
|
|
52
|
+
[elements]="contactsAsDynElemByRole.get(role)"
|
|
53
|
+
(elementsChange)="handleContactsChanged($event)"
|
|
54
|
+
></gn-ui-sortable-list>
|
|
55
|
+
</ng-container>
|
|
56
|
+
<ng-container *ngIf="contacts.length === 1">
|
|
57
|
+
<ng-container *ngFor="let contact of contacts">
|
|
58
|
+
<gn-ui-contact-card
|
|
59
|
+
[contact]="contact"
|
|
60
|
+
(contactRemoved)="removeContact(index)"
|
|
61
|
+
></gn-ui-contact-card> </ng-container
|
|
62
|
+
></ng-container>
|
|
63
|
+
</ng-container>
|
|
64
|
+
|
|
65
|
+
<hr class="border-t-[#D6D3D1] mt-4 mb-6" *ngIf="!isLast" />
|
|
66
|
+
</div>
|
|
67
|
+
</div>
|
|
68
|
+
<ng-template #noContact>
|
|
69
|
+
<div
|
|
70
|
+
class="p-4 border border-primary bg-primary-lightest rounded-lg"
|
|
71
|
+
translate
|
|
72
|
+
>
|
|
73
|
+
editor.record.form.field.contactsForResource.noContact
|
|
74
|
+
</div>
|
|
75
|
+
</ng-template>
|
|
76
|
+
</div>
|