Skip to content

Commit ac72ee0

Browse files
committed
feat(projects): restore Projects Index migration files
1 parent ec53762 commit ac72ee0

File tree

4 files changed

+149
-3
lines changed

4 files changed

+149
-3
lines changed

src/app/projects/states/index/index.coffee

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,16 @@ angular.module('doubtfire.projects.states.index', [])
1313
templateUrl: "units/states/index/index.tpl.html" # We can re-use unit's index here
1414
data:
1515
pageTitle: "_Home_"
16+
roleWhitelist: ['Student', 'Tutor', 'Convenor', 'Admin', 'Auditor']
1617
}
1718
)
1819

19-
.controller("ProjectsIndexStateCtrl", ($scope, $rootScope, $state, $stateParams, newProjectService, listenerService, globalStateService) ->
20+
.controller("ProjectsIndexStateCtrl", ($scope, $rootScope, $state, $stateParams, newProjectService, listenerService, GlobalStateService) ->
2021
# Error - required projectId is missing!
2122
projectId = +$stateParams.projectId
2223
return $state.go('home') unless projectId
2324

24-
globalStateService.onLoad () ->
25+
GlobalStateService.onLoad () ->
2526
# Load in project
2627
newProjectService.get(projectId, {
2728
# Ensure that we cache queries here... so that we get any projects we are in
@@ -38,7 +39,7 @@ angular.module('doubtfire.projects.states.index', [])
3839
$scope.project = project
3940
$scope.unit = project.unit if project.unit.taskDefinitions.length > 0 && project.tasks.length == project.unit.taskDefinitions.length
4041

41-
globalStateService.setView('PROJECT', $scope.project)
42+
GlobalStateService.setView('PROJECT', $scope.project)
4243

4344
# Go home if no project was found
4445
return $state.go('home') unless project?
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<div class="p-6 bg-white shadow-md rounded-md">
2+
<!-- Page Heading -->
3+
<h1 class="text-2xl font-bold mb-4 text-gray-800">Projects Index</h1>
4+
5+
<!-- Project Info -->
6+
<div *ngIf="project; else loadingOrError">
7+
<p class="text-gray-700">
8+
<span class="font-semibold">Project:</span> {{ project.name }}
9+
</p>
10+
<p class="text-gray-700">
11+
<span class="font-semibold">Unit:</span> {{ unit?.name }}
12+
</p>
13+
14+
<!-- Tasks Section -->
15+
<h2 class="text-xl font-semibold mt-6 mb-2 text-gray-800">Tasks</h2>
16+
17+
<ul *ngIf="project?.tasks?.length > 0; else noTasks" class="space-y-2">
18+
<li *ngFor="let task of project.tasks"
19+
class="flex items-center justify-between bg-gray-100 p-3 rounded shadow-sm">
20+
<span class="text-gray-700">
21+
{{ task.name }} – Status:
22+
<span class="font-medium">{{ task.status }}</span>
23+
</span>
24+
<button class="px-3 py-1 bg-blue-600 text-white rounded hover:bg-blue-700 transition"
25+
(click)="completeTask(task)">
26+
Complete
27+
</button>
28+
</li>
29+
</ul>
30+
31+
<!-- No Tasks -->
32+
<ng-template #noTasks>
33+
<p class="text-gray-500 italic">No tasks found for this project.</p>
34+
</ng-template>
35+
</div>
36+
37+
<!-- Loading / Error States -->
38+
<ng-template #loadingOrError>
39+
<div *ngIf="isLoading" class="text-gray-500">Loading project...</div>
40+
<div *ngIf="hasError" class="text-red-600">Error loading project.</div>
41+
</ng-template>
42+
</div>
43+
<ng-template #loadingOrError>
44+
<div *ngIf="isLoading">Loading project...</div>
45+
<div *ngIf="hasError">Error loading project.</div>
46+
</ng-template>

src/app/projects/states/index/index.component.scss

Whitespace-only changes.
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import { Component, OnInit, OnDestroy } from '@angular/core';
2+
import { ActivatedRoute, Router, ParamMap } from '@angular/router';
3+
import { Subject } from 'rxjs';
4+
import { switchMap, takeUntil } from 'rxjs/operators';
5+
import { GlobalStateService } from './global-state.service';
6+
import { ProjectService } from '../../services/project.service';
7+
import { ListenerService } from '../../services/listener.service';
8+
import { ViewType } from '../../common/types/view-type';
9+
10+
@Component({
11+
selector: 'app-projects-index',
12+
templateUrl: './index.component.html',
13+
styleUrls: ['./index.component.scss']
14+
})
15+
export class ProjectsIndexComponent implements OnInit, OnDestroy {
16+
projectId!: number;
17+
project: any;
18+
unit: any;
19+
isLoading = true;
20+
hasError = false;
21+
22+
private destroy$ = new Subject<void>();
23+
24+
constructor(
25+
private route: ActivatedRoute,
26+
private router: Router,
27+
private projectService: ProjectService,
28+
private listenerService: ListenerService,
29+
private globalStateService: GlobalStateService
30+
) { }
31+
32+
ngOnInit(): void {
33+
// Wait for global state to be ready, then react to route changes
34+
this.globalStateService.onLoad(() => {
35+
this.route.paramMap
36+
.pipe(
37+
takeUntil(this.destroy$),
38+
switchMap((params: ParamMap) => {
39+
const idParam = params.get('projectId');
40+
this.projectId = Number(idParam);
41+
42+
if (!this.projectId || Number.isNaN(this.projectId)) {
43+
this.hasError = true;
44+
this.isLoading = false;
45+
this.router.navigate(['/home']);
46+
// Return an empty observable when navigation happens
47+
return new Subject<never>();
48+
}
49+
50+
this.isLoading = true;
51+
this.hasError = false;
52+
53+
return this.projectService.get(this.projectId, {
54+
cacheBehaviourOnGet: 'cacheQuery',
55+
mappingCompleteCallback: (project: any) => {
56+
this.unit = project?.unit;
57+
}
58+
});
59+
})
60+
)
61+
.subscribe({
62+
next: (project: any) => {
63+
this.project = project;
64+
65+
// Guard against null/undefined project
66+
if (!project) {
67+
this.hasError = true;
68+
this.isLoading = false;
69+
this.router.navigate(['/home']);
70+
return;
71+
}
72+
73+
// Ensure unit is set when tasks match task definitions
74+
if (
75+
project.unit?.taskDefinitions?.length > 0 &&
76+
project.tasks?.length === project.unit.taskDefinitions.length
77+
) {
78+
this.unit = project.unit;
79+
}
80+
81+
// Use enum for view type
82+
this.globalStateService.setView(ViewType.PROJECT, this.project);
83+
84+
this.isLoading = false;
85+
},
86+
error: () => {
87+
this.isLoading = false;
88+
this.hasError = true;
89+
this.router.navigate(['/home']);
90+
}
91+
});
92+
});
93+
}
94+
95+
ngOnDestroy(): void {
96+
this.destroy$.next();
97+
this.destroy$.complete();
98+
}
99+
}

0 commit comments

Comments
 (0)