geonetwork-ui 2.4.0-dev.ac57b75b → 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/lib/xml-utils.mjs +5 -3
- package/esm2022/libs/common/domain/src/lib/model/record/contact.model.mjs +28 -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 +17 -24
- 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.component.mjs +17 -3
- package/esm2022/libs/feature/editor/src/lib/components/record-form/record-form.component.mjs +3 -4
- package/esm2022/libs/feature/editor/src/lib/fields.config.mjs +19 -2
- 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 +6 -1
- package/esm2022/libs/ui/elements/src/lib/sortable-list/sortable-list.component.mjs +2 -1
- package/esm2022/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.mjs +19 -5
- package/esm2022/libs/ui/inputs/src/lib/image-input/image-input.component.mjs +5 -11
- package/esm2022/translations/de.json +25 -0
- package/esm2022/translations/en.json +25 -0
- package/esm2022/translations/es.json +25 -0
- package/esm2022/translations/fr.json +26 -1
- package/esm2022/translations/it.json +25 -0
- package/esm2022/translations/nl.json +25 -0
- package/esm2022/translations/pt.json +25 -0
- package/fesm2022/geonetwork-ui.mjs +546 -49
- package/fesm2022/geonetwork-ui.mjs.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/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 +1 -1
- package/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.d.ts.map +1 -1
- 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.component.d.ts +4 -0
- 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/components/record-form/record-form.component.d.ts.map +1 -1
- package/libs/feature/editor/src/lib/fields.config.d.ts +6 -0
- package/libs/feature/editor/src/lib/fields.config.d.ts.map +1 -1
- package/libs/feature/search/src/lib/results-table/results-table-container.component.d.ts.map +1 -1
- package/libs/ui/elements/src/lib/sortable-list/sortable-list.component.d.ts +1 -2
- 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/inputs/src/lib/image-input/image-input.component.d.ts +2 -4
- package/libs/ui/inputs/src/lib/image-input/image-input.component.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/libs/api/metadata-converter/src/lib/xml-utils.ts +5 -5
- package/src/libs/common/domain/src/lib/model/record/contact.model.ts +28 -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 +0 -1
- package/src/libs/feature/editor/src/lib/components/overview-upload/overview-upload.component.ts +18 -23
- 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.component.html +12 -1
- package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +17 -0
- package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.html +1 -1
- package/src/libs/feature/editor/src/lib/components/record-form/record-form.component.ts +0 -1
- package/src/libs/feature/editor/src/lib/fields.config.ts +20 -1
- 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.ts +12 -0
- package/src/libs/ui/elements/src/lib/sortable-list/sortable-list.component.ts +2 -2
- package/src/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts +15 -3
- package/src/libs/ui/inputs/src/lib/image-input/image-input.component.html +1 -1
- package/src/libs/ui/inputs/src/lib/image-input/image-input.component.ts +2 -7
- package/translations/de.json +25 -0
- package/translations/en.json +25 -0
- package/translations/es.json +25 -0
- package/translations/fr.json +26 -1
- package/translations/it.json +25 -0
- package/translations/nl.json +25 -0
- package/translations/pt.json +25 -0
- package/translations/sk.json +25 -0
|
@@ -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>
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import { CommonModule } from '@angular/common'
|
|
2
|
+
import {
|
|
3
|
+
ChangeDetectionStrategy,
|
|
4
|
+
ChangeDetectorRef,
|
|
5
|
+
Component,
|
|
6
|
+
Input,
|
|
7
|
+
OnDestroy,
|
|
8
|
+
OnInit,
|
|
9
|
+
} from '@angular/core'
|
|
10
|
+
import { FormControl } from '@angular/forms'
|
|
11
|
+
import {
|
|
12
|
+
AutocompleteComponent,
|
|
13
|
+
DropdownSelectorComponent,
|
|
14
|
+
UiInputsModule,
|
|
15
|
+
} from '../../../../../../../../../libs/ui/inputs/src'
|
|
16
|
+
import { UiWidgetsModule } from '../../../../../../../../../libs/ui/widgets/src'
|
|
17
|
+
import {
|
|
18
|
+
Individual,
|
|
19
|
+
Organization,
|
|
20
|
+
Role,
|
|
21
|
+
RoleLabels,
|
|
22
|
+
} from '../../../../../../../../../libs/common/domain/src/lib/model/record'
|
|
23
|
+
import { TranslateModule } from '@ngx-translate/core'
|
|
24
|
+
import {
|
|
25
|
+
debounceTime,
|
|
26
|
+
distinctUntilChanged,
|
|
27
|
+
firstValueFrom,
|
|
28
|
+
Observable,
|
|
29
|
+
Subscription,
|
|
30
|
+
switchMap,
|
|
31
|
+
} from 'rxjs'
|
|
32
|
+
import { UserModel } from '../../../../../../../../../libs/common/domain/src/lib/model/user'
|
|
33
|
+
import { PlatformServiceInterface } from '../../../../../../../../../libs/common/domain/src/lib/platform.service.interface'
|
|
34
|
+
import { OrganizationsServiceInterface } from '../../../../../../../../../libs/common/domain/src/lib/organizations.service.interface'
|
|
35
|
+
import { ContactCardComponent } from '../../../contact-card/contact-card.component'
|
|
36
|
+
import {
|
|
37
|
+
DynamicElement,
|
|
38
|
+
SortableListComponent,
|
|
39
|
+
} from '../../../../../../../../../libs/ui/elements/src'
|
|
40
|
+
import { createFuzzyFilter } from '../../../../../../../../../libs/util/shared/src'
|
|
41
|
+
import { map } from 'rxjs/operators'
|
|
42
|
+
|
|
43
|
+
@Component({
|
|
44
|
+
selector: 'gn-ui-form-field-contacts-for-resource',
|
|
45
|
+
templateUrl: './form-field-contacts-for-resource.component.html',
|
|
46
|
+
styleUrls: ['./form-field-contacts-for-resource.component.css'],
|
|
47
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
48
|
+
standalone: true,
|
|
49
|
+
imports: [
|
|
50
|
+
DropdownSelectorComponent,
|
|
51
|
+
UiInputsModule,
|
|
52
|
+
CommonModule,
|
|
53
|
+
UiWidgetsModule,
|
|
54
|
+
AutocompleteComponent,
|
|
55
|
+
TranslateModule,
|
|
56
|
+
ContactCardComponent,
|
|
57
|
+
SortableListComponent,
|
|
58
|
+
],
|
|
59
|
+
})
|
|
60
|
+
export class FormFieldContactsForResourceComponent
|
|
61
|
+
implements OnInit, OnDestroy
|
|
62
|
+
{
|
|
63
|
+
@Input() control: FormControl<Individual[]>
|
|
64
|
+
|
|
65
|
+
subscription: Subscription = new Subscription()
|
|
66
|
+
|
|
67
|
+
allUsers$: Observable<UserModel[]>
|
|
68
|
+
|
|
69
|
+
contactsForRessourceByRole: Map<Role, Individual[]> = new Map()
|
|
70
|
+
|
|
71
|
+
contactsAsDynElemByRole: Map<Role, DynamicElement[]> = new Map()
|
|
72
|
+
|
|
73
|
+
rolesToPick: Role[] = [
|
|
74
|
+
'resource_provider',
|
|
75
|
+
'custodian',
|
|
76
|
+
'owner',
|
|
77
|
+
'point_of_contact',
|
|
78
|
+
'author',
|
|
79
|
+
]
|
|
80
|
+
|
|
81
|
+
roleSectionsToDisplay: Role[] = []
|
|
82
|
+
|
|
83
|
+
allOrganizations: Map<string, Organization> = new Map()
|
|
84
|
+
|
|
85
|
+
constructor(
|
|
86
|
+
private platformServiceInterface: PlatformServiceInterface,
|
|
87
|
+
private organizationsServiceInterface: OrganizationsServiceInterface,
|
|
88
|
+
private changeDetectorRef: ChangeDetectorRef
|
|
89
|
+
) {
|
|
90
|
+
this.allUsers$ = this.platformServiceInterface.getUsers()
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async ngOnInit(): Promise<void> {
|
|
94
|
+
this.allOrganizations = new Map<string, Organization>(
|
|
95
|
+
(
|
|
96
|
+
await firstValueFrom(this.organizationsServiceInterface.organisations$)
|
|
97
|
+
).map((organization) => [organization.name, organization])
|
|
98
|
+
)
|
|
99
|
+
this.updateContactsForRessource()
|
|
100
|
+
this.manageRoleSectionsToDisplay(this.control.value)
|
|
101
|
+
this.filterRolesToPick()
|
|
102
|
+
|
|
103
|
+
this.changeDetectorRef.markForCheck()
|
|
104
|
+
|
|
105
|
+
this.subscription.add(
|
|
106
|
+
this.control.valueChanges.subscribe((contactsForResource) => {
|
|
107
|
+
this.updateContactsForRessource()
|
|
108
|
+
this.manageRoleSectionsToDisplay(contactsForResource)
|
|
109
|
+
this.filterRolesToPick()
|
|
110
|
+
|
|
111
|
+
this.changeDetectorRef.markForCheck()
|
|
112
|
+
})
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
addRoleToDisplay(roleToAdd: string) {
|
|
117
|
+
this.roleSectionsToDisplay.push(roleToAdd)
|
|
118
|
+
this.filterRolesToPick()
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
filterRolesToPick() {
|
|
122
|
+
this.rolesToPick = this.rolesToPick.filter(
|
|
123
|
+
(role) => !this.roleSectionsToDisplay.includes(role)
|
|
124
|
+
)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
updateContactsForRessource() {
|
|
128
|
+
this.contactsForRessourceByRole = this.control.value.reduce(
|
|
129
|
+
(acc, contact) => {
|
|
130
|
+
const completeOrganization = this.allOrganizations.get(
|
|
131
|
+
contact.organization.name
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
const updatedContact = {
|
|
135
|
+
...contact,
|
|
136
|
+
organization:
|
|
137
|
+
completeOrganization ??
|
|
138
|
+
({ name: contact.organization.name } as Organization),
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (!acc.has(contact.role)) {
|
|
142
|
+
acc.set(contact.role, [])
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
acc.get(contact.role).push(updatedContact)
|
|
146
|
+
|
|
147
|
+
return acc
|
|
148
|
+
},
|
|
149
|
+
new Map<Role, Individual[]>()
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
this.contactsAsDynElemByRole = this.control.value.reduce((acc, contact) => {
|
|
153
|
+
const completeOrganization = this.allOrganizations.get(
|
|
154
|
+
contact.organization.name
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
const updatedContact = {
|
|
158
|
+
...contact,
|
|
159
|
+
organization:
|
|
160
|
+
completeOrganization ??
|
|
161
|
+
({ name: contact.organization.name } as Organization),
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
const contactAsDynElem = {
|
|
165
|
+
component: ContactCardComponent,
|
|
166
|
+
inputs: {
|
|
167
|
+
contact: updatedContact,
|
|
168
|
+
removable: false,
|
|
169
|
+
},
|
|
170
|
+
} as DynamicElement
|
|
171
|
+
|
|
172
|
+
if (!acc.has(contact.role)) {
|
|
173
|
+
acc.set(contact.role, [])
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
acc.get(contact.role).push(contactAsDynElem)
|
|
177
|
+
|
|
178
|
+
return acc
|
|
179
|
+
}, new Map<Role, DynamicElement[]>())
|
|
180
|
+
|
|
181
|
+
this.changeDetectorRef.markForCheck()
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
manageRoleSectionsToDisplay(contactsForResource: Individual[]) {
|
|
185
|
+
const roles = contactsForResource.map(
|
|
186
|
+
(contact: Individual) => contact.role
|
|
187
|
+
) as Role[]
|
|
188
|
+
|
|
189
|
+
roles.forEach((role: Role) => {
|
|
190
|
+
if (!this.roleSectionsToDisplay.includes(role)) {
|
|
191
|
+
this.roleSectionsToDisplay.push(role)
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
removeContact(index: number) {
|
|
197
|
+
const newContactsforRessource = this.control.value.filter(
|
|
198
|
+
(_, i) => i !== index
|
|
199
|
+
)
|
|
200
|
+
this.control.setValue(newContactsforRessource)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
handleContactsChanged(event: DynamicElement[]) {
|
|
204
|
+
const newContactsOrdered = event.map(
|
|
205
|
+
(contactAsDynElem) => contactAsDynElem.inputs['contact']
|
|
206
|
+
) as Individual[]
|
|
207
|
+
|
|
208
|
+
const role = newContactsOrdered[0].role
|
|
209
|
+
|
|
210
|
+
this.contactsForRessourceByRole.set(role, newContactsOrdered)
|
|
211
|
+
|
|
212
|
+
const newControlValue = Array.from(
|
|
213
|
+
this.contactsForRessourceByRole.values()
|
|
214
|
+
).flat()
|
|
215
|
+
|
|
216
|
+
this.control.setValue(newControlValue)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
protected roleToLabel(role: string): string {
|
|
220
|
+
return RoleLabels.get(role)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* gn-ui-autocomplete
|
|
225
|
+
*/
|
|
226
|
+
displayWithFn: (user: UserModel) => string = (user) =>
|
|
227
|
+
`${user.name} ${user.surname} ${
|
|
228
|
+
user.organisation ? `(${user.organisation})` : ''
|
|
229
|
+
}`
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* gn-ui-autocomplete
|
|
233
|
+
*/
|
|
234
|
+
autoCompleteAction = (query: string) => {
|
|
235
|
+
const fuzzyFilter = createFuzzyFilter(query)
|
|
236
|
+
return this.allUsers$.pipe(
|
|
237
|
+
switchMap((users) => [
|
|
238
|
+
users.filter((user) => fuzzyFilter(user.username)),
|
|
239
|
+
]),
|
|
240
|
+
map((results) => results.slice(0, 10)),
|
|
241
|
+
debounceTime(300),
|
|
242
|
+
distinctUntilChanged()
|
|
243
|
+
)
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* gn-ui-autocomplete
|
|
248
|
+
*/
|
|
249
|
+
addContact(contact: UserModel, role: string) {
|
|
250
|
+
const newContactsForRessource = {
|
|
251
|
+
firstName: contact.name ?? '',
|
|
252
|
+
lastName: contact.surname ?? '',
|
|
253
|
+
organization:
|
|
254
|
+
this.allOrganizations.get(contact.organisation) ??
|
|
255
|
+
({ name: contact.organisation } as Organization),
|
|
256
|
+
email: contact.email ?? '',
|
|
257
|
+
role,
|
|
258
|
+
address: '',
|
|
259
|
+
phone: '',
|
|
260
|
+
position: '',
|
|
261
|
+
} as Individual
|
|
262
|
+
|
|
263
|
+
const newControlValue = [...this.control.value, newContactsForRessource]
|
|
264
|
+
|
|
265
|
+
this.control.setValue(newControlValue)
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
ngOnDestroy(): void {
|
|
269
|
+
this.subscription.unsubscribe()
|
|
270
|
+
}
|
|
271
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ChangeDetectionStrategy,
|
|
3
|
+
Component,
|
|
4
|
+
EventEmitter,
|
|
5
|
+
Input,
|
|
6
|
+
OnChanges,
|
|
7
|
+
OnInit,
|
|
8
|
+
Output,
|
|
9
|
+
SimpleChanges,
|
|
10
|
+
} from '@angular/core'
|
|
11
|
+
import { FormControl } from '@angular/forms'
|
|
12
|
+
import { CheckToggleComponent } from '../../../../../../../../../libs/ui/inputs/src'
|
|
13
|
+
import { TranslateModule } from '@ngx-translate/core'
|
|
14
|
+
import { OPEN_DATA_LICENSES } from './../../../../fields.config'
|
|
15
|
+
import { Subscription } from 'rxjs'
|
|
16
|
+
|
|
17
|
+
@Component({
|
|
18
|
+
selector: 'gn-ui-form-field-open-data',
|
|
19
|
+
templateUrl: './form-field-open-data.component.html',
|
|
20
|
+
styleUrls: ['./form-field-open-data.component.css'],
|
|
21
|
+
standalone: true,
|
|
22
|
+
imports: [CheckToggleComponent, TranslateModule],
|
|
23
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
24
|
+
})
|
|
25
|
+
export class FormFieldOpenDataComponent implements OnInit {
|
|
26
|
+
@Input() control: FormControl
|
|
27
|
+
value = false
|
|
28
|
+
@Output() visibilityChange = new EventEmitter<boolean>()
|
|
29
|
+
subscription: Subscription
|
|
30
|
+
|
|
31
|
+
get config() {
|
|
32
|
+
return OPEN_DATA_LICENSES
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
ngOnInit() {
|
|
36
|
+
this.initToggle()
|
|
37
|
+
this.subscription = new Subscription()
|
|
38
|
+
|
|
39
|
+
this.subscription.add(
|
|
40
|
+
this.control.valueChanges.subscribe((value) => {
|
|
41
|
+
this.value = this.config.includes(value[0].text)
|
|
42
|
+
this.visibilityChange.emit(this.value)
|
|
43
|
+
})
|
|
44
|
+
)
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
initToggle() {
|
|
48
|
+
this.value = this.config.includes(this.control.value[0].text)
|
|
49
|
+
this.visibilityChange.emit(this.value)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
onOpenDataToggled(boolean) {
|
|
53
|
+
if (boolean) {
|
|
54
|
+
this.control.setValue([{ text: this.config[0] }])
|
|
55
|
+
}
|
|
56
|
+
this.value = !this.value
|
|
57
|
+
this.visibilityChange.emit(boolean)
|
|
58
|
+
}
|
|
59
|
+
}
|
package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html
CHANGED
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
<
|
|
1
|
+
<ng-container *ngIf="isOpenData">
|
|
2
|
+
<gn-ui-form-field-open-data
|
|
3
|
+
[control]="formControl"
|
|
4
|
+
(visibilityChange)="onVisibilityChange($event)"
|
|
5
|
+
></gn-ui-form-field-open-data>
|
|
6
|
+
</ng-container>
|
|
7
|
+
<div class="flex flex-col h-full" *ngIf="!isHidden">
|
|
2
8
|
<ng-container *ngIf="withoutWrapper; else withGenericWrapper">
|
|
3
9
|
<ng-container *ngTemplateOutlet="fieldContent"></ng-container>
|
|
4
10
|
</ng-container>
|
|
@@ -87,4 +93,9 @@
|
|
|
87
93
|
[control]="formControl"
|
|
88
94
|
></gn-ui-form-field-keywords>
|
|
89
95
|
</ng-container>
|
|
96
|
+
<ng-container *ngIf="isContactsForResource">
|
|
97
|
+
<gn-ui-form-field-contacts-for-resource
|
|
98
|
+
[control]="formControl"
|
|
99
|
+
></gn-ui-form-field-contacts-for-resource>
|
|
100
|
+
</ng-container>
|
|
90
101
|
</ng-template>
|
package/src/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
ChangeDetectionStrategy,
|
|
4
4
|
Component,
|
|
5
5
|
ElementRef,
|
|
6
|
+
EventEmitter,
|
|
6
7
|
Input,
|
|
7
8
|
Output,
|
|
8
9
|
ViewChild,
|
|
@@ -32,6 +33,8 @@ import { FormFieldOverviewsComponent } from './form-field-overviews/form-field-o
|
|
|
32
33
|
import { map, take } from 'rxjs/operators'
|
|
33
34
|
import { EditorFacade } from '../../../+state/editor.facade'
|
|
34
35
|
import { FormFieldConfig } from '../../../models'
|
|
36
|
+
import { FormFieldContactsForResourceComponent } from './form-field-contacts-for-resource/form-field-contacts-for-resource.component'
|
|
37
|
+
import { FormFieldOpenDataComponent } from './form-field-open-data/form-field-open-data.component'
|
|
35
38
|
|
|
36
39
|
@Component({
|
|
37
40
|
selector: 'gn-ui-form-field',
|
|
@@ -59,6 +62,8 @@ import { FormFieldConfig } from '../../../models'
|
|
|
59
62
|
FormFieldKeywordsComponent,
|
|
60
63
|
TranslateModule,
|
|
61
64
|
FormFieldOverviewsComponent,
|
|
65
|
+
FormFieldContactsForResourceComponent,
|
|
66
|
+
FormFieldOpenDataComponent,
|
|
62
67
|
],
|
|
63
68
|
})
|
|
64
69
|
export class FormFieldComponent {
|
|
@@ -71,6 +76,7 @@ export class FormFieldComponent {
|
|
|
71
76
|
}
|
|
72
77
|
|
|
73
78
|
@Output() valueChange: Observable<unknown>
|
|
79
|
+
isHidden = false
|
|
74
80
|
|
|
75
81
|
@ViewChild('titleInput') titleInput: ElementRef
|
|
76
82
|
|
|
@@ -89,6 +95,10 @@ export class FormFieldComponent {
|
|
|
89
95
|
this.titleInput.nativeElement.children[0].focus()
|
|
90
96
|
}
|
|
91
97
|
|
|
98
|
+
onVisibilityChange(visibility: boolean) {
|
|
99
|
+
this.isHidden = visibility
|
|
100
|
+
}
|
|
101
|
+
|
|
92
102
|
get isTitle() {
|
|
93
103
|
return this.model === 'title'
|
|
94
104
|
}
|
|
@@ -122,8 +132,15 @@ export class FormFieldComponent {
|
|
|
122
132
|
get isKeywords() {
|
|
123
133
|
return this.model === 'keywords'
|
|
124
134
|
}
|
|
135
|
+
get isContactsForResource() {
|
|
136
|
+
return this.model === 'contactsForResource'
|
|
137
|
+
}
|
|
125
138
|
|
|
126
139
|
get withoutWrapper() {
|
|
127
140
|
return this.model === 'title' || this.model === 'abstract'
|
|
128
141
|
}
|
|
142
|
+
|
|
143
|
+
get isOpenData() {
|
|
144
|
+
return this.model === 'licenses'
|
|
145
|
+
}
|
|
129
146
|
}
|
|
@@ -83,6 +83,13 @@ export const RECORD_ABSTRACT_FIELD: EditorField = {
|
|
|
83
83
|
},
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
export const CONTACTS_FOR_RESOURCE_FIELD: EditorField = {
|
|
87
|
+
model: 'contactsForResource',
|
|
88
|
+
formFieldConfig: {
|
|
89
|
+
labelKey: '',
|
|
90
|
+
},
|
|
91
|
+
}
|
|
92
|
+
|
|
86
93
|
export const RECORD_GRAPHICAL_OVERVIEW_FIELD: EditorField = {
|
|
87
94
|
model: 'overviews',
|
|
88
95
|
formFieldConfig: {
|
|
@@ -157,7 +164,7 @@ export const DATA_MANAGERS_SECTION: EditorSection = {
|
|
|
157
164
|
labelKey: marker('editor.record.form.section.dataManagers.label'),
|
|
158
165
|
descriptionKey: marker('editor.record.form.section.dataManagers.description'),
|
|
159
166
|
hidden: false,
|
|
160
|
-
fields: [],
|
|
167
|
+
fields: [CONTACTS_FOR_RESOURCE_FIELD],
|
|
161
168
|
}
|
|
162
169
|
|
|
163
170
|
export const DATA_POINT_OF_CONTACT_SECTION: EditorSection = {
|
|
@@ -194,3 +201,15 @@ export const DEFAULT_CONFIGURATION: EditorConfig = {
|
|
|
194
201
|
},
|
|
195
202
|
],
|
|
196
203
|
}
|
|
204
|
+
|
|
205
|
+
/************************************************************
|
|
206
|
+
*************** LICENSES *****************
|
|
207
|
+
************************************************************
|
|
208
|
+
*/
|
|
209
|
+
export const OPEN_DATA_LICENSES: string[] = [
|
|
210
|
+
'etalab',
|
|
211
|
+
'etalab-v2',
|
|
212
|
+
'odbl',
|
|
213
|
+
'odc-by',
|
|
214
|
+
'pddl',
|
|
215
|
+
]
|
|
@@ -54,6 +54,18 @@ export class ResultsTableContainerComponent implements OnDestroy {
|
|
|
54
54
|
next: () => {
|
|
55
55
|
this.recordsRepository.clearRecordDraft(uniqueIdentifier)
|
|
56
56
|
this.searchFacade.requestNewResults()
|
|
57
|
+
this.notificationsService.showNotification(
|
|
58
|
+
{
|
|
59
|
+
type: 'success',
|
|
60
|
+
title: this.translateService.instant(
|
|
61
|
+
'editor.record.deleteSuccess.title'
|
|
62
|
+
),
|
|
63
|
+
text: `${this.translateService.instant(
|
|
64
|
+
'editor.record.deleteSuccess.body'
|
|
65
|
+
)}`,
|
|
66
|
+
},
|
|
67
|
+
2500
|
|
68
|
+
)
|
|
57
69
|
},
|
|
58
70
|
error: (error) => {
|
|
59
71
|
this.notificationsService.showNotification({
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
import { MatIconModule } from '@angular/material/icon'
|
|
18
18
|
import { ButtonComponent } from '../../../../../../libs/ui/inputs/src'
|
|
19
19
|
|
|
20
|
-
type DynamicElement = {
|
|
20
|
+
export type DynamicElement = {
|
|
21
21
|
component: Type<unknown>
|
|
22
22
|
inputs: Record<string, unknown>
|
|
23
23
|
}
|
|
@@ -40,7 +40,7 @@ type DynamicElement = {
|
|
|
40
40
|
})
|
|
41
41
|
export class SortableListComponent {
|
|
42
42
|
@Input() elements: Array<DynamicElement>
|
|
43
|
-
@Input() addOptions: Array<{ buttonLabel: string; eventName: string }>
|
|
43
|
+
@Input() addOptions: Array<{ buttonLabel: string; eventName: string }> = []
|
|
44
44
|
@Output() elementsChange = new EventEmitter<Array<DynamicElement>>()
|
|
45
45
|
@Output() add = new EventEmitter<string>()
|
|
46
46
|
|
|
@@ -61,6 +61,7 @@ export class AutocompleteComponent
|
|
|
61
61
|
@Input() action: (value: string) => Observable<AutocompleteItem[]>
|
|
62
62
|
@Input() value?: AutocompleteItem
|
|
63
63
|
@Input() clearOnSelection = false
|
|
64
|
+
@Input() preventCompleteOnSelection = false
|
|
64
65
|
@Input() autoFocus = false
|
|
65
66
|
@Input() minCharacterCount? = 3
|
|
66
67
|
@Input() allowSubmit = true
|
|
@@ -206,13 +207,24 @@ export class AutocompleteComponent
|
|
|
206
207
|
this.inputSubmitted.emit(this.inputRef.nativeElement.value)
|
|
207
208
|
}
|
|
208
209
|
|
|
210
|
+
/**
|
|
211
|
+
* This function is triggered when an item is selected in the list of displayed items.
|
|
212
|
+
* If preventCompleteOnSelection is true then the input will be left as entered by the user.
|
|
213
|
+
* If preventCompleteOnSelection is false (by default) then the input will be completed with the item selected by the user.
|
|
214
|
+
* If clearOnSelection is true then the input will be cleared upon selection.
|
|
215
|
+
* @param event
|
|
216
|
+
*/
|
|
209
217
|
handleSelection(event: MatAutocompleteSelectedEvent) {
|
|
210
218
|
this.cancelEnter = true
|
|
211
219
|
this.itemSelected.emit(event.option.value)
|
|
212
|
-
if (this.
|
|
213
|
-
this.lastInputValue$.pipe(first()).subscribe((
|
|
214
|
-
this.inputRef.nativeElement.value =
|
|
220
|
+
if (this.preventCompleteOnSelection) {
|
|
221
|
+
this.lastInputValue$.pipe(first()).subscribe((lastInputValue) => {
|
|
222
|
+
this.inputRef.nativeElement.value = lastInputValue
|
|
215
223
|
})
|
|
224
|
+
return
|
|
225
|
+
}
|
|
226
|
+
if (this.clearOnSelection) {
|
|
227
|
+
this.inputRef.nativeElement.value = ''
|
|
216
228
|
}
|
|
217
229
|
}
|
|
218
230
|
}
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
<div class="w-full h-full flex flex-col gap-2">
|
|
52
52
|
<label
|
|
53
53
|
gnUiFilesDrop
|
|
54
|
-
class="block flex-1 border-2 border-dashed border-gray-300 rounded-lg p-6 flex flex-col items-center justify-center gap-4
|
|
54
|
+
class="block flex-1 border-2 border-dashed border-gray-300 rounded-lg p-6 flex flex-col items-center justify-center gap-4"
|
|
55
55
|
(dragFilesOver)="handleDragFilesOver($event)"
|
|
56
56
|
(dropFiles)="handleDropFiles($event)"
|
|
57
57
|
>
|