Skip to content

Commit be9eb62

Browse files
committed
Settings: Storing changed progress stages
1 parent fe40b82 commit be9eb62

6 files changed

Lines changed: 150 additions & 32 deletions

File tree

src/app/component/activity-description/activity-description.component.html

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ <h1>
2020

2121
<div class="activity-subheader">
2222
<span class="level">Level {{ currentActivity.level }}</span>
23-
<div class="uuid"><span class="uuid-label">id: </span><span class="uuid-value">{{ currentActivity.uuid }}</span></div>
23+
<div class="uuid">
24+
<span class="uuid-label">id: </span><span class="uuid-value">{{ currentActivity.uuid }}</span>
25+
</div>
2426
</div>
2527
<mat-accordion class="activity-details" multi="true">
2628
<mat-expansion-panel *ngIf="currentActivity.description?.hasContent()" [expanded]="true">
@@ -213,7 +215,8 @@ <h3><b>Usefulness</b></h3>
213215
*ngFor="let openCRE of currentActivity?.references?.openCRE"
214216
target="_blank"
215217
[href]="openCRE">
216-
View OpenCRE</a>
218+
View OpenCRE</a
219+
>
217220
</span>
218221
<ng-template #creEmpty>-</ng-template>
219222
</span>
@@ -336,9 +339,7 @@ <h4 class="tool-name" [innerHTML]="implement['name']"></h4>
336339
<mat-panel-title>
337340
<b>Implemented by</b>
338341
<div class="references-summary" [class.hidden]="implementedByPanel.expanded">
339-
<span
340-
class="ref-section"
341-
*ngFor="let progressTitle of progressTitlesWithTeams">
342+
<span class="ref-section" *ngFor="let progressTitle of progressTitlesWithTeams">
342343
<strong>{{ progressTitle }}:</strong>
343344
<span class="ref-values">
344345
<span
@@ -357,7 +358,9 @@ <h4 class="tool-name" [innerHTML]="implement['name']"></h4>
357358
<div class="reference-column" *ngFor="let progressTitle of progressTitlesWithTeams">
358359
<div class="reference-header">{{ progressTitle }}</div>
359360
<div class="reference-values">
360-
<span class="reference-value" *ngFor="let teamName of teamsByProgressTitle.get(progressTitle)">
361+
<span
362+
class="reference-value"
363+
*ngFor="let teamName of teamsByProgressTitle.get(progressTitle)">
361364
{{ teamName }}
362365
</span>
363366
</div>
@@ -369,7 +372,9 @@ <h4 class="tool-name" [innerHTML]="implement['name']"></h4>
369372
</mat-expansion-panel>
370373
</mat-accordion>
371374

372-
<div class="tags-section" *ngIf="currentActivity.tags?.length && currentActivity.tags?.[0] !== 'none'">
375+
<div
376+
class="tags-section"
377+
*ngIf="currentActivity.tags?.length && currentActivity.tags?.[0] !== 'none'">
373378
<div class="tags-container">
374379
<span class="tag-chip" *ngFor="let tag of currentActivity.tags">{{ tag }}</span>
375380
</div>

src/app/component/activity-description/activity-description.component.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export class ActivityDescriptionComponent implements OnInit, OnChanges {
7979
this.ResourceLabel = dataStore.getMetaString('labels', activity.difficultyOfImplementation.resources - 1);
8080
this.UsefulnessLabel = dataStore.getMetaString('labels', activity.usefulness - 1);
8181
/* eslint-enable */
82-
82+
8383
// Get teams that have implemented this activity
8484
this.updateTeamsImplemented();
8585
}
@@ -89,7 +89,7 @@ export class ActivityDescriptionComponent implements OnInit, OnChanges {
8989
this.teamsImplemented.clear();
9090
this.teamsByProgressTitle.clear();
9191
this.progressTitlesWithTeams = [];
92-
92+
9393
const dataStore = this.loader.datastore;
9494
if (!dataStore || !dataStore.progressStore || !dataStore.meta || !this.currentActivity.uuid) {
9595
return;
@@ -108,11 +108,11 @@ export class ActivityDescriptionComponent implements OnInit, OnChanges {
108108
for (const teamName of teams) {
109109
const progressTitle = progressStore.getTeamProgressTitle(activityUuid, teamName);
110110
const progressValue = progressStore.getTeamActivityProgressValue(activityUuid, teamName);
111-
111+
112112
// Only include teams that have made progress (value > 0)
113113
if (progressValue > 0) {
114114
this.teamsImplemented.set(teamName, progressTitle);
115-
115+
116116
// Group teams by progress title
117117
if (!this.teamsByProgressTitle.has(progressTitle)) {
118118
this.teamsByProgressTitle.set(progressTitle, []);

src/app/model/progress-store.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,47 @@ export class ProgressStore {
104104
}
105105
}
106106

107+
public renameProgressTitle(oldTitle: ProgressTitle, newTitle: ProgressTitle): void {
108+
if (!this._progress) return;
109+
110+
console.log(`${perfNow()} Renaming progress title '${oldTitle}' to '${newTitle}' in progress store`); // eslint-disable-line
111+
112+
// Update progress data
113+
for (let activityUuid in this._progress) {
114+
for (let teamName in this._progress[activityUuid]) {
115+
if (this._progress[activityUuid][teamName][oldTitle]) {
116+
this._progress[activityUuid][teamName][newTitle] = this._progress[activityUuid][teamName][oldTitle]; // eslint-disable-line
117+
delete this._progress[activityUuid][teamName][oldTitle];
118+
}
119+
}
120+
}
121+
122+
// Update temporary progress data
123+
for (let activityUuid in this._tempProgress) {
124+
for (let teamName in this._tempProgress[activityUuid]) {
125+
if (this._tempProgress[activityUuid][teamName][oldTitle]) {
126+
this._tempProgress[activityUuid][teamName][newTitle] = this._tempProgress[activityUuid][teamName][oldTitle]; // eslint-disable-line
127+
delete this._tempProgress[activityUuid][teamName][oldTitle];
128+
}
129+
}
130+
}
131+
132+
// Update progress titles arrays
133+
if (this._progressTitles) {
134+
const index = this._progressTitles.indexOf(oldTitle);
135+
if (index !== -1) {
136+
this._progressTitles[index] = newTitle;
137+
}
138+
}
139+
140+
if (this._progressTitlesDescOrder) {
141+
const index = this._progressTitlesDescOrder.indexOf(oldTitle);
142+
if (index !== -1) {
143+
this._progressTitlesDescOrder[index] = newTitle;
144+
}
145+
}
146+
}
147+
107148
public getTeamProgress(
108149
activityUuid: Uuid,
109150
teamName: TeamName,

src/app/pages/settings/settings.component.html

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -85,12 +85,11 @@ <h2>Progress Definitions</h2>
8585
<ng-container formArrayName="definitions">
8686
<ng-container *ngFor="let definition of definitionsFormArray.controls; let i = index">
8787
<ng-container [formGroup]="getDefinitionGroup(i)">
88+
<input type="hidden" formControlName="pid" />
89+
8890
<mat-form-field appearance="outline" class="grid-cell progress-key edit">
8991
<mat-label>Name</mat-label>
90-
<input
91-
matInput
92-
required minlength="1"
93-
formControlName="key" />
92+
<input matInput required minlength="1" formControlName="key" />
9493
</mat-form-field>
9594

9695
<mat-form-field appearance="outline" class="grid-cell progress-score edit">
@@ -110,7 +109,8 @@ <h2>Progress Definitions</h2>
110109
<mat-label>Definition</mat-label>
111110
<textarea
112111
matInput
113-
required minlength="1"
112+
required
113+
minlength="1"
114114
formControlName="definition"
115115
cdkTextareaAutosize
116116
cdkAutosizeMinRows="1"

src/app/pages/settings/settings.component.spec.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
import { HttpClient, HttpHandler } from '@angular/common/http';
22
import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
3+
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
4+
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
5+
import { MatSelectModule } from '@angular/material/select';
6+
import { MatFormFieldModule } from '@angular/material/form-field';
7+
import { MatInputModule } from '@angular/material/input';
8+
import { MatSliderModule } from '@angular/material/slider';
9+
import { MatCardModule } from '@angular/material/card';
10+
import { MatIconModule } from '@angular/material/icon';
11+
import { MatButtonModule } from '@angular/material/button';
312
import { SettingsComponent } from './settings.component';
413
import { SettingsService } from '../../service/settings/settings.service';
514
import { LoaderService } from '../../service/loader/data-loader.service';
@@ -29,11 +38,27 @@ describe('SettingsComponent', () => {
2938

3039
beforeEach(async () => {
3140
await mockLoaderService.load();
32-
settingsService = jasmine.createSpyObj('SettingsService', ['getMaxLevel', 'setMaxLevel']);
41+
settingsService = jasmine.createSpyObj('SettingsService', [
42+
'getMaxLevel',
43+
'setMaxLevel',
44+
'getDateFormat',
45+
'setDateFormat',
46+
]);
3347
modalComponent = jasmine.createSpyObj('ModalMessageComponent', ['openDialog']);
3448

3549
await TestBed.configureTestingModule({
36-
// imports: [BrowserAnimationsModule],
50+
imports: [
51+
FormsModule,
52+
ReactiveFormsModule,
53+
NoopAnimationsModule,
54+
MatSelectModule,
55+
MatFormFieldModule,
56+
MatInputModule,
57+
MatSliderModule,
58+
MatCardModule,
59+
MatIconModule,
60+
MatButtonModule,
61+
],
3762
declarations: [SettingsComponent],
3863
providers: [
3964
HttpClient,

src/app/pages/settings/settings.component.ts

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
} from 'src/app/component/modal-message/modal-message.component';
1111
import { dateStr, deepCopy } from 'src/app/util/util';
1212
import { MetaStore } from 'src/app/model/meta-store';
13+
import { ProgressStore } from 'src/app/model/progress-store';
1314

1415
@Component({
1516
selector: 'app-settings',
@@ -18,6 +19,7 @@ import { MetaStore } from 'src/app/model/meta-store';
1819
})
1920
export class SettingsComponent implements OnInit {
2021
meta!: MetaStore;
22+
progressStore!: ProgressStore;
2123
dataStoreMaxLevel!: number;
2224
selectedMaxLevel!: number;
2325
selectedMaxLevelCaption: String = '';
@@ -82,6 +84,10 @@ export class SettingsComponent implements OnInit {
8284
this.selectedMaxLevel = this.settingsService.getMaxLevel() || this.dataStoreMaxLevel;
8385
this.updateMaxLevelCaption();
8486

87+
if (dataStore.progressStore) {
88+
this.progressStore = dataStore.progressStore;
89+
}
90+
8591
// Load progress definitions
8692
if (dataStore.meta) {
8793
this.meta = dataStore.meta;
@@ -134,9 +140,10 @@ export class SettingsComponent implements OnInit {
134140
private updateProgressDefinitionsForm(): void {
135141
this.definitionsFormArray.clear();
136142

137-
Object.entries(this.tempProgressDefinitions).forEach(([key, progDef]) => {
143+
Object.entries(this.tempProgressDefinitions).forEach(([key, progDef], index) => {
138144
this.definitionsFormArray.push(
139145
this.formBuilder.group({
146+
pid: [index],
140147
key: [key],
141148
score: [progDef.score * 100],
142149
definition: [progDef.definition],
@@ -154,6 +161,7 @@ export class SettingsComponent implements OnInit {
154161
this.definitionsFormArray.insert(
155162
index,
156163
this.formBuilder.group({
164+
pid: [-1], // -1 indicates a new item
157165
key: [''],
158166
score: [score],
159167
definition: [''],
@@ -166,35 +174,74 @@ export class SettingsComponent implements OnInit {
166174
}
167175

168176
saveProgressDefinitions(): void {
177+
// Validate form
169178
if (this.progressDefinitionsForm.invalid) {
170179
this.progressDefinitionsForm.markAllAsTouched();
171-
172-
this.modal.openDialog(new DialogInfo(
173-
'All definitions must have a name, and the score must be between 0% and 100%'
174-
));
175-
176-
return; // Exit early if form is invalid
180+
181+
this.modal.openDialog(
182+
new DialogInfo(
183+
'All definitions must have a name, and the score must be between 0% and 100%'
184+
)
185+
);
186+
return;
177187
}
178188

179-
// Build new progress definitions from form data
189+
// Get the original keys in order
190+
const originalKeys = Object.keys(this.tempProgressDefinitions);
191+
192+
// Build new progress definitions from form data and identify changes
180193
const newProgressDefinitions: ProgressDefinitions = {};
181-
182-
this.definitionsFormArray.controls.forEach((control) => {
194+
const renamedItems: Array<{ originalKey: string; newKey: string; pid: number }> = [];
195+
196+
this.definitionsFormArray.controls.forEach(control => {
183197
const formGroup = control as FormGroup;
198+
const pid = formGroup.get('pid')?.value;
184199
const key = formGroup.get('key')?.value;
185200
const score = formGroup.get('score')?.value / 100; // Convert from percentage back to decimal
186201
const definition = formGroup.get('definition')?.value;
187-
188-
if (key && key.trim()) { // Only add if key is not empty
202+
203+
if (key && key.trim()) {
204+
// Only add if key is not empty
189205
newProgressDefinitions[key] = {
190206
score: score,
191-
definition: definition
207+
definition: definition,
192208
};
209+
210+
// Check if this is a renamed item
211+
if (pid >= 0 && pid < originalKeys.length) {
212+
const originalKey = originalKeys[pid];
213+
if (originalKey !== key) {
214+
renamedItems.push({
215+
originalKey: originalKey,
216+
newKey: key,
217+
pid: pid,
218+
});
219+
}
220+
}
193221
}
194222
});
195223

224+
// Log renamed items for debugging/tracking
225+
if (renamedItems.length > 0) {
226+
console.log('Renamed progress definitions:', renamedItems);
227+
for (const item of renamedItems) {
228+
console.log(`- PID ${item.pid}: "${item.originalKey}" renamed to "${item.newKey}"`);
229+
this.progressStore.renameProgressTitle(item.originalKey, item.newKey);
230+
}
231+
}
232+
196233
// Sort the definitions by score in ascending order
197234
this.tempProgressDefinitions = this.sortObjectByScore(newProgressDefinitions);
235+
236+
// Save the new progress definitions to MetaStore and localStorage
237+
this.meta.saveProgressDefinition(this.tempProgressDefinitions);
238+
239+
// Reinitialize the ProgressStore with the new definitions
240+
this.progressStore.init(this.tempProgressDefinitions);
241+
242+
// Save progress data to localStorage
243+
this.progressStore.saveToLocalStorage();
244+
198245
this.editingProgressDefinitions = false;
199246
this.updateProgressDefinitionsForm();
200247
}
@@ -220,7 +267,7 @@ export class SettingsComponent implements OnInit {
220267
*/
221268
sortObjectByScore<T extends { score: number }>(obj: { [key: string]: T }): { [key: string]: T } {
222269
const sortedEntries = Object.entries(obj).sort(([, a], [, b]) => a.score - b.score);
223-
270+
224271
// Convert back to object
225272
return Object.fromEntries(sortedEntries);
226273
}

0 commit comments

Comments
 (0)