Skip to content

Commit 4448075

Browse files
committed
Signal-Migration: Report-Page and ReportConfigModal
1 parent 408342c commit 4448075

4 files changed

Lines changed: 248 additions & 217 deletions

File tree

src/app/component/report-config-modal/report-config-modal.component.html

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ <h3>Display Configuration</h3>
99
<span class="config-row-label">Column Grouping:</span>
1010
<mat-button-toggle-group
1111
class="column-toggle"
12-
[value]="config.columnGrouping"
12+
[value]="config().columnGrouping"
1313
(change)="setColumnGrouping($event.value)">
1414
<mat-button-toggle value="byProgress">By Progress Stage</mat-button-toggle>
1515
<mat-button-toggle value="byTeam">By Team</mat-button-toggle>
@@ -26,42 +26,42 @@ <h3>Activity Attributes</h3>
2626

2727
<div class="checkbox-grid">
2828
<mat-checkbox
29-
[checked]="config.activityAttributes.showUuid"
29+
[checked]="config().activityAttributes.showUuid"
3030
(change)="toggleActivityAttribute('showUuid')">
3131
UUID
3232
</mat-checkbox>
3333
<mat-checkbox
34-
[checked]="config.activityAttributes.showDescription"
34+
[checked]="config().activityAttributes.showDescription"
3535
(change)="toggleActivityAttribute('showDescription')">
3636
Description
3737
</mat-checkbox>
3838
<mat-checkbox
39-
[checked]="config.activityAttributes.showRisk"
39+
[checked]="config().activityAttributes.showRisk"
4040
(change)="toggleActivityAttribute('showRisk')">
4141
Risk
4242
</mat-checkbox>
4343
<mat-checkbox
44-
[checked]="config.activityAttributes.showMeasure"
44+
[checked]="config().activityAttributes.showMeasure"
4545
(change)="toggleActivityAttribute('showMeasure')">
4646
Measure
4747
</mat-checkbox>
4848
<mat-checkbox
49-
[checked]="config.activityAttributes.showDifficultyOfImplementation"
49+
[checked]="config().activityAttributes.showDifficultyOfImplementation"
5050
(change)="toggleActivityAttribute('showDifficultyOfImplementation')">
5151
Difficulty of Implementation
5252
</mat-checkbox>
5353
<mat-checkbox
54-
[checked]="config.activityAttributes.showUsefulness"
54+
[checked]="config().activityAttributes.showUsefulness"
5555
(change)="toggleActivityAttribute('showUsefulness')">
5656
Usefulness
5757
</mat-checkbox>
5858
<mat-checkbox
59-
[checked]="config.activityAttributes.showImplementation"
59+
[checked]="config().activityAttributes.showImplementation"
6060
(change)="toggleActivityAttribute('showImplementation')">
6161
Implementation
6262
</mat-checkbox>
6363
<mat-checkbox
64-
[checked]="config.activityAttributes.showDependsOn"
64+
[checked]="config().activityAttributes.showDependsOn"
6565
(change)="toggleActivityAttribute('showDependsOn')">
6666
Depends On
6767
</mat-checkbox>
@@ -70,32 +70,32 @@ <h3>Activity Attributes</h3>
7070
<!-- References with sub-toggles -->
7171
<div class="references-section">
7272
<mat-checkbox
73-
[checked]="config.activityAttributes.showReferences"
73+
[checked]="config().activityAttributes.showReferences"
7474
(change)="toggleActivityAttribute('showReferences')">
7575
References
7676
</mat-checkbox>
77-
<div class="references-subtoggle" *ngIf="config.activityAttributes.showReferences">
77+
<div class="references-subtoggle" *ngIf="config().activityAttributes.showReferences">
7878
<mat-checkbox
7979
class="subtoggle-checkbox"
80-
[checked]="config.activityAttributes.showReferencesIso27001_2017"
80+
[checked]="config().activityAttributes.showReferencesIso27001_2017"
8181
(change)="toggleActivityAttribute('showReferencesIso27001_2017')">
8282
ISO 27001:2017
8383
</mat-checkbox>
8484
<mat-checkbox
8585
class="subtoggle-checkbox"
86-
[checked]="config.activityAttributes.showReferencesIso27001_2022"
86+
[checked]="config().activityAttributes.showReferencesIso27001_2022"
8787
(change)="toggleActivityAttribute('showReferencesIso27001_2022')">
8888
ISO 27001:2022
8989
</mat-checkbox>
9090
<mat-checkbox
9191
class="subtoggle-checkbox"
92-
[checked]="config.activityAttributes.showReferencesSamm2"
92+
[checked]="config().activityAttributes.showReferencesSamm2"
9393
(change)="toggleActivityAttribute('showReferencesSamm2')">
9494
SAMM2
9595
</mat-checkbox>
9696
<mat-checkbox
9797
class="subtoggle-checkbox"
98-
[checked]="config.activityAttributes.showReferencesOpenCRE"
98+
[checked]="config().activityAttributes.showReferencesOpenCRE"
9999
(change)="toggleActivityAttribute('showReferencesOpenCRE')">
100100
OpenCRE
101101
</mat-checkbox>
@@ -104,38 +104,38 @@ <h3>Activity Attributes</h3>
104104

