Skip to content

Commit a485788

Browse files
committed
Add #uuid to heatmap url
2 parents d4267b0 + bac6070 commit a485788

4 files changed

Lines changed: 86 additions & 17 deletions

File tree

src/app/pages/circular-heatmap/circular-heatmap.component.html

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,9 @@ <h2>Nothing to show</h2>
106106
<b>Dependencies</b>
107107
</mat-panel-title>
108108
</mat-expansion-panel-header>
109-
<app-dependency-graph [activityName]="showActivityDetails?.name || ''">
109+
<app-dependency-graph
110+
[activityName]="showActivityDetails?.name || ''"
111+
(activityClicked)="onDependencyClicked($event)">
110112
</app-dependency-graph>
111113
</mat-expansion-panel>
112114

@@ -248,9 +250,7 @@ <h2>Nothing to show</h2>
248250
(closed)="onPanelClosed(activity)">
249251
<mat-expansion-panel-header>
250252
<mat-panel-title>
251-
<button
252-
class="title-button"
253-
(click)="openActivityDetails(showActivityCard.dimension, activity['name'])">
253+
<button class="title-button" (click)="openActivityDetails(activity['uuid'])">
254254
{{ activity['name'] }}
255255
</button>
256256
</mat-panel-title>

src/app/pages/circular-heatmap/circular-heatmap.component.ts

Lines changed: 80 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
import { Component, OnInit } from '@angular/core';
1+
import { Component, OnInit, OnDestroy } from '@angular/core';
22
import { equalArray } from 'src/app/util/util';
33
import { LoaderService } from 'src/app/service/loader/data-loader.service';
44
import * as d3 from 'd3';
5-
import { Router } from '@angular/router';
5+
import { Router, ActivatedRoute } from '@angular/router';
6+
import { Location } from '@angular/common';
67
import { MatChip } from '@angular/material/chips';
8+
import { Subject } from 'rxjs';
9+
import { takeUntil, distinctUntilChanged } from 'rxjs/operators';
710
import * as md from 'markdown-it';
811
import {
912
ModalMessageComponent,
@@ -30,7 +33,7 @@ import { SettingsService } from 'src/app/service/settings/settings.service';
3033
templateUrl: './circular-heatmap.component.html',
3134
styleUrls: ['./circular-heatmap.component.css'],
3235
})
33-
export class CircularHeatmapComponent implements OnInit {
36+
export class CircularHeatmapComponent implements OnInit, OnDestroy {
3437
Routing: string = '/activity-description';
3538
markdown: md = md();
3639
showOverlay: boolean = false;
@@ -59,11 +62,16 @@ export class CircularHeatmapComponent implements OnInit {
5962
theme: string;
6063
theme_colors!: Record<string, string>;
6164

65+
private destroy$ = new Subject<void>();
66+
6267
constructor(
6368
private loader: LoaderService,
6469
private sectorService: SectorService,
6570
private settings: SettingsService,
6671
private themeService: ThemeService,
72+
private router: Router,
73+
private route: ActivatedRoute,
74+
private location: Location,
6775
public modal: ModalMessageComponent
6876
) {
6977
this.theme = this.themeService.getTheme();
@@ -118,6 +126,9 @@ export class CircularHeatmapComponent implements OnInit {
118126
// For now, just draw the sectors (no activities yet)
119127
this.loadCircularHeatMap('#chart', this.allSectors, this.dimensionLabels, this.maxLevel);
120128
console.log(`${perfNow()}: Page loaded: Circular Heatmap`);
129+
130+
// Check if there's a URL fragment and open the corresponding activity
131+
this.checkUrlFragmentForActivity();
121132
})
122133
.catch(err => {
123134
this.displayMessage(new DialogInfo(err.message, 'An error occurred'));
@@ -127,7 +138,7 @@ export class CircularHeatmapComponent implements OnInit {
127138
});
128139
});
129140
// Reactively handle theme changes (if user toggles later)
130-
this.themeService.theme$.subscribe((theme: string) => {
141+
this.themeService.theme$.pipe(takeUntil(this.destroy$)).subscribe((theme: string) => {
131142
const css = getComputedStyle(document.body);
132143
this.theme_colors = {
133144
background: css.getPropertyValue('--heatmap-background').trim(),
@@ -141,6 +152,22 @@ export class CircularHeatmapComponent implements OnInit {
141152
});
142153
}
143154

155+
ngOnDestroy(): void {
156+
this.destroy$.next();
157+
this.destroy$.complete();
158+
}
159+
160+
checkUrlFragmentForActivity() {
161+
// Check if there's a URL fragment that might be an activity UUID
162+
this.route.fragment
163+
.pipe(takeUntil(this.destroy$), distinctUntilChanged())
164+
.subscribe(fragment => {
165+
if (fragment && this.dataStore) {
166+
this.navigateToActivityByUuid(fragment);
167+
}
168+
});
169+
}
170+
144171
displayMessage(dialogInfo: DialogInfo) {
145172
this.modal.openDialog(dialogInfo);
146173
}
@@ -245,6 +272,14 @@ export class CircularHeatmapComponent implements OnInit {
245272
return this.sectorService.getSectorProgress(sector.activities);
246273
}
247274

275+
onDependencyClicked(activityName: string) {
276+
console.log(`${perfNow()}: Heat: Dependency clicked: '${activityName}'`);
277+
const activity = this.dataStore?.activityStore?.getActivityByName(activityName);
278+
if (activity?.uuid) {
279+
this.navigateToActivityByUuid(activity.uuid);
280+
}
281+
}
282+
248283
loadCircularHeatMap(
249284
dom_element_to_append_to: string,
250285
dataset: any,
@@ -548,38 +583,71 @@ export class CircularHeatmapComponent implements OnInit {
548583
console.log(`${perfNow()}: Heat: Card Panel closed: '${activity.name}'`);
549584
}
550585

551-
openActivityDetails(dimension: string, activityName: string) {
586+
openActivityDetails(uuid: string) {
552587
// Find the activity in the selected sector
553-
console.log(`${perfNow()}: Heat: Open Overlay: '${activityName}'`);
554-
if (!this.dataStore) {
555-
console.error(`Data store is not initialized. Cannot open activity ${activityName}`);
588+
if (!this.dataStore || !this.dataStore.activityStore) {
589+
console.error(`Data store is not initialized. Cannot open activity ${uuid}`);
556590
return;
557591
}
558592
if (!this.showActivityCard || !this.showActivityCard.activities) {
559593
this.showOverlay = true;
560594
return;
561595
}
562-
const activity = this.showActivityCard.activities.find(
563-
(a: any) => a.activityName === activityName || a.name === activityName
564-
);
596+
597+
const activity: Activity = this.dataStore.activityStore.getActivityByUuid(uuid);
565598
if (!activity) {
566599
this.showOverlay = true;
567600
return;
568601
}
602+
569603
// Prepare navigationExtras and details
570604
/* eslint-disable */
605+
console.log(`${perfNow()}: Heat: Open Overlay: '${activity.name}'`);
571606
this.showActivityDetails = activity;
572607
this.KnowledgeLabel = this.dataStore.getMetaString('knowledgeLabels', activity.difficultyOfImplementation.knowledge);
573608
this.TimeLabel = this.dataStore.getMetaString('labels', activity.difficultyOfImplementation.time);
574609
this.ResourceLabel = this.dataStore.getMetaString('labels', activity.difficultyOfImplementation.resources);
575610
this.UsefulnessLabel = this.dataStore.getMetaString('labels', activity.usefulness);
576611
this.showOverlay = true;
612+
613+
// Update URL with activity UUID as fragment
614+
if (activity.uuid) {
615+
this.router.navigate([], {
616+
relativeTo: this.route,
617+
fragment: activity.uuid,
618+
queryParamsHandling: 'preserve'
619+
});
620+
}
577621
/* eslint-enable */
578622
}
579623

624+
navigateToActivityByUuid(uuid: string) {
625+
console.log(`${perfNow()}: Heat: Attempting to open activity with UUID: ${uuid}`);
626+
if (!this.dataStore || !this.dataStore.activityStore) {
627+
console.error('Data store is not initialized. Cannot open activity by UUID');
628+
return;
629+
}
630+
const activity: Activity = this.dataStore.activityStore.getActivityByUuid(uuid);
631+
const sector = this.allSectors.find(s => s.activities.some(a => a.uuid === uuid));
632+
if (activity && sector) {
633+
this.selectedSector = sector;
634+
this.showActivityCard = sector;
635+
this.openActivityDetails(activity.uuid);
636+
} else {
637+
// Only close the overlay, do not update the URL
638+
this.showOverlay = false;
639+
console.warn(`Heat: Activity with UUID ${uuid} not found.`);
640+
}
641+
}
642+
580643
closeOverlay() {
644+
// Clear the URL fragment when closing overlay
645+
this.router.navigate([], {
646+
relativeTo: this.route,
647+
fragment: undefined,
648+
queryParamsHandling: 'preserve',
649+
});
581650
this.showOverlay = false;
582-
// console.log(`${perfNow()}: Heat: Close Overlay: '${this.old_activityDetails.name}'`);
583651
}
584652

585653
toggleFilters() {

src/assets/Markdown Files/TODO-v4.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@
8585
- Meta.yaml: Allow admins to customize the terms 'Team' and 'Group' (e.g. to 'App' and 'Portfolio')
8686

8787
# Done
88+
- Heatmap: Add #uuid to URL, and allow navigation on clicks in dependencies
8889
- Fix dependsOn that is uuid (e.g. 83057028-0b77-4d2e-8135-40969768ae88)
8990
- Settings: Make settings page for Date format and Max maturity level
9091
- Dependency: Make connecting nodes clickable for navigation
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
export const environment = {
2-
version: '4.0a3',
2+
version: '4.0a4',
33
production: true,
44
};

0 commit comments

Comments
 (0)