Skip to content

Commit 6619ee8

Browse files
committed
Merge branch 'feat/central-loader-teams' into experiment
2 parents 1eb0ad2 + 6ce9057 commit 6619ee8

14 files changed

Lines changed: 189 additions & 138 deletions

src/app/component/teams-groups-editor/selectable-list.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<div class="selectable-list">
33
<div class="selectable-list-header">
44
<span>{{ title }}</span>
5-
<button mat-icon-button (click)="toggleEditMode()" title="Edit {{ title }}" color="red" *ngIf="!editMode && !relationshipEditMode">
5+
<button mat-icon-button *ngIf="canEdit && !relationshipEditMode" (click)="toggleEditMode()" title="Edit {{ title }}" color="red">
66
<mat-icon>edit</mat-icon>
77
</button>
88
<span *ngIf="editMode" >
@@ -28,7 +28,7 @@
2828
(click)="onItemClicked(name)">
2929
<span *ngIf="editMode && editingOrgName === name; then editItem else displayItem"></span>
3030
<ng-template #editItem>
31-
<input #editInput [(ngModel)]="editingName" (keydown.enter)="saveEditedItem(name)" (blur)="saveEditedItem(name)" />
31+
<input #editInput [(ngModel)]="editingName" (keydown.escape)="cancelEditItem(name)" (keydown.enter)="saveEditedItem(name)" (blur)="saveEditedItem(name)" />
3232
</ng-template>
3333
<ng-template #displayItem>
3434
<span>{{ name }}</span>

src/app/component/teams-groups-editor/selectable-list.component.ts

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ export class SelectableListComponent {
1111
@Input() items: string[] = [];
1212
@Input() selectedItem: string | null = null;
1313
@Input() highlightedItems: string[] = [];
14+
@Input() canEdit = false;
1415
@Input() editMode = false;
1516
@Input() addLabel = 'Add';
1617
@Input() typeLabel = '';
@@ -32,15 +33,10 @@ export class SelectableListComponent {
3233

3334
onItemClicked(name: string) {
3435
console.log(`Item clicked: ${name}`);
35-
if (this.relationshipEditMode) {
36-
this.relationshipToggle.emit(name);
37-
if (this.highlightedItems.includes(name)) {
38-
delete this.highlightedItems[this.highlightedItems.indexOf(name)];
39-
} else {
40-
this.highlightedItems.push(name);
41-
}
42-
} else {
36+
if (!this.relationshipEditMode) {
4337
this.itemSelected.emit(name);
38+
} else {
39+
this.relationshipToggle.emit(name);
4440
}
4541
}
4642

@@ -66,12 +62,18 @@ export class SelectableListComponent {
6662
});
6763
}
6864

65+
cancelEditItem(oldName: string) {
66+
console.log(`${perfNow()}: Cancel editing: ${oldName}`);
67+
this.editingName = '';
68+
this.editingOrgName = '';
69+
}
70+
6971
saveEditedItem(oldName: string) {
7072
let newName: string = this.editingName?.trim() || oldName;
73+
console.log(`${perfNow()}: Save Item: Setting new name: ${newName}`);
7174
if (this.editingName?.trim() && this.editingName !== oldName) {
7275
this.renameItem.emit({ oldName, newName });
7376
}
74-
console.log(`${perfNow()}: Save Item: Setting new name: ${newName}`);
7577
this.editingName = '';
7678
this.editingOrgName = '';
7779
}

src/app/component/teams-groups-editor/teams-groups-editor.component.html

Lines changed: 7 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -8,59 +8,38 @@ <h2>Teams &amp; Groups Editor</h2>
88
<app-selectable-list
99
title="Teams"
1010
[items]="localCopyTeams"
11-
[selectedItem]="selectedTeamId"
11+
[selectedItem]="selectedTeam"
1212
[highlightedItems]="highlightedTeams"
13+
[canEdit]="canEdit"
1314
[editMode]="editMode === Mode.TEAMS"
1415
[relationshipEditMode]="editMode === Mode.GROUPS"
1516
(itemSelected)="onTeamSelected($event)"
1617
(addItem)="onAddTeam()"
1718
(renameItem)="onRenameTeam($event)"
1819
(deleteItem)="onDeleteTeam($event)"
1920
(editModeChange)="onTeamsEditModeChange($event)"
20-
(relationshipToggle)="onTeamGroupToggle($event, selectedGroupId)"
21+
(relationshipToggle)="onTeamGroupToggle($event, selectedGroup)"
2122
(save)="onSave()"
2223
(cancel)="onCancelEdit()"
2324
></app-selectable-list>
2425
</div>
2526
<div class="editor-list-panel">
2627
<app-selectable-list
2728
title="Groups"
28-
[items]="localCopyGroupNames"
29-
[selectedItem]="selectedGroupId"
29+
[items]="keys(localCopyTeamGroups)"
30+
[selectedItem]="selectedGroup"
3031
[highlightedItems]="highlightedGroups"
32+
[canEdit]="canEdit"
3133
[editMode]="this.editMode === Mode.GROUPS"
3234
[relationshipEditMode]="this.editMode === Mode.TEAMS"
3335
(itemSelected)="onGroupSelected($event)"
3436
(addItem)="onAddGroup()"
3537
(renameItem)="onRenameGroup($event)"
3638
(deleteItem)="onDeleteGroup($event)"
3739
(editModeChange)="onGroupsEditModeChange($event)"
38-
(relationshipToggle)="onTeamGroupToggle(selectedTeamId, $event)"
40+
(relationshipToggle)="onTeamGroupToggle(selectedTeam, $event)"
3941
(save)="onSave()"
4042
(cancel)="onCancelEdit()"
4143
></app-selectable-list>
4244
</div>
4345
</div>
44-
<!-- <div class="editor-footer">
45-
<button mat-raised-button color="primary" (click)="editMode = !editMode">
46-
{{ editMode ? 'Exit Edit Mode' : 'Enter Edit Mode' }}
47-
</button>
48-
</div> -->
49-
50-
<!-- <div class="editor-debug">
51-
<h3>Editor Debug</h3>
52-
<div>
53-
<table>
54-
<tr><td></td><td>Local</td><td>Original</td></tr>
55-
<tr><td>All teams:</td><td>{{ localCopyTeams }}</td><td>{{ teams }}</td></tr>
56-
<tr>Groups</tr>
57-
<tr *ngFor="let groupName of localCopyGroupNames">
58-
<td>{{ groupName }} </td><td> {{ localCopyTeamGroups[groupName] }} </td><td> {{ teamGroups[groupName] }} </td>
59-
</tr>
60-
<tr><td>Selected Team </td><td> {{ selectedTeamId }} </td></tr>
61-
<tr><td>Selected Group </td><td> {{ selectedGroupId }} </td><td> {{ localCopyTeamGroups[selectedGroupId || ''] }} </td></tr>
62-
</table>
63-
<p>Edit Mode: {{ editMode }}</p>
64-
<p>Highlighted Teams: {{ highlightedTeams }}</p>
65-
<p>Highlighted Groups: {{ highlightedGroups }}</p>
66-
</div> -->

src/app/component/teams-groups-editor/teams-groups-editor.component.ts

Lines changed: 87 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,17 @@ import { perfNow, renameArrayElement } from 'src/app/util/util';
66

77
enum EditMode { NONE, TEAMS, GROUPS};
88

9-
export class TeamsGroupsChanged {
9+
export class SelectionChangedEvent {
10+
selectedTeam: TeamName | null = null;
11+
selectedGroup: GroupName | null = null;
12+
13+
constructor(selectedTeam: TeamName | null, selectedGroup: GroupName | null) {
14+
this.selectedTeam = selectedTeam;
15+
this.selectedGroup = selectedGroup;
16+
}
17+
}
18+
19+
export class TeamsGroupsChangedEvent {
1020
teams: TeamNames = [];
1121
teamGroups: TeamGroups = {};
1222
teamsRenamed: Record<TeamName, TeamName> = {};
@@ -21,44 +31,54 @@ export class TeamsGroupsEditorComponent {
2131
Mode = EditMode;
2232
@Input() teams: TeamNames = [];
2333
@Input() teamGroups: TeamGroups = {};
24-
@Input() highlightedTeams: string[] = [];
25-
@Input() highlightedGroups: string[] = [];
26-
@Output() changed = new EventEmitter<TeamsGroupsChanged>();
34+
@Input() highlightedTeams: TeamName[] = [];
35+
@Input() highlightedGroups: GroupName[] = [];
36+
@Input() canEdit: boolean = true;
37+
@Output() selectionChanged = new EventEmitter<SelectionChangedEvent>();
38+
@Output() namesChanged = new EventEmitter<TeamsGroupsChangedEvent>();
2739

2840
editMode: EditMode = EditMode.NONE;
29-
selectedTeamId: string | null = null;
30-
selectedGroupId: string | null = null;
41+
selectedTeam: string | null = null;
42+
selectedGroup: string | null = null;
3143

3244
// Make a local copy of parent
3345
localCopyTeams: TeamNames = [];
3446
localCopyTeamsRenamed: Record<TeamName, TeamName> = {};
3547
localCopyTeamGroups: TeamGroups = {};
36-
localCopyGroupNames: GroupName[] = [];
3748

3849

3950

4051
ngOnChanges() {
4152
this.makeLocalCopy();
42-
console.log('EDitor: ngOnChanges(): groupNames: ', this.localCopyGroupNames);
43-
if (this.teams.length > 0) {
53+
if (this.teams.length > 0 && !this.selectedTeam && !this.selectedGroup) {
54+
console.log(`${perfNow()}: Teams: No team or group selected, selecting first team`);
4455
this.onTeamSelected(this.teams[0]);
4556
}
4657
}
4758

48-
ngOnInit() {
49-
}
50-
59+
// Makes a local copy to allow editing without affecting the original data
5160
makeLocalCopy() {
5261
this.localCopyTeams = this.teams.slice();
5362
this.localCopyTeamGroups = this.cloneTeamGroups(this.teamGroups);
54-
this.localCopyGroupNames = Object.keys(this.teamGroups);
5563
}
5664

5765
saveLocalCopy() {
5866
this.teams = this.localCopyTeams.slice();
5967
this.teamGroups = this.cloneTeamGroups(this.localCopyTeamGroups);
6068
}
6169

70+
getGroupNames(): GroupName[] {
71+
return Object.keys(this.teamGroups);
72+
}
73+
74+
getRelatedGroups(team: TeamName): GroupName[] {
75+
return Object.keys(this.localCopyTeamGroups).filter(group => this.localCopyTeamGroups[group].includes(team));
76+
}
77+
78+
getRelatedTeams(group: GroupName): TeamNames {
79+
return this.localCopyTeamGroups[group] || [];
80+
}
81+
6282
cloneTeamGroups(original: TeamGroups): TeamGroups {
6383
let clone: TeamGroups = {};
6484
for (let group in original) {
@@ -75,33 +95,46 @@ export class TeamsGroupsEditorComponent {
7595
this.editMode = editing ? EditMode.GROUPS : EditMode.NONE;
7696
}
7797

78-
onTeamGroupToggle(team: string | null, group: string | null) {
98+
onTeamGroupToggle(team: TeamName | null, group: GroupName | null) {
7999
if (!team || !group) return;
80100
const members = this.localCopyTeamGroups[group] || [];
81101
if (members.includes(team)) {
82102
this.localCopyTeamGroups[group] = members.filter(t => t !== team);
83103
} else {
84104
this.localCopyTeamGroups[group] = [...members, team];
85105
}
106+
107+
if (this.editMode === EditMode.TEAMS) {
108+
this.highlightedGroups = this.getRelatedGroups(team);
109+
} else if (this.editMode === EditMode.GROUPS) {
110+
this.highlightedTeams = this.getRelatedTeams(group);
111+
} else {
112+
console.warn(`${perfNow()}: onTeamGroupToggle called in unexpected edit mode: ${this.editMode}`);
113+
}
86114
}
87115

88-
onTeamSelected(teamId: string) {
89-
console.log(`${perfNow()}: Selecting team: ${teamId}`);
90-
this.selectedGroupId = null;
116+
onTeamSelected(team: string) {
117+
console.log(`${perfNow()}: Selecting team: ${team}`);
118+
this.selectedGroup = null;
91119
this.highlightedTeams = [];
92-
this.selectedTeamId = teamId;
93-
this.highlightedGroups = this.localCopyGroupNames.filter(group => (this.localCopyTeamGroups[group] || []).includes(teamId));
120+
this.selectedTeam = team;
121+
this.highlightedGroups = this.getRelatedGroups(team);
122+
123+
this.selectionChanged.emit(new SelectionChangedEvent(team, null));
94124
}
95125

96-
onGroupSelected(groupId: string) {
97-
this.selectedTeamId = null;
126+
onGroupSelected(group: string) {
127+
console.log(`${perfNow()}: Selecting group: ${group}`);
128+
this.selectedTeam = null;
98129
this.highlightedGroups = [];
99-
this.selectedGroupId = groupId;
100-
this.highlightedTeams = this.localCopyTeamGroups[groupId] || [];
130+
this.selectedGroup = group;
131+
this.highlightedTeams = this.getRelatedTeams(group);
132+
133+
this.selectionChanged.emit(new SelectionChangedEvent(null, group));
101134
}
102135

103136
onAddTeam() {
104-
let newName: string = `Team ${this.localCopyTeams.length + 1}`;
137+
let newName: string = this.findNextName(this.localCopyTeams, 'Team');
105138
this.localCopyTeams.push(newName);
106139
this.onTeamSelected(newName);
107140
}
@@ -127,32 +160,29 @@ export class TeamsGroupsEditorComponent {
127160

128161
}
129162

130-
onDeleteTeam(teamId: TeamName) {
131-
this.localCopyTeams = this.localCopyTeams.filter(team => team !== teamId);
163+
onDeleteTeam(team: TeamName) {
164+
this.localCopyTeams = this.localCopyTeams.filter(t => t !== team);
132165
for (let group in this.localCopyTeamGroups) {
133-
this.localCopyTeamGroups[group] = this.localCopyTeamGroups[group].filter(team => team !== teamId);
166+
this.localCopyTeamGroups[group] = this.localCopyTeamGroups[group].filter(team => team !== team);
134167
}
135168

136169
this.onTeamSelected('');
137170
}
138171

139172
onAddGroup() {
140-
let newName: string = `Group ${this.localCopyGroupNames.length + 1}`;
141-
this.localCopyGroupNames.push(newName);
173+
let newName: string = this.findNextName(this.keys(this.localCopyTeamGroups), 'Group');
142174
this.localCopyTeamGroups[newName] = [];
143175
this.onGroupSelected(newName);
144176
}
145177

146178
onRenameGroup(event: { oldName: string, newName: string }) {
147179
console.log(`${perfNow()}: Rename team: ${event.oldName} to ${event.newName}`);
148-
this.localCopyGroupNames = renameArrayElement(this.localCopyGroupNames, event.oldName, event.newName);
149180
this.localCopyTeamGroups[event.newName] = this.localCopyTeamGroups[event.oldName] || [];
150181
delete this.localCopyTeamGroups[event.oldName];
151182
}
152183

153-
onDeleteGroup(groupId: string) {
154-
delete this.localCopyTeamGroups[groupId];
155-
this.localCopyGroupNames = this.localCopyGroupNames.filter(group => group !== groupId);
184+
onDeleteGroup(group: string) {
185+
delete this.localCopyTeamGroups[group];
156186
}
157187

158188
onSave() {
@@ -167,28 +197,41 @@ export class TeamsGroupsEditorComponent {
167197
}
168198

169199
this.editMode = EditMode.NONE;
170-
this.highlightedTeams = [];
171-
this.highlightedGroups = [];
172-
this.selectedTeamId = null;
173-
this.selectedGroupId = null;
174200

175201
// Copy the local copy to the main data
176202
this.saveLocalCopy();
177203

178-
this.changed.emit(
204+
this.namesChanged.emit(
179205
{teams: this.teams, teamGroups: this.teamGroups, teamsRenamed: teamsRenamed}
180206
);
181207
}
182208

183209
onCancelEdit() {
184210
console.log(`${perfNow()}: Cancel editing teams and groups`);
185-
this.editMode = EditMode.NONE;
186-
this.selectedTeamId = null;
187-
this.selectedGroupId = null;
188-
this.highlightedTeams = [];
189-
this.highlightedGroups = [];
190211

191-
// Make a _new_ local copy of the original values
212+
// Recreate the local copy from original values
192213
this.makeLocalCopy();
214+
215+
// Re-select and highlight the original values
216+
if (this.editMode === EditMode.TEAMS) {
217+
this.onTeamSelected(this.selectedTeam || '');
218+
} else if (this.editMode === EditMode.GROUPS) {
219+
this.onGroupSelected(this.selectedGroup || '');
220+
}
221+
this.editMode = EditMode.NONE;
222+
}
223+
224+
findNextName(existingNames: string[], prefix: string): string {
225+
let i: number = existingNames.length + 1;
226+
let newName: string | null = null;
227+
while (newName == null || existingNames.includes(newName)) {
228+
newName = `${prefix} ${i}`;
229+
i++;
230+
}
231+
return newName;
232+
}
233+
234+
keys(obj: any): string[] {
235+
return Object.keys(obj);
193236
}
194237
}

src/app/model/meta-store.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export class MetaStore {
2222
teams: TeamNames = [];
2323
activityFiles: string[] = [];
2424
teamProgressFile: string = '';
25+
allowChangeTeamNameInBrowser: boolean = false;
2526

2627
public init(metaData: any): void {
2728
if (metaData) {
@@ -33,6 +34,7 @@ export class MetaStore {
3334
this.teams = metaData.teams || this.teams || [];
3435
this.activityFiles = metaData.activityFiles || this.activityFiles || [];
3536
this.teamProgressFile = metaData.teamProgressFile || this.teamProgressFile || '';
37+
if (metaData.allowChangeTeamNameInBrowser !== undefined) this.allowChangeTeamNameInBrowser = metaData.allowChangeTeamNameInBrowser;
3638
}
3739
}
3840

@@ -44,7 +46,6 @@ export class MetaStore {
4446

4547
public saveToLocalStorage() {
4648
let yamlStr: string = this.yamlService.stringify({teams: this.teams, teamGroups: this.teamGroups});
47-
console.log(`${perfNow()}: Saved meta.team: ${yamlStr}`);
4849
localStorage.setItem(LOCALSTORAGE_KEY, yamlStr);
4950
}
5051

0 commit comments

Comments
 (0)