From 777e101b5ef2e961b2c3abb49579ddcd85ba905d Mon Sep 17 00:00:00 2001
From: aasandei-vsp
Date: Thu, 7 May 2026 16:47:13 +0300
Subject: [PATCH] Support multiple Archive Stewards per archive
Adapt the Legacy Planning panel to render one card per Archive Steward
instead of a single steward record. Each card has a $PR-blue-100 border,
a $PR-blue-25 hover background, and a material-icons edit icon in the
top-right; the whole card is clickable and reuses the existing edit form
to update that steward. The bottom button becomes "Add a new Archive
Steward" and opens the same form with no pre-filled directive. The
legacy-contact requirement now only gates the very first steward; once
at least one exists, additional ones can be added without it. Errors
when loading or saving stewards surface through MessageService.showError.
Issue: PER-10415
---
.../directive-dialog.component.html | 6 +-
.../directive-dialog.component.spec.ts | 73 ++++++++
.../directive-dialog.component.ts | 25 ++-
.../directive-display.component.html | 72 ++++----
.../directive-display.component.scss | 21 +++
.../directive-display.component.spec.ts | 157 ++++++++++++++----
.../directive-display.component.ts | 51 ++++--
.../directive-display/test-utils.ts | 29 +++-
.../directive-edit.component.ts | 4 +-
.../services/api/directive.repo.spec.ts | 9 +-
src/app/shared/services/api/directive.repo.ts | 12 +-
src/styles/_colors.scss | 2 +
12 files changed, 358 insertions(+), 103 deletions(-)
diff --git a/src/app/directive/components/directive-dialog/directive-dialog.component.html b/src/app/directive/components/directive-dialog/directive-dialog.component.html
index d8a98eb42..247ce6f5a 100644
--- a/src/app/directive/components/directive-dialog/directive-dialog.component.html
+++ b/src/app/directive/components/directive-dialog/directive-dialog.component.html
@@ -2,8 +2,8 @@
@default {
}
@@ -12,7 +12,7 @@
}
diff --git a/src/app/directive/components/directive-dialog/directive-dialog.component.spec.ts b/src/app/directive/components/directive-dialog/directive-dialog.component.spec.ts
index 4397c0ef1..c17f564c7 100644
--- a/src/app/directive/components/directive-dialog/directive-dialog.component.spec.ts
+++ b/src/app/directive/components/directive-dialog/directive-dialog.component.spec.ts
@@ -7,8 +7,32 @@ import { ApiService } from '@shared/services/api/api.service';
import { provideHttpClientTesting } from '@angular/common/http/testing';
import { AccountService } from '@shared/services/account/account.service';
import { AccountVO } from '@models/account-vo';
+import { Directive } from '@models/directive';
import { DirectiveDialogComponent } from './directive-dialog.component';
+const buildDirective = (
+ directiveId: string,
+ stewardEmail: string,
+ note: string,
+): Directive =>
+ new Directive({
+ directiveId,
+ archiveId: 1,
+ type: 'admin',
+ createdDt: new Date(),
+ updatedDt: new Date(),
+ trigger: {
+ directiveTriggerId: directiveId,
+ directiveId,
+ type: 'admin',
+ createdDt: new Date(),
+ updatedDt: new Date(),
+ },
+ steward: { email: stewardEmail, name: '' },
+ note,
+ executionDt: null,
+ });
+
describe('DirectiveDialogComponent', () => {
let component: DirectiveDialogComponent;
let fixture: ComponentFixture;
@@ -38,4 +62,53 @@ describe('DirectiveDialogComponent', () => {
it('should create', () => {
expect(component).toBeTruthy();
});
+
+ it('should switch to edit mode with the selected directive', () => {
+ const directive = buildDirective(
+ 'directive-1',
+ 'first@example.com',
+ 'note',
+ );
+ component.switchToEdit(directive);
+
+ expect(component.mode).toBe('edit');
+ expect(component.editing).toBe(directive);
+ });
+
+ it('should switch to edit mode with null when adding a new steward', () => {
+ component.switchToEdit(null);
+
+ expect(component.mode).toBe('edit');
+ expect(component.editing).toBeNull();
+ });
+
+ it('should append a saved directive when its id is not in the list', () => {
+ component.directives = [
+ buildDirective('directive-1', 'first@example.com', 'one'),
+ ];
+ const newDirective = buildDirective(
+ 'directive-2',
+ 'second@example.com',
+ 'two',
+ );
+
+ component.saveEditedDirective(newDirective);
+
+ expect(component.directives.length).toBe(2);
+ expect(component.directives[1]).toBe(newDirective);
+ expect(component.mode).toBe('display');
+ expect(component.editing).toBeNull();
+ });
+
+ it('should replace a saved directive when its id matches an existing entry', () => {
+ const existing = buildDirective('directive-1', 'old@example.com', 'old');
+ component.directives = [existing];
+ const updated = buildDirective('directive-1', 'new@example.com', 'new');
+
+ component.saveEditedDirective(updated);
+
+ expect(component.directives.length).toBe(1);
+ expect(component.directives[0]).toBe(updated);
+ expect(component.directives[0]).not.toBe(existing);
+ });
});
diff --git a/src/app/directive/components/directive-dialog/directive-dialog.component.ts b/src/app/directive/components/directive-dialog/directive-dialog.component.ts
index 4f473bc61..ecf61d2aa 100644
--- a/src/app/directive/components/directive-dialog/directive-dialog.component.ts
+++ b/src/app/directive/components/directive-dialog/directive-dialog.component.ts
@@ -18,19 +18,32 @@ export class DirectiveDialogComponent {
});
}
public mode: DialogState = 'display';
- public directive: Directive;
+ public directives: Directive[] = [];
+ public editing: Directive | null = null;
- public setSavedDirective(directive: Directive): void {
- this.directive = directive;
+ public setLoadedDirectives(directives: Directive[]): void {
+ this.directives = directives;
}
- public switchToEdit(directive: Directive): void {
- this.setSavedDirective(directive);
+ public switchToEdit(directive: Directive | null): void {
+ this.editing = directive;
this.mode = 'edit';
}
public saveEditedDirective(directive: Directive): void {
- this.setSavedDirective(directive);
+ const matchingIndex = this.directives.findIndex(
+ (existing) => existing.directiveId === directive.directiveId,
+ );
+ if (matchingIndex >= 0) {
+ this.directives = [
+ ...this.directives.slice(0, matchingIndex),
+ directive,
+ ...this.directives.slice(matchingIndex + 1),
+ ];
+ } else {
+ this.directives = [...this.directives, directive];
+ }
+ this.editing = null;
this.mode = 'display';
}
}
diff --git a/src/app/directive/components/directive-display/directive-display.component.html b/src/app/directive/components/directive-display/directive-display.component.html
index 05f8e2c49..a06da56a2 100644
--- a/src/app/directive/components/directive-display/directive-display.component.html
+++ b/src/app/directive/components/directive-display/directive-display.component.html
@@ -25,9 +25,9 @@
>
- Archive Steward: The {{ archiveName }} Archive
+ Archive Stewards: The {{ archiveName }} Archive
- @if (noPlan) {
+ @if (showNoPlanWarning) {
@@ -47,47 +47,61 @@
}
- The Archive Steward for this archive will receive your note when your Legacy
- Plan is activated.
+ Each Archive Steward for this archive will receive your note when your
+ Legacy Plan is activated.
@if (error) {
- An error occured while trying to find the Archive Steward for the current
+ An error occured while trying to find the Archive Stewards for the current
archive. Please reload the page and try again.