diff --git a/angular.json b/angular.json
index f2ecb001f1..c7e114b0dd 100644
--- a/angular.json
+++ b/angular.json
@@ -32,6 +32,7 @@
"src/manifest.webmanifest"
],
"styles": [
+ "@angular/material/prebuilt-themes/azure-blue.css",
"src/theme.scss",
"src/styles.scss",
"./node_modules/bootstrap/dist/css/bootstrap.css",
@@ -139,6 +140,7 @@
"tsConfig": "src/tsconfig.spec.json",
"karmaConfig": "src/karma.conf.js",
"styles": [
+ "@angular/material/prebuilt-themes/azure-blue.css",
"./node_modules/bootstrap/dist/css/bootstrap.css",
"./node_modules/ngx-bootstrap/datepicker/bs-datepicker.css"
],
diff --git a/package.json b/package.json
index cc4a67fd36..b7b58ce083 100644
--- a/package.json
+++ b/package.json
@@ -165,4 +165,4 @@
"@nx/nx-linux-x64-gnu": "^18.0",
"@nx/nx-win32-x64-msvc": "^18.0"
}
-}
+}
\ No newline at end of file
diff --git a/src/app/admin/modals/grant-extension-form/grant-extension-dialog.component.ts b/src/app/admin/modals/grant-extension-form/grant-extension-dialog.component.ts
new file mode 100644
index 0000000000..9e8ff15782
--- /dev/null
+++ b/src/app/admin/modals/grant-extension-form/grant-extension-dialog.component.ts
@@ -0,0 +1,27 @@
+import { Component } from '@angular/core';
+import { MatDialogRef } from '@angular/material/dialog';
+import { GrantExtensionFormComponent } from './grant-extension-form.component';
+import { MatDialogModule } from '@angular/material/dialog';
+import { CommonModule } from '@angular/common';
+
+@Component({
+ selector: 'f-grant-extension-dialog',
+ standalone: true,
+ imports: [MatDialogModule, CommonModule, GrantExtensionFormComponent],
+ template: `
+
Grant Extension
+
+
+
+
+
+
+ `
+})
+export class GrantExtensionDialogComponent {
+ constructor(private dialogRef: MatDialogRef) {}
+
+ close(): void {
+ this.dialogRef.close();
+ }
+}
diff --git a/src/app/admin/modals/grant-extension-form/grant-extension-form.component.html b/src/app/admin/modals/grant-extension-form/grant-extension-form.component.html
new file mode 100644
index 0000000000..4c8ea67f29
--- /dev/null
+++ b/src/app/admin/modals/grant-extension-form/grant-extension-form.component.html
@@ -0,0 +1,65 @@
+Grant Extension
+
+
+
+
+ Student
+
+ Select a student
+
+ {{ student.name }}
+
+
+
+ Please select a student.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Reason
+
+
+ Please provide a reason for the extension.
+
+
+
+
+
+ Additional Notes (optional)
+
+
+
+
+
+
+
+
diff --git a/src/app/admin/modals/grant-extension-form/grant-extension-form.component.scss b/src/app/admin/modals/grant-extension-form/grant-extension-form.component.scss
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/src/app/admin/modals/grant-extension-form/grant-extension-form.component.spec.ts b/src/app/admin/modals/grant-extension-form/grant-extension-form.component.spec.ts
new file mode 100644
index 0000000000..6d3c33c448
--- /dev/null
+++ b/src/app/admin/modals/grant-extension-form/grant-extension-form.component.spec.ts
@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { GrantExtensionFormComponent } from './grant-extension-form.component';
+
+describe('GrantExtensionFormComponent', () => {
+ let component: GrantExtensionFormComponent;
+ let fixture: ComponentFixture;
+
+ beforeEach(async () => {
+ await TestBed.configureTestingModule({
+ imports: [GrantExtensionFormComponent]
+ })
+ .compileComponents();
+
+ fixture = TestBed.createComponent(GrantExtensionFormComponent);
+ component = fixture.componentInstance;
+ fixture.detectChanges();
+ });
+
+ it('should create', () => {
+ expect(component).toBeTruthy();
+ });
+});
diff --git a/src/app/admin/modals/grant-extension-form/grant-extension-form.component.ts b/src/app/admin/modals/grant-extension-form/grant-extension-form.component.ts
new file mode 100644
index 0000000000..b5d522908b
--- /dev/null
+++ b/src/app/admin/modals/grant-extension-form/grant-extension-form.component.ts
@@ -0,0 +1,85 @@
+import { Component, OnInit, Inject } from '@angular/core';
+import { FormGroup, FormBuilder, Validators, ReactiveFormsModule } from '@angular/forms';
+import { CommonModule } from '@angular/common';
+import { MatFormFieldModule } from '@angular/material/form-field';
+import { MatSelectModule } from '@angular/material/select';
+import { MatInputModule } from '@angular/material/input';
+import { MatSliderModule } from '@angular/material/slider';
+import { MatButtonModule } from '@angular/material/button';
+import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
+
+@Component({
+ selector: 'f-grant-extension-form',
+ standalone: true,
+ imports: [
+ ReactiveFormsModule,
+ CommonModule,
+ MatFormFieldModule,
+ MatSelectModule,
+ MatInputModule,
+ MatSliderModule,
+ MatButtonModule,
+ MatDialogModule
+ ],
+ templateUrl: './grant-extension-form.component.html',
+ styleUrls: ['./grant-extension-form.component.scss']
+})
+export class GrantExtensionFormComponent implements OnInit {
+ grantExtensionForm!: FormGroup;
+ isSubmitting = false;
+
+ students = [
+ { id: 1, name: 'Joe M' },
+ { id: 2, name: 'Sahiru W' },
+ { id: 3, name: 'Samindi M' }
+ ];
+
+ constructor(
+ private fb: FormBuilder,
+ private dialogRef: MatDialogRef,
+ @Inject(MAT_DIALOG_DATA) public data: { unitId: number; taskDefinitionId: number }
+ ) {}
+
+ ngOnInit(): void {
+ this.grantExtensionForm = this.fb.group({
+ student: ['', Validators.required],
+ extension: [1, [Validators.required, Validators.min(1)]],
+ reason: ['', Validators.required],
+ notes: [''],
+ });
+ console.log('Received dialog data:', this.data);
+ }
+
+ onSubmit(): void {
+ if (this.grantExtensionForm.invalid) {
+ this.grantExtensionForm.markAllAsTouched();
+ return;
+ }
+
+ this.isSubmitting = true;
+
+ setTimeout(() => {
+ console.log('Form submitted:', {
+ ...this.grantExtensionForm.value,
+ unitId: this.data.unitId,
+ taskDefinitionId: this.data.taskDefinitionId
+ });
+
+ this.grantExtensionForm.reset({
+ student: '',
+ extension: 1,
+ reason: '',
+ notes: ''
+ });
+
+ this.isSubmitting = false;
+ this.dialogRef.close();
+ }, 1000);
+ console.log('Submitting with data:', this.data);
+
+
+ }
+ close(): void {
+ this.dialogRef.close();
+ }
+}
diff --git a/src/app/doubtfire-angular.module.ts b/src/app/doubtfire-angular.module.ts
index c714ff0e5a..d1538d5b27 100644
--- a/src/app/doubtfire-angular.module.ts
+++ b/src/app/doubtfire-angular.module.ts
@@ -267,6 +267,7 @@ const MY_DATE_FORMAT = {
},
};
import { UnitStudentEnrolmentModalComponent } from './units/modals/unit-student-enrolment-modal/unit-student-enrolment-modal.component';
+import { provideAnimationsAsync } from '@angular/platform-browser/animations/async';
@NgModule({
// Components we declare
@@ -468,6 +469,7 @@ import { UnitStudentEnrolmentModalComponent } from './units/modals/unit-student-
provideLottieOptions({
player: () => player,
}),
+ provideAnimationsAsync(),
],
imports: [
FlexLayoutModule,
diff --git a/src/app/doubtfire.states.ts b/src/app/doubtfire.states.ts
index 7baa910f57..94334b33a0 100644
--- a/src/app/doubtfire.states.ts
+++ b/src/app/doubtfire.states.ts
@@ -271,6 +271,21 @@ const ProjectDashboardState: NgHybridStateDeclaration = {
},
};
+const ProjectDashboardStateV1: NgHybridStateDeclaration = {
+ name: 'dashboard',
+ parent: 'projects',
+ url: '/dashboard',
+ views: {
+ projectView: {
+ component: ProjectDashboardComponent, // <-- Your new Angular Component
+ },
+ },
+ data: {
+ pageTitle: 'Unit Dashboard',
+ },
+};
+
+
const ViewAllUnits: NgHybridStateDeclaration = {
name: 'view-all-units',
url: '/view-all-units',
@@ -428,6 +443,7 @@ export const doubtfireStates = [
UnauthoriedState,
ProjectRootState,
ProjectDashboardState,
+ ProjectDashboardStateV1,
UnitRootState,
TaskViewerState,
ScormPlayerNormalState,
diff --git a/src/app/projects/states/dashboard/directives/task-dashboard/directives/task-status-card/task-status-card.component.html b/src/app/projects/states/dashboard/directives/task-dashboard/directives/task-status-card/task-status-card.component.html
index e210890833..1c78a97c34 100644
--- a/src/app/projects/states/dashboard/directives/task-dashboard/directives/task-status-card/task-status-card.component.html
+++ b/src/app/projects/states/dashboard/directives/task-dashboard/directives/task-status-card/task-status-card.component.html
@@ -1,44 +1,40 @@
@if (triggers?.length > 0) {
-
-
-
-
-
- {{ task?.statusLabel() }}
-
-
- @for (trigger of triggers; track trigger) {
-
-
- {{ trigger.label }}
-
- }
-
-
- } @if (triggers?.length < 0) {
-
-
-
- {{ task?.statusLabel() }}
-
-
+
+
+
+
+
+ {{ task?.statusLabel() }}
+
+
+ @for (trigger of triggers; track trigger) {
+
+
+
+ {{ trigger.label }}
+
+
+ }
+
+
}
{{ task?.statusHelp().reason }} {{ task?.statusHelp().action }}
- @if ( task?.unit.currentUserIsStaff || task?.canApplyForExtension() || (task?.inSubmittedState() &&
- task?.requiresFileUpload()) ) {
@if (task?.canApplyForExtension()) {
-
- } @if (task?.inSubmittedState() && task?.requiresFileUpload()) {
-
+
}
+ @if (task?.inSubmittedState() && task?.requiresFileUpload()) {
+
+ }
+
+
+
- }
diff --git a/src/app/projects/states/dashboard/directives/task-dashboard/directives/task-status-card/task-status-card.component.ts b/src/app/projects/states/dashboard/directives/task-dashboard/directives/task-status-card/task-status-card.component.ts
index d33646b066..976b4d2335 100644
--- a/src/app/projects/states/dashboard/directives/task-dashboard/directives/task-status-card/task-status-card.component.ts
+++ b/src/app/projects/states/dashboard/directives/task-dashboard/directives/task-status-card/task-status-card.component.ts
@@ -5,6 +5,9 @@ import { Task } from 'src/app/api/models/task';
import { TaskStatusEnum } from 'src/app/api/models/task-status';
import { TaskService } from 'src/app/api/services/task.service';
import { ExtensionModalService } from 'src/app/common/modals/extension-modal/extension-modal.service';
+import { MatDialog } from '@angular/material/dialog';
+import { GrantExtensionFormComponent } from 'src/app/admin/modals/grant-extension-form/grant-extension-form.component';
+
@Component({
selector: 'f-task-status-card',
@@ -19,6 +22,7 @@ export class TaskStatusCardComponent implements OnChanges, AfterViewInit {
private extensions: ExtensionModalService,
private taskService: TaskService,
private router: UIRouter,
+ private dialog: MatDialog,
) {}
@Input() task: Task;
@@ -66,4 +70,19 @@ export class TaskStatusCardComponent implements OnChanges, AfterViewInit {
this.task.refresh();
});
}
+
+ openGrantExtensionDialog(): void {
+ this.dialog.open(GrantExtensionFormComponent, {
+ width: '100%',
+ maxWidth: '600px',
+ disableClose: true,
+ data: {
+ unitId: this.task.unit.id,
+ taskDefinitionId: this.task.definition.id
+ }
+ });
+
+ console.log('TASK:', this.task);
+ }
+
}
diff --git a/src/app/projects/states/dashboard/project-dashboard/project-dashboard.component.ts b/src/app/projects/states/dashboard/project-dashboard/project-dashboard.component.ts
index ac792b582e..b9230045f9 100644
--- a/src/app/projects/states/dashboard/project-dashboard/project-dashboard.component.ts
+++ b/src/app/projects/states/dashboard/project-dashboard/project-dashboard.component.ts
@@ -15,7 +15,7 @@ import {ProjectService} from 'src/app/api/services/project.service';
import {GlobalStateService} from '../../index/global-state.service';
import {UserService} from 'src/app/api/services/user.service';
import {Project, TaskDefinition} from 'src/app/api/models/doubtfire-model';
-
+import { MatDialog } from '@angular/material/dialog';
@Component({
selector: 'f-project-dashboard',
templateUrl: './project-dashboard.component.html',
@@ -43,6 +43,7 @@ export class ProjectDashboardComponent implements OnInit {
private currentUser: UserService,
private projectService: ProjectService,
private globalStateService: GlobalStateService,
+ private dialog: MatDialog,
) {}
startedDragging(event: CdkDragStart, div: HTMLDivElement) {
diff --git a/src/styles.scss b/src/styles.scss
index 0df654e6ba..54ee9aadf2 100644
--- a/src/styles.scss
+++ b/src/styles.scss
@@ -64,3 +64,6 @@ $main-view-max-height: calc((var(--vh, 1vh) * (100)) - 85px);
}
}
+
+html, body { height: 100%; }
+body { margin: 0; font-family: Roboto, "Helvetica Neue", sans-serif; }