geonetwork-ui 2.10.0-dev.02185426a → 2.10.0-dev.0b57ca615
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 +272 -102
- package/fesm2022/geonetwork-ui.mjs.map +1 -1
- package/index.d.ts +39 -18
- package/index.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/libs/api/metadata-converter/src/lib/dcat-ap/dcat-ap.converter.ts +11 -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 +22 -0
- package/src/libs/api/metadata-converter/src/lib/fixtures/geo2france.records.reuse+ongules.ts +1 -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 +22 -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 +1 -0
- 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 +1 -1
- package/src/libs/api/metadata-converter/src/lib/iso19115-3/iso19115-3.converter.ts +4 -0
- package/src/libs/api/metadata-converter/src/lib/iso19115-3/read-parts.ts +13 -0
- package/src/libs/api/metadata-converter/src/lib/iso19115-3/write-parts.ts +45 -1
- package/src/libs/api/metadata-converter/src/lib/iso19139/iso19139.converter.ts +13 -1
- package/src/libs/api/metadata-converter/src/lib/iso19139/read-parts.ts +40 -0
- package/src/libs/api/metadata-converter/src/lib/iso19139/write-parts.ts +47 -1
- package/src/libs/api/repository/src/lib/gn4/gn4-repository.ts +7 -3
- package/src/libs/api/repository/src/lib/gn4/platform/gn4-platform.service.ts +9 -20
- package/src/libs/common/domain/src/lib/model/record/metadata.model.ts +10 -0
- package/src/libs/common/domain/src/lib/repository/records-repository.interface.ts +2 -1
- package/src/libs/common/fixtures/src/lib/records.fixtures.ts +6 -0
- package/src/libs/feature/editor/src/index.ts +1 -0
- package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.html +7 -6
- package/src/libs/feature/editor/src/lib/components/contact-details/contact-details-form.component.ts +10 -8
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +7 -0
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +2 -0
- 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 +44 -1
- package/src/libs/feature/editor/src/lib/models/editor-config.model.ts +4 -0
- package/src/libs/feature/notify-reuse/src/index.ts +1 -0
- package/src/libs/feature/notify-reuse/src/lib/edit-delete-reuse-buttons/edit-delete-reuse-buttons.component.css +0 -0
- package/src/libs/feature/notify-reuse/src/lib/edit-delete-reuse-buttons/edit-delete-reuse-buttons.component.html +23 -0
- package/src/libs/feature/notify-reuse/src/lib/edit-delete-reuse-buttons/edit-delete-reuse-buttons.component.ts +114 -0
- package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.html +111 -1
- package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.ts +219 -8
- package/src/libs/feature/notify-reuse/src/lib/utils/url.ts +16 -0
- 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/widgets/src/lib/spinning-loader/spinning-loader.component.html +1 -1
- package/tailwind.base.css +0 -2
- package/translations/de.json +31 -2
- package/translations/en.json +36 -7
- package/translations/es.json +31 -2
- package/translations/fr.json +36 -7
- package/translations/it.json +32 -3
- package/translations/nl.json +31 -2
- package/translations/pt.json +31 -2
- package/translations/sk.json +31 -2
- package/material-styles.css +0 -483
package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts
CHANGED
|
@@ -44,6 +44,7 @@ import { FormFieldContactsComponent } from './form-field-contacts/form-field-con
|
|
|
44
44
|
import { FormFieldKeywordsComponent } from './form-field-keywords/form-field-keywords.component'
|
|
45
45
|
import { FormFieldOnlineLinkResourcesComponent } from './form-field-online-link-resources/form-field-online-link-resources.component'
|
|
46
46
|
import { FormFieldOnlineResourcesComponent } from './form-field-online-resources/form-field-online-resources.component'
|
|
47
|
+
import { FormFieldOnlineSingleLinkResourceComponent } from './form-field-online-single-link-resource/form-field-online-single-link-resource.component'
|
|
47
48
|
import { FormFieldOverviewsComponent } from './form-field-overviews/form-field-overviews.component'
|
|
48
49
|
import { FormFieldRichComponent } from './form-field-rich/form-field-rich.component'
|
|
49
50
|
import { FormFieldSimpleComponent } from './form-field-simple/form-field-simple.component'
|
|
@@ -78,6 +79,7 @@ import { FormFieldTopicsComponent } from './form-field-topics/form-field-topics.
|
|
|
78
79
|
FormFieldContactsForResourceComponent,
|
|
79
80
|
FormFieldOnlineResourcesComponent,
|
|
80
81
|
FormFieldOnlineLinkResourcesComponent,
|
|
82
|
+
FormFieldOnlineSingleLinkResourceComponent,
|
|
81
83
|
FormFieldContactsComponent,
|
|
82
84
|
FormFieldConstraintsComponent,
|
|
83
85
|
FormFieldConstraintsShortcutsComponent,
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
) {
|
|
6
6
|
@if (evaluateExpression(section.hidden)() !== true) {
|
|
7
7
|
<div
|
|
8
|
-
class="flex flex-col gap-6 border p-8 rounded-[8px] shadow scroll-mt-[90px]"
|
|
8
|
+
class="flex flex-col gap-6 border p-8 rounded-[8px] shadow scroll-mt-[90px] bg-white"
|
|
9
9
|
gnUiFieldFocus
|
|
10
10
|
#sectionFocus="fieldFocus"
|
|
11
11
|
>
|
|
@@ -8,7 +8,9 @@ export interface ISOTopic {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* This file contains the configuration of the fields
|
|
11
|
+
* This file contains the configuration of the fields
|
|
12
|
+
* that will be displayed in the editor
|
|
13
|
+
* and the reuse light editor.
|
|
12
14
|
*/
|
|
13
15
|
|
|
14
16
|
// keeping track of labels to not lose existing translations
|
|
@@ -281,6 +283,47 @@ export const DEFAULT_CONFIGURATION: EditorConfig = {
|
|
|
281
283
|
],
|
|
282
284
|
}
|
|
283
285
|
|
|
286
|
+
// reduced configuration used by the light edition page for reuse records;
|
|
287
|
+
// the point of contact is edited outside the record form on that page
|
|
288
|
+
export const REUSE_LIGHT_CONFIGURATION: EditorConfig = {
|
|
289
|
+
pages: [
|
|
290
|
+
{
|
|
291
|
+
sections: [
|
|
292
|
+
{
|
|
293
|
+
hidden: false,
|
|
294
|
+
fields: [
|
|
295
|
+
{
|
|
296
|
+
model: 'title',
|
|
297
|
+
formFieldConfig: {
|
|
298
|
+
labelKey: marker('editor.record.form.field.title'),
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
model: 'abstract',
|
|
303
|
+
formFieldConfig: {
|
|
304
|
+
labelKey: marker('editor.record.form.field.abstract'),
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
{
|
|
308
|
+
model: 'onlineResources',
|
|
309
|
+
modelSpecifier: 'onlineResourceType:singleLink',
|
|
310
|
+
formFieldConfig: {
|
|
311
|
+
labelKey: marker('editor.record.form.field.reuseUrl'),
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
model: 'overviews',
|
|
316
|
+
formFieldConfig: {
|
|
317
|
+
labelKey: marker('editor.record.form.field.overviews'),
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
],
|
|
321
|
+
},
|
|
322
|
+
],
|
|
323
|
+
},
|
|
324
|
+
],
|
|
325
|
+
}
|
|
326
|
+
|
|
284
327
|
/**
|
|
285
328
|
* OTHER SETTINGS
|
|
286
329
|
*/
|
|
@@ -19,12 +19,16 @@ 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
|
+
// Renders a single URL input bound to the first online resource of the record
|
|
23
|
+
// (FormFieldOnlineSingleLinkResourceComponent)
|
|
24
|
+
type OnlineSingleLinkResourceSpecifier = `onlineResourceType:singleLink`
|
|
22
25
|
// When set on the `contacts` field, contacts are rendered as editable detail
|
|
23
26
|
// fields (ContactDetailsFormComponent) instead of cards (ContactCardComponent)
|
|
24
27
|
type EditableContactDetailsSpecifier = `contact:editableDetails`
|
|
25
28
|
export type FieldModelSpecifier =
|
|
26
29
|
| OnlineLinkResourceSpecifier
|
|
27
30
|
| DatasetDistributionsSpecifier
|
|
31
|
+
| OnlineSingleLinkResourceSpecifier
|
|
28
32
|
| EditableContactDetailsSpecifier
|
|
29
33
|
|
|
30
34
|
export type FormFieldComponentName =
|
|
File without changes
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<div class="flex flex-row gap-6">
|
|
2
|
+
<gn-ui-button
|
|
3
|
+
class="grow"
|
|
4
|
+
type="primary"
|
|
5
|
+
extraClass="w-full flex flex-row justify-between"
|
|
6
|
+
[style.--gn-ui-button-rounded]="'10px'"
|
|
7
|
+
(buttonClick)="editReuse()"
|
|
8
|
+
data-cy="editReuseButton"
|
|
9
|
+
>
|
|
10
|
+
<span translate>notify.reuse.record.action.edit</span>
|
|
11
|
+
<ng-icon class="mr-[5px]" name="iconoirEdit"></ng-icon>
|
|
12
|
+
</gn-ui-button>
|
|
13
|
+
<gn-ui-button
|
|
14
|
+
type="outline"
|
|
15
|
+
[style.--gn-ui-button-rounded]="'10px'"
|
|
16
|
+
[style.--gn-ui-button-height]="'50px'"
|
|
17
|
+
[title]="'notify.reuse.record.action.delete' | translate"
|
|
18
|
+
(buttonClick)="deleteReuse()"
|
|
19
|
+
data-cy="deleteReuseButton"
|
|
20
|
+
>
|
|
21
|
+
<ng-icon name="iconoirTrash"></ng-icon>
|
|
22
|
+
</gn-ui-button>
|
|
23
|
+
</div>
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { LocationStrategy } from '@angular/common'
|
|
2
|
+
import { ChangeDetectionStrategy, Component, inject } from '@angular/core'
|
|
3
|
+
import { MatDialog } from '@angular/material/dialog'
|
|
4
|
+
import { RecordsRepositoryInterface } from '../../../../../../libs/common/domain/src/lib/repository/records-repository.interface'
|
|
5
|
+
import { NotificationsService } from '../../../../../../libs/feature/notifications/src'
|
|
6
|
+
import { MdViewFacade } from '../../../../../../libs/feature/record/src'
|
|
7
|
+
import { RouterFacade } from '../../../../../../libs/feature/router/src'
|
|
8
|
+
import { ConfirmationDialogComponent } from '../../../../../../libs/ui/elements/src'
|
|
9
|
+
import { ButtonComponent } from '../../../../../../libs/ui/inputs/src'
|
|
10
|
+
import { NgIcon, provideIcons, provideNgIconsConfig } from '@ng-icons/core'
|
|
11
|
+
import { iconoirEdit, iconoirTrash } from '@ng-icons/iconoir'
|
|
12
|
+
import {
|
|
13
|
+
TranslateDirective,
|
|
14
|
+
TranslatePipe,
|
|
15
|
+
TranslateService,
|
|
16
|
+
} from '@ngx-translate/core'
|
|
17
|
+
import { filter, map, take } from 'rxjs/operators'
|
|
18
|
+
import { REUSE_FORM_URL } from '../../index'
|
|
19
|
+
import { navigateToLightEdit } from '../utils/url'
|
|
20
|
+
|
|
21
|
+
@Component({
|
|
22
|
+
selector: 'gn-ui-edit-delete-reuse-buttons',
|
|
23
|
+
templateUrl: './edit-delete-reuse-buttons.component.html',
|
|
24
|
+
styleUrl: './edit-delete-reuse-buttons.component.css',
|
|
25
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
26
|
+
standalone: true,
|
|
27
|
+
imports: [ButtonComponent, TranslatePipe, TranslateDirective, NgIcon],
|
|
28
|
+
viewProviders: [
|
|
29
|
+
provideIcons({
|
|
30
|
+
iconoirEdit,
|
|
31
|
+
iconoirTrash,
|
|
32
|
+
}),
|
|
33
|
+
provideNgIconsConfig({
|
|
34
|
+
size: '1.5em',
|
|
35
|
+
}),
|
|
36
|
+
],
|
|
37
|
+
})
|
|
38
|
+
export class EditDeleteReuseButtonsComponent {
|
|
39
|
+
metadataViewFacade = inject(MdViewFacade)
|
|
40
|
+
private recordsRepository = inject(RecordsRepositoryInterface)
|
|
41
|
+
private notificationsService = inject(NotificationsService)
|
|
42
|
+
private routerFacade = inject(RouterFacade)
|
|
43
|
+
private translateService = inject(TranslateService)
|
|
44
|
+
private dialog = inject(MatDialog)
|
|
45
|
+
reuseFormUrl = inject(REUSE_FORM_URL, { optional: true })
|
|
46
|
+
private locationStrategy = inject(LocationStrategy)
|
|
47
|
+
|
|
48
|
+
metadataUuid$ = this.metadataViewFacade.metadata$.pipe(
|
|
49
|
+
map((record) => record?.uniqueIdentifier),
|
|
50
|
+
filter(Boolean)
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
editReuse() {
|
|
54
|
+
this.metadataUuid$.pipe(take(1)).subscribe((uniqueIdentifier) => {
|
|
55
|
+
navigateToLightEdit(
|
|
56
|
+
this.reuseFormUrl,
|
|
57
|
+
this.locationStrategy.getBaseHref(),
|
|
58
|
+
uniqueIdentifier
|
|
59
|
+
)
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
deleteReuse() {
|
|
64
|
+
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
|
|
65
|
+
data: {
|
|
66
|
+
title: this.translateService.instant(
|
|
67
|
+
'record.reuse.delete.confirmation.title'
|
|
68
|
+
),
|
|
69
|
+
message: this.translateService.instant(
|
|
70
|
+
'record.reuse.delete.confirmation.message'
|
|
71
|
+
),
|
|
72
|
+
confirmText: this.translateService.instant(
|
|
73
|
+
'record.reuse.delete.confirmation.confirmText'
|
|
74
|
+
),
|
|
75
|
+
cancelText: this.translateService.instant(
|
|
76
|
+
'record.reuse.delete.confirmation.cancelText'
|
|
77
|
+
),
|
|
78
|
+
focusCancel: true,
|
|
79
|
+
},
|
|
80
|
+
restoreFocus: true,
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
dialogRef.afterClosed().subscribe((confirmed) => {
|
|
84
|
+
if (confirmed) {
|
|
85
|
+
this.performReuseDeletion()
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private performReuseDeletion() {
|
|
91
|
+
this.metadataUuid$.pipe(take(1)).subscribe((uuid) =>
|
|
92
|
+
this.recordsRepository.deleteRecord(uuid).subscribe({
|
|
93
|
+
next: () => {
|
|
94
|
+
this.routerFacade.setSearch()
|
|
95
|
+
},
|
|
96
|
+
error: (error) => {
|
|
97
|
+
this.notificationsService.showNotification(
|
|
98
|
+
{
|
|
99
|
+
type: 'error',
|
|
100
|
+
title: this.translateService.instant(
|
|
101
|
+
'record.reuse.deleteError.title'
|
|
102
|
+
),
|
|
103
|
+
text: `${this.translateService.instant(
|
|
104
|
+
'record.reuse.deleteError.body'
|
|
105
|
+
)} ${error}`,
|
|
106
|
+
},
|
|
107
|
+
undefined,
|
|
108
|
+
error
|
|
109
|
+
)
|
|
110
|
+
},
|
|
111
|
+
})
|
|
112
|
+
)
|
|
113
|
+
}
|
|
114
|
+
}
|
package/src/libs/feature/notify-reuse/src/lib/notify-reuse-form/notify-reuse-form.component.html
CHANGED
|
@@ -1 +1,111 @@
|
|
|
1
|
-
|
|
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>notify.reuse.record.action.declare</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
CHANGED
|
@@ -1,21 +1,232 @@
|
|
|
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'
|
|
1
9
|
import {
|
|
2
10
|
ChangeDetectionStrategy,
|
|
3
11
|
Component,
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
12
|
+
computed,
|
|
13
|
+
inject,
|
|
14
|
+
InjectionToken,
|
|
15
|
+
input,
|
|
16
|
+
OnDestroy,
|
|
17
|
+
signal,
|
|
18
|
+
TemplateRef,
|
|
19
|
+
viewChild,
|
|
20
|
+
ViewContainerRef,
|
|
7
21
|
} from '@angular/core'
|
|
8
|
-
import {
|
|
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 { NOT_KNOWN_CONSTRAINT } from '../../../../../../libs/feature/editor/src'
|
|
34
|
+
import { NotificationsService } from '../../../../../../libs/feature/notifications/src'
|
|
35
|
+
import { ButtonComponent, TextInputComponent } from '../../../../../../libs/ui/inputs/src'
|
|
36
|
+
import { getOptionalEditorConfig } from '../../../../../../libs/util/app-config/src'
|
|
37
|
+
import { NgIcon, provideIcons, provideNgIconsConfig } from '@ng-icons/core'
|
|
38
|
+
import { iconoirAppWindow, iconoirPlusCircle } from '@ng-icons/iconoir'
|
|
39
|
+
import { matCloseOutline } from '@ng-icons/material-icons/outline'
|
|
40
|
+
import {
|
|
41
|
+
TranslateDirective,
|
|
42
|
+
TranslatePipe,
|
|
43
|
+
TranslateService,
|
|
44
|
+
} from '@ngx-translate/core'
|
|
45
|
+
import { navigateToLightEdit } from '../utils/url'
|
|
46
|
+
|
|
47
|
+
marker('notify.reuse.form.error.title')
|
|
48
|
+
marker('notify.reuse.form.error.body')
|
|
49
|
+
|
|
50
|
+
export const REUSE_FORM_URL = new InjectionToken<string>('reuseFormUrl')
|
|
9
51
|
|
|
10
52
|
@Component({
|
|
11
53
|
selector: 'gn-ui-notify-reuse-form',
|
|
12
54
|
standalone: true,
|
|
13
|
-
imports: [
|
|
55
|
+
imports: [
|
|
56
|
+
CommonModule,
|
|
57
|
+
OverlayModule,
|
|
58
|
+
TextInputComponent,
|
|
59
|
+
ButtonComponent,
|
|
60
|
+
TranslatePipe,
|
|
61
|
+
TranslateDirective,
|
|
62
|
+
NgIcon,
|
|
63
|
+
MatProgressSpinner,
|
|
64
|
+
],
|
|
14
65
|
templateUrl: './notify-reuse-form.component.html',
|
|
15
66
|
styleUrl: './notify-reuse-form.component.css',
|
|
16
67
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
68
|
+
viewProviders: [
|
|
69
|
+
provideIcons({
|
|
70
|
+
iconoirAppWindow,
|
|
71
|
+
iconoirPlusCircle,
|
|
72
|
+
matCloseOutline,
|
|
73
|
+
}),
|
|
74
|
+
provideNgIconsConfig({
|
|
75
|
+
size: '1.5em',
|
|
76
|
+
}),
|
|
77
|
+
],
|
|
17
78
|
})
|
|
18
|
-
export class NotifyReuseFormComponent {
|
|
19
|
-
|
|
20
|
-
|
|
79
|
+
export class NotifyReuseFormComponent implements OnDestroy {
|
|
80
|
+
private overlay = inject(Overlay)
|
|
81
|
+
private viewContainerRef = inject(ViewContainerRef)
|
|
82
|
+
private scrollStrategies = inject(ScrollStrategyOptions)
|
|
83
|
+
private overlayRef: OverlayRef | null = null
|
|
84
|
+
private recordsRepository = inject(RecordsRepositoryInterface)
|
|
85
|
+
private readonly translate = inject(TranslateService)
|
|
86
|
+
private readonly notificationsService = inject(NotificationsService)
|
|
87
|
+
private readonly platformServiceInterface = inject(PlatformServiceInterface)
|
|
88
|
+
reuseFormUrl = inject(REUSE_FORM_URL, { optional: true })
|
|
89
|
+
private locationStrategy = inject(LocationStrategy)
|
|
90
|
+
|
|
91
|
+
me = toSignal(this.platformServiceInterface.getMe())
|
|
92
|
+
initialEmail = computed(() => this.me()?.email)
|
|
93
|
+
templateRef = viewChild<TemplateRef<HTMLElement>>('notifyReuseForm')
|
|
94
|
+
record = input<Partial<CatalogRecord | null>>(null)
|
|
95
|
+
|
|
96
|
+
// TODO: use a form() signal once migrated to Angular 22
|
|
97
|
+
title = signal('')
|
|
98
|
+
url = signal('')
|
|
99
|
+
email = signal(this.initialEmail() ?? '')
|
|
100
|
+
|
|
101
|
+
titleInput = viewChild<TextInputComponent>('titleInput')
|
|
102
|
+
urlInput = viewChild<TextInputComponent>('urlInput')
|
|
103
|
+
emailInput = viewChild<TextInputComponent>('emailInput')
|
|
104
|
+
|
|
105
|
+
loading = signal(false)
|
|
106
|
+
|
|
107
|
+
isFormValid = computed(
|
|
108
|
+
() =>
|
|
109
|
+
this.titleInput().isValid() &&
|
|
110
|
+
this.urlInput().isValid() &&
|
|
111
|
+
this.emailInput().isValid()
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
clearInputs() {
|
|
115
|
+
this.title.set('')
|
|
116
|
+
this.url.set('')
|
|
117
|
+
this.email.set(this.initialEmail() ?? '')
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
toggleOverlay() {
|
|
121
|
+
if (this.overlayRef) {
|
|
122
|
+
this.closeOverlay()
|
|
123
|
+
} else {
|
|
124
|
+
this.openOverlay()
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
openOverlay() {
|
|
129
|
+
const positionStrategy = this.overlay
|
|
130
|
+
.position()
|
|
131
|
+
.global()
|
|
132
|
+
.centerVertically()
|
|
133
|
+
.right('16px')
|
|
134
|
+
|
|
135
|
+
this.overlayRef = this.overlay.create({
|
|
136
|
+
positionStrategy,
|
|
137
|
+
hasBackdrop: false,
|
|
138
|
+
backdropClass: 'cdk-overlay-transparent-backdrop',
|
|
139
|
+
scrollStrategy: this.scrollStrategies.noop(),
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
this.overlayRef.attach(
|
|
143
|
+
new TemplatePortal(this.templateRef(), this.viewContainerRef)
|
|
144
|
+
)
|
|
145
|
+
this.overlayRef.backdropClick().subscribe(() => this.closeOverlay())
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
closeOverlay() {
|
|
149
|
+
this.clearInputs()
|
|
150
|
+
this.overlayRef?.dispose()
|
|
151
|
+
this.overlayRef = null
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
ngOnDestroy() {
|
|
155
|
+
this.closeOverlay()
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
submit() {
|
|
159
|
+
if (!this.isFormValid()) return
|
|
160
|
+
const defaultLang =
|
|
161
|
+
getOptionalEditorConfig()?.NEW_RECORD_DEFAULT_LANGUAGE ??
|
|
162
|
+
this.translate.currentLang
|
|
163
|
+
const onlineResource: OnlineLinkResource = {
|
|
164
|
+
type: 'link',
|
|
165
|
+
url: new URL(this.url()),
|
|
166
|
+
description: this.translate.instant('notify.reuse.link.description'),
|
|
167
|
+
}
|
|
168
|
+
const contact: Individual = {
|
|
169
|
+
email: this.email(),
|
|
170
|
+
role: 'point_of_contact',
|
|
171
|
+
organization: { name: this.me()?.organisation },
|
|
172
|
+
}
|
|
173
|
+
const reuseRecord: ReuseRecord = {
|
|
174
|
+
kind: 'reuse',
|
|
175
|
+
reuseType: 'application',
|
|
176
|
+
uniqueIdentifier: null,
|
|
177
|
+
title: this.title(),
|
|
178
|
+
abstract: '',
|
|
179
|
+
overviews: [],
|
|
180
|
+
ownerOrganization: {
|
|
181
|
+
name: 'Owner organization',
|
|
182
|
+
},
|
|
183
|
+
recordUpdated: new Date(),
|
|
184
|
+
updateFrequency: 'unknown',
|
|
185
|
+
otherLanguages: [],
|
|
186
|
+
defaultLanguage: defaultLang,
|
|
187
|
+
topics: [],
|
|
188
|
+
keywords: [],
|
|
189
|
+
licenses: [],
|
|
190
|
+
legalConstraints: [NOT_KNOWN_CONSTRAINT],
|
|
191
|
+
securityConstraints: [],
|
|
192
|
+
otherConstraints: [],
|
|
193
|
+
contacts: [],
|
|
194
|
+
contactsForResource: [contact],
|
|
195
|
+
lineage: '',
|
|
196
|
+
sourceRecords: [
|
|
197
|
+
{
|
|
198
|
+
uuid: this.record()?.uniqueIdentifier ?? '',
|
|
199
|
+
title: this.record()?.title ?? '',
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
associatedRecords: [],
|
|
203
|
+
onlineResources: [onlineResource],
|
|
204
|
+
spatialExtents: [],
|
|
205
|
+
temporalExtents: [],
|
|
206
|
+
}
|
|
207
|
+
this.loading.set(true)
|
|
208
|
+
this.recordsRepository.saveRecord(reuseRecord, undefined, false).subscribe({
|
|
209
|
+
next: (uniqueIdentifier) => {
|
|
210
|
+
this.loading.set(false)
|
|
211
|
+
this.closeOverlay()
|
|
212
|
+
navigateToLightEdit(
|
|
213
|
+
this.reuseFormUrl,
|
|
214
|
+
this.locationStrategy.getBaseHref(),
|
|
215
|
+
uniqueIdentifier
|
|
216
|
+
)
|
|
217
|
+
},
|
|
218
|
+
error: (err) => {
|
|
219
|
+
this.loading.set(false)
|
|
220
|
+
this.notificationsService.showNotification(
|
|
221
|
+
{
|
|
222
|
+
type: 'error',
|
|
223
|
+
title: this.translate.instant('notify.reuse.form.error.title'),
|
|
224
|
+
text: this.translate.instant('notify.reuse.form.error.body'),
|
|
225
|
+
},
|
|
226
|
+
undefined,
|
|
227
|
+
err
|
|
228
|
+
)
|
|
229
|
+
},
|
|
230
|
+
})
|
|
231
|
+
}
|
|
21
232
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export const navigateToLightEdit = (
|
|
2
|
+
reuseFormUrl: string,
|
|
3
|
+
baseHref: string,
|
|
4
|
+
uniqueIdentifier: string
|
|
5
|
+
) => {
|
|
6
|
+
const baseUrl = `${reuseFormUrl ?? ''}`.replace(/\/+$/, '')
|
|
7
|
+
// Normalisation du baseHref pour éviter les doubles /
|
|
8
|
+
const baseHrefClean = baseHref.replace(/^\/+|\/+$/g, '')
|
|
9
|
+
const prefix = baseHrefClean ? `/${baseHrefClean}` : ''
|
|
10
|
+
const rawRedirectUrl = `${window.location.origin}${prefix}/reuse/${uniqueIdentifier}`
|
|
11
|
+
const redirect_on_leave = encodeURIComponent(rawRedirectUrl)
|
|
12
|
+
window.open(
|
|
13
|
+
`${baseUrl}/light-edit/${uniqueIdentifier}?redirect_on_leave=${redirect_on_leave}`,
|
|
14
|
+
'_self'
|
|
15
|
+
)
|
|
16
|
+
}
|
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
<input
|
|
2
2
|
#input
|
|
3
3
|
class="gn-ui-text-input"
|
|
4
|
-
[
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
[class]="extraClass()"
|
|
5
|
+
[ngClass]="{
|
|
6
|
+
'border-red-400 focus:border-red-800 hover:border-red-800 text-red-800':
|
|
7
|
+
!isValid(),
|
|
8
|
+
}"
|
|
9
|
+
[type]="type()"
|
|
10
|
+
[(ngModel)]="inputModel"
|
|
7
11
|
(change)="handleChange($event)"
|
|
8
12
|
(input)="handleChange($event)"
|
|
9
|
-
[placeholder]="placeholder"
|
|
10
|
-
[attr.aria-label]="placeholder"
|
|
11
|
-
[attr.required]="required || null"
|
|
12
|
-
[disabled]="disabled"
|
|
13
|
+
[placeholder]="placeholder()"
|
|
14
|
+
[attr.aria-label]="placeholder()"
|
|
15
|
+
[attr.required]="required() || null"
|
|
16
|
+
[disabled]="disabled()"
|
|
13
17
|
/>
|