From db62bda9fca6534aab29360a19b77fa8afdc3635 Mon Sep 17 00:00:00 2001 From: Romuald Caplier Date: Mon, 29 Jul 2024 14:12:05 +0200 Subject: [PATCH] feat(me): Add contacts for resource field --- apps/metadata-editor-e2e/src/e2e/edit.cy.ts | 76 ++++++++ .../page-selector.component.html | 5 +- .../src/lib/model/record/contact.model.ts | 34 ++++ .../contact-card/contact-card.component.css} | 0 .../contact-card/contact-card.component.html} | 3 +- .../contact-card.component.spec.ts | 78 ++++++++ .../contact-card.component.stories.ts | 54 ++++++ .../contact-card/contact-card.component.ts} | 8 +- ...-field-contacts-for-resource.component.css | 0 ...field-contacts-for-resource.component.html | 52 +++++ ...ld-contacts-for-resource.component.spec.ts | 181 +++++++++++++++++ ...m-field-contacts-for-resource.component.ts | 183 ++++++++++++++++++ .../form-field/form-field.component.html | 5 + .../form-field/form-field.component.spec.ts | 43 ++++ .../form-field/form-field.component.ts | 5 + .../record-form/record-form.component.html | 2 +- libs/feature/editor/src/lib/fields.config.ts | 10 +- libs/ui/elements/src/index.ts | 2 +- .../editor-contact-card.component.spec.ts | 32 --- .../editor-contact-card.component.stories.ts | 39 ---- .../ui/elements/src/lib/ui-elements.module.ts | 3 - .../autocomplete.component.spec.ts | 16 +- .../autocomplete/autocomplete.component.ts | 7 +- translations/de.json | 42 ++-- translations/en.json | 30 ++- translations/es.json | 30 ++- translations/fr.json | 32 ++- translations/it.json | 30 ++- translations/nl.json | 30 ++- translations/pt.json | 30 ++- translations/sk.json | 30 ++- 31 files changed, 927 insertions(+), 165 deletions(-) rename libs/{ui/elements/src/lib/editor-contact-card/editor-contact-card.component.css => feature/editor/src/lib/components/contact-card/contact-card.component.css} (100%) rename libs/{ui/elements/src/lib/editor-contact-card/editor-contact-card.component.html => feature/editor/src/lib/components/contact-card/contact-card.component.html} (92%) create mode 100644 libs/feature/editor/src/lib/components/contact-card/contact-card.component.spec.ts create mode 100644 libs/feature/editor/src/lib/components/contact-card/contact-card.component.stories.ts rename libs/{ui/elements/src/lib/editor-contact-card/editor-contact-card.component.ts => feature/editor/src/lib/components/contact-card/contact-card.component.ts} (78%) create mode 100644 libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.css create mode 100644 libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.html create mode 100644 libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.spec.ts create mode 100644 libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.ts delete mode 100644 libs/ui/elements/src/lib/editor-contact-card/editor-contact-card.component.spec.ts delete mode 100644 libs/ui/elements/src/lib/editor-contact-card/editor-contact-card.component.stories.ts diff --git a/apps/metadata-editor-e2e/src/e2e/edit.cy.ts b/apps/metadata-editor-e2e/src/e2e/edit.cy.ts index ed249ca863..9b88fc3a35 100644 --- a/apps/metadata-editor-e2e/src/e2e/edit.cy.ts +++ b/apps/metadata-editor-e2e/src/e2e/edit.cy.ts @@ -15,6 +15,18 @@ describe('editor form', () => { cy.get('[data-cy=save-status]') .invoke('attr', 'data-cy-value') .as('saveStatus') + cy.get('[data-test=pageSelectorButtons]') + .find('gn-ui-button') + .eq(0) + .as('resourceDescriptionPageSelectorButton') + cy.get('[data-test=pageSelectorButtons]') + .find('gn-ui-button') + .eq(1) + .as('resourcePageSelectorButton') + cy.get('[data-test=pageSelectorButtons]') + .find('gn-ui-button') + .eq(2) + .as('accessAndContactPageSelectorButton') }) it('form shows correctly', () => { @@ -57,4 +69,68 @@ describe('editor form', () => { }) cy.get('md-editor-publish-button').click() }) + + describe('record fields', () => { + describe('contacts for resources', () => { + beforeEach(() => { + cy.login('admin', 'admin', false) + + // Alpine convention record + cy.visit('/edit/accroche_velos') + + cy.get('@accessAndContactPageSelectorButton').click() + }) + + it('show the contacts for resource of the dataset', () => { + cy.get('[data-test=displayedRoles]').children().should('have.length', 1) + }) + + it('show the 5 roles available to add', () => { + cy.get('[data-test=rolesToPick]').children().should('have.length', 5) + }) + + it('click on a role adds it to the list of displayed role', () => { + cy.get('[data-test="rolesToPick"]').children().eq(2).click() + + cy.get('[data-test=rolesToPick]').children().should('have.length', 4) + + cy.get('[data-test=displayedRoles]').children().should('have.length', 2) + }) + + it('add a contact for resource', () => { + cy.get('[data-test=displayedRoles]') + .children() + .find('gn-ui-contact-card') + .should('have.length', 1) + + cy.get('[data-test=displayedRoles]') + .find('gn-ui-autocomplete') + .type('bar') + + cy.get('mat-option').should('have.text', ' Barbara Roberts ').click() + + cy.get('[data-test=displayedRoles]') + .children() + .find('gn-ui-contact-card') + .should('have.length', 2) + }) + + it('delete a contact for resource', () => { + cy.get('[data-test=displayedRoles]') + .children() + .find('gn-ui-contact-card') + .should('have.length', 1) + + cy.get('[data-test=displayedRoles]') + .children() + .get('[data-test=removeContactButton]') + .click() + + cy.get('[data-test=displayedRoles]') + .children() + .find('gn-ui-contact-card') + .should('not.exist') + }) + }) + }) }) diff --git a/apps/metadata-editor/src/app/edit/components/page-selector/page-selector.component.html b/apps/metadata-editor/src/app/edit/components/page-selector/page-selector.component.html index 279daf25d9..98cbaa381b 100644 --- a/apps/metadata-editor/src/app/edit/components/page-selector/page-selector.component.html +++ b/apps/metadata-editor/src/app/edit/components/page-selector/page-selector.component.html @@ -1,4 +1,7 @@ -
+
diff --git a/libs/common/domain/src/lib/model/record/contact.model.ts b/libs/common/domain/src/lib/model/record/contact.model.ts index ebe025902e..3372fb603c 100644 --- a/libs/common/domain/src/lib/model/record/contact.model.ts +++ b/libs/common/domain/src/lib/model/record/contact.model.ts @@ -1,4 +1,5 @@ import { Organization } from './organization.model' +import { marker } from '@biesbjerg/ngx-translate-extract-marker' export const RoleValues = [ 'unspecified', @@ -24,6 +25,39 @@ export const RoleValues = [ 'user', // Party who uses the resource ] +export const RoleLabels = new Map([ + ['unspecified', marker('metadata.contactForResource.role.unspecified')], + ['other', marker('metadata.contactForResource.role.other')], + ['author', marker('metadata.contactForResource.role.author')], + ['collaborator', marker('metadata.contactForResource.role.collaborator')], + ['contributor', marker('metadata.contactForResource.role.contributor')], + ['custodian', marker('metadata.contactForResource.role.custodian')], + ['distributor', marker('metadata.contactForResource.role.distributor')], + ['editor', marker('metadata.contactForResource.role.editor')], + ['funder', marker('metadata.contactForResource.role.funder')], + ['mediator', marker('metadata.contactForResource.role.mediator')], + ['originator', marker('metadata.contactForResource.role.originator')], + ['owner', marker('metadata.contactForResource.role.owner')], + [ + 'point_of_contact', + marker('metadata.contactForResource.role.point_of_contact'), + ], + [ + 'principal_investigator', + marker('metadata.contactForResource.role.principal_investigator'), + ], + ['processor', marker('metadata.contactForResource.role.processor')], + ['publisher', marker('metadata.contactForResource.role.publisher')], + [ + 'resource_provider', + marker('metadata.contactForResource.role.resource_provider'), + ], + ['rights_holder', marker('metadata.contactForResource.role.rights_holder')], + ['sponsor', marker('metadata.contactForResource.role.sponsor')], + ['stakeholder', marker('metadata.contactForResource.role.stakeholder')], + ['user', marker('metadata.contactForResource.role.user')], +]) + export type Role = typeof RoleValues[number] export interface Individual { diff --git a/libs/ui/elements/src/lib/editor-contact-card/editor-contact-card.component.css b/libs/feature/editor/src/lib/components/contact-card/contact-card.component.css similarity index 100% rename from libs/ui/elements/src/lib/editor-contact-card/editor-contact-card.component.css rename to libs/feature/editor/src/lib/components/contact-card/contact-card.component.css diff --git a/libs/ui/elements/src/lib/editor-contact-card/editor-contact-card.component.html b/libs/feature/editor/src/lib/components/contact-card/contact-card.component.html similarity index 92% rename from libs/ui/elements/src/lib/editor-contact-card/editor-contact-card.component.html rename to libs/feature/editor/src/lib/components/contact-card/contact-card.component.html index ac23076b16..849b22ef30 100644 --- a/libs/ui/elements/src/lib/editor-contact-card/editor-contact-card.component.html +++ b/libs/feature/editor/src/lib/components/contact-card/contact-card.component.html @@ -9,7 +9,8 @@ >{{ contact.firstName }} {{ contact.lastName }} diff --git a/libs/feature/editor/src/lib/components/contact-card/contact-card.component.spec.ts b/libs/feature/editor/src/lib/components/contact-card/contact-card.component.spec.ts new file mode 100644 index 0000000000..1ce24cb81e --- /dev/null +++ b/libs/feature/editor/src/lib/components/contact-card/contact-card.component.spec.ts @@ -0,0 +1,78 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing' +import { ContactCardComponent } from './contact-card.component' +import { + Individual, + Organization, +} from '@geonetwork-ui/common/domain/model/record' +import { MatIconModule } from '@angular/material/icon' +import { CommonModule } from '@angular/common' +import { ButtonComponent } from '@geonetwork-ui/ui/inputs' + +describe('ContactCardComponent', () => { + let component: ContactCardComponent + let fixture: ComponentFixture + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + CommonModule, + MatIconModule, + ButtonComponent, + ContactCardComponent, + ], + }).compileComponents() + }) + + beforeEach(() => { + fixture = TestBed.createComponent(ContactCardComponent) + component = fixture.componentInstance + fixture.detectChanges() + }) + + it('should create the component', () => { + expect(component).toBeTruthy() + }) + + it('should have a defined contact input', () => { + const mockContact: Individual = { + firstName: 'John', + lastName: 'Doe', + organization: { name: 'Org1' } as Organization, + email: 'john.doe@example.com', + role: 'admin', + address: '', + phone: '', + position: '', + } + component.contact = mockContact + fixture.detectChanges() + expect(component.contact).toEqual(mockContact) + }) + + it('should have a defined organization input', () => { + const mockOrganization: Organization = { + name: 'Org1', + } + component.organization = mockOrganization + fixture.detectChanges() + expect(component.organization).toEqual(mockOrganization) + }) + + it('should emit contactRemoved event with the correct contact', () => { + const mockContact: Individual = { + firstName: 'John', + lastName: 'Doe', + organization: { name: 'Org1' } as Organization, + email: 'john.doe@example.com', + role: 'admin', + address: '', + phone: '', + position: '', + } + component.contact = mockContact + + const contactRemovedSpy = jest.spyOn(component.contactRemoved, 'emit') + component.removeContact(mockContact) + expect(contactRemovedSpy).toHaveBeenCalledWith(mockContact) + }) +}) diff --git a/libs/feature/editor/src/lib/components/contact-card/contact-card.component.stories.ts b/libs/feature/editor/src/lib/components/contact-card/contact-card.component.stories.ts new file mode 100644 index 0000000000..3c75b032e8 --- /dev/null +++ b/libs/feature/editor/src/lib/components/contact-card/contact-card.component.stories.ts @@ -0,0 +1,54 @@ +import { + applicationConfig, + componentWrapperDecorator, + Meta, + moduleMetadata, + StoryObj, +} from '@storybook/angular' +import { ContactCardComponent } from './contact-card.component' +import { BrowserAnimationsModule } from '@angular/platform-browser/animations' +import { importProvidersFrom } from '@angular/core' +import { MatIconModule } from '@angular/material/icon' +import { CommonModule } from '@angular/common' +import { ButtonComponent } from '@geonetwork-ui/ui/inputs' + +export default { + title: 'Elements/ContactCardComponent', + component: ContactCardComponent, + decorators: [ + moduleMetadata({ + imports: [ + CommonModule, + MatIconModule, + ButtonComponent, + ContactCardComponent, + ], + }), + applicationConfig({ + providers: [importProvidersFrom(BrowserAnimationsModule)], + }), + componentWrapperDecorator( + (story) => `
${story}
` + ), + ], +} as Meta + +export const Primary: StoryObj = { + args: { + contact: { + firstName: 'John', + lastName: 'Doe', + organization: { + name: 'Example Organization', + }, + email: 'john.doe@example.com', + role: 'Developer', + address: '123 Main St', + phone: '123-456-7890', + position: 'Senior Developer', + }, + organization: { + name: 'Example Organization', + }, + }, +} diff --git a/libs/ui/elements/src/lib/editor-contact-card/editor-contact-card.component.ts b/libs/feature/editor/src/lib/components/contact-card/contact-card.component.ts similarity index 78% rename from libs/ui/elements/src/lib/editor-contact-card/editor-contact-card.component.ts rename to libs/feature/editor/src/lib/components/contact-card/contact-card.component.ts index b919d8e7a7..a178564f4b 100644 --- a/libs/ui/elements/src/lib/editor-contact-card/editor-contact-card.component.ts +++ b/libs/feature/editor/src/lib/components/contact-card/contact-card.component.ts @@ -14,14 +14,14 @@ import { CommonModule } from '@angular/common' import { ButtonComponent } from '@geonetwork-ui/ui/inputs' @Component({ - selector: 'gn-ui-editor-contact-card', - templateUrl: './editor-contact-card.component.html', - styleUrls: ['./editor-contact-card.component.css'], + selector: 'gn-ui-contact-card', + templateUrl: './contact-card.component.html', + styleUrls: ['./contact-card.component.css'], changeDetection: ChangeDetectionStrategy.OnPush, standalone: true, imports: [CommonModule, MatIconModule, ButtonComponent], }) -export class EditorContactCardComponent { +export class ContactCardComponent { @Input() contact: Individual @Input() organization: Organization @Output() contactRemoved = new EventEmitter() diff --git a/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.css b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.css new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.html b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.html new file mode 100644 index 0000000000..530521aa53 --- /dev/null +++ b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.html @@ -0,0 +1,52 @@ +
+
+ + +
+ + {{ roleToLabel(role) }} +
+
+
+
+
+
+
+ {{ + roleToLabel(role) + }} +
+ + + + + + + +
+
+
+
diff --git a/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.spec.ts b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.spec.ts new file mode 100644 index 0000000000..680de56f29 --- /dev/null +++ b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.spec.ts @@ -0,0 +1,181 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing' +import { FormFieldContactsForResourceComponent } from './form-field-contacts-for-resource.component' +import { PlatformServiceInterface } from '@geonetwork-ui/common/domain/platform.service.interface' +import { BehaviorSubject } from 'rxjs' +import { + Individual, + Organization, +} from '@geonetwork-ui/common/domain/model/record' +import { ChangeDetectorRef, SimpleChanges } from '@angular/core' +import { UserModel } from '@geonetwork-ui/common/domain/model/user' +import { CommonModule } from '@angular/common' +import { TranslateModule } from '@ngx-translate/core' +import { ContactCardComponent } from '../../../contact-card/contact-card.component' +import { + DropdownSelectorComponent, + UiInputsModule, +} from '@geonetwork-ui/ui/inputs' +import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface' +import { FormControl } from '@angular/forms' + +const organizationBarbie: Organization = { + name: 'Barbie Inc.', +} + +const organizationGoogle: Organization = { + name: 'Google', +} + +class MockPlatformServiceInterface { + getUsers = jest.fn(() => new BehaviorSubject([])) +} + +class MockOrganizationsServiceInterface { + organisations$ = new BehaviorSubject([organizationBarbie, organizationGoogle]) +} + +describe('FormFieldContactsForResourceComponent', () => { + let component: FormFieldContactsForResourceComponent + let fixture: ComponentFixture + + const contactJohn: Individual = { + firstName: 'John', + lastName: 'Doe', + organization: { name: 'Org1' } as Organization, + email: 'john.doe@example.com', + role: 'author', + } + + const contactJane: Individual = { + firstName: 'John', + lastName: 'Doe', + organization: { name: 'Org1' } as Organization, + email: 'john.doe@example.com', + role: 'custodian', + } + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [ + FormFieldContactsForResourceComponent, + CommonModule, + TranslateModule.forRoot(), + UiInputsModule, + ContactCardComponent, + DropdownSelectorComponent, + ], + providers: [ + { + provide: PlatformServiceInterface, + useClass: MockPlatformServiceInterface, + }, + { + provide: OrganizationsServiceInterface, + useClass: MockOrganizationsServiceInterface, + }, + ChangeDetectorRef, + ], + }).compileComponents() + }) + + beforeEach(() => { + fixture = TestBed.createComponent(FormFieldContactsForResourceComponent) + component = fixture.componentInstance + component.control = new FormControl([]) + fixture.detectChanges() + }) + + it('should create', () => { + expect(component).toBeTruthy() + }) + + it('should initialize with default values', () => { + expect(component.rolesToPick).toEqual([ + 'resource_provider', + 'custodian', + 'owner', + 'point_of_contact', + 'author', + ]) + expect(component.rolesToDisplay).toEqual([]) + expect(component.allOrganizations.size).toBe(2) + }) + + it('should fetch users on initialization', () => { + const platformService = TestBed.inject(PlatformServiceInterface) + expect(platformService.getUsers).toHaveBeenCalled() + }) + + it('should subscribe to organizations and update the allOrganizations map', () => { + component.ngOnInit() + expect(component.allOrganizations.size).toBe(2) + expect(component.allOrganizations.get('Barbie Inc.')).toEqual( + organizationBarbie + ) + }) + + it('should add role to rolesToDisplay and remove from rolesToPick', () => { + component.addRoleToDisplay('owner') + expect(component.rolesToPick).not.toContain('owner') + expect(component.rolesToDisplay).toContain('owner') + }) + + it('should remove contact by index', () => { + component.control.setValue([ + { firstName: 'John', lastName: 'Doe' } as Individual, + ]) + component.removeContact(0) + expect(component.control.value.length).toBe(0) + }) + + it('should add a new contact with correct data', async () => { + const contact = { + name: 'John', + surname: 'Doe', + organisation: 'Org1', + email: 'john.doe@example.com', + } as UserModel + jest + .spyOn(component, 'getOrganizationByName') + .mockReturnValue({ name: 'Org1' } as Organization) + await component.addContact(contact, 'owner') + expect(component.control.value.length).toBe(1) + expect(component.control.value[0].firstName).toBe('John') + expect(component.control.value[0].lastName).toBe('Doe') + expect(component.control.value[0].organization.name).toBe('Org1') + }) + + it('should handle ngOnChanges and update rolesToDisplay', () => { + const changes = { + control: { + currentValue: { + value: [contactJane, contactJohn], + }, + previousValue: { value: [] }, + firstChange: false, + }, + } as unknown as SimpleChanges + component.ngOnChanges(changes) + expect(component.rolesToDisplay).toEqual([ + contactJane.role, + contactJohn.role, + ]) + }) + + it('should return contacts by role', () => { + const contacts: Individual[] = [ + { role: 'owner' } as Individual, + { role: 'custodian' } as Individual, + { role: 'owner' } as Individual, + ] + component.control.setValue(contacts) + const result = component.getContactsByRole('owner') + expect(result.length).toBe(2) + }) + + it('should return the correct organization by name', () => { + const org = { name: 'Org1' } as Organization + component.allOrganizations.set('Org1', org) + expect(component.getOrganizationByName('Org1')).toBe(org) + }) +}) diff --git a/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.ts b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.ts new file mode 100644 index 0000000000..1d84bd24f4 --- /dev/null +++ b/libs/feature/editor/src/lib/components/record-form/form-field/form-field-contacts-for-resource/form-field-contacts-for-resource.component.ts @@ -0,0 +1,183 @@ +import { CommonModule } from '@angular/common' +import { + ChangeDetectionStrategy, + ChangeDetectorRef, + Component, + Input, + OnChanges, + OnInit, + SimpleChanges, +} from '@angular/core' +import { FormControl } from '@angular/forms' +import { + AutocompleteComponent, + DropdownSelectorComponent, + UiInputsModule, +} from '@geonetwork-ui/ui/inputs' +import { UiWidgetsModule } from '@geonetwork-ui/ui/widgets' +import { + Individual, + Organization, + RoleLabels, +} from '@geonetwork-ui/common/domain/model/record' +import { TranslateModule } from '@ngx-translate/core' +import { debounceTime, distinctUntilChanged, Observable, switchMap } from 'rxjs' +import { UserModel } from '@geonetwork-ui/common/domain/model/user' +import { PlatformServiceInterface } from '@geonetwork-ui/common/domain/platform.service.interface' +import { ContactCardComponent } from '@geonetwork-ui/ui/elements' +import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface' + +@Component({ + selector: 'gn-ui-form-field-contacts-for-resource', + templateUrl: './form-field-contacts-for-resource.component.html', + styleUrls: ['./form-field-contacts-for-resource.component.css'], + changeDetection: ChangeDetectionStrategy.OnPush, + standalone: true, + imports: [ + DropdownSelectorComponent, + UiInputsModule, + CommonModule, + UiWidgetsModule, + AutocompleteComponent, + TranslateModule, + ContactCardComponent, + ], +}) +export class FormFieldContactsForResourceComponent + implements OnInit, OnChanges +{ + @Input() control: FormControl + + allUser$: Observable + + rolesToPick: string[] = [ + 'resource_provider', + 'custodian', + 'owner', + 'point_of_contact', + 'author', + ] + + rolesToDisplay = [] + + allOrganizations: Map = new Map() + + constructor( + private platformServiceInterface: PlatformServiceInterface, + private organizationsServiceInterface: OrganizationsServiceInterface, + private changeDetectorRef: ChangeDetectorRef + ) { + this.allUser$ = this.platformServiceInterface.getUsers() + } + + ngOnInit(): void { + this.organizationsServiceInterface.organisations$.subscribe( + (organizations) => { + this.allOrganizations = new Map( + organizations.map((organization) => [organization.name, organization]) + ) + this.changeDetectorRef.markForCheck() + } + ) + } + + addRoleToDisplay(roleToAdd: string) { + this.rolesToPick.splice( + this.rolesToPick.findIndex((role) => role === roleToAdd), + 1 + ) + this.rolesToDisplay.push(roleToAdd) + } + + removeContact(index: number) { + const newContactsforRessource = this.control.value.filter( + (_, i) => i !== index + ) + this.control.setValue(newContactsforRessource) + } + + protected roleToLabel(role: string): string { + return RoleLabels.get(role) + } + + /** + * gn-ui-autocomplete + */ + displayWithFn: (user: UserModel) => string = (user) => + `${user.name} ${user.surname}` + + /** + * gn-ui-autocomplete + */ + autoCompleteAction = (query: string) => { + return this.allUser$.pipe( + switchMap((users) => [ + users.filter((user) => user.username.includes(query)), + ]), + debounceTime(300), + distinctUntilChanged() + ) + } + + /** + * gn-ui-autocomplete + */ + async addContact(contact: UserModel, role: string) { + let newContactsforRessource = { + firstName: contact.name ?? '', + lastName: contact.surname ?? '', + organization: { + name: contact.organisation, + }, + email: contact.email ?? '', + role, + address: '', + phone: '', + position: '', + } as Individual + + const newContactOrganization = this.getOrganizationByName( + contact.organisation + ) + + newContactsforRessource = { + ...newContactsforRessource, + organization: newContactOrganization, + } + + const newControlValue = [...this.control.value, newContactsforRessource] + + this.control.setValue(newControlValue) + } + + async ngOnChanges(changes: SimpleChanges): Promise { + const contactsForResource = changes['control'] + + if ( + contactsForResource && + contactsForResource.currentValue !== contactsForResource.previousValue + ) { + const rolesToAdd = contactsForResource.currentValue.value.map( + (contact: Individual) => contact.role + ) + + rolesToAdd.forEach((role: string) => { + if (!this.rolesToDisplay.includes(role)) { + this.rolesToDisplay.push(role) + } + }) + + this.changeDetectorRef.markForCheck() + } + } + + getContactsByRole(role: string): Individual[] { + return this.control.value.filter((contact: Individual) => { + return contact.role === role + }) + } + + getOrganizationByName(name: string): Organization { + return this.allOrganizations.get(name) + } +} diff --git a/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html b/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html index 5ac61e653d..33b31e74ef 100644 --- a/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html +++ b/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.html @@ -81,4 +81,9 @@ [control]="formControl" >
+ + + diff --git a/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.spec.ts b/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.spec.ts index 6337d1d9b3..e0f0f32baa 100644 --- a/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.spec.ts +++ b/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.spec.ts @@ -10,6 +10,28 @@ import { FormFieldSpatialExtentComponent } from './form-field-spatial-extent/for import { FormFieldUpdateFrequencyComponent } from './form-field-update-frequency/form-field-update-frequency.component' import { FormFieldComponent } from './form-field.component' import { FormFieldTemporalExtentsComponent } from './form-field-temporal-extents/form-field-temporal-extents.component' +import { FormFieldContactsForResourceComponent } from './form-field-contacts-for-resource/form-field-contacts-for-resource.component' +import { PlatformServiceInterface } from '@geonetwork-ui/common/domain/platform.service.interface' +import { NO_ERRORS_SCHEMA } from '@angular/core' +import { OrganizationsServiceInterface } from '@geonetwork-ui/common/domain/organizations.service.interface' +import { BehaviorSubject } from 'rxjs' +import { Organization } from '@geonetwork-ui/common/domain/model/record' + +const organizationBarbie: Organization = { + name: 'Barbie Inc.', +} + +const organizationGoogle: Organization = { + name: 'Google', +} + +class MockPlatformService { + getUsers = jest.fn(() => new BehaviorSubject([])) +} + +class MockOrganizationsService { + organisations$ = new BehaviorSubject([organizationBarbie, organizationGoogle]) +} describe('FormFieldComponent', () => { let component: FormFieldComponent @@ -18,6 +40,14 @@ describe('FormFieldComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ imports: [FormFieldComponent, TranslateModule.forRoot()], + providers: [ + { provide: PlatformServiceInterface, useClass: MockPlatformService }, + { + provide: OrganizationsServiceInterface, + useClass: MockOrganizationsService, + }, + ], + schemas: [NO_ERRORS_SCHEMA], }).compileComponents() fixture = TestBed.createComponent(FormFieldComponent) @@ -149,4 +179,17 @@ describe('FormFieldComponent', () => { expect(formField).toBeTruthy() }) }) + describe('contacts for resource field', () => { + let formField + beforeEach(() => { + component.model = 'contactsForResource' + fixture.detectChanges() + formField = fixture.debugElement.query( + By.directive(FormFieldContactsForResourceComponent) + ).componentInstance + }) + it('creates a contacts for resource field', () => { + expect(formField).toBeTruthy() + }) + }) }) diff --git a/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts b/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts index 44621437a5..ca0c01259a 100644 --- a/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts +++ b/libs/feature/editor/src/lib/components/record-form/form-field/form-field.component.ts @@ -29,6 +29,7 @@ import { FormFieldUpdateFrequencyComponent } from './form-field-update-frequency import { CatalogRecordKeys } from '@geonetwork-ui/common/domain/model/record' import { FormFieldKeywordsComponent } from './form-field-keywords/form-field-keywords.component' import { FormFieldConfig } from '../../../models' +import { FormFieldContactsForResourceComponent } from './form-field-contacts-for-resource/form-field-contacts-for-resource.component' @Component({ selector: 'gn-ui-form-field', @@ -55,6 +56,7 @@ import { FormFieldConfig } from '../../../models' FormFieldArrayComponent, FormFieldKeywordsComponent, TranslateModule, + FormFieldContactsForResourceComponent, ], }) export class FormFieldComponent { @@ -110,6 +112,9 @@ export class FormFieldComponent { get isKeywords() { return this.model === 'keywords' } + get isContactsForResource() { + return this.model === 'contactsForResource' + } get withoutWrapper() { return this.model === 'title' || this.model === 'abstract' diff --git a/libs/feature/editor/src/lib/components/record-form/record-form.component.html b/libs/feature/editor/src/lib/components/record-form/record-form.component.html index 277b79d049..208206241e 100644 --- a/libs/feature/editor/src/lib/components/record-form/record-form.component.html +++ b/libs/feature/editor/src/lib/components/record-form/record-form.component.html @@ -14,7 +14,7 @@ >
{{ section.labelKey }} diff --git a/libs/feature/editor/src/lib/fields.config.ts b/libs/feature/editor/src/lib/fields.config.ts index 8c22644b48..57bd3626f1 100644 --- a/libs/feature/editor/src/lib/fields.config.ts +++ b/libs/feature/editor/src/lib/fields.config.ts @@ -83,6 +83,14 @@ export const RECORD_ABSTRACT_FIELD: EditorField = { }, } +export const CONTACTS_FOR_RESOURCE_FIELD: EditorField = { + model: 'contactsForResource', + formFieldConfig: { + // labelKey: marker('editor.record.form.field.contactsForResource') + labelKey: '', + }, +} + /************************************************************ *************** SECTIONS ***************** ************************************************************ @@ -146,7 +154,7 @@ export const DATA_MANAGERS_SECTION: EditorSection = { labelKey: marker('editor.record.form.section.dataManagers.label'), descriptionKey: marker('editor.record.form.section.dataManagers.description'), hidden: false, - fields: [], + fields: [CONTACTS_FOR_RESOURCE_FIELD], } export const DATA_POINT_OF_CONTACT_SECTION: EditorSection = { diff --git a/libs/ui/elements/src/index.ts b/libs/ui/elements/src/index.ts index 15ee8b6cb3..10008ff8e2 100644 --- a/libs/ui/elements/src/index.ts +++ b/libs/ui/elements/src/index.ts @@ -3,7 +3,7 @@ export * from './lib/avatar/avatar.component' export * from './lib/content-ghost/content-ghost.component' export * from './lib/download-item/download-item.component' export * from './lib/downloads-list/downloads-list.component' -export * from './lib/editor-contact-card/editor-contact-card.component' +export * from '../../../feature/editor/src/lib/components/contact-card/contact-card.component' export * from './lib/error/error.component' export * from './lib/image-overlay-preview/image-overlay-preview.component' export * from './lib/link-card/link-card.component' diff --git a/libs/ui/elements/src/lib/editor-contact-card/editor-contact-card.component.spec.ts b/libs/ui/elements/src/lib/editor-contact-card/editor-contact-card.component.spec.ts deleted file mode 100644 index 90e0e6e922..0000000000 --- a/libs/ui/elements/src/lib/editor-contact-card/editor-contact-card.component.spec.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { NO_ERRORS_SCHEMA } from '@angular/core' -import { ComponentFixture, TestBed } from '@angular/core/testing' -import { EditorContactCardComponent } from './editor-contact-card.component' - -describe('LinkCardComponent', () => { - let component: EditorContactCardComponent - let fixture: ComponentFixture - - beforeEach(async () => { - await TestBed.configureTestingModule({ - schemas: [NO_ERRORS_SCHEMA], - imports: [EditorContactCardComponent], - }).compileComponents() - }) - - beforeEach(() => { - fixture = TestBed.createComponent(EditorContactCardComponent) - component = fixture.componentInstance - component.link = { - name: 'Consulter sur Géoclip', - description: - 'Lorem ipsum dolor sit amet, consect etur adipiscing elit. Donec id condim entum ex. Etiam sed molestie est.', - url: new URL('https://example.com/someurlpath'), - type: 'link', - } - fixture.detectChanges() - }) - - it('should create', () => { - expect(component).toBeTruthy() - }) -}) diff --git a/libs/ui/elements/src/lib/editor-contact-card/editor-contact-card.component.stories.ts b/libs/ui/elements/src/lib/editor-contact-card/editor-contact-card.component.stories.ts deleted file mode 100644 index d4ddcde93d..0000000000 --- a/libs/ui/elements/src/lib/editor-contact-card/editor-contact-card.component.stories.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { - applicationConfig, - componentWrapperDecorator, - Meta, - moduleMetadata, - StoryObj, -} from '@storybook/angular' -import { EditorContactCardComponent } from './editor-contact-card.component' -import { BrowserAnimationsModule } from '@angular/platform-browser/animations' -import { importProvidersFrom } from '@angular/core' - -export default { - title: 'Elements/LinkCardComponent', - component: EditorContactCardComponent, - decorators: [ - moduleMetadata({ - imports: [EditorContactCardComponent], - }), - applicationConfig({ - providers: [importProvidersFrom(BrowserAnimationsModule)], - }), - componentWrapperDecorator( - (story) => `
${story}
` - ), - ], -} as Meta - -export const Primary: StoryObj = { - args: { - compact: false, - link: { - type: 'link', - name: 'Consulter sur Géoclip', - description: - 'Lorem ipsum dolor sit amet, consect etur adipiscing elit. Donec id condim entum ex. Etiam sed molestie est.', - url: new URL('https://example.com/someurlpath'), - }, - }, -} diff --git a/libs/ui/elements/src/lib/ui-elements.module.ts b/libs/ui/elements/src/lib/ui-elements.module.ts index 6897aaef22..274cbc06fe 100644 --- a/libs/ui/elements/src/lib/ui-elements.module.ts +++ b/libs/ui/elements/src/lib/ui-elements.module.ts @@ -31,7 +31,6 @@ import { MarkdownParserComponent } from './markdown-parser/markdown-parser.compo import { ImageOverlayPreviewComponent } from './image-overlay-preview/image-overlay-preview.component' import { UserFeedbackItemComponent } from './user-feedback-item/user-feedback-item.component' import { TimeSincePipe } from './user-feedback-item/time-since.pipe' -import { EditorContactCardComponent } from './editor-contact-card/editor-contact-card.component' @NgModule({ imports: [ @@ -51,7 +50,6 @@ import { EditorContactCardComponent } from './editor-contact-card/editor-contact TimeSincePipe, BadgeComponent, MaxLinesComponent, - EditorContactCardComponent, ], declarations: [ MetadataInfoComponent, @@ -79,7 +77,6 @@ import { EditorContactCardComponent } from './editor-contact-card/editor-contact ContentGhostComponent, DownloadItemComponent, DownloadsListComponent, - EditorContactCardComponent, ApiCardComponent, RelatedRecordCardComponent, MetadataContactComponent, diff --git a/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.spec.ts b/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.spec.ts index 138118521c..847aedbfed 100644 --- a/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.spec.ts +++ b/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.spec.ts @@ -302,7 +302,6 @@ describe('AutocompleteComponent', () => { } describe('when true', () => { beforeEach(() => { - component.clearOnSelection = true component.itemSelected.subscribe((event) => (selectionEmitted = event)) fixture.detectChanges() component.handleSelection(selectionEvent) @@ -313,8 +312,11 @@ describe('AutocompleteComponent', () => { it('emits selection event', () => { expect(selectionEmitted).toEqual('first') }) - describe('if clear on selection', () => { + describe('if completeOnSelection on selection', () => { it('set input value to last entered text', () => { + component.clearOnSelection = false + component.completeOnSelection = true + component.control.setValue('second') component.handleSelection(selectionEvent) expect(component.inputRef.nativeElement.value).toEqual('second') @@ -323,6 +325,16 @@ describe('AutocompleteComponent', () => { expect(component.inputRef.nativeElement.value).toEqual('second') }) }) + describe('if clearOnSelection on selection', () => { + it('set input value to empty string', () => { + component.clearOnSelection = true + component.completeOnSelection = false + + component.control.setValue('second') + component.handleSelection(selectionEvent) + expect(component.inputRef.nativeElement.value).toEqual('') + }) + }) }) }) diff --git a/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts b/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts index 02761e908d..3e5efe0d8a 100644 --- a/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts +++ b/libs/ui/inputs/src/lib/autocomplete/autocomplete.component.ts @@ -61,6 +61,7 @@ export class AutocompleteComponent @Input() action: (value: string) => Observable @Input() value?: AutocompleteItem @Input() clearOnSelection = false + @Input() completeOnSelection = false @Input() autoFocus = false @Input() minCharacterCount? = 3 @Input() allowSubmit = true @@ -209,10 +210,14 @@ export class AutocompleteComponent handleSelection(event: MatAutocompleteSelectedEvent) { this.cancelEnter = true this.itemSelected.emit(event.option.value) - if (this.clearOnSelection) { + if (this.completeOnSelection) { this.lastInputValue$.pipe(first()).subscribe((any) => { this.inputRef.nativeElement.value = any }) + return + } + if (this.clearOnSelection) { + this.inputRef.nativeElement.value = '' } } } diff --git a/translations/de.json b/translations/de.json index 8e2341a956..1a485beea4 100644 --- a/translations/de.json +++ b/translations/de.json @@ -168,18 +168,15 @@ "editor.record.form.bottomButtons.comeBackLater": "", "editor.record.form.bottomButtons.next": "", "editor.record.form.bottomButtons.previous": "", - "editor.record.form.field.abstract": "", + "editor.record.form.field.abstract": "Kurzbeschreibung", "editor.record.form.field.keywords": "Schlagwörter", "editor.record.form.field.license": "Lizenz", - "editor.record.form.field.recordUpdated": "", - "editor.record.form.field.resourceUpdated": "", - "editor.record.form.field.temporalExtents": "", + "editor.record.form.field.recordUpdated": "Datensatz zuletzt aktualisiert", + "editor.record.form.field.resourceUpdated": "Letztes Aktualisierungsdatum", + "editor.record.form.field.temporalExtents": "Zeitlicher Umfang", "editor.record.form.field.title": "", - "editor.record.form.field.uniqueIdentifier": "", - "editor.record.form.field.updateFrequency": "", - "editor.record.form.abstract": "Kurzbeschreibung", - "editor.record.form.keywords": "Schlüsselwörter", - "editor.record.form.license": "Lizenz", + "editor.record.form.field.uniqueIdentifier": "Eindeutige Kennung (ID)", + "editor.record.form.field.updateFrequency": "Aktualisierungshäufigkeit", "editor.record.form.license.cc-by": "Creative Commons CC-BY", "editor.record.form.license.cc-by-sa": "Creative Commons CC-BY-SA", "editor.record.form.license.cc-zero": "Creative Commons CC-0", @@ -205,16 +202,10 @@ "editor.record.form.section.dataPointOfContact.label": "", "editor.record.form.section.geographicalCoverage.label": "", "editor.record.form.section.useAndAccessConditions.label": "", - "editor.record.form.metadata.title": "Metadaten-Titel", - "editor.record.form.record.updated": "Datensatz zuletzt aktualisiert", - "editor.record.form.resourceUpdated": "Letztes Aktualisierungsdatum", - "editor.record.form.temporalExtents": "Zeitlicher Umfang", "editor.record.form.temporalExtents.addDate": "Zeitpunkt", "editor.record.form.temporalExtents.addRange": "Zeitraum", "editor.record.form.temporalExtents.date": "Datum", "editor.record.form.temporalExtents.range": "Datumsbereich", - "editor.record.form.unique.identifier": "Eindeutige Kennung (ID)", - "editor.record.form.updateFrequency": "Aktualisierungshäufigkeit", "editor.record.form.updateFrequency.planned": "Die Daten sollten regelmäßig aktualisiert werden.", "editor.record.loadError.body": "Der Datensatz konnte nicht geladen werden:", "editor.record.loadError.closeMessage": "Verstanden", @@ -287,6 +278,27 @@ "map.select.layer": "Datenquelle", "map.wfs.urlInput.hint": "Geben Sie die WFS URL ein", "map.wms.urlInput.hint": "Geben Sie die WMS URL ein", + "metadata.contactForResource.role.author": "", + "metadata.contactForResource.role.collaborator": "", + "metadata.contactForResource.role.contributor": "", + "metadata.contactForResource.role.custodian": "", + "metadata.contactForResource.role.distributor": "", + "metadata.contactForResource.role.editor": "", + "metadata.contactForResource.role.funder": "", + "metadata.contactForResource.role.mediator": "", + "metadata.contactForResource.role.originator": "", + "metadata.contactForResource.role.other": "", + "metadata.contactForResource.role.owner": "", + "metadata.contactForResource.role.point_of_contact": "", + "metadata.contactForResource.role.principal_investigator": "", + "metadata.contactForResource.role.processor": "", + "metadata.contactForResource.role.publisher": "", + "metadata.contactForResource.role.resource_provider": "", + "metadata.contactForResource.role.rights_holder": "", + "metadata.contactForResource.role.sponsor": "", + "metadata.contactForResource.role.stakeholder": "", + "metadata.contactForResource.role.unspecified": "", + "metadata.contactForResource.role.user": "", "multiselect.filter.placeholder": "Suche", "nav.back": "Zurück", "next": "weiter", diff --git a/translations/en.json b/translations/en.json index b9078b516d..73681ff475 100644 --- a/translations/en.json +++ b/translations/en.json @@ -177,9 +177,6 @@ "editor.record.form.field.title": "Metadata title", "editor.record.form.field.uniqueIdentifier": "Unique identifier", "editor.record.form.field.updateFrequency": "Update frequency", - "editor.record.form.abstract": "Abstract", - "editor.record.form.keywords": "Keywords", - "editor.record.form.license": "License", "editor.record.form.license.cc-by": "Creative Commons CC-BY", "editor.record.form.license.cc-by-sa": "Creative Commons CC-BY-SA", "editor.record.form.license.cc-zero": "Creative Commons CC-0", @@ -205,16 +202,10 @@ "editor.record.form.section.dataPointOfContact.label": "Data point of contact", "editor.record.form.section.geographicalCoverage.label": "Geographical coverage", "editor.record.form.section.useAndAccessConditions.label": "Use and access conditions", - "editor.record.form.metadata.title": "Metadata title", - "editor.record.form.record.updated": "Record updated", - "editor.record.form.resourceUpdated": "Last update date", - "editor.record.form.temporalExtents": "Temporal extent", "editor.record.form.temporalExtents.addDate": "Time instant", "editor.record.form.temporalExtents.addRange": "Time period", "editor.record.form.temporalExtents.date": "Date", "editor.record.form.temporalExtents.range": "Date range", - "editor.record.form.unique.identifier": "Unique identifier", - "editor.record.form.updateFrequency": "Update frequency", "editor.record.form.updateFrequency.planned": "The data should be updated regularly.", "editor.record.loadError.body": "The record could not be loaded:", "editor.record.loadError.closeMessage": "Understood", @@ -287,6 +278,27 @@ "map.select.layer": "Data source", "map.wfs.urlInput.hint": "Enter WFS service URL", "map.wms.urlInput.hint": "Enter WMS service URL", + "metadata.contactForResource.role.author": "Author", + "metadata.contactForResource.role.collaborator": "Collaborator", + "metadata.contactForResource.role.contributor": "Contributor", + "metadata.contactForResource.role.custodian": "Custodian", + "metadata.contactForResource.role.distributor": "Distributor", + "metadata.contactForResource.role.editor": "Editor", + "metadata.contactForResource.role.funder": "Funder", + "metadata.contactForResource.role.mediator": "Mediator", + "metadata.contactForResource.role.originator": "Originator", + "metadata.contactForResource.role.other": "Other", + "metadata.contactForResource.role.owner": "Owner", + "metadata.contactForResource.role.point_of_contact": "Point of contact", + "metadata.contactForResource.role.principal_investigator": "Principal investigator", + "metadata.contactForResource.role.processor": "Processor", + "metadata.contactForResource.role.publisher": "Publisher", + "metadata.contactForResource.role.resource_provider": "Resource provider", + "metadata.contactForResource.role.rights_holder": "Rights holder", + "metadata.contactForResource.role.sponsor": "Sponsor", + "metadata.contactForResource.role.stakeholder": "Stakeholder", + "metadata.contactForResource.role.unspecified": "Unspecified", + "metadata.contactForResource.role.user": "User", "multiselect.filter.placeholder": "Search", "nav.back": "Back", "next": "next", diff --git a/translations/es.json b/translations/es.json index b5a2d014cd..4fb5d2f07c 100644 --- a/translations/es.json +++ b/translations/es.json @@ -177,9 +177,6 @@ "editor.record.form.field.title": "", "editor.record.form.field.uniqueIdentifier": "", "editor.record.form.field.updateFrequency": "", - "editor.record.form.abstract": "", - "editor.record.form.keywords": "", - "editor.record.form.license": "", "editor.record.form.license.cc-by": "", "editor.record.form.license.cc-by-sa": "", "editor.record.form.license.cc-zero": "", @@ -205,16 +202,10 @@ "editor.record.form.section.dataPointOfContact.label": "", "editor.record.form.section.geographicalCoverage.label": "", "editor.record.form.section.useAndAccessConditions.label": "", - "editor.record.form.metadata.title": "", - "editor.record.form.record.updated": "", - "editor.record.form.resourceUpdated": "", - "editor.record.form.temporalExtents": "", "editor.record.form.temporalExtents.addDate": "", "editor.record.form.temporalExtents.addRange": "", "editor.record.form.temporalExtents.date": "", "editor.record.form.temporalExtents.range": "", - "editor.record.form.unique.identifier": "", - "editor.record.form.updateFrequency": "", "editor.record.form.updateFrequency.planned": "", "editor.record.loadError.body": "", "editor.record.loadError.closeMessage": "", @@ -287,6 +278,27 @@ "map.select.layer": "", "map.wfs.urlInput.hint": "", "map.wms.urlInput.hint": "", + "metadata.contactForResource.role.author": "", + "metadata.contactForResource.role.collaborator": "", + "metadata.contactForResource.role.contributor": "", + "metadata.contactForResource.role.custodian": "", + "metadata.contactForResource.role.distributor": "", + "metadata.contactForResource.role.editor": "", + "metadata.contactForResource.role.funder": "", + "metadata.contactForResource.role.mediator": "", + "metadata.contactForResource.role.originator": "", + "metadata.contactForResource.role.other": "", + "metadata.contactForResource.role.owner": "", + "metadata.contactForResource.role.point_of_contact": "", + "metadata.contactForResource.role.principal_investigator": "", + "metadata.contactForResource.role.processor": "", + "metadata.contactForResource.role.publisher": "", + "metadata.contactForResource.role.resource_provider": "", + "metadata.contactForResource.role.rights_holder": "", + "metadata.contactForResource.role.sponsor": "", + "metadata.contactForResource.role.stakeholder": "", + "metadata.contactForResource.role.unspecified": "", + "metadata.contactForResource.role.user": "", "multiselect.filter.placeholder": "", "nav.back": "", "next": "", diff --git a/translations/fr.json b/translations/fr.json index c481e041ce..86196e1151 100644 --- a/translations/fr.json +++ b/translations/fr.json @@ -174,12 +174,9 @@ "editor.record.form.field.recordUpdated": "Date de dernière révision", "editor.record.form.field.resourceUpdated": "Date de dernière révision", "editor.record.form.field.temporalExtents": "Étendue temporelle", - "editor.record.form.field.title": "Titre", + "editor.record.form.field.title": "", "editor.record.form.field.uniqueIdentifier": "Identifiant unique", "editor.record.form.field.updateFrequency": "Fréquence de mise à jour", - "editor.record.form.abstract": "", - "editor.record.form.keywords": "", - "editor.record.form.license": "Licence", "editor.record.form.license.cc-by": "", "editor.record.form.license.cc-by-sa": "", "editor.record.form.license.cc-zero": "", @@ -205,16 +202,10 @@ "editor.record.form.section.dataPointOfContact.label": "Point de contact de la metadonee", "editor.record.form.section.geographicalCoverage.label": "Couverture geographique", "editor.record.form.section.useAndAccessConditions.label": "Conditions d'acces et usage", - "editor.record.form.metadata.title": "", - "editor.record.form.record.updated": "", - "editor.record.form.resourceUpdated": "Date de dernière révision", - "editor.record.form.temporalExtents": "Étendue temporelle", "editor.record.form.temporalExtents.addDate": "Date déterminée", "editor.record.form.temporalExtents.addRange": "Période de temps", "editor.record.form.temporalExtents.date": "Date concernée", "editor.record.form.temporalExtents.range": "Période concernée", - "editor.record.form.unique.identifier": "", - "editor.record.form.updateFrequency": "Fréquence de mise à jour", "editor.record.form.updateFrequency.planned": "Ces données doivent être mise à jour régulièrement.", "editor.record.loadError.body": "", "editor.record.loadError.closeMessage": "", @@ -287,6 +278,27 @@ "map.select.layer": "Source de données", "map.wfs.urlInput.hint": "Entrez l'URL du service WFS", "map.wms.urlInput.hint": "Entrez l'URL du service WMS", + "metadata.contactForResource.role.author": "Auteur", + "metadata.contactForResource.role.collaborator": "Collaborateur", + "metadata.contactForResource.role.contributor": "Contributeur", + "metadata.contactForResource.role.custodian": "Gestionnaire", + "metadata.contactForResource.role.distributor": "Distributeur", + "metadata.contactForResource.role.editor": "Éditeur", + "metadata.contactForResource.role.funder": "Financeur", + "metadata.contactForResource.role.mediator": "Médiateur", + "metadata.contactForResource.role.originator": "Créateur", + "metadata.contactForResource.role.other": "Autre", + "metadata.contactForResource.role.owner": "Propriétaire", + "metadata.contactForResource.role.point_of_contact": "Point de contact", + "metadata.contactForResource.role.principal_investigator": "Chercheur principal", + "metadata.contactForResource.role.processor": "Processeur", + "metadata.contactForResource.role.publisher": "Éditeur", + "metadata.contactForResource.role.resource_provider": "Fournisseur", + "metadata.contactForResource.role.rights_holder": "Détenteur des droits", + "metadata.contactForResource.role.sponsor": "Sponsor", + "metadata.contactForResource.role.stakeholder": "Partie prenante", + "metadata.contactForResource.role.unspecified": "Non spécifié", + "metadata.contactForResource.role.user": "Utilisateur", "multiselect.filter.placeholder": "Rechercher", "nav.back": "Retour", "next": "suivant", diff --git a/translations/it.json b/translations/it.json index 73f3bbeca0..a15e4468b6 100644 --- a/translations/it.json +++ b/translations/it.json @@ -177,9 +177,6 @@ "editor.record.form.field.title": "", "editor.record.form.field.uniqueIdentifier": "", "editor.record.form.field.updateFrequency": "", - "editor.record.form.abstract": "", - "editor.record.form.keywords": "", - "editor.record.form.license": "Licenza", "editor.record.form.license.cc-by": "", "editor.record.form.license.cc-by-sa": "", "editor.record.form.license.cc-zero": "", @@ -205,16 +202,10 @@ "editor.record.form.section.dataPointOfContact.label": "", "editor.record.form.section.geographicalCoverage.label": "", "editor.record.form.section.useAndAccessConditions.label": "", - "editor.record.form.metadata.title": "", - "editor.record.form.record.updated": "", - "editor.record.form.resourceUpdated": "", - "editor.record.form.temporalExtents": "", "editor.record.form.temporalExtents.addDate": "", "editor.record.form.temporalExtents.addRange": "", "editor.record.form.temporalExtents.date": "", "editor.record.form.temporalExtents.range": "", - "editor.record.form.unique.identifier": "", - "editor.record.form.updateFrequency": "", "editor.record.form.updateFrequency.planned": "", "editor.record.loadError.body": "", "editor.record.loadError.closeMessage": "", @@ -287,6 +278,27 @@ "map.select.layer": "Sorgente dati", "map.wfs.urlInput.hint": "Inserisci URL del servizio WFS", "map.wms.urlInput.hint": "Inserisci URL del servizio WMS", + "metadata.contactForResource.role.author": "", + "metadata.contactForResource.role.collaborator": "", + "metadata.contactForResource.role.contributor": "", + "metadata.contactForResource.role.custodian": "", + "metadata.contactForResource.role.distributor": "", + "metadata.contactForResource.role.editor": "", + "metadata.contactForResource.role.funder": "", + "metadata.contactForResource.role.mediator": "", + "metadata.contactForResource.role.originator": "", + "metadata.contactForResource.role.other": "", + "metadata.contactForResource.role.owner": "", + "metadata.contactForResource.role.point_of_contact": "", + "metadata.contactForResource.role.principal_investigator": "", + "metadata.contactForResource.role.processor": "", + "metadata.contactForResource.role.publisher": "", + "metadata.contactForResource.role.resource_provider": "", + "metadata.contactForResource.role.rights_holder": "", + "metadata.contactForResource.role.sponsor": "", + "metadata.contactForResource.role.stakeholder": "", + "metadata.contactForResource.role.unspecified": "", + "metadata.contactForResource.role.user": "", "multiselect.filter.placeholder": "Cerca", "nav.back": "Indietro", "next": "successivo", diff --git a/translations/nl.json b/translations/nl.json index b706426447..a8ac362d4d 100644 --- a/translations/nl.json +++ b/translations/nl.json @@ -177,9 +177,6 @@ "editor.record.form.field.title": "", "editor.record.form.field.uniqueIdentifier": "", "editor.record.form.field.updateFrequency": "", - "editor.record.form.abstract": "", - "editor.record.form.keywords": "", - "editor.record.form.license": "", "editor.record.form.license.cc-by": "", "editor.record.form.license.cc-by-sa": "", "editor.record.form.license.cc-zero": "", @@ -205,16 +202,10 @@ "editor.record.form.section.dataPointOfContact.label": "", "editor.record.form.section.geographicalCoverage.label": "", "editor.record.form.section.useAndAccessConditions.label": "", - "editor.record.form.metadata.title": "", - "editor.record.form.record.updated": "", - "editor.record.form.resourceUpdated": "", - "editor.record.form.temporalExtents": "", "editor.record.form.temporalExtents.addDate": "", "editor.record.form.temporalExtents.addRange": "", "editor.record.form.temporalExtents.date": "", "editor.record.form.temporalExtents.range": "", - "editor.record.form.unique.identifier": "", - "editor.record.form.updateFrequency": "", "editor.record.form.updateFrequency.planned": "", "editor.record.loadError.body": "", "editor.record.loadError.closeMessage": "", @@ -287,6 +278,27 @@ "map.select.layer": "", "map.wfs.urlInput.hint": "", "map.wms.urlInput.hint": "", + "metadata.contactForResource.role.author": "", + "metadata.contactForResource.role.collaborator": "", + "metadata.contactForResource.role.contributor": "", + "metadata.contactForResource.role.custodian": "", + "metadata.contactForResource.role.distributor": "", + "metadata.contactForResource.role.editor": "", + "metadata.contactForResource.role.funder": "", + "metadata.contactForResource.role.mediator": "", + "metadata.contactForResource.role.originator": "", + "metadata.contactForResource.role.other": "", + "metadata.contactForResource.role.owner": "", + "metadata.contactForResource.role.point_of_contact": "", + "metadata.contactForResource.role.principal_investigator": "", + "metadata.contactForResource.role.processor": "", + "metadata.contactForResource.role.publisher": "", + "metadata.contactForResource.role.resource_provider": "", + "metadata.contactForResource.role.rights_holder": "", + "metadata.contactForResource.role.sponsor": "", + "metadata.contactForResource.role.stakeholder": "", + "metadata.contactForResource.role.unspecified": "", + "metadata.contactForResource.role.user": "", "multiselect.filter.placeholder": "", "nav.back": "", "next": "", diff --git a/translations/pt.json b/translations/pt.json index ef163b9e4f..f22aee5e88 100644 --- a/translations/pt.json +++ b/translations/pt.json @@ -177,9 +177,6 @@ "editor.record.form.field.title": "", "editor.record.form.field.uniqueIdentifier": "", "editor.record.form.field.updateFrequency": "", - "editor.record.form.abstract": "", - "editor.record.form.keywords": "", - "editor.record.form.license": "", "editor.record.form.license.cc-by": "", "editor.record.form.license.cc-by-sa": "", "editor.record.form.license.cc-zero": "", @@ -205,16 +202,10 @@ "editor.record.form.section.dataPointOfContact.label": "", "editor.record.form.section.geographicalCoverage.label": "", "editor.record.form.section.useAndAccessConditions.label": "", - "editor.record.form.metadata.title": "", - "editor.record.form.record.updated": "", - "editor.record.form.resourceUpdated": "", - "editor.record.form.temporalExtents": "", "editor.record.form.temporalExtents.addDate": "", "editor.record.form.temporalExtents.addRange": "", "editor.record.form.temporalExtents.date": "", "editor.record.form.temporalExtents.range": "", - "editor.record.form.unique.identifier": "", - "editor.record.form.updateFrequency": "", "editor.record.form.updateFrequency.planned": "", "editor.record.loadError.body": "", "editor.record.loadError.closeMessage": "", @@ -287,6 +278,27 @@ "map.select.layer": "", "map.wfs.urlInput.hint": "", "map.wms.urlInput.hint": "", + "metadata.contactForResource.role.author": "", + "metadata.contactForResource.role.collaborator": "", + "metadata.contactForResource.role.contributor": "", + "metadata.contactForResource.role.custodian": "", + "metadata.contactForResource.role.distributor": "", + "metadata.contactForResource.role.editor": "", + "metadata.contactForResource.role.funder": "", + "metadata.contactForResource.role.mediator": "", + "metadata.contactForResource.role.originator": "", + "metadata.contactForResource.role.other": "", + "metadata.contactForResource.role.owner": "", + "metadata.contactForResource.role.point_of_contact": "", + "metadata.contactForResource.role.principal_investigator": "", + "metadata.contactForResource.role.processor": "", + "metadata.contactForResource.role.publisher": "", + "metadata.contactForResource.role.resource_provider": "", + "metadata.contactForResource.role.rights_holder": "", + "metadata.contactForResource.role.sponsor": "", + "metadata.contactForResource.role.stakeholder": "", + "metadata.contactForResource.role.unspecified": "", + "metadata.contactForResource.role.user": "", "multiselect.filter.placeholder": "", "nav.back": "", "next": "", diff --git a/translations/sk.json b/translations/sk.json index 4fe8fe1082..80b02618bd 100644 --- a/translations/sk.json +++ b/translations/sk.json @@ -177,9 +177,6 @@ "editor.record.form.field.title": "", "editor.record.form.field.uniqueIdentifier": "", "editor.record.form.field.updateFrequency": "", - "editor.record.form.abstract": "", - "editor.record.form.keywords": "", - "editor.record.form.license": "Licencia", "editor.record.form.license.cc-by": "", "editor.record.form.license.cc-by-sa": "", "editor.record.form.license.cc-zero": "", @@ -205,16 +202,10 @@ "editor.record.form.section.dataPointOfContact.label": "", "editor.record.form.section.geographicalCoverage.label": "", "editor.record.form.section.useAndAccessConditions.label": "", - "editor.record.form.metadata.title": "", - "editor.record.form.record.updated": "", - "editor.record.form.resourceUpdated": "", - "editor.record.form.temporalExtents": "", "editor.record.form.temporalExtents.addDate": "", "editor.record.form.temporalExtents.addRange": "", "editor.record.form.temporalExtents.date": "", "editor.record.form.temporalExtents.range": "", - "editor.record.form.unique.identifier": "", - "editor.record.form.updateFrequency": "", "editor.record.form.updateFrequency.planned": "", "editor.record.loadError.body": "", "editor.record.loadError.closeMessage": "", @@ -287,6 +278,27 @@ "map.select.layer": "Zdroj dát", "map.wfs.urlInput.hint": "Zadajte URL adresu služby WFS", "map.wms.urlInput.hint": "Zadajte URL adresu služby WMS", + "metadata.contactForResource.role.author": "", + "metadata.contactForResource.role.collaborator": "", + "metadata.contactForResource.role.contributor": "", + "metadata.contactForResource.role.custodian": "", + "metadata.contactForResource.role.distributor": "", + "metadata.contactForResource.role.editor": "", + "metadata.contactForResource.role.funder": "", + "metadata.contactForResource.role.mediator": "", + "metadata.contactForResource.role.originator": "", + "metadata.contactForResource.role.other": "", + "metadata.contactForResource.role.owner": "", + "metadata.contactForResource.role.point_of_contact": "", + "metadata.contactForResource.role.principal_investigator": "", + "metadata.contactForResource.role.processor": "", + "metadata.contactForResource.role.publisher": "", + "metadata.contactForResource.role.resource_provider": "", + "metadata.contactForResource.role.rights_holder": "", + "metadata.contactForResource.role.sponsor": "", + "metadata.contactForResource.role.stakeholder": "", + "metadata.contactForResource.role.unspecified": "", + "metadata.contactForResource.role.user": "", "multiselect.filter.placeholder": "Hľadať", "nav.back": "Späť", "next": "Ďalej",