From e6c41da9f829b931bcb7d8c99ad2ebca6e170115 Mon Sep 17 00:00:00 2001
From: Vera Malieske
Date: Wed, 29 Apr 2026 20:18:15 +0200
Subject: [PATCH 1/4] Update Metadata: AgentForm
- delete unneccassary roles
- implement outlined-input for agent-form
- implement functionality for custom-roles
- make sure custom-roles will be displayed in list after adding a new one
- fix issue when selecting an agent from autocomplete where the value field didn't update accordingly
- add a pipe to transform the role type of an agent in a label-form to serve outputs on multiple components
---
.../actionbar/actionbar.component.ts | 10 +-
.../agent-list/agent-list.component.html | 45 +---
.../metadata/agents/agents.component.html | 235 ++++++++----------
.../metadata/agents/agents.component.scss | 10 +
.../metadata/agents/agents.component.ts | 62 ++++-
.../outlined-input.component.ts | 3 +
src/app/metadata/index.ts | 51 ++--
src/app/pipes/role-label.pipe.ts | 11 +
8 files changed, 212 insertions(+), 215 deletions(-)
create mode 100644 src/app/pipes/role-label.pipe.ts
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..5c900a13 100644
--- a/src/app/components/metadata/agents/agents.component.html
+++ b/src/app/components/metadata/agents/agents.component.html
@@ -12,15 +12,11 @@
-
- {{ 'E-Mail address' | translate }}
-
+
@if (agentIsEditable()) {
-
-
- {{ 'Phone number' }}
-
+
+
+
@if (agentIsEditable()) {
-
+
{{ 'edit' | translate }}
}
-
+
-
- {{ 'Name' | translate }}
-
+
-
+
-
- {{ 'Department' | translate }}
-
+
@if (agentIsEditable()) {
-
+
{{ 'edit' | translate }}
}
-
+
@@ -291,6 +245,15 @@
{{ role.value }}
}
+
+
+ {{ 'Other:' | translate }}
+
+
+
diff --git a/src/app/components/metadata/optional/creation/creation.component.ts b/src/app/components/metadata/optional/creation/creation.component.ts
index 038853b6..a8e79d81 100644
--- a/src/app/components/metadata/optional/creation/creation.component.ts
+++ b/src/app/components/metadata/optional/creation/creation.component.ts
@@ -1,5 +1,14 @@
import { formatDate } from '@angular/common';
-import { ChangeDetectionStrategy, Component, computed, input, OnInit } from '@angular/core';
+import {
+ ChangeDetectionStrategy,
+ Component,
+ computed,
+ EventEmitter,
+ inject,
+ input,
+ OnInit,
+ Output,
+} from '@angular/core';
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
@@ -15,6 +24,7 @@ import { OptionalCardListComponent } from '../optional-card-list/optional-card-l
import { OutlinedInputComponent } from 'src/app/components/outlined-input/outlined-input.component';
import { toSignal } from '@angular/core/rxjs-interop';
+import { CounterService } from 'src/app/services/single-number-counter.service';
@Component({
selector: 'app-creation',
@@ -36,8 +46,11 @@ import { toSignal } from '@angular/core/rxjs-interop';
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CreationComponent implements OnInit {
+ public propertyType = 'creation';
public entity = input.required();
+ @Output() itemAdded = new EventEmitter<{ item: object; type: string }>();
+
form = new FormGroup({
technique: new FormControl(''),
software: new FormControl(''),
@@ -71,6 +84,7 @@ export class CreationComponent implements OnInit {
this.resetFormFields();
this.entity().creation.push(creationInstance);
+ this.itemAdded.emit({ item: creationInstance, type: this.propertyType });
}
isFormValid = computed(() => {
diff --git a/src/app/components/metadata/optional/external-ids/external-ids.component.html b/src/app/components/metadata/optional/external-ids/external-ids.component.html
index 2d180698..2da3ba62 100644
--- a/src/app/components/metadata/optional/external-ids/external-ids.component.html
+++ b/src/app/components/metadata/optional/external-ids/external-ids.component.html
@@ -29,4 +29,4 @@
}
-
+
diff --git a/src/app/components/metadata/optional/external-ids/external-ids.component.ts b/src/app/components/metadata/optional/external-ids/external-ids.component.ts
index 8e2a1c4c..c229098c 100644
--- a/src/app/components/metadata/optional/external-ids/external-ids.component.ts
+++ b/src/app/components/metadata/optional/external-ids/external-ids.component.ts
@@ -1,5 +1,5 @@
import { AsyncPipe } from '@angular/common';
-import { Component, computed, input } from '@angular/core';
+import { Component, computed, EventEmitter, inject, input, Output } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatDividerModule } from '@angular/material/divider';
@@ -10,6 +10,7 @@ import { AnyEntity, PhysicalEntity, TypeValueTuple } from 'src/app/metadata';
import { TranslatePipe } from 'src/app/pipes';
import { OptionalCardListComponent } from '../optional-card-list/optional-card-list.component';
import { OutlinedInputComponent } from 'src/app/components/outlined-input/outlined-input.component';
+import { CounterService } from 'src/app/services/single-number-counter.service';
@Component({
selector: 'app-external-ids',
@@ -29,7 +30,9 @@ import { OutlinedInputComponent } from 'src/app/components/outlined-input/outlin
styleUrl: './external-ids.component.scss',
})
export class ExternalIdsComponent {
+ public propertyType = 'externalId';
public entity = input.required();
+ @Output() itemAdded = new EventEmitter<{ item: object; type: string }>();
public valueControl = new FormControl('', { nonNullable: true });
public typeControl = new FormControl('', { nonNullable: true });
@@ -50,6 +53,7 @@ export class ExternalIdsComponent {
if (identifierInstance.isValid) {
this.entity().externalId.push(identifierInstance);
this.resetFormFields();
+ this.itemAdded.emit({ item: identifierInstance, type: this.propertyType });
}
}
diff --git a/src/app/components/metadata/optional/links/links.component.html b/src/app/components/metadata/optional/links/links.component.html
index e0c76097..d8eb10f5 100644
--- a/src/app/components/metadata/optional/links/links.component.html
+++ b/src/app/components/metadata/optional/links/links.component.html
@@ -30,4 +30,4 @@
}
-
+
diff --git a/src/app/components/metadata/optional/links/links.component.ts b/src/app/components/metadata/optional/links/links.component.ts
index 5883246e..240e5bd1 100644
--- a/src/app/components/metadata/optional/links/links.component.ts
+++ b/src/app/components/metadata/optional/links/links.component.ts
@@ -1,5 +1,5 @@
import { AsyncPipe } from '@angular/common';
-import { Component, computed, input } from '@angular/core';
+import { Component, computed, EventEmitter, input, Output } from '@angular/core';
import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
@@ -32,6 +32,8 @@ import { OutlinedInputComponent } from 'src/app/components/outlined-input/outlin
})
export class LinksComponent {
public entity = input.required();
+ public propertyType = 'link';
+ @Output() itemAdded = new EventEmitter<{ item: object; type: string }>();
public valueControl = new FormControl('', { nonNullable: true });
public descriptionControl = new FormControl('', { nonNullable: true });
@@ -52,6 +54,7 @@ export class LinksComponent {
if (linkInstance.isValid) {
this.entity().externalLink.push(linkInstance);
this.resetFormFields();
+ this.itemAdded.emit({ item: linkInstance, type: this.propertyType });
}
}
diff --git a/src/app/components/metadata/optional/metadata-files/metadata-files.component.html b/src/app/components/metadata/optional/metadata-files/metadata-files.component.html
index 2cb19f59..5b578a7c 100644
--- a/src/app/components/metadata/optional/metadata-files/metadata-files.component.html
+++ b/src/app/components/metadata/optional/metadata-files/metadata-files.component.html
@@ -22,7 +22,7 @@
({{ file.file_size | filesize }})
-
+
close
diff --git a/src/app/components/metadata/optional/metadata-files/metadata-files.component.ts b/src/app/components/metadata/optional/metadata-files/metadata-files.component.ts
index 42a7e4c7..5c5130ba 100644
--- a/src/app/components/metadata/optional/metadata-files/metadata-files.component.ts
+++ b/src/app/components/metadata/optional/metadata-files/metadata-files.component.ts
@@ -1,9 +1,10 @@
-import { Component, computed, input } from '@angular/core';
+import { Component, computed, EventEmitter, inject, input, Output } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatDividerModule } from '@angular/material/divider';
import { MatIconModule } from '@angular/material/icon';
import { AnyEntity, FileTuple, PhysicalEntity } from 'src/app/metadata';
import { FilesizePipe, TranslatePipe } from 'src/app/pipes';
+import { CounterService } from 'src/app/services/single-number-counter.service';
@Component({
selector: 'app-metadata-files',
@@ -14,11 +15,21 @@ import { FilesizePipe, TranslatePipe } from 'src/app/pipes';
})
export class MetadataFilesComponent {
public entity = input.required();
+ public propertyType = 'metadata_files';
+ @Output() itemAdded = new EventEmitter<{ item: object; type: string }>();
isPhysical = computed(() => this.entity() instanceof PhysicalEntity);
+ #counterService = inject(CounterService);
+
public removeProperty(property: string, index: number) {
if (Array.isArray(this.entity()[property])) {
const removed = this.entity()[property].splice(index, 1)[0];
+
+ if (this.#counterService.isNew(removed)) {
+ this.#counterService.decrementCounter(this.propertyType);
+ this.#counterService.removeTracking(removed);
+ }
+
if (!removed) {
return console.warn('No item removed');
}
@@ -77,6 +88,7 @@ export class MetadataFilesComponent {
const metadataFile = await readfile(file);
if (!metadataFile) continue;
this.entity().metadata_files.push(metadataFile);
+ this.itemAdded.emit({ item: metadataFile, type: this.propertyType });
}
}
}
diff --git a/src/app/components/metadata/optional/optional-card-list/optional-card-list.component.ts b/src/app/components/metadata/optional/optional-card-list/optional-card-list.component.ts
index 0d767a50..c1717924 100644
--- a/src/app/components/metadata/optional/optional-card-list/optional-card-list.component.ts
+++ b/src/app/components/metadata/optional/optional-card-list/optional-card-list.component.ts
@@ -10,6 +10,7 @@ import {
} from 'src/app/pipes/tuple-helper.pipes';
import { MetadataCommunicationService } from 'src/app/services/metadata-communication.service';
import type { DataTuple, IDescriptionValueTuple, IDimensionTuple } from '@kompakkt/common';
+import { CounterService } from 'src/app/services/single-number-counter.service';
@Component({
selector: 'app-optional-card-list',
@@ -26,11 +27,18 @@ import type { DataTuple, IDescriptionValueTuple, IDimensionTuple } from '@kompak
})
export class OptionalCardListComponent {
#metadataCommunicationService = inject(MetadataCommunicationService);
+ #counterService = inject(CounterService);
optionalData = input.required();
propertyType = input('');
public onRemove(index: number) {
+ const item = this.optionalData()[index];
+
+ if (this.#counterService.isNew(item)) {
+ this.#counterService.decrementCounter(this.propertyType());
+ this.#counterService.removeTracking(item);
+ }
this.optionalData().splice(index, 1);
}
diff --git a/src/app/services/dialog-helper.service.ts b/src/app/services/dialog-helper.service.ts
index 604b947b..90d352d9 100644
--- a/src/app/services/dialog-helper.service.ts
+++ b/src/app/services/dialog-helper.service.ts
@@ -32,6 +32,7 @@ import {
RemoveFromCompilationResult,
} from '../dialogs/remove-from-compilation/remove-from-compilation.component';
import { ManageOwnershipComponent } from '../dialogs/manage-ownership/manage-ownership.component';
+import { CounterService } from './single-number-counter.service';
@Injectable({
providedIn: 'root',
@@ -40,6 +41,7 @@ export class DialogHelperService {
#account = inject(AccountService);
#dialog = inject(MatDialog);
#events = inject(EventsService);
+ #counter = inject(CounterService);
public openLoginDialog() {
const ref = this.#dialog.open(AuthDialogComponent);
@@ -120,6 +122,7 @@ export class DialogHelperService {
firstValueFrom(ref.afterClosed()).then(() => {
this.#account.updateTrigger$.next(Collection.entity);
this.#events.updateSearchEvent();
+ this.#counter.resetAll();
});
return ref;
diff --git a/src/app/services/single-number-counter.service.ts b/src/app/services/single-number-counter.service.ts
new file mode 100644
index 00000000..f3299000
--- /dev/null
+++ b/src/app/services/single-number-counter.service.ts
@@ -0,0 +1,45 @@
+import { Injectable, signal, WritableSignal } from '@angular/core';
+
+@Injectable({ providedIn: 'root' })
+export class CounterService {
+ private counters = new Map>();
+ private newItems = new Set