Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/app/projects/states/index/index.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@ angular.module('doubtfire.projects.states.index', [])
templateUrl: "units/states/index/index.tpl.html" # We can re-use unit's index here
data:
pageTitle: "_Home_"
roleWhitelist: ['Student', 'Tutor', 'Convenor', 'Admin', 'Auditor']
}
)

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

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

globalStateService.setView('PROJECT', $scope.project)
GlobalStateService.setView('PROJECT', $scope.project)

# Go home if no project was found
return $state.go('home') unless project?
Expand Down
37 changes: 37 additions & 0 deletions src/app/projects/states/index/index.component.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<div class="p-6 bg-white shadow-md rounded-md">
<!-- Page Heading -->
<h1 class="text-2xl font-bold mb-4 text-gray-800">Projects Index</h1>

<!-- Project Info -->
<div *ngIf="project; else loadingOrError">
<p class="text-gray-700">
<span class="font-semibold">Project:</span> {{ project.name }}
</p>
<p class="text-gray-700">
<span class="font-semibold">Unit:</span> {{ unit?.name }}
</p>

<!-- Tasks Section -->
<h2 class="text-xl font-semibold mt-6 mb-2 text-gray-800">Tasks</h2>

<ul *ngIf="project?.tasks?.length > 0; else noTasks" class="space-y-2">
<li *ngFor="let task of project.tasks"
class="flex items-center justify-between bg-gray-100 p-3 rounded shadow-sm">
<span class="text-gray-700">
{{ task.name }} – Status:
<span class="font-medium">{{ task.status }}</span>
</span>
</li>
</ul>

<!-- No Tasks -->
<ng-template #noTasks>
<p class="text-gray-500 italic">No tasks found for this project.</p>
</ng-template>
</div>

<!-- Loading / Error States -->
<ng-template #loadingOrError>
<div *ngIf="isLoading" class="text-gray-500">Loading project...</div>
<div *ngIf="hasError" class="text-red-600">Error loading project.</div>
</ng-template>
113 changes: 113 additions & 0 deletions src/app/projects/states/index/index.component.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ActivatedRoute, Router, ParamMap } from '@angular/router';
import { Subject, EMPTY } from 'rxjs';
import { switchMap, takeUntil } from 'rxjs/operators';
import { GlobalStateService } from './global-state.service';
import { ProjectService } from '../../services/project.service';
import { ViewType } from '../../common/types/view-type';

// ----------------------
// Added Interfaces
// ----------------------
interface Unit {
id: number;
name: string;
taskDefinitions?: any[];
}

interface Project {
id: number;
name: string;
tasks?: any[];
unit?: Unit;
}

@Component({
selector: 'app-projects-index',
templateUrl: './index.component.html',
styleUrls: ['./index.component.scss']
})
export class ProjectsIndexComponent implements OnInit, OnDestroy {
projectId!: number;
project: Project | null = null;
unit: Unit | null = null;
isLoading = true;
hasError = false;

private destroy$ = new Subject<void>();

constructor(
private route: ActivatedRoute,
private router: Router,
private projectService: ProjectService,
private globalStateService: GlobalStateService
) { }

ngOnInit(): void {
// Wait for global state to be ready, then react to route changes
this.globalStateService.onLoad(() => {
this.route.paramMap
.pipe(
takeUntil(this.destroy$),
switchMap((params: ParamMap) => {
const idParam = params.get('projectId');
this.projectId = Number(idParam);

if (!this.projectId || Number.isNaN(this.projectId)) {
this.hasError = true;
this.isLoading = false;
this.router.navigate(['/home']);
// Use EMPTY instead of new Subject()
return EMPTY;
}

this.isLoading = true;
this.hasError = false;

return this.projectService.get(this.projectId, {
cacheBehaviourOnGet: 'cacheQuery',
mappingCompleteCallback: (project: Project) => {
this.unit = project?.unit ?? null;
}
});
})
)
.subscribe({
next: (project: Project) => {
this.project = project;

// Guard against null/undefined project
if (!project) {
this.hasError = true;
this.isLoading = false;
this.router.navigate(['/home']);
return;
}

// Ensure unit is set when tasks match task definitions
if (
project.unit?.taskDefinitions?.length > 0 &&
project.tasks?.length === project.unit.taskDefinitions.length
) {
this.unit = project.unit;
}

// Use enum for view type
this.globalStateService.setView(ViewType.PROJECT, this.project);

this.isLoading = false;
},
error: () => {
this.isLoading = false;
this.hasError = true;
this.router.navigate(['/home']);
}
});
});
}

ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
}