From 20b07c0a663f0616ac8adc0a9651664232cff03c Mon Sep 17 00:00:00 2001 From: JoeMacl Date: Sat, 29 Mar 2025 15:12:26 +1100 Subject: [PATCH 1/4] feat: add Grant Extension form component --- .../grant-extension-form.component.html | 64 +++++++++++++++++++ .../grant-extension-form.component.scss | 0 .../grant-extension-form.component.spec.ts | 22 +++++++ .../grant-extension-form.component.ts | 34 ++++++++++ 4 files changed, 120 insertions(+) create mode 100644 src/app/admin/modals/grant-extension-form/grant-extension-form.component.html create mode 100644 src/app/admin/modals/grant-extension-form/grant-extension-form.component.scss create mode 100644 src/app/admin/modals/grant-extension-form/grant-extension-form.component.spec.ts create mode 100644 src/app/admin/modals/grant-extension-form/grant-extension-form.component.ts 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..406a6ffd9f --- /dev/null +++ b/src/app/admin/modals/grant-extension-form/grant-extension-form.component.html @@ -0,0 +1,64 @@ +
+

Grant Extension

+ +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ + +
+ +
+
+
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..6e3fc90c6d --- /dev/null +++ b/src/app/admin/modals/grant-extension-form/grant-extension-form.component.ts @@ -0,0 +1,34 @@ +import {Component, OnInit} from '@angular/core'; +import {FormGroup, FormBuilder, Validators, ReactiveFormsModule} from '@angular/forms'; +import {CommonModule} from '@angular/common'; + +@Component({ + selector: 'app-grant-extension-form', + standalone: true, + imports: [ReactiveFormsModule, CommonModule], + templateUrl: './grant-extension-form.component.html', + styleUrls: ['./grant-extension-form.component.scss'] +}) +export class GrantExtensionFormComponent implements OnInit { + grantExtensionForm!: FormGroup; + students = []; + + constructor(private fb: FormBuilder) {} + + ngOnInit(): void { + this.grantExtensionForm = this.fb.group({ + student: ['', Validators.required], + extension: [1, [Validators.required, Validators.min(1)]], + reason: ['', Validators.required], + notes: [''], + }); + } + + onSubmit(): void { + console.log('Clicked!'); + if (this.grantExtensionForm.valid) { + console.log('Submitted', this.grantExtensionForm.value); + // API goes here + } + } +} From 7c338a69df796ff5d485d1bea89959108aef9fd5 Mon Sep 17 00:00:00 2001 From: JoeMacl Date: Fri, 4 Apr 2025 18:19:05 +1100 Subject: [PATCH 2/4] feat: updated grant extension form with validation and styling --- .../grant-extension-form.component.html | 37 ++++++++++------ .../grant-extension-form.component.ts | 43 +++++++++++++++---- src/app/doubtfire.states.ts | 16 +++++++ 3 files changed, 73 insertions(+), 23 deletions(-) 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 index 406a6ffd9f..2f3b364c34 100644 --- 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 @@ -7,25 +7,28 @@

Grant Extension

+ +
+ Please select a student.
-
@@ -38,11 +41,14 @@

Grant Extension

class="w-full border border-gray-300 rounded-md px-4 py-3 text-base focus:ring-2 focus:ring-blue-500 focus:outline-none" required > + +
+ Please provide a reason for the extension.
- + - -
+ + + + 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.ts b/src/app/admin/modals/grant-extension-form/grant-extension-form.component.ts index 74a5a0dfa0..440ac2b6c9 100644 --- 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 @@ -1,59 +1,73 @@ -import {Component, OnInit} from '@angular/core'; -import {FormGroup, FormBuilder, Validators, ReactiveFormsModule} from '@angular/forms'; -import {CommonModule} from '@angular/common'; +import { Component, OnInit } 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 } from '@angular/material/dialog'; @Component({ selector: 'f-grant-extension-form', standalone: true, - imports: [ReactiveFormsModule, CommonModule], + 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; - // Tracks if the form is currently submitting isSubmitting = false; - // Test list of students (to be replaced by API data) + students = [ - { id: 1, name: 'Joe M' }, - { id: 2, name: 'Sahiru W' }, - { id: 3, name: 'Samindi M' } + { id: 1, name: 'Joe M' }, + { id: 2, name: 'Sahiru W' }, + { id: 3, name: 'Samindi M' } ]; - constructor(private fb: FormBuilder) {} + constructor(private fb: FormBuilder, private dialogRef: MatDialogRef) {} ngOnInit(): void { - // Initialize the form and apply validators to required fields this.grantExtensionForm = this.fb.group({ - student: ['', Validators.required], // Student must be selected - extension: [1, [Validators.required, Validators.min(1)]], // Minimum value of 1 - reason: ['', Validators.required], // Must provide reason + student: ['', Validators.required], + extension: [1, [Validators.required, Validators.min(1)]], + reason: ['', Validators.required], notes: [''], }); } onSubmit(): void { - // If form is invalid. Validation errors are shown if (this.grantExtensionForm.invalid) { - this.grantExtensionForm.markAllAsTouched(); // Triggers validation messages + this.grantExtensionForm.markAllAsTouched(); return; } - this.isSubmitting = true; // Disables the button for loading state + this.isSubmitting = true; - // Submission delay test setTimeout(() => { console.log('Form submitted:', this.grantExtensionForm.value); - // Resets form values this.grantExtensionForm.reset({ student: '', extension: 1, reason: '', notes: '' }); - // Reset the submit button + this.isSubmitting = false; - }, 1000); // Delay for submission + this.dialogRef.close(); + }, 1000); + } + 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 ec7945bb27..94334b33a0 100644 --- a/src/app/doubtfire.states.ts +++ b/src/app/doubtfire.states.ts @@ -14,7 +14,6 @@ import {ProjectRootState} from './projects/states/project-root-state.component'; import { TaskViewerState } from './units/task-viewer/task-viewer-state.component'; import {ScormPlayerComponent} from './common/scorm-player/scorm-player.component'; import { Ng2ViewDeclaration } from '@uirouter/angular'; -import { GrantExtensionFormComponent } from './admin/modals/grant-extension-form/grant-extension-form.component'; /* * Use this file to store any states that are sourced by angular components. @@ -237,20 +236,6 @@ const ViewAllProjectsState: NgHybridStateDeclaration = { }, }; -const GrantExtensionState: NgHybridStateDeclaration = { - name: 'grant-extension', - url: '/grant-extension', - views: { - main: { - component: GrantExtensionFormComponent, - }, - }, - data: { - pageTitle: 'Grant Extension', - roleWhitelist: ['Admin'] - } -}; - const AdministerUnits: NgHybridStateDeclaration = { name: 'admin/units', // This is the name of the state to jump to - so ui-sref="users" to jump here url: '/admin/units', // You get here with this url @@ -286,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', @@ -443,10 +443,10 @@ export const doubtfireStates = [ UnauthoriedState, ProjectRootState, ProjectDashboardState, + ProjectDashboardStateV1, UnitRootState, TaskViewerState, ScormPlayerNormalState, ScormPlayerReviewState, ScormPlayerStudentReviewState, - GrantExtensionState ]; 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..c6ef65f697 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,12 @@ export class TaskStatusCardComponent implements OnChanges, AfterViewInit { this.task.refresh(); }); } + + openGrantExtensionDialog(): void { + this.dialog.open(GrantExtensionFormComponent, { + width: '600px', + disableClose: true, + }); + } + } 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; } From b3b5f973804decb3aa583fe26f34d3a4e6686a3e Mon Sep 17 00:00:00 2001 From: JoeMacl Date: Tue, 13 May 2025 12:14:08 +1000 Subject: [PATCH 4/4] style: improve modal responsiveness and log form submission --- .../grant-extension-form.component.ts | 20 +++++++++++++++---- .../task-status-card.component.ts | 9 ++++++++- 2 files changed, 24 insertions(+), 5 deletions(-) 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 index 440ac2b6c9..b5d522908b 100644 --- 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 @@ -1,4 +1,4 @@ -import { Component, OnInit } from '@angular/core'; +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'; @@ -6,7 +6,7 @@ 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 } from '@angular/material/dialog'; +import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; @Component({ selector: 'f-grant-extension-form', @@ -34,7 +34,11 @@ export class GrantExtensionFormComponent implements OnInit { { id: 3, name: 'Samindi M' } ]; - constructor(private fb: FormBuilder, private dialogRef: MatDialogRef) {} + constructor( + private fb: FormBuilder, + private dialogRef: MatDialogRef, + @Inject(MAT_DIALOG_DATA) public data: { unitId: number; taskDefinitionId: number } + ) {} ngOnInit(): void { this.grantExtensionForm = this.fb.group({ @@ -43,6 +47,7 @@ export class GrantExtensionFormComponent implements OnInit { reason: ['', Validators.required], notes: [''], }); + console.log('Received dialog data:', this.data); } onSubmit(): void { @@ -54,7 +59,11 @@ export class GrantExtensionFormComponent implements OnInit { this.isSubmitting = true; setTimeout(() => { - console.log('Form submitted:', this.grantExtensionForm.value); + console.log('Form submitted:', { + ...this.grantExtensionForm.value, + unitId: this.data.unitId, + taskDefinitionId: this.data.taskDefinitionId + }); this.grantExtensionForm.reset({ student: '', @@ -66,6 +75,9 @@ export class GrantExtensionFormComponent implements OnInit { this.isSubmitting = false; this.dialogRef.close(); }, 1000); + console.log('Submitting with data:', this.data); + + } close(): void { this.dialogRef.close(); 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 c6ef65f697..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 @@ -73,9 +73,16 @@ export class TaskStatusCardComponent implements OnChanges, AfterViewInit { openGrantExtensionDialog(): void { this.dialog.open(GrantExtensionFormComponent, { - width: '600px', + width: '100%', + maxWidth: '600px', disableClose: true, + data: { + unitId: this.task.unit.id, + taskDefinitionId: this.task.definition.id + } }); + + console.log('TASK:', this.task); } }