Skip to content

Commit 1a89e3a

Browse files
committed
Activity: Display Tags and Implemented by
1 parent a2e6922 commit 1a89e3a

3 files changed

Lines changed: 149 additions & 28 deletions

File tree

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

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,35 @@
5555
list-style: none;
5656
}
5757

58+
/* Ensure panel titles don't wrap */
59+
mat-panel-title b {
60+
white-space: nowrap;
61+
}
62+
63+
/* Implemented By Grid - dynamic columns based on content */
64+
.implemented-by-grid {
65+
display: grid;
66+
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
67+
gap: 20px;
68+
padding: 0;
69+
width: 100%;
70+
}
71+
72+
/* Responsive design for smaller screens */
73+
@media (max-width: 550px) {
74+
.implemented-by-grid {
75+
grid-template-columns: repeat(2, 1fr);
76+
gap: 15px;
77+
}
78+
}
79+
80+
@media (max-width: 350px) {
81+
.implemented-by-grid {
82+
grid-template-columns: 1fr;
83+
gap: 10px;
84+
}
85+
}
86+
5887
.difficulty-summary {
5988
display: flex;
6089
gap: 15px;
@@ -96,7 +125,7 @@
96125
justify-content: space-around;
97126
flex-wrap: wrap;
98127
gap: 15px;
99-
padding: 10px 0;
128+
padding: 0;
100129
}
101130

102131
.difficulty-detail-item {
@@ -197,6 +226,7 @@ mat-icon.mat-icon {
197226
gap: 4px;
198227
flex: 1;
199228
min-width: 0;
229+
max-height: 37px;
200230
}
201231

202232
.ref-section strong {
@@ -224,7 +254,7 @@ mat-icon.mat-icon {
224254
display: grid;
225255
grid-template-columns: repeat(4, 1fr);
226256
gap: 20px;
227-
padding: 15px 0;
257+
padding: 0;
228258
width: 100%;
229259
}
230260

@@ -382,6 +412,30 @@ mat-icon.mat-icon {
382412
margin: 0;
383413
}
384414

415+
.tags-section {
416+
margin-top: 20px;
417+
padding: 16px 0;
418+
}
419+
420+
.tags-container {
421+
display: flex;
422+
flex-wrap: wrap;
423+
gap: 8px;
424+
}
425+
426+
.tag-chip {
427+
display: inline-flex;
428+
align-items: center;
429+
padding: 4px 8px;
430+
background-color: var(--background-secondary);
431+
color: var(--text-secondary);
432+
border-radius: 16px;
433+
font-size: 0.6em;
434+
font-weight: 500;
435+
border: 1px solid var(--text-tertiary);
436+
transition: background-color 0.2s, transform 0.1s;
437+
}
438+
385439
/* Dark Mode Support */
386440
@media (prefers-color-scheme: dark) {
387441
/* Level badges in dark mode - maintain readability */

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

Lines changed: 42 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ <h1>
2222
<span class="level">Level {{ currentActivity.level }}</span>
2323
<div class="uuid"><span class="uuid-label">id: </span><span class="uuid-value">{{ currentActivity.uuid }}</span></div>
2424
</div>
25-
<mat-accordion multi="true">
25+
<mat-accordion class="activity-details" multi="true">
2626
<mat-expansion-panel *ngIf="currentActivity.description?.hasContent()" [expanded]="true">
2727
<mat-expansion-panel-header>
2828
<mat-panel-title>
@@ -209,7 +209,11 @@ <h3><b>Usefulness</b></h3>
209209
<span
210210
class="ref-values"
211211
*ngIf="currentActivity?.references?.openCRE?.length; else creEmpty">
212-
<a href="https://www.opencre.org/" target="_blank" class="cre-link">View OpenCRE</a>
212+
<a
213+
*ngFor="let openCRE of currentActivity?.references?.openCRE"
214+
target="_blank"
215+
[href]="openCRE">
216+
View OpenCRE</a>
213217
</span>
214218
<ng-template #creEmpty>-</ng-template>
215219
</span>
@@ -304,7 +308,7 @@ <h3><b>Usefulness</b></h3>
304308
*ngIf="currentActivity.implementation?.length">
305309
<mat-expansion-panel-header>
306310
<mat-panel-title>
307-
<b>Implementation</b>
311+
<b>Tools</b>
308312
<div class="implementation-preview" [class.hidden]="implementationPanel.expanded">
309313
<span class="tool-count-badge"
310314
>{{ currentActivity.implementation?.length || 0 }} tools</span
@@ -327,35 +331,47 @@ <h4 class="tool-name" [innerHTML]="implement['name']"></h4>
327331
</div>
328332
</mat-expansion-panel>
329333

330-
<mat-expansion-panel>
334+
<mat-expansion-panel [expanded]="isNarrowScreen" #implementedByPanel>
331335
<mat-expansion-panel-header>
332336
<mat-panel-title>
333-
<b>Implemented By</b>
337+
<b>Implemented by</b>
338+
<div class="references-summary" [class.hidden]="implementedByPanel.expanded">
339+
<span
340+
class="ref-section"
341+
*ngFor="let progressTitle of progressTitlesWithTeams">
342+
<strong>{{ progressTitle }}:</strong>
343+
<span class="ref-values">
344+
<span
345+
*ngFor="let teamName of teamsByProgressTitle.get(progressTitle); let last = last"
346+
>{{ teamName }}<span *ngIf="!last">, </span></span
347+
>
348+
</span>
349+
</span>
350+
<span class="ref-section" *ngIf="teamsImplemented.size === 0">
351+
<span class="ref-values">No teams have started this activity yet</span>
352+
</span>
353+
</div>
334354
</mat-panel-title>
335355
</mat-expansion-panel-header>
336-
<div *ngIf="currentActivity.teamsImplemented; then thenBlock; else elseBlock"></div>
337-
<ng-template #thenBlock>
338-
<ul class="teams-implemented-list">
339-
<li *ngFor="let item of currentActivity.teamsImplemented | keyvalue">
340-
<b>{{ item.key }}</b
341-
>:{{ item.value }}
342-
</li>
343-
</ul>
344-
</ng-template>
345-
<ng-template #elseBlock
346-
>teamsImplemented variable not present <br />
347-
<p *ngIf="currentActivity.isImplemented === false">Not Implemented</p>
348-
<p *ngIf="currentActivity.isImplemented === true">Implemented</p>
356+
<div class="implemented-by-grid" *ngIf="teamsImplemented.size > 0; else noTeamsBlock">
357+
<div class="reference-column" *ngFor="let progressTitle of progressTitlesWithTeams">
358+
<div class="reference-header">{{ progressTitle }}</div>
359+
<div class="reference-values">
360+
<span class="reference-value" *ngFor="let teamName of teamsByProgressTitle.get(progressTitle)">
361+
{{ teamName }}
362+
</span>
363+
</div>
364+
</div>
365+
</div>
366+
<ng-template #noTeamsBlock>
367+
<p>No teams have started implementing this activity yet.</p>
349368
</ng-template>
350369
</mat-expansion-panel>
351370
</mat-accordion>
352371

353-
<mat-expansion-panel>
354-
<mat-expansion-panel-header>
355-
<mat-panel-title>
356-
<b>Tags</b>
357-
</mat-panel-title>
358-
</mat-expansion-panel-header>
359-
<p [innerHTML]="currentActivity.tags"></p>
360-
</mat-expansion-panel>
372+
<div class="tags-section" *ngIf="currentActivity.tags?.length && currentActivity.tags?.[0] !== 'none'">
373+
<div class="tags-container">
374+
<span class="tag-chip" *ngFor="let tag of currentActivity.tags">{{ tag }}</span>
375+
</div>
376+
</div>
361377
</div>

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

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {
1313
import { MatAccordion } from '@angular/material/expansion';
1414
import { Activity } from '../../model/activity-store';
1515
import { LoaderService } from '../../service/loader/data-loader.service';
16+
import { TeamName, ProgressTitle } from '../../model/types';
1617

1718
@Component({
1819
selector: 'app-activity-description',
@@ -36,6 +37,9 @@ export class ActivityDescriptionComponent implements OnInit, OnChanges {
3637
ISO22Version: string = 'ISO 27001:2022';
3738
openCREVersion: string = 'OpenCRE';
3839
isNarrowScreen: boolean = false;
40+
teamsImplemented: Map<TeamName, ProgressTitle> = new Map();
41+
teamsByProgressTitle: Map<ProgressTitle, TeamName[]> = new Map();
42+
progressTitlesWithTeams: ProgressTitle[] = [];
3943

4044
@ViewChildren(MatAccordion) accordion!: QueryList<MatAccordion>;
4145

@@ -75,6 +79,53 @@ export class ActivityDescriptionComponent implements OnInit, OnChanges {
7579
this.ResourceLabel = dataStore.getMetaString('labels', activity.difficultyOfImplementation.resources - 1);
7680
this.UsefulnessLabel = dataStore.getMetaString('labels', activity.usefulness - 1);
7781
/* eslint-enable */
82+
83+
// Get teams that have implemented this activity
84+
this.updateTeamsImplemented();
85+
}
86+
}
87+
88+
updateTeamsImplemented() {
89+
this.teamsImplemented.clear();
90+
this.teamsByProgressTitle.clear();
91+
this.progressTitlesWithTeams = [];
92+
93+
const dataStore = this.loader.datastore;
94+
if (!dataStore || !dataStore.progressStore || !dataStore.meta || !this.currentActivity.uuid) {
95+
return;
96+
}
97+
98+
const teams = dataStore.meta.teams;
99+
const progressStore = dataStore.progressStore;
100+
const activityUuid = this.currentActivity.uuid;
101+
102+
// Get all progress titles (excluding the first one which is "Not started")
103+
const inProgressTitles = progressStore.getInProgressTitles();
104+
const completedTitle = progressStore.getCompletedProgressTitle();
105+
const allProgressTitles = [...inProgressTitles, completedTitle];
106+
107+
// Check each team to see if they have started or completed this activity
108+
for (const teamName of teams) {
109+
const progressTitle = progressStore.getTeamProgressTitle(activityUuid, teamName);
110+
const progressValue = progressStore.getTeamActivityProgressValue(activityUuid, teamName);
111+
112+
// Only include teams that have made progress (value > 0)
113+
if (progressValue > 0) {
114+
this.teamsImplemented.set(teamName, progressTitle);
115+
116+
// Group teams by progress title
117+
if (!this.teamsByProgressTitle.has(progressTitle)) {
118+
this.teamsByProgressTitle.set(progressTitle, []);
119+
}
120+
this.teamsByProgressTitle.get(progressTitle)!.push(teamName);
121+
}
122+
}
123+
124+
// Create ordered list of progress titles that have teams (skip "Not started")
125+
for (const progressTitle of allProgressTitles) {
126+
if (this.teamsByProgressTitle.has(progressTitle)) {
127+
this.progressTitlesWithTeams.push(progressTitle);
128+
}
78129
}
79130
}
80131

0 commit comments

Comments
 (0)