105105
<div class="references-section">
106106
<mat-checkbox
107-
[checked]="config.activityAttributes.showEvidence"
107+
[checked]="config().activityAttributes.showEvidence"
108108
(change)="toggleActivityAttribute('showEvidence')">
109109
Evidence
110110
</mat-checkbox>
111-
<div class="references-subtoggle" *ngIf="config.activityAttributes.showEvidence">
111+
<div class="references-subtoggle" *ngIf="config().activityAttributes.showEvidence">
112112
<mat-checkbox
113113
class="subtoggle-checkbox"
114-
[checked]="config.activityAttributes.showEvidenceTitle"
114+
[checked]="config().activityAttributes.showEvidenceTitle"
115115
(change)="toggleActivityAttribute('showEvidenceTitle')">
116116
Title
117117
</mat-checkbox>
118118
<mat-checkbox
119119
class="subtoggle-checkbox"
120-
[checked]="config.activityAttributes.showEvidenceDescription"
120+
[checked]="config().activityAttributes.showEvidenceDescription"
121121
(change)="toggleActivityAttribute('showEvidenceDescription')">
122122
Description
123123
</mat-checkbox>
124124
<mat-checkbox
125125
class="subtoggle-checkbox"
126-
[checked]="config.activityAttributes.showEvidenceDate"
126+
[checked]="config().activityAttributes.showEvidenceDate"
127127
(change)="toggleActivityAttribute('showEvidenceDate')">
128128
Date Recorded
129129
</mat-checkbox>
130130
<mat-checkbox
131131
class="subtoggle-checkbox"
132-
[checked]="config.activityAttributes.showEvidenceReviewer"
132+
[checked]="config().activityAttributes.showEvidenceReviewer"
133133
(change)="toggleActivityAttribute('showEvidenceReviewer')">
134134
Reviewer
135135
</mat-checkbox>
136136
<mat-checkbox
137137
class="subtoggle-checkbox"
138-
[checked]="config.activityAttributes.showEvidenceAttachments"
138+
[checked]="config().activityAttributes.showEvidenceAttachments"
139139
(change)="toggleActivityAttribute('showEvidenceAttachments')">
140140
Attachments
141141
</mat-checkbox>
@@ -144,12 +144,12 @@ <h3>Activity Attributes</h3>
144144

145145
<div class="checkbox-grid" style="margin-top: 8px">
146146
<mat-checkbox
147-
[checked]="config.activityAttributes.showTags"
147+
[checked]="config().activityAttributes.showTags"
148148
(change)="toggleActivityAttribute('showTags')">
149149
Tags
150150
</mat-checkbox>
151151
</div>
152-
<div class="config-row slider-row" *ngIf="hasAnyMarkdownAttribute">
152+
<div class="config-row slider-row" *ngIf="hasAnyMarkdownAttribute()">
153153
<span class="config-row-label">
154154
Word Cap
155155
<button
@@ -159,7 +159,7 @@ <h3>Activity Attributes</h3>
159159
<mat-icon class="info-icon">info</mat-icon>
160160
</button>
161161
:
162-
<strong>{{ config.descriptionWordCap }}</strong>
162+
<strong>{{ config().descriptionWordCap }}</strong>
163163
</span>
164164
<mat-slider
165165
class="word-cap-slider"
@@ -171,7 +171,7 @@ <h3>Activity Attributes</h3>
171171
#ngSlider
172172
><input
173173
matSliderThumb
174-
[value]="config.descriptionWordCap"
174+
[value]="config().descriptionWordCap"
175175
(input)="
176176
onWordCapChange({ source: ngSliderThumb, parent: ngSlider, value: ngSliderThumb.value })
177177
"
@@ -220,12 +220,16 @@ <h3>Individual Activities</h3>
220220
<p class="config-hint">Search and uncheck individual activities to exclude them.</p>
221221
<mat-form-field appearance="outline" class="search-field">
222222
<mat-label>Search activities or dimensions</mat-label>
223-
<input matInput [(ngModel)]="activitySearchQuery" placeholder="Type to filter..." />
223+
<input
224+
matInput
225+
[ngModel]="activitySearchQuery()"
226+
(ngModelChange)="activitySearchQuery.set($event)"
227+
placeholder="Type to filter..." />
224228
<mat-icon matSuffix>search</mat-icon>
225229
</mat-form-field>
226230
<div class="activity-list">
227231
<mat-checkbox
228-
*ngFor="let activity of filteredActivities"
232+
*ngFor="let activity of filteredActivities()"
229233
[checked]="!isActivityExcluded(activity.uuid)"
230234
(change)="toggleActivity(activity.uuid)">
231235
<span class="activity-checkbox-label">
@@ -240,7 +244,8 @@ <h3>Individual Activities</h3>
240244
<!-- Teams -->
241245
<app-team-selector
242246
[allTeams]="allTeams"
243-
[(selectedTeams)]="config.selectedTeams"
247+
[selectedTeams]="config().selectedTeams"
248+
(selectedTeamsChange)="onTeamsChanged($event)"
244249
[teamGroups]="teamGroups"></app-team-selector>
245250

246251
<mat-divider></mat-divider>

src/app/component/report-config-modal/report-config-modal.component.ts

Lines changed: 64 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, Inject } from '@angular/core';
1+
import { Component, Inject, signal, computed } from '@angular/core';
22
import { MAT_DIALOG_DATA, MatDialogRef, MatDialogModule } from '@angular/material/dialog';
33
import {
44
ReportConfig,
@@ -54,22 +54,37 @@ export interface ReportConfigModalData {
5454
],
5555
})
5656
export class ReportConfigModalComponent {
57-
config: ReportConfig;
57+
config = signal<ReportConfig>(JSON.parse(JSON.stringify({})));
5858
allActivities: Activity[];
5959
allTeams: string[];
6060
allDimensions: string[];
6161
allSubdimensions: string[];
6262
allProgressTitles: ProgressTitle[];
6363
teamGroups: TeamGroups;
64-
activitySearchQuery: string = '';
64+
activitySearchQuery = signal('');
6565
maxWordCap: number = MAX_DESCRIPTION_WORD_CAP;
6666

67+
filteredActivities = computed(() => {
68+
const query = this.activitySearchQuery().trim().toLowerCase();
69+
if (!query) {
70+
return this.allActivities;
71+
}
72+
return this.allActivities.filter(
73+
a => a.name.toLowerCase().includes(query) || a.dimension.toLowerCase().includes(query)
74+
);
75+
});
76+
77+
hasAnyMarkdownAttribute = computed(() => {
78+
const a = this.config().activityAttributes;
79+
return a.showDescription || a.showRisk || a.showMeasure || a.showEvidence;
80+
});
81+
6782
constructor(
6883
public dialogRef: MatDialogRef<ReportConfigModalComponent>,
6984
@Inject(MAT_DIALOG_DATA) public data: ReportConfigModalData
7085
) {
7186
// Deep copy config to avoid mutating the original until save
72-
this.config = JSON.parse(JSON.stringify(data.config));
87+
this.config.set(JSON.parse(JSON.stringify(data.config)));
7388
this.allActivities = data.allActivities;
7489
this.allTeams = data.allTeams;
7590
this.allDimensions = data.allDimensions;
@@ -79,7 +94,7 @@ export class ReportConfigModalComponent {
7994
}
8095

8196
setColumnGrouping(grouping: ColumnGrouping): void {
82-
this.config.columnGrouping = grouping;
97+
this.config.update(c => ({ ...c, columnGrouping: grouping }));
8398
}
8499

85100
wordCapLabel(value: number): string {
@@ -88,75 +103,82 @@ export class ReportConfigModalComponent {
88103

89104
onWordCapChange(event: any): void {
90105
if (event.value != null) {
91-
this.config.descriptionWordCap = event.value;
106+
this.config.update(c => ({ ...c, descriptionWordCap: event.value }));
92107
}
93108
}
94109

95110
// --- Activity attribute toggling ---
96111
toggleActivityAttribute(key: keyof ActivityAttributes): void {
97-
(this.config.activityAttributes as any)[key] = !this.config.activityAttributes[key];
98-
}
99-
100-
get hasAnyMarkdownAttribute(): boolean {
101-
const a = this.config.activityAttributes;
102-
return a.showDescription || a.showRisk || a.showMeasure || a.showEvidence;
112+
this.config.update(c => ({
113+
...c,
114+
activityAttributes: {
115+
...c.activityAttributes,
116+
[key]: !c.activityAttributes[key],
117+
},
118+
}));
103119
}
104120

105121
// --- Dimension toggling ---
106122
isDimensionExcluded(dim: string): boolean {
107-
return this.config.excludedDimensions.includes(dim);
123+
return this.config().excludedDimensions.includes(dim);
108124
}
109125

110126
toggleDimension(dim: string): void {
111-
const idx = this.config.excludedDimensions.indexOf(dim);
112-
if (idx >= 0) {
113-
this.config.excludedDimensions.splice(idx, 1);
114-
} else {
115-
this.config.excludedDimensions.push(dim);
116-
}
127+
this.config.update(c => {
128+
const excluded = [...c.excludedDimensions];
129+
const idx = excluded.indexOf(dim);
130+
if (idx >= 0) {
131+
excluded.splice(idx, 1);
132+
} else {
133+
excluded.push(dim);
134+
}
135+
return { ...c, excludedDimensions: excluded };
136+
});
117137
}
118138

119139
// --- Subdimension toggling ---
120140
isSubdimensionExcluded(subdim: string): boolean {
121-
return this.config.excludedSubdimensions.includes(subdim);
141+
return this.config().excludedSubdimensions.includes(subdim);
122142
}
123143

124144
toggleSubdimension(subdim: string): void {
125-
const idx = this.config.excludedSubdimensions.indexOf(subdim);
126-
if (idx >= 0) {
127-
this.config.excludedSubdimensions.splice(idx, 1);
128-
} else {
129-
this.config.excludedSubdimensions.push(subdim);
130-
}
145+
this.config.update(c => {
146+
const excluded = [...c.excludedSubdimensions];
147+
const idx = excluded.indexOf(subdim);
148+
if (idx >= 0) {
149+
excluded.splice(idx, 1);
150+
} else {
151+
excluded.push(subdim);
152+
}
153+
return { ...c, excludedSubdimensions: excluded };
154+
});
131155
}
132156

133157
// --- Activity toggling ---
134158
isActivityExcluded(uuid: string): boolean {
135-
return this.config.excludedActivities.includes(uuid);
159+
return this.config().excludedActivities.includes(uuid);
136160
}
137161

138162
toggleActivity(uuid: string): void {
139-
const idx = this.config.excludedActivities.indexOf(uuid);
140-
if (idx >= 0) {
141-
this.config.excludedActivities.splice(idx, 1);
142-
} else {
143-
this.config.excludedActivities.push(uuid);
144-
}
163+
this.config.update(c => {
164+
const excluded = [...c.excludedActivities];
165+
const idx = excluded.indexOf(uuid);
166+
if (idx >= 0) {
167+
excluded.splice(idx, 1);
168+
} else {
169+
excluded.push(uuid);
170+
}
171+
return { ...c, excludedActivities: excluded };
172+
});
145173
}
146174

147-
get filteredActivities(): Activity[] {
148-
if (!this.activitySearchQuery.trim()) {
149-
return this.allActivities;
150-
}
151-
const query = this.activitySearchQuery.toLowerCase();
152-
return this.allActivities.filter(
153-
a => a.name.toLowerCase().includes(query) || a.dimension.toLowerCase().includes(query)
154-
);
175+
onTeamsChanged(teams: string[]): void {
176+
this.config.update(c => ({ ...c, selectedTeams: teams }));
155177
}
156178

157179
// --- Actions ---
158180
onSave(): void {
159-
this.dialogRef.close(this.config);
181+
this.dialogRef.close(this.config());
160182
}
161183

162184
onCancel(): void {

0 commit comments

Comments
 (0)