diff --git a/src/app/components/actionbar/actionbar.component.ts b/src/app/components/actionbar/actionbar.component.ts index 4dfabfc4..c3c64b00 100644 --- a/src/app/components/actionbar/actionbar.component.ts +++ b/src/app/components/actionbar/actionbar.component.ts @@ -1,12 +1,4 @@ -import { - filter, - firstValueFrom, - map, - of, - shareReplay, - switchMap, - tap, -} from 'rxjs'; +import { filter, firstValueFrom, map, of, shareReplay, switchMap, tap } from 'rxjs'; import { Component, computed, input, signal } from '@angular/core'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; diff --git a/src/app/components/metadata/agents/agent-list/agent-list.component.html b/src/app/components/metadata/agents/agent-list/agent-list.component.html index 6a0a8c3f..41a34cdd 100644 --- a/src/app/components/metadata/agents/agent-list/agent-list.component.html +++ b/src/app/components/metadata/agents/agent-list/agent-list.component.html @@ -44,41 +44,18 @@ } } - @if (entity.creatorList(); as creatorList) { - @if (creatorList.length > 0) { -

Creator:

- @for (agent of creatorList; track $index) { - - } - } - } + @if (entity.customRolesList(); as customRoleList) { + @if (customRoleList.length > 0) { + @for (item of customRoleList; track item.role) { +

{{ item.label + ':' }}

- @if (entity.editorList(); as editorList) { - @if (editorList.length > 0) { -

Editor:

- @for (agent of editorList; track $index) { - - } - } - } - - @if (entity.dataCreatorList(); as dataCreatorList) { - @if (dataCreatorList.length > 0) { -

Data Creator:

- @for (agent of dataCreatorList; track $index) { - + @for (agent of item.agents; track agent) { + + } } } } diff --git a/src/app/components/metadata/agents/agents.component.html b/src/app/components/metadata/agents/agents.component.html index 4973e080..63de60a0 100644 --- a/src/app/components/metadata/agents/agents.component.html +++ b/src/app/components/metadata/agents/agents.component.html @@ -12,15 +12,11 @@
@if (personSelected()) { - - {{ 'Prename' | translate }} - + @if (agent | isAgentSelf) { (you) } - @if (entity() | isAgentInEntity: agent) { + @if (entity() | isAgentInEntity: agent : entityId()) { (already added, see below to edit) } {{ agent.fullName }} @@ -50,17 +46,14 @@ } } - + } - - {{ 'Name' | translate }} - + + @if (agent | isAgentSelf) { (you) } - @if (entity() | isAgentInEntity: agent) { + @if (entity() | isAgentInEntity: agent : entityId()) { (already added, see below to edit) } {{ agent.fullName }} @@ -90,51 +83,43 @@ } } - +
- - {{ 'E-Mail address' | translate }} - + @if (agentIsEditable()) { - } - - - {{ 'Phone number' }} - + + + @if (agentIsEditable()) { - } - +
- - {{ 'Name' | translate }} - + - @if (entity() | isAgentInEntity: agent) { + @if (entity() | isAgentInEntity: agent : entityId()) { (already added, see below to edit) } {{ agent.name }} @@ -161,125 +146,96 @@ } } - + - - {{ 'Department' | translate }} - + @if (agentIsEditable()) { - } - +
- - {{ 'Country' | translate }} - + @if (agentIsEditable()) { - } - - - {{ 'Postal Code' | translate }} - + + + @if (agentIsEditable()) { - } - - - {{ 'City' | translate }} - + + + @if (agentIsEditable()) { - } - +
- - {{ 'Street' | translate }} - + @if (agentIsEditable()) { - } - + - - {{ 'Number' | translate }} - + @if (agentIsEditable()) { - } - + - - {{ 'Building' | translate }} - + @if (agentIsEditable()) { - } - +
@@ -291,6 +247,15 @@ {{ role.value }} } +
+ + {{ 'Other:' | translate }} + + +
diff --git a/src/app/components/metadata/agents/agents.component.scss b/src/app/components/metadata/agents/agents.component.scss index e184caba..7773482b 100644 --- a/src/app/components/metadata/agents/agents.component.scss +++ b/src/app/components/metadata/agents/agents.component.scss @@ -53,6 +53,16 @@ display: none; } +.custom-role-row { + display: flex; + align-items: center; + gap: 8px; + + app-outlined-input { + padding-top: calc((32px - 12px) / 2); + } +} + ::ng-deep { .mdc-dialog__container .mdc-text-field--filled .mdc-floating-label { font-size: 14px !important; @@ -84,10 +94,6 @@ display: flex !important; } - .font-color-on-edit { - color: black !important; - } - .mat-mdc-checkbox .mat-internal-form-field { font-size: 16px !important; } diff --git a/src/app/components/metadata/agents/agents.component.ts b/src/app/components/metadata/agents/agents.component.ts index 79eea1d3..38e75b3b 100644 --- a/src/app/components/metadata/agents/agents.component.ts +++ b/src/app/components/metadata/agents/agents.component.ts @@ -5,6 +5,7 @@ import { input, OnChanges, OnDestroy, + OnInit, signal, SimpleChanges, ViewChild, @@ -55,6 +56,7 @@ import { IsInstitutionPipe } from 'src/app/pipes/is-institution.pipe'; import { CacheManagerService } from 'src/app/services/cache-manager.service'; import { IsAgentInEntityPipe } from './is-agent-in-entity.pipe'; import { IsAgentSelfPipe } from './is-agent-self.pipe'; +import { OutlinedInputComponent } from '../../outlined-input/outlined-input.component'; const withoutProps = (obj: T, ...props: K[]): Omit => { const copy = { ...obj }; @@ -86,11 +88,12 @@ const withoutProps = (obj: T, ...props: K[]): Omit = AsyncPipe, IsAgentInEntityPipe, IsAgentSelfPipe, + OutlinedInputComponent, ], templateUrl: './agents.component.html', styleUrl: './agents.component.scss', }) -export class AgentsComponent implements OnDestroy, OnChanges { +export class AgentsComponent implements OnDestroy, OnChanges, OnInit { entity = input.required(); entityId = computed(() => this.entity()._id); @@ -186,7 +189,9 @@ export class AgentsComponent implements OnDestroy, OnChanges { ); availableInstitutions = this.cache .getItem('metadata-agents-institutions', () => - this.backend.getUserDataCollection(Collection.institution, { full: true }), + this.backend.getUserDataCollection(Collection.institution, { + full: true, + }), ) .pipe( map(institutions => institutions ?? []), @@ -196,16 +201,27 @@ export class AgentsComponent implements OnDestroy, OnChanges { for (const institution of institutions) { const pairs = Object.values(institution.addresses) .filter(isAddress) - .map(addr => withoutProps(addr, '_id', 'creation_date')) - .map(addr => - Object.values(addr) - .filter(_ => _) - .join('-'), - ) - .filter(pair => pair) + .map(cr => withoutProps(cr, '_id', 'creation_date')) + .filter(pair => Object.values(pair).filter(_ => _).length > 0) .flat(); for (const pair of pairs) { - map.set(pair, institution); + const pairString = Object.values(pair).join('-'); + map.set(pairString, { + ...institution, + addresses: { + ...institution.addresses, + [this.entityId()]: + institution.addresses[this.entityId()] ?? + new Address({ + building: pair.building, + number: pair.number, + street: pair.street, + postcode: pair.postcode, + city: pair.city, + country: pair.country, + }), + }, + }); } } return Array.from(map.values()); @@ -272,12 +288,11 @@ export class AgentsComponent implements OnDestroy, OnChanges { availableRoles = [ { type: 'RIGHTS_OWNER', value: 'Rightsowner', checked: false }, - { type: 'CREATOR', value: 'Creator', checked: false }, - { type: 'EDITOR', value: 'Editor', checked: false }, - { type: 'DATA_CREATOR', value: 'Data creator', checked: false }, { type: 'CONTACT_PERSON', value: 'Contact person', checked: false }, ]; + newCustomRole = { value: '', checked: false }; + constructor( public account: AccountService, public backend: BackendService, @@ -309,7 +324,10 @@ export class AgentsComponent implements OnDestroy, OnChanges { } get atLeastOneRoleSelected(): boolean { - return this.availableRoles.some(role => role.checked); + return ( + this.availableRoles.some(role => role.checked) || + (this.newCustomRole.checked && this.newCustomRole.value != '') + ); } get selectionIsValid(): boolean { @@ -317,7 +335,16 @@ export class AgentsComponent implements OnDestroy, OnChanges { } get currentRoleSelection(): string[] { - return this.availableRoles.filter(role => role.checked).map(role => role.type); + const availableRoleTypes = this.availableRoles + .filter(role => role.checked) + .map(role => role.type); + + const customRole = + this.newCustomRole.checked && this.newCustomRole.value.trim() + ? this.entity().formatRoleLabel(this.newCustomRole.value) + : ''; + + return customRole ? [...availableRoleTypes, customRole] : availableRoleTypes; } get newContactRef(): Address | ContactReference { @@ -369,6 +396,7 @@ export class AgentsComponent implements OnDestroy, OnChanges { : institutions.find(i => i._id === agentId); if (!currentAgent) throw new Error(`Agent with ID ${agentId} not found`); + this.agentIsSelected.set(true); this.setAgentInForm(currentAgent); } @@ -459,6 +487,7 @@ export class AgentsComponent implements OnDestroy, OnChanges { this.entity().addPerson(personInstance); this.metaDataCommunicationService.setEntity(this.entity()); + this.addCustomRole(); } private addInstitution() { @@ -471,6 +500,26 @@ export class AgentsComponent implements OnDestroy, OnChanges { this.entity().addInstitution(institutionInstance); this.metaDataCommunicationService.setEntity(this.entity()); + this.addCustomRole(); + } + + private addCustomRole() { + const newCustomRole = this.newCustomRole; + if (!newCustomRole.checked || !newCustomRole.value.trim()) return; + + this.addRoleIfNotExists(newCustomRole.value); + } + + private addRoleIfNotExists(newRoleName: string) { + const entity = this.entity(); + const cleanedUpRoleName = entity.formatRoleLabel(newRoleName); + if (!cleanedUpRoleName || this.availableRoles.some(r => r.value === cleanedUpRoleName)) return; + + this.availableRoles.push({ + type: cleanedUpRoleName, + value: cleanedUpRoleName, + checked: false, + }); } public updateAgent() { @@ -479,22 +528,31 @@ export class AgentsComponent implements OnDestroy, OnChanges { const agentOnEntity: Person | Institution | undefined = (() => { if (this.personSelected()) { - const person = this.entity().persons.find(p => p._id.toString() === currentAgentId); - if (!person) return undefined; + const index = this.entity().persons.findIndex(p => p._id.toString() === currentAgentId); + if (index === -1) return undefined; + const person = this.entity().persons[index]; + const contactRef = this.newContactRef; if (!isContact(contactRef)) return undefined; + person.contact_references[this.entityId()] = contactRef; - return person; + this.entity().persons[index] = new Person(person); + + return this.entity().persons[index]; } else if (this.institutionSelected()) { - const institution = this.entity().institutions.find( + const index = this.entity().institutions.findIndex( i => i._id.toString() === currentAgentId, ); - if (!institution) return undefined; + if (index === -1) return undefined; + const institution = this.entity().institutions[index]; + const address = this.newContactRef; if (!isAddress(address)) return undefined; institution.addresses[this.entityId()] = address; + institution.university = this.formControls.university.value ?? ''; - return institution; + this.entity().institutions[index] = new Institution(institution); + return this.entity().institutions[index]; } return undefined; })(); @@ -539,6 +597,7 @@ export class AgentsComponent implements OnDestroy, OnChanges { this.availableRoles.forEach(role => { role.checked = false; }); + this.newCustomRole = { value: '', checked: false }; } clearAgentInformation() { @@ -557,6 +616,19 @@ export class AgentsComponent implements OnDestroy, OnChanges { this.metaDataCommunicationService.setEntity(currentEntity); } + ngOnInit(): void { + const fixedRoles = this.availableRoles.map(r => r.type); + const allAgents = [...this.entity().persons, ...this.entity().institutions]; + + for (const agent of allAgents) { + for (const role of agent.roles?.[this.entityId()] ?? []) { + if (!fixedRoles.includes(role)) { + this.addRoleIfNotExists(role); + } + } + } + } + ngOnDestroy(): void { this.resetAgentForm(); this.selectedAgent.set(null); diff --git a/src/app/components/metadata/agents/is-agent-in-entity.pipe.ts b/src/app/components/metadata/agents/is-agent-in-entity.pipe.ts index fba4b525..716c4949 100644 --- a/src/app/components/metadata/agents/is-agent-in-entity.pipe.ts +++ b/src/app/components/metadata/agents/is-agent-in-entity.pipe.ts @@ -4,11 +4,20 @@ import { AnyEntity, Institution, Person } from 'src/app/metadata'; @Pipe({ name: 'isAgentInEntity' }) export class IsAgentInEntityPipe implements PipeTransform { - transform(value: AnyEntity, agent: Person | Institution): boolean { + transform(value: AnyEntity, agent: Person | Institution, entityId: string): boolean { if (isPerson(agent)) { - return value.persons.some(p => p._id === agent._id); + return value.persons.some( + p => + p.contact_references[entityId]?.mail === agent.contact_references[entityId]?.mail && + p.fullName === agent.fullName, + ); } else if (isInstitution(agent)) { - return value.institutions.some(i => i._id === agent._id); + return value.institutions.some( + i => + i.name === agent.name && + i.addresses[entityId]?.street === agent.addresses[entityId]?.street && + i.addresses[entityId]?.number === agent.addresses[entityId]?.number, + ); } return false; } diff --git a/src/app/components/metadata/entity/entity.component.html b/src/app/components/metadata/entity/entity.component.html index 2dabb78c..4754b7d0 100644 --- a/src/app/components/metadata/entity/entity.component.html +++ b/src/app/components/metadata/entity/entity.component.html @@ -82,6 +82,9 @@ label_important {{ 'Creation' | translate }} + @if (digitalEntity.creation.length; as count) { + {{ '(' + count + ')' }} + } } @@ -100,6 +103,9 @@ label_important {{ 'External Identifiers' | translate }} + @if (entity.externalId.length; as count) { + {{ '(' + count + ')' }} + } @@ -220,6 +240,7 @@ @if (digitalEntity(); as digitalEntity) { + } diff --git a/src/app/components/metadata/optional/biblio-ref/biblio-ref.component.html b/src/app/components/metadata/optional/biblio-ref/biblio-ref.component.html index a98ef775..77cd670f 100644 --- a/src/app/components/metadata/optional/biblio-ref/biblio-ref.component.html +++ b/src/app/components/metadata/optional/biblio-ref/biblio-ref.component.html @@ -10,7 +10,7 @@ label="{{ 'Description' | translate }}" placeholder="{{ 'Enter a description' | translate }}" [formControl]="descriptionControl" - [class.font-color-on-edit]="descriptionControl.disabled && descriptionControl.value" + [class.editable-input-field]="descriptionControl.disabled && descriptionControl.value" > @if (dataIsEditable) {