From 745f6bc97a5481f8326574178fe967532e3f6372 Mon Sep 17 00:00:00 2001 From: Jude Roz Date: Tue, 19 Nov 2024 21:36:58 +1100 Subject: [PATCH 1/4] update --- .../progress-dashboard.coffee | 42 ++++-- .../progress-dashboard.tpl.html | 130 +++++++++++------- 2 files changed, 113 insertions(+), 59 deletions(-) diff --git a/src/app/projects/states/dashboard/directives/progress-dashboard/progress-dashboard.coffee b/src/app/projects/states/dashboard/directives/progress-dashboard/progress-dashboard.coffee index 0a82f1ab9a..4c53c74ee0 100644 --- a/src/app/projects/states/dashboard/directives/progress-dashboard/progress-dashboard.coffee +++ b/src/app/projects/states/dashboard/directives/progress-dashboard/progress-dashboard.coffee @@ -1,19 +1,17 @@ angular.module('doubtfire.projects.states.dashboard.directives.progress-dashboard', []) -# -# Summary dashboard showing some graphs and way to change the -# current target grade -# -.directive('progressDashboard', -> +.directive 'progressDashboard', -> restrict: 'E' templateUrl: 'projects/states/dashboard/directives/progress-dashboard/progress-dashboard.tpl.html' scope: project: '=' onUpdateTargetGrade: '=' - controller: ($scope, $stateParams, newProjectService, gradeService, analyticsService, alertService) -> + controller: ($scope, $stateParams, newProjectService, gradeService, analyticsService, alertService, $http, DoubtfireConstants) -> + # Is the current user a tutor? $scope.tutor = $stateParams.tutor + # Number of tasks completed and remaining - updateTaskCompletionValues = -> + updateTaskCompletionValues = -> completedTasks = $scope.project.numberTasks("complete") $scope.numberOfTasks = completed: completedTasks @@ -25,6 +23,15 @@ angular.module('doubtfire.projects.states.dashboard.directives.progress-dashboar names: gradeService.grades values: gradeService.gradeValues + # Fetch Target Grade History + $scope.targetGradeHistory = [] + + $http.get("#{DoubtfireConstants.API_URL}/projects/#{$scope.project.id}") + .then (response) -> + $scope.targetGradeHistory = response.data.target_grade_histories + .catch (error) -> + alertService.error("Failed to load target grade history", 4000) + $scope.updateTargetGrade = (newGrade) -> $scope.project.targetGrade = newGrade newProjectService.update($scope.project).subscribe( @@ -35,10 +42,21 @@ angular.module('doubtfire.projects.states.dashboard.directives.progress-dashboar updateTaskCompletionValues() $scope.renderTaskStatusPieChart?() $scope.onUpdateTargetGrade?() - analyticsService.event("Student Project View - Progress Dashboard", "Grade Changed", $scope.grades.names[newGrade]) - alertService.success( "Updated target grade successfully", 2000) + analyticsService.event( + "Student Project View - Progress Dashboard", + "Grade Changed", + $scope.grades.names[newGrade] + ) + + # Fetch updated target grade history + $http.get("#{DoubtfireConstants.API_URL}/projects/#{$scope.project.id}") + .then (response) -> + $scope.targetGradeHistory = response.data.target_grade_histories + .catch (error) -> + alertService.error("Failed to reload target grade history", 4000) + + alertService.success("Updated target grade successfully", 2000) - (failure) -> - alertService.error( "Failed to update target grade", 4000) + , (failure) -> + alertService.error("Failed to update target grade", 4000) ) -) diff --git a/src/app/projects/states/dashboard/directives/progress-dashboard/progress-dashboard.tpl.html b/src/app/projects/states/dashboard/directives/progress-dashboard/progress-dashboard.tpl.html index 53269a2a95..c8e017e8c9 100644 --- a/src/app/projects/states/dashboard/directives/progress-dashboard/progress-dashboard.tpl.html +++ b/src/app/projects/states/dashboard/directives/progress-dashboard/progress-dashboard.tpl.html @@ -5,54 +5,90 @@

-
-
-
-

Target Grade

-
-
- -
-
-
-
-

Progress Burndown

-
- The burndown chart shows how much work remains for you to achieve your target grade. +
+ +
+ +
+
+

Target Grade

+
+
+ +
+
+ + +
+
+

Progress Burndown

+
+ The burndown chart shows how much work remains for you to achieve your target grade. +
+
+
+ +
+ +
+
+ + +
+ +
+
+

Task Statuses

+
+ Breakdown summary of each of your task statuses. +
+
+
+ + +
+
+ + +
+
+

Target Grade Change History

-
-
- -
- -
-
-
-
-
-

Task Statuses

-
- Breakdown summary of each of your task statuses. +
+ + + + + + + + + + + + + + + + + +
Changed AtPrevious GradeNew GradeChanged By
{{ history.changed_at | date:'medium' }}{{ grades.names[history.previous_grade.toString()] }}{{ grades.names[history.new_grade.toString()] }}{{ history.changed_by.first_name }} {{ history.changed_by.last_name }}
-
-
- - -
-
-
+
+
+
From c9528d6438504fd4dd7cea98857a7c9e5d5672d1 Mon Sep 17 00:00:00 2001 From: Jude Roz Date: Sun, 1 Dec 2024 06:18:47 +1100 Subject: [PATCH 2/4] feat(progress-dashboard): re-done the front end with pagination and page numbers --- src/app/doubtfire-angular.module.ts | 3 +- src/app/doubtfire-angularjs.module.ts | 9 + .../progress-dashboard.tpl.html | 30 +--- .../target-grade-history.component.html | 59 +++++++ .../target-grade-history.component.scss | 0 .../target-grade-history.component.spec.ts | 23 +++ .../target-grade-history.component.ts | 160 ++++++++++++++++++ 7 files changed, 257 insertions(+), 27 deletions(-) create mode 100644 src/app/projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component.html create mode 100644 src/app/projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component.scss create mode 100644 src/app/projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component.spec.ts create mode 100644 src/app/projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component.ts diff --git a/src/app/doubtfire-angular.module.ts b/src/app/doubtfire-angular.module.ts index 9284745988..b5fb34d461 100644 --- a/src/app/doubtfire-angular.module.ts +++ b/src/app/doubtfire-angular.module.ts @@ -224,7 +224,7 @@ import {FTaskSheetViewComponent} from './units/states/tasks/viewer/directives/f- import {TasksViewerComponent} from './units/states/tasks/tasks-viewer/tasks-viewer.component'; import {UnitCodeComponent} from './common/unit-code/unit-code.component'; import {GradeService} from './common/services/grade.service'; - +import { TargetGradeHistoryComponent } from './projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component'; @NgModule({ // Components we declare declarations: [ @@ -325,6 +325,7 @@ import {GradeService} from './common/services/grade.service'; FUsersComponent, FTaskBadgeComponent, FUnitsComponent, + TargetGradeHistoryComponent, ], // Services we provide providers: [ diff --git a/src/app/doubtfire-angularjs.module.ts b/src/app/doubtfire-angularjs.module.ts index 868e381213..92cead1df5 100644 --- a/src/app/doubtfire-angularjs.module.ts +++ b/src/app/doubtfire-angularjs.module.ts @@ -225,6 +225,9 @@ import {FUnitsComponent} from './admin/states/f-units/f-units.component'; import {MarkedPipe} from './common/pipes/marked.pipe'; import {AlertService} from './common/services/alert.service'; import {GradeService} from './common/services/grade.service'; + +import {TargetGradeHistoryComponent} from './projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component'; + export const DoubtfireAngularJSModule = angular.module('doubtfire', [ 'doubtfire.config', 'doubtfire.sessions', @@ -464,6 +467,12 @@ DoubtfireAngularJSModule.directive( ); DoubtfireAngularJSModule.directive('newFUnits', downgradeComponent({component: FUnitsComponent})); +DoubtfireAngularJSModule.directive( + 'targetGradeHistory', + downgradeComponent({ + component: TargetGradeHistoryComponent + }) +); // Global configuration // If the user enters a URL that doesn't match any known URL (state), send them to `/home` diff --git a/src/app/projects/states/dashboard/directives/progress-dashboard/progress-dashboard.tpl.html b/src/app/projects/states/dashboard/directives/progress-dashboard/progress-dashboard.tpl.html index c8e017e8c9..de54423c9d 100644 --- a/src/app/projects/states/dashboard/directives/progress-dashboard/progress-dashboard.tpl.html +++ b/src/app/projects/states/dashboard/directives/progress-dashboard/progress-dashboard.tpl.html @@ -63,32 +63,10 @@

Task Statuses

-
-
-

Target Grade Change History

-
-
- - - - - - - - - - - - - - - - - -
Changed AtPrevious GradeNew GradeChanged By
{{ history.changed_at | date:'medium' }}{{ grades.names[history.previous_grade.toString()] }}{{ grades.names[history.new_grade.toString()] }}{{ history.changed_by.first_name }} {{ history.changed_by.last_name }}
-
-
- + + + + diff --git a/src/app/projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component.html b/src/app/projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component.html new file mode 100644 index 0000000000..4aea714f72 --- /dev/null +++ b/src/app/projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component.html @@ -0,0 +1,59 @@ +
+
+

Target Grade Change History

+
+
+
Loading history...
+
+ {{ error }} +
+
+ No grade history available +
+ + + + + + + + + + + + + + + + + +
Changed AtPrevious GradeNew GradeChanged By
{{ history.changed_at | date: 'medium' }}{{ history.previous_grade }}{{ history.new_grade }}{{ history.changed_by?.first_name }} {{ history.changed_by?.last_name }}
+
+ + + +
+
+
diff --git a/src/app/projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component.scss b/src/app/projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/app/projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component.spec.ts b/src/app/projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component.spec.ts new file mode 100644 index 0000000000..972bab2ed8 --- /dev/null +++ b/src/app/projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { TargetGradeHistoryComponent } from './target-grade-history.component'; + +describe('TargetGradeHistoryComponent', () => { + let component: TargetGradeHistoryComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [TargetGradeHistoryComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(TargetGradeHistoryComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component.ts b/src/app/projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component.ts new file mode 100644 index 0000000000..8b7aacb859 --- /dev/null +++ b/src/app/projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component.ts @@ -0,0 +1,160 @@ +import {Component, Input, OnInit, OnDestroy} from '@angular/core'; +import {HttpClient} from '@angular/common/http'; +import {catchError} from 'rxjs/operators'; +import {of, interval, Subscription} from 'rxjs'; +import {GradeService} from 'src/app/common/services/grade.service'; +import {DoubtfireConstants} from 'src/app/config/constants/doubtfire-constants'; + +interface User { + id: number; + email: string; + first_name: string; + last_name: string; + username: string; + nickname: string; +} + +interface TargetGradeHistory { + previous_grade: string | number; + new_grade: string | number; + changed_at: string; + changed_by: User; +} + +@Component({ + selector: 'f-target-grade-history', + templateUrl: './target-grade-history.component.html', + styleUrls: ['./target-grade-history.component.scss'], +}) +export class TargetGradeHistoryComponent implements OnInit, OnDestroy { + @Input() projectId!: number; + + targetGradeHistory: TargetGradeHistory[] = []; + paginatedGradeHistory: TargetGradeHistory[] = []; + loading = false; + error: string | null = null; + + currentPage = 1; + itemsPerPage = 10; + totalPages = 1; + private gradeCheckSubscription!: Subscription; + + constructor( + private http: HttpClient, + private gradeService: GradeService, + private constants: DoubtfireConstants, + ) {} + + ngOnInit(): void { + if (this.projectId) { + this.loadTargetGradeHistory(); + this.startGradeChangeListener(); + } + } + + ngOnDestroy(): void { + if (this.gradeCheckSubscription) { + this.gradeCheckSubscription.unsubscribe(); + } + } + + private loadTargetGradeHistory(): void { + if (!this.projectId) { + console.error('No projectId provided'); + this.error = 'Project ID is required'; + return; + } + + this.loading = true; + this.error = null; + + const url = `${this.constants.API_URL}/projects/${this.projectId}`; + + this.http + .get(url) + .pipe( + catchError((error) => { + console.error('Error fetching target grade history:', error); + this.error = 'Failed to load target grade history'; + return of({target_grade_histories: []}); + }), + ) + .subscribe({ + next: (response) => { + this.targetGradeHistory = response.target_grade_histories + .filter( + (history: TargetGradeHistory) => + history.previous_grade !== undefined && history.new_grade !== undefined, + ) + .map((history: TargetGradeHistory) => ({ + ...history, + previous_grade: this.gradeService.grades[history.previous_grade] || 'N/A', + new_grade: this.gradeService.grades[history.new_grade] || 'N/A', + })) + .sort((a, b) => new Date(b.changed_at).getTime() - new Date(a.changed_at).getTime()); // Sort newest first + + this.updatePagination(); + this.loading = false; + }, + error: () => { + this.error = 'Failed to load target grade history'; + this.loading = false; + }, + }); + } + + private startGradeChangeListener(): void { + const checkInterval = 3000; // 3 seconds + this.gradeCheckSubscription = interval(checkInterval).subscribe(() => { + this.checkForGradeChange(); + }); + } + + private checkForGradeChange(): void { + const url = `${this.constants.API_URL}/projects/${this.projectId}`; + this.http + .get(url) + .pipe( + catchError((error) => { + console.error('Error checking for grade change:', error); + return of(null); + }), + ) + .subscribe((response) => { + if (response && response.target_grade_histories) { + const latestHistory = response.target_grade_histories.map( + (history: TargetGradeHistory) => ({ + ...history, + previous_grade: this.gradeService.grades[history.previous_grade] || 'N/A', + new_grade: this.gradeService.grades[history.new_grade] || 'N/A', + }), + ); + + if (JSON.stringify(this.targetGradeHistory) !== JSON.stringify(latestHistory)) { + this.targetGradeHistory = latestHistory.sort( + (a, b) => new Date(b.changed_at).getTime() - new Date(a.changed_at).getTime(), + ); + this.updatePagination(); + } + } + }); + } + + private updatePagination(): void { + this.totalPages = Math.ceil(this.targetGradeHistory.length / this.itemsPerPage); + this.updatePaginatedGradeHistory(); + } + + private updatePaginatedGradeHistory(): void { + const startIndex = (this.currentPage - 1) * this.itemsPerPage; + const endIndex = startIndex + this.itemsPerPage; + this.paginatedGradeHistory = this.targetGradeHistory.slice(startIndex, endIndex); + } + + public goToPage(page: number): void { + if (page >= 1 && page <= this.totalPages) { + this.currentPage = page; + this.updatePaginatedGradeHistory(); + } + } +} From 614169823bfc3159ff6992b9a78ba0064d862780 Mon Sep 17 00:00:00 2001 From: Jude Roz Date: Mon, 23 Dec 2024 01:44:08 +1100 Subject: [PATCH 3/4] Removed polling and added to Pull data on change to reduce data download amounts --- .../progress-dashboard.tpl.html | 15 ++-- .../target-grade-history.component.ts | 85 +++++++------------ 2 files changed, 39 insertions(+), 61 deletions(-) diff --git a/src/app/projects/states/dashboard/directives/progress-dashboard/progress-dashboard.tpl.html b/src/app/projects/states/dashboard/directives/progress-dashboard/progress-dashboard.tpl.html index de54423c9d..fc37ddf176 100644 --- a/src/app/projects/states/dashboard/directives/progress-dashboard/progress-dashboard.tpl.html +++ b/src/app/projects/states/dashboard/directives/progress-dashboard/progress-dashboard.tpl.html @@ -57,16 +57,21 @@

Task Statuses

+ update-data="renderTaskStatusPieChart" + >
- - + + - + + - + \ No newline at end of file diff --git a/src/app/projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component.ts b/src/app/projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component.ts index 8b7aacb859..c219cb9b6b 100644 --- a/src/app/projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component.ts +++ b/src/app/projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component.ts @@ -1,9 +1,9 @@ -import {Component, Input, OnInit, OnDestroy} from '@angular/core'; -import {HttpClient} from '@angular/common/http'; -import {catchError} from 'rxjs/operators'; -import {of, interval, Subscription} from 'rxjs'; -import {GradeService} from 'src/app/common/services/grade.service'; -import {DoubtfireConstants} from 'src/app/config/constants/doubtfire-constants'; +import { Component, Input, OnInit, OnChanges, SimpleChanges } from '@angular/core'; +import { HttpClient } from '@angular/common/http'; +import { catchError } from 'rxjs/operators'; +import { of } from 'rxjs'; +import { GradeService } from 'src/app/common/services/grade.service'; +import { DoubtfireConstants } from 'src/app/config/constants/doubtfire-constants'; interface User { id: number; @@ -26,8 +26,9 @@ interface TargetGradeHistory { templateUrl: './target-grade-history.component.html', styleUrls: ['./target-grade-history.component.scss'], }) -export class TargetGradeHistoryComponent implements OnInit, OnDestroy { +export class TargetGradeHistoryComponent implements OnInit, OnChanges { @Input() projectId!: number; + @Input() targetGrade!: string; // <-- so we can detect changes in targetGrade targetGradeHistory: TargetGradeHistory[] = []; paginatedGradeHistory: TargetGradeHistory[] = []; @@ -37,24 +38,32 @@ export class TargetGradeHistoryComponent implements OnInit, OnDestroy { currentPage = 1; itemsPerPage = 10; totalPages = 1; - private gradeCheckSubscription!: Subscription; constructor( private http: HttpClient, private gradeService: GradeService, - private constants: DoubtfireConstants, + private constants: DoubtfireConstants ) {} ngOnInit(): void { if (this.projectId) { this.loadTargetGradeHistory(); - this.startGradeChangeListener(); } } - ngOnDestroy(): void { - if (this.gradeCheckSubscription) { - this.gradeCheckSubscription.unsubscribe(); + /** + * Detect input changes (projectId or targetGrade). + * If 'targetGrade' changes, we may re-fetch the data. + */ + ngOnChanges(changes: SimpleChanges): void { + // If projectId changes (and not the first time), reload. + if (changes.projectId && !changes.projectId.firstChange) { + this.loadTargetGradeHistory(); + } + + // If targetGrade changes (and not the first time), reload TGH + if (changes.targetGrade && !changes.targetGrade.firstChange) { + this.loadTargetGradeHistory(); } } @@ -76,22 +85,23 @@ export class TargetGradeHistoryComponent implements OnInit, OnDestroy { catchError((error) => { console.error('Error fetching target grade history:', error); this.error = 'Failed to load target grade history'; - return of({target_grade_histories: []}); - }), + return of({ target_grade_histories: [] }); + }) ) .subscribe({ next: (response) => { - this.targetGradeHistory = response.target_grade_histories + const rawHistories = response.target_grade_histories || []; + this.targetGradeHistory = rawHistories .filter( (history: TargetGradeHistory) => - history.previous_grade !== undefined && history.new_grade !== undefined, + history.previous_grade !== undefined && history.new_grade !== undefined ) .map((history: TargetGradeHistory) => ({ ...history, previous_grade: this.gradeService.grades[history.previous_grade] || 'N/A', new_grade: this.gradeService.grades[history.new_grade] || 'N/A', })) - .sort((a, b) => new Date(b.changed_at).getTime() - new Date(a.changed_at).getTime()); // Sort newest first + .sort((a, b) => new Date(b.changed_at).getTime() - new Date(a.changed_at).getTime()); // newest first this.updatePagination(); this.loading = false; @@ -103,43 +113,6 @@ export class TargetGradeHistoryComponent implements OnInit, OnDestroy { }); } - private startGradeChangeListener(): void { - const checkInterval = 3000; // 3 seconds - this.gradeCheckSubscription = interval(checkInterval).subscribe(() => { - this.checkForGradeChange(); - }); - } - - private checkForGradeChange(): void { - const url = `${this.constants.API_URL}/projects/${this.projectId}`; - this.http - .get(url) - .pipe( - catchError((error) => { - console.error('Error checking for grade change:', error); - return of(null); - }), - ) - .subscribe((response) => { - if (response && response.target_grade_histories) { - const latestHistory = response.target_grade_histories.map( - (history: TargetGradeHistory) => ({ - ...history, - previous_grade: this.gradeService.grades[history.previous_grade] || 'N/A', - new_grade: this.gradeService.grades[history.new_grade] || 'N/A', - }), - ); - - if (JSON.stringify(this.targetGradeHistory) !== JSON.stringify(latestHistory)) { - this.targetGradeHistory = latestHistory.sort( - (a, b) => new Date(b.changed_at).getTime() - new Date(a.changed_at).getTime(), - ); - this.updatePagination(); - } - } - }); - } - private updatePagination(): void { this.totalPages = Math.ceil(this.targetGradeHistory.length / this.itemsPerPage); this.updatePaginatedGradeHistory(); @@ -157,4 +130,4 @@ export class TargetGradeHistoryComponent implements OnInit, OnDestroy { this.updatePaginatedGradeHistory(); } } -} +} \ No newline at end of file From d861bd5014ff57297c6b75dc37099b913733fe93 Mon Sep 17 00:00:00 2001 From: Jude Roz Date: Mon, 23 Dec 2024 02:13:00 +1100 Subject: [PATCH 4/4] Added backend to retrive data based on page number and also to return the total amount of histories for the page numbers --- .../target-grade-history.component.ts | 94 ++++++++----------- 1 file changed, 38 insertions(+), 56 deletions(-) diff --git a/src/app/projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component.ts b/src/app/projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component.ts index c219cb9b6b..5bdb384dcf 100644 --- a/src/app/projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component.ts +++ b/src/app/projects/states/dashboard/directives/progress-dashboard/target-grade-history/target-grade-history.component.ts @@ -15,6 +15,7 @@ interface User { } interface TargetGradeHistory { + id: number; previous_grade: string | number; new_grade: string | number; changed_at: string; @@ -24,14 +25,17 @@ interface TargetGradeHistory { @Component({ selector: 'f-target-grade-history', templateUrl: './target-grade-history.component.html', - styleUrls: ['./target-grade-history.component.scss'], + styleUrls: ['./target-grade-history.component.scss'] }) export class TargetGradeHistoryComponent implements OnInit, OnChanges { - @Input() projectId!: number; - @Input() targetGrade!: string; // <-- so we can detect changes in targetGrade + @Input() projectId!: number; + @Input() targetGrade?: string; + // Data targetGradeHistory: TargetGradeHistory[] = []; paginatedGradeHistory: TargetGradeHistory[] = []; + + // UI State loading = false; error: string | null = null; @@ -51,25 +55,22 @@ export class TargetGradeHistoryComponent implements OnInit, OnChanges { } } - /** - * Detect input changes (projectId or targetGrade). - * If 'targetGrade' changes, we may re-fetch the data. - */ + ngOnChanges(changes: SimpleChanges): void { - // If projectId changes (and not the first time), reload. if (changes.projectId && !changes.projectId.firstChange) { + this.currentPage = 1; this.loadTargetGradeHistory(); } - // If targetGrade changes (and not the first time), reload TGH if (changes.targetGrade && !changes.targetGrade.firstChange) { this.loadTargetGradeHistory(); } } + private loadTargetGradeHistory(): void { if (!this.projectId) { - console.error('No projectId provided'); + console.error('No projectId provided to TargetGradeHistoryComponent'); this.error = 'Project ID is required'; return; } @@ -77,57 +78,38 @@ export class TargetGradeHistoryComponent implements OnInit, OnChanges { this.loading = true; this.error = null; - const url = `${this.constants.API_URL}/projects/${this.projectId}`; - - this.http - .get(url) - .pipe( - catchError((error) => { - console.error('Error fetching target grade history:', error); - this.error = 'Failed to load target grade history'; - return of({ target_grade_histories: [] }); - }) - ) - .subscribe({ - next: (response) => { - const rawHistories = response.target_grade_histories || []; - this.targetGradeHistory = rawHistories - .filter( - (history: TargetGradeHistory) => - history.previous_grade !== undefined && history.new_grade !== undefined - ) - .map((history: TargetGradeHistory) => ({ - ...history, - previous_grade: this.gradeService.grades[history.previous_grade] || 'N/A', - new_grade: this.gradeService.grades[history.new_grade] || 'N/A', - })) - .sort((a, b) => new Date(b.changed_at).getTime() - new Date(a.changed_at).getTime()); // newest first - - this.updatePagination(); - this.loading = false; - }, - error: () => { - this.error = 'Failed to load target grade history'; - this.loading = false; - }, - }); - } - - private updatePagination(): void { - this.totalPages = Math.ceil(this.targetGradeHistory.length / this.itemsPerPage); - this.updatePaginatedGradeHistory(); - } - - private updatePaginatedGradeHistory(): void { - const startIndex = (this.currentPage - 1) * this.itemsPerPage; - const endIndex = startIndex + this.itemsPerPage; - this.paginatedGradeHistory = this.targetGradeHistory.slice(startIndex, endIndex); + const url = `${this.constants.API_URL}/projects/${this.projectId}/target_grade_histories` + + `?page=${this.currentPage}&limit=${this.itemsPerPage}`; + + this.http.get(url).pipe( + catchError((err) => { + console.error('Error fetching target grade history:', err); + this.error = 'Failed to load target grade history'; + return of({ target_grade_histories: [], total_histories: 0 }); + }) + ).subscribe(response => { + const histories = response.target_grade_histories || []; + const totalCount = response.total_histories || 0; + + this.targetGradeHistory = histories.map((h: any) => ({ + ...h, + previous_grade: this.gradeService.grades[h.previous_grade] || h.previous_grade, + new_grade: this.gradeService.grades[h.new_grade] || h.new_grade + })); + + + this.paginatedGradeHistory = this.targetGradeHistory; + this.totalPages = Math.ceil(totalCount / this.itemsPerPage); + + this.loading = false; + }); } + public goToPage(page: number): void { if (page >= 1 && page <= this.totalPages) { this.currentPage = page; - this.updatePaginatedGradeHistory(); + this.loadTargetGradeHistory(); } } } \ No newline at end of file