Skip to content

Commit 7b42dae

Browse files
fix(portfolios): restore legacy progress dashboard and lazy-load select tab
1 parent a317d72 commit 7b42dae

File tree

6 files changed

+82
-131
lines changed

6 files changed

+82
-131
lines changed

src/app/doubtfire-angular.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,7 @@ import {TaskSubmissionCardComponent} from './projects/states/dashboard/directive
214214
import {TaskDashboardComponent} from './projects/states/dashboard/directives/task-dashboard/task-dashboard.component';
215215
import {InboxComponent} from './units/states/tasks/inbox/inbox.component';
216216
import {PortfoliosComponent} from './units/states/portfolios/portfolios.component';
217+
import {AjsProjectProgressDashboardComponent} from './units/states/portfolios/ajs-project-progress-dashboard.component';
217218
import {ProjectProgressBarComponent} from './common/project-progress-bar/project-progress-bar.component';
218219
import {TeachingPeriodListComponent} from './admin/states/teaching-periods/teaching-period-list/teaching-period-list.component';
219220
import {FChipComponent} from './common/f-chip/f-chip.component';
@@ -456,6 +457,7 @@ const GANTT_CHART_CONFIG = {
456457
TaskDashboardComponent,
457458
InboxComponent,
458459
PortfoliosComponent,
460+
AjsProjectProgressDashboardComponent,
459461
ProjectProgressBarComponent,
460462
TeachingPeriodListComponent,
461463
CreateNewUnitModal,

src/app/projects/project-progress-dashboard/project-progress-dashboard.coffee

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,31 @@ angular.module('doubtfire.projects.project-progress-dashboard',[])
1010
.directive('projectProgressDashboard', ->
1111
restrict: 'E'
1212
templateUrl: 'projects/project-progress-dashboard/project-progress-dashboard.tpl.html'
13+
# Explicit bindings for use inside upgraded Angular hosts (f-portfolios). Link copies from
14+
# $parent when attributes are omitted so legacy templates without project="..." still work.
15+
scope:
16+
project: '<'
17+
unit: '<'
1318
controller: ($scope, $state, $rootScope, $stateParams, newProjectService, alertService, gradeService, newTaskService, listenerService) ->
14-
if $stateParams.projectId?
15-
$scope.studentProjectId = $stateParams.projectId
16-
else if $scope.project?
17-
$scope.studentProjectId = $scope.project.id
18-
1919
$scope.grades = gradeService.grades
2020

2121
$scope.currentVisualisation = 'burndown'
2222

23+
$scope.taskStats = {}
24+
25+
updateTaskCompletionStats = ->
26+
return unless $scope.project?
27+
$scope.taskStats.numberOfTasksCompleted = $scope.project.tasksByStatus(newTaskService.completeStatus).length
28+
$scope.taskStats.numberOfTasksRemaining = $scope.project.activeTasks().length - $scope.taskStats.numberOfTasksCompleted
29+
30+
syncStudentProjectId = ->
31+
if $stateParams.projectId?
32+
$scope.studentProjectId = $stateParams.projectId
33+
else if $scope.project?
34+
$scope.studentProjectId = $scope.project.id
35+
2336
$scope.chooseGrade = (idx) ->
37+
return unless $scope.project?
2438
$scope.project.targetGrade = idx
2539
newProjectService.update($scope.project).subscribe(
2640
(response) ->
@@ -29,18 +43,19 @@ angular.module('doubtfire.projects.project-progress-dashboard',[])
2943
updateTaskCompletionStats()
3044

3145
$scope.taskCount = ->
32-
$scope.unit.taskDefinitionCount
33-
34-
$scope.taskStats = {}
35-
36-
# Update move to task and project...
37-
updateTaskCompletionStats = ->
38-
$scope.taskStats.numberOfTasksCompleted = $scope.project.tasksByStatus(newTaskService.completeStatus).length
39-
$scope.taskStats.numberOfTasksRemaining = $scope.project.activeTasks().length - $scope.taskStats.numberOfTasksCompleted
46+
$scope.unit?.taskDefinitionCount
4047

4148
$scope.$on 'TaskStatusUpdated', ->
4249
updateTaskCompletionStats()
4350

51+
$scope.$watch 'project', (project) ->
52+
return unless project?
53+
syncStudentProjectId()
54+
updateTaskCompletionStats()
4455

45-
updateTaskCompletionStats()
56+
link: (scope, _el, _attrs) ->
57+
unless scope.project?
58+
scope.project = scope.$parent.project if scope.$parent?
59+
unless scope.unit?
60+
scope.unit = scope.$parent.unit if scope.$parent?
4661
)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { Component, ElementRef, Inject, Input, Injector, OnInit, Optional } from '@angular/core';
2+
import { UpgradeComponent } from '@angular/upgrade/static';
3+
import { Project } from 'src/app/api/models/project';
4+
import { Unit } from 'src/app/api/models/unit';
5+
import { visualisations } from 'src/app/ajs-upgraded-providers';
6+
7+
/**
8+
* Hosts the AngularJS `projectProgressDashboard` directive inside Angular templates
9+
* (same charts and target-grade UI as the legacy portfolios “View Progress” tab).
10+
*/
11+
@Component({
12+
selector: 'f-ajs-project-progress-dashboard',
13+
template: '',
14+
})
15+
export class AjsProjectProgressDashboardComponent extends UpgradeComponent implements OnInit {
16+
@Input() project: Project;
17+
@Input() unit: Unit;
18+
19+
constructor(
20+
elementRef: ElementRef,
21+
injector: Injector,
22+
@Optional() @Inject(visualisations) private readonly visualisationApi: { refreshAll?: () => void } | null,
23+
) {
24+
super('projectProgressDashboard', elementRef, injector);
25+
}
26+
27+
override ngOnInit(): void {
28+
super.ngOnInit();
29+
// Burndown / pie use legacy visualisation lifecycle; match UnitPortfoliosStateCtrl.refreshCharts.
30+
setTimeout(() => this.visualisationApi?.refreshAll?.(), 0);
31+
}
32+
}

src/app/units/states/portfolios/portfolios.component.html

Lines changed: 11 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<div class="tab-title">{{ tabs.selectStudent.title }}</div>
99
</ng-template>
1010

11+
<ng-template matTabContent>
1112
<!-- Select Student Tab -->
1213
<div class="select-student-wrap">
1314
<div class="toolbar">
@@ -196,6 +197,7 @@
196197
></mat-paginator>
197198
</div>
198199
</div>
200+
</ng-template>
199201
</mat-tab>
200202

201203
<mat-tab [disabled]="!selectedStudent" label="View Progress">
@@ -205,47 +207,18 @@
205207
<p class="mt-3">Loading student project…</p>
206208
</div>
207209

208-
<div *ngIf="project && !loadingProject" class="panel">
209-
<div class="panel-header">
210-
<h3 class="panel-title">Review Progress of {{ selectedStudent?.student.name }}</h3>
211-
<p class="panel-subtitle">Progress through the unit's tasks.</p>
212-
</div>
213-
214-
<div class="panel-body">
215-
<div class="target-grade">
216-
<div class="target-grade-title">Target Grade</div>
217-
<div class="target-grade-options">
218-
<button
219-
mat-stroked-button
220-
*ngFor="let grade of gradeService.allGradeValues"
221-
[color]="grade === project.targetGrade ? 'primary' : 'accent'"
222-
(click)="setTargetGrade(grade)"
223-
>
224-
<span class="grade-icon-circle small" [style.background-color]="gradeColor(grade)">{{
225-
gradeAcronym(grade)
226-
}}</span>
227-
<span class="ml-2">{{ gradeService.grades[String(grade)] }}</span>
228-
</button>
229-
</div>
210+
<div *ngIf="project && !loadingProject" class="panel progress-panel">
211+
<div class="panel-header">
212+
<h3 class="panel-title">Review Progress of {{ selectedStudent?.student.name }}</h3>
213+
<p class="panel-subtitle">Progress through the unit's tasks.</p>
230214
</div>
231-
232-
<div class="progress-summary">
233-
<div class="progress-summary-title">Task Status Summary</div>
234-
<div class="progress-summary-grid">
235-
<div class="progress-summary-item" *ngFor="let bar of project.taskStats">
236-
<div class="progress-summary-item-head">
237-
<span class="status-dot" [style.background-color]="taskSegmentColor(bar.key)"></span>
238-
<span class="status-label">{{ bar.key }}</span>
239-
<span class="status-value">{{ bar.value }}%</span>
240-
</div>
241-
<div class="progress-line">
242-
<div class="progress-line-fill" [style.width.%]="bar.value"></div>
243-
</div>
244-
</div>
245-
</div>
215+
<div class="panel-body progress-panel-body">
216+
<f-ajs-project-progress-dashboard
217+
[project]="project"
218+
[unit]="unit"
219+
></f-ajs-project-progress-dashboard>
246220
</div>
247221
</div>
248-
</div>
249222
</ng-template>
250223
</mat-tab>
251224

src/app/units/states/portfolios/portfolios.component.scss

Lines changed: 3 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -131,61 +131,9 @@
131131
padding: 0.25rem 1rem 1rem;
132132
}
133133

134-
.target-grade-title,
135-
.progress-summary-title {
136-
font-weight: 800;
137-
margin-bottom: 0.5rem;
138-
}
139-
140-
.target-grade-options {
141-
display: flex;
142-
flex-wrap: wrap;
143-
gap: 0.5rem;
144-
margin-top: 0.25rem;
145-
}
146-
147-
.progress-summary-grid {
148-
display: flex;
149-
flex-direction: column;
150-
gap: 0.75rem;
151-
}
152-
153-
.progress-summary-item {
154-
padding: 0.25rem 0;
155-
}
156-
157-
.progress-summary-item-head {
158-
display: flex;
159-
align-items: center;
160-
gap: 0.6rem;
161-
}
162-
163-
.status-dot {
164-
width: 0.9rem;
165-
height: 0.9rem;
166-
border-radius: 999px;
167-
}
168-
169-
.status-label {
170-
flex: 1;
171-
text-transform: capitalize;
172-
opacity: 0.85;
173-
}
174-
175-
.status-value {
176-
font-variant-numeric: tabular-nums;
177-
}
178-
179-
.progress-line {
180-
margin-top: 0.35rem;
181-
height: 10px;
182-
border-radius: 999px;
183-
background-color: #f0f0f0;
184-
overflow: hidden;
185-
}
186-
187-
.progress-line-fill {
188-
height: 100%;
134+
.progress-panel-body {
135+
overflow-x: auto;
136+
padding-top: 0.5rem;
189137
}
190138

191139
// Assess panel

src/app/units/states/portfolios/portfolios.component.ts

Lines changed: 5 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Component, Input, OnChanges, OnDestroy, OnInit, SimpleChanges, ViewChild } from '@angular/core';
1+
import { Component, Inject, Input, OnChanges, OnDestroy, OnInit, Optional, SimpleChanges, ViewChild } from '@angular/core';
22
import { MatPaginator, PageEvent } from '@angular/material/paginator';
33
import { MatTableDataSource } from '@angular/material/table';
44
import { GradeService } from 'src/app/common/services/grade.service';
@@ -12,6 +12,7 @@ import { FileDownloaderService } from 'src/app/common/file-downloader/file-downl
1212
import { AlertService } from 'src/app/common/services/alert.service';
1313
import { TaskService } from 'src/app/api/services/task.service';
1414
import { Subscription } from 'rxjs';
15+
import { visualisations } from 'src/app/ajs-upgraded-providers';
1516

1617
type StudentTabKey = 'selectStudent' | 'viewProgress' | 'viewPortfolio' | 'assessPortfolio';
1718

@@ -97,6 +98,7 @@ export class PortfoliosComponent implements OnInit, OnChanges, OnDestroy {
9798
private fileDownloader: FileDownloaderService,
9899
private alerts: AlertService,
99100
private taskService: TaskService,
101+
@Optional() @Inject(visualisations) private readonly visualisationApi: { refreshAll?: () => void } | null,
100102
) {}
101103

102104
ngOnInit(): void {
@@ -161,8 +163,9 @@ export class PortfoliosComponent implements OnInit, OnChanges, OnDestroy {
161163
public onTabChange(tabKey: StudentTabKey): void {
162164
this.activeTab = tabKey;
163165
if (tabKey === 'viewProgress' && this.project) {
164-
// Ensure project stats are up-to-date when opening the progress panel.
165166
this.project.refreshBurndownChartData();
167+
// Legacy charts (nv/d3) need a refresh after the tab body is shown.
168+
setTimeout(() => this.visualisationApi?.refreshAll?.(), 0);
166169
}
167170
}
168171

@@ -342,32 +345,10 @@ export class PortfoliosComponent implements OnInit, OnChanges, OnDestroy {
342345
return this.taskService.statusColors.get(taskStatusKey as any) ?? '#CCCCCC';
343346
}
344347

345-
public taskProgressSegments(taskStats: { key: string; value: number }[] | undefined): { key: string; value: number }[] {
346-
if (!taskStats) return [];
347-
// Keep legacy behaviour: hide tiny segments when rendering labels.
348-
return taskStats.filter((bar) => bar.value !== undefined);
349-
}
350-
351348
public downloadPortfolios(): void {
352349
// Intentionally left out: legacy button was commented out.
353350
}
354351

355-
public setTargetGrade(grade: number): void {
356-
if (!this.project) return;
357-
const next = grade;
358-
if (this.project.targetGrade === next) return;
359-
360-
this.project.targetGrade = next;
361-
this.projectService.update(this.project).subscribe({
362-
next: () => {
363-
this.project.refreshBurndownChartData();
364-
},
365-
error: (message) => {
366-
this.alerts.error(message, 6000);
367-
},
368-
});
369-
}
370-
371352
public saveGrade(): void {
372353
if (!this.project) return;
373354
const score = this.project.grade;

0 commit comments

Comments
 (0)