@@ -9,6 +9,8 @@ import { MatButtonModule } from '@angular/material/button';
99import { MatDialogModule , MatDialogRef } from '@angular/material/dialog' ;
1010import { MatSnackBar } from '@angular/material/snack-bar' ;
1111import { ExtensionService } from 'src/app/api/services/extension.service' ;
12+ import { FormsModule } from '@angular/forms' ;
13+ import { MatCheckboxModule } from '@angular/material/checkbox' ;
1214
1315@Component ( {
1416 selector : 'f-grant-extension-form' ,
@@ -21,13 +23,18 @@ import { ExtensionService } from 'src/app/api/services/extension.service';
2123 MatInputModule ,
2224 MatSliderModule ,
2325 MatButtonModule ,
24- MatDialogModule
26+ MatDialogModule ,
27+ FormsModule ,
28+ MatCheckboxModule
2529 ] ,
2630 templateUrl : './grant-extension-form.component.html'
2731} )
2832export class GrantExtensionFormComponent implements OnInit {
2933 grantExtensionForm ! : FormGroup ;
3034 isSubmitting = false ;
35+ searchQuery = '' ;
36+ selectedStudents : number [ ] = [ ] ;
37+ showStudentList = false ;
3138
3239 // Temporary values will be replaced with dynamic context
3340 unitId = 1 ;
@@ -37,8 +44,26 @@ export class GrantExtensionFormComponent implements OnInit {
3744 students = [
3845 { id : 1 , name : 'Joe M' } ,
3946 { id : 2 , name : 'Sahiru W' } ,
40- { id : 3 , name : 'Samindi M' }
47+ { id : 3 , name : 'Samindi M' } ,
48+ { id : 4 , name : 'Student 4' } ,
49+ { id : 5 , name : 'Student 5' } ,
50+ { id : 6 , name : 'Student 6' } ,
51+ { id : 7 , name : 'Student 7' } ,
52+ { id : 8 , name : 'Student 8' } ,
53+ { id : 9 , name : 'Student 9' } ,
54+ { id : 10 , name : 'Student 10' } ,
55+ { id : 11 , name : 'Student 11' } ,
56+ { id : 12 , name : 'Student 12' } ,
57+ { id : 13 , name : 'Student 13' } ,
58+ { id : 14 , name : 'Student 14' } ,
59+ { id : 15 , name : 'Student 15' } ,
60+ { id : 16 , name : 'Student 16' } ,
61+ { id : 17 , name : 'Student 17' } ,
62+ { id : 18 , name : 'Student 18' } ,
63+ { id : 19 , name : 'Student 19' } ,
64+ { id : 20 , name : 'Student 20' }
4165 ] ;
66+ filteredStudents = this . students ;
4267
4368 constructor (
4469 private fb : FormBuilder ,
@@ -50,13 +75,81 @@ export class GrantExtensionFormComponent implements OnInit {
5075 // Initialize the reactive form with validators for each field
5176 ngOnInit ( ) : void {
5277 this . grantExtensionForm = this . fb . group ( {
53- student : [ '' , Validators . required ] ,
78+ students : [ [ ] , Validators . required ] ,
5479 extension : [ 1 , [ Validators . required , Validators . min ( 1 ) ] ] ,
5580 reason : [ '' , Validators . required ] ,
5681 notes : [ '' ]
5782 } ) ;
5883 }
5984
85+ /**
86+ * Filters the student list based on the search query.
87+ * Matches against both student name and ID.
88+ * If no query is provided, shows all students.
89+ */
90+ filterStudents ( ) : void {
91+ if ( ! this . searchQuery ) {
92+ this . filteredStudents = this . students ;
93+ } else {
94+ const query = this . searchQuery . toLowerCase ( ) ;
95+ this . filteredStudents = this . students . filter ( student =>
96+ student . name . toLowerCase ( ) . includes ( query ) ||
97+ student . id . toString ( ) . includes ( query )
98+ ) ;
99+ }
100+ }
101+
102+ /**
103+ * Toggles the selection state of a student.
104+ * Adds the student to selectedStudents if not already selected,
105+ * removes them if already selected.
106+ * Updates the form control value accordingly.
107+ */
108+ toggleStudent ( studentId : number ) : void {
109+ const index = this . selectedStudents . indexOf ( studentId ) ;
110+ if ( index === - 1 ) {
111+ this . selectedStudents . push ( studentId ) ;
112+ } else {
113+ this . selectedStudents . splice ( index , 1 ) ;
114+ }
115+ this . grantExtensionForm . patchValue ( { students : this . selectedStudents } ) ;
116+ }
117+
118+ /**
119+ * Handles keyboard navigation for student selection.
120+ * Allows selection/deselection using Enter or Space keys.
121+ * Prevents default browser behavior for these keys.
122+ */
123+ handleStudentKeydown ( event : KeyboardEvent , studentId : number ) : void {
124+ if ( event . key === 'Enter' || event . key === ' ' ) {
125+ event . preventDefault ( ) ;
126+ this . toggleStudent ( studentId ) ;
127+ }
128+ }
129+
130+ /**
131+ * Toggles selection of all currently filtered students.
132+ * If all filtered students are selected, deselects all.
133+ * If not all are selected, selects all filtered students.
134+ * Updates the form control value accordingly.
135+ */
136+ toggleSelectAll ( ) : void {
137+ if ( this . selectedStudents . length === this . filteredStudents . length ) {
138+ this . selectedStudents = [ ] ;
139+ } else {
140+ this . selectedStudents = this . filteredStudents . map ( student => student . id ) ;
141+ }
142+ this . grantExtensionForm . patchValue ( { students : this . selectedStudents } ) ;
143+ }
144+
145+ /**
146+ * Checks if a student is currently selected.
147+ * Used for UI state management and visual feedback.
148+ */
149+ isStudentSelected ( studentId : number ) : boolean {
150+ return this . selectedStudents . includes ( studentId ) ;
151+ }
152+
60153 // Handles form submission.
61154 // Builds the payload and sends it to the backend via the ExtensionService.
62155 // Displays a success or error message and closes the dialog on success.
@@ -68,10 +161,10 @@ export class GrantExtensionFormComponent implements OnInit {
68161
69162 this . isSubmitting = true ;
70163
71- const { student , extension, reason, notes } = this . grantExtensionForm . value ;
164+ const { students , extension, reason, notes } = this . grantExtensionForm . value ;
72165 const unitId = 1 ; // temporary value
73166 const payload = {
74- student_ids : [ student ] ,
167+ student_ids : students ,
75168 task_definition_id : 25 ,
76169 weeks_requested : extension ,
77170 comment : reason ,
0 commit comments