cloud-ide-academics 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1536 @@
1
+ import { authGuard } from 'cloud-ide-auth';
2
+ import * as i0 from '@angular/core';
3
+ import { inject, Injectable, DestroyRef, signal, Component, viewChild, computed } from '@angular/core';
4
+ import * as i1$1 from '@angular/common';
5
+ import { CommonModule } from '@angular/common';
6
+ import * as i1 from '@angular/forms';
7
+ import { FormBuilder, Validators, ReactiveFormsModule, FormsModule } from '@angular/forms';
8
+ import { Router, ActivatedRoute } from '@angular/router';
9
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
10
+ import { of, forkJoin } from 'rxjs';
11
+ import { switchMap, catchError, map } from 'rxjs/operators';
12
+ import { CideInputComponent, CideTextareaComponent, CideEleButtonComponent, CideEleTabComponent, CideIconComponent, CideSelectComponent, ConfirmationService, NotificationService, CideEleDataGridComponent, CideEleDropdownComponent } from 'cloud-ide-element';
13
+ import { HttpClient } from '@angular/common/http';
14
+ import { generateStringFromObject, cidePath, hostManagerRoutesUrl, academicsRoutesUrl, generateObjectFromString } from 'cloud-ide-lms-model';
15
+ import { CideCoreEntityManagementService } from 'cloud-ide-core';
16
+ import { AppStateHelperService } from 'cloud-ide-layout';
17
+
18
+ const academicsRoutes = [
19
+ // Academic Year Management Routes
20
+ {
21
+ path: 'academic_year',
22
+ loadComponent: () => Promise.resolve().then(function () { return academicYearList_component; }).then(c => c.AcademicYearListComponent),
23
+ title: 'Academic Year Management',
24
+ canActivate: [authGuard],
25
+ data: {
26
+ reuseTab: true,
27
+ sypg_page_code: "academic_year_list"
28
+ }
29
+ },
30
+ {
31
+ path: 'academic_year/create',
32
+ loadComponent: () => Promise.resolve().then(function () { return academicYearCreate_component; }).then(c => c.AcademicYearCreateComponent),
33
+ title: 'Create Academic Year',
34
+ canActivate: [authGuard],
35
+ data: {
36
+ reuseTab: true,
37
+ sypg_page_code: "academic_year_list"
38
+ }
39
+ },
40
+ {
41
+ path: 'academic_year/edit/:query',
42
+ loadComponent: () => Promise.resolve().then(function () { return academicYearCreate_component; }).then(c => c.AcademicYearCreateComponent),
43
+ title: 'Edit Academic Year',
44
+ canActivate: [authGuard],
45
+ data: {
46
+ reuseTab: true,
47
+ sypg_page_code: "academic_year_list"
48
+ }
49
+ },
50
+ {
51
+ path: 'academic_year/view/:query',
52
+ loadComponent: () => Promise.resolve().then(function () { return academicYearCreate_component; }).then(c => c.AcademicYearCreateComponent),
53
+ title: 'View Academic Year',
54
+ canActivate: [authGuard],
55
+ data: {
56
+ reuseTab: true,
57
+ sypg_page_code: "academic_year_list"
58
+ }
59
+ }
60
+ ];
61
+
62
+ class CideLytAcademicYearService {
63
+ http = inject(HttpClient);
64
+ /**
65
+ * Get list of academic years
66
+ * @param payload - Query parameters for filtering/pagination
67
+ * @returns Observable of academic year list response
68
+ */
69
+ getAcademicYearList(payload) {
70
+ console.log("payload", payload);
71
+ const query = generateStringFromObject(payload);
72
+ const url = cidePath.join([
73
+ hostManagerRoutesUrl.cideSuiteHost,
74
+ academicsRoutesUrl.module,
75
+ academicsRoutesUrl.academicYear,
76
+ query
77
+ ]);
78
+ return this.http.get(url);
79
+ }
80
+ /**
81
+ * Get academic year by ID
82
+ * @param payload - Academic year ID payload
83
+ * @returns Observable of academic year data
84
+ */
85
+ getAcademicYearById(payload) {
86
+ const query = generateStringFromObject(payload);
87
+ const url = cidePath.join([
88
+ hostManagerRoutesUrl.cideSuiteHost,
89
+ academicsRoutesUrl.module,
90
+ academicsRoutesUrl.academicYear,
91
+ 'byId',
92
+ query
93
+ ]);
94
+ return this.http.get(url);
95
+ }
96
+ /**
97
+ * Create or update academic year
98
+ * @param data - Academic year data to save
99
+ * @returns Observable of the save response
100
+ */
101
+ saveUpdateAcademicYear(data) {
102
+ const url = cidePath.join([
103
+ hostManagerRoutesUrl.cideSuiteHost,
104
+ academicsRoutesUrl.module,
105
+ academicsRoutesUrl.academicYear
106
+ ]);
107
+ return this.http.post(url, data);
108
+ }
109
+ /**
110
+ * Create or update academic year with mappings
111
+ * @param data - Academic year data with mappings
112
+ * @returns Observable of the save response
113
+ */
114
+ saveUpdateAcademicYearWithMappings(data) {
115
+ // Extract mappings from the data
116
+ const mappings = data.acayr_academic_year_mappings || [];
117
+ const academicYearData = { ...data };
118
+ delete academicYearData.acayr_academic_year_mappings;
119
+ // First save/update the academic year
120
+ return this.saveUpdateAcademicYear(academicYearData).pipe(switchMap((academicYearResponse) => {
121
+ if (!academicYearResponse.success || !academicYearResponse.data) {
122
+ return of(academicYearResponse);
123
+ }
124
+ const academicYearId = academicYearResponse.data._id;
125
+ // If no mappings, return the academic year response
126
+ if (mappings.length === 0) {
127
+ return of(academicYearResponse);
128
+ }
129
+ // Process mappings
130
+ const mappingObservables = mappings.map((mapping) => {
131
+ const mappingPayload = {
132
+ _id: mapping._id && !mapping._id.startsWith('temp_') ? mapping._id : undefined,
133
+ acayrmp_academic_year_id_acayr: academicYearId,
134
+ acayrmp_entity_id_syen: mapping.acayrmp_entity_id_syen,
135
+ acayrmp_islocked: mapping.acayrmp_islocked || false,
136
+ acayrmp_iscurrent: mapping.acayrmp_iscurrent || false
137
+ };
138
+ const mappingUrl = cidePath.join([
139
+ hostManagerRoutesUrl.cideSuiteHost,
140
+ academicsRoutesUrl.module,
141
+ academicsRoutesUrl.academicYearMapping
142
+ ]);
143
+ return this.http.post(mappingUrl, mappingPayload).pipe(catchError((error) => {
144
+ console.error('Error saving mapping:', error);
145
+ return of({ success: false, error });
146
+ }));
147
+ });
148
+ // Execute all mapping operations
149
+ return forkJoin(mappingObservables).pipe(map((mappingResponses) => {
150
+ // Check if all mappings were successful
151
+ const allMappingsSuccessful = mappingResponses.every((response) => response.success);
152
+ if (allMappingsSuccessful) {
153
+ return {
154
+ ...academicYearResponse,
155
+ message: 'Academic year and mappings saved successfully'
156
+ };
157
+ }
158
+ else {
159
+ return {
160
+ ...academicYearResponse,
161
+ message: 'Academic year saved but some mappings failed',
162
+ mappingErrors: mappingResponses.filter((response) => !response.success)
163
+ };
164
+ }
165
+ }));
166
+ }), catchError((error) => {
167
+ console.error('Error in saveUpdateAcademicYearWithMappings:', error);
168
+ return of({
169
+ success: false,
170
+ code: 500,
171
+ message: 'Error saving academic year with mappings',
172
+ error
173
+ });
174
+ }));
175
+ }
176
+ /**
177
+ * Delete academic year
178
+ * @param payload - Academic year ID payload
179
+ * @returns Observable of the delete response
180
+ */
181
+ deleteAcademicYear(payload) {
182
+ const query = generateStringFromObject(payload);
183
+ const url = cidePath.join([
184
+ hostManagerRoutesUrl.cideSuiteHost,
185
+ academicsRoutesUrl.module,
186
+ academicsRoutesUrl.academicYear,
187
+ 'delete',
188
+ query
189
+ ]);
190
+ return this.http.delete(url);
191
+ }
192
+ /**
193
+ * Toggle academic year status
194
+ * @param payload - Academic year ID payload
195
+ * @returns Observable of the toggle response
196
+ */
197
+ toggleAcademicYearStatus(payload) {
198
+ const url = cidePath.join([
199
+ hostManagerRoutesUrl.cideSuiteHost,
200
+ academicsRoutesUrl.module,
201
+ academicsRoutesUrl.academicYear,
202
+ 'toggle-status'
203
+ ]);
204
+ console.log('🔄 Making PATCH request to toggle academic year status:', { url, payload });
205
+ return this.http.patch(url, payload);
206
+ }
207
+ /**
208
+ * Delete academic year mapping
209
+ * @param payload - Academic year mapping ID payload
210
+ * @returns Observable of the delete response
211
+ */
212
+ deleteAcademicYearMapping(payload) {
213
+ const url = cidePath.join([
214
+ hostManagerRoutesUrl.cideSuiteHost,
215
+ academicsRoutesUrl.module,
216
+ academicsRoutesUrl.academicYearMapping
217
+ ]);
218
+ return this.http.delete(url, { body: payload });
219
+ }
220
+ /**
221
+ * Toggle academic year mapping status
222
+ * @param payload - Academic year mapping ID payload
223
+ * @returns Observable of the toggle response
224
+ */
225
+ toggleAcademicYearMappingStatus(payload) {
226
+ const url = cidePath.join([
227
+ hostManagerRoutesUrl.cideSuiteHost,
228
+ academicsRoutesUrl.module,
229
+ academicsRoutesUrl.academicYearMapping,
230
+ 'toggle-status'
231
+ ]);
232
+ console.log('🔄 Making PATCH request to toggle academic year mapping status:', { url, payload });
233
+ return this.http.patch(url, payload);
234
+ }
235
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideLytAcademicYearService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
236
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideLytAcademicYearService, providedIn: 'root' });
237
+ }
238
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideLytAcademicYearService, decorators: [{
239
+ type: Injectable,
240
+ args: [{
241
+ providedIn: 'root'
242
+ }]
243
+ }] });
244
+
245
+ class CideLytAcademicYearMappingService {
246
+ http = inject(HttpClient);
247
+ /**
248
+ * Get list of academic year mappings
249
+ * @param payload - Query parameters for filtering/pagination
250
+ * @returns Observable of academic year mapping list response
251
+ */
252
+ getAcademicYearMappingList(payload) {
253
+ const query = generateStringFromObject(payload);
254
+ const url = cidePath.join([
255
+ hostManagerRoutesUrl.cideSuiteHost,
256
+ academicsRoutesUrl.module,
257
+ academicsRoutesUrl.academicYearMapping,
258
+ query
259
+ ]);
260
+ return this.http.get(url);
261
+ }
262
+ /**
263
+ * Get academic year mapping by ID
264
+ * @param payload - Academic year mapping ID payload
265
+ * @returns Observable of academic year mapping data
266
+ */
267
+ getAcademicYearMappingById(payload) {
268
+ const query = generateStringFromObject(payload);
269
+ const url = cidePath.join([
270
+ hostManagerRoutesUrl.cideSuiteHost,
271
+ academicsRoutesUrl.module,
272
+ academicsRoutesUrl.academicYearMapping,
273
+ 'byId',
274
+ query
275
+ ]);
276
+ return this.http.get(url);
277
+ }
278
+ /**
279
+ * Create or update academic year mapping
280
+ * @param data - Academic year mapping data to save
281
+ * @returns Observable of the save response
282
+ */
283
+ saveUpdateAcademicYearMapping(data) {
284
+ const url = cidePath.join([
285
+ hostManagerRoutesUrl.cideSuiteHost,
286
+ academicsRoutesUrl.module,
287
+ academicsRoutesUrl.academicYearMapping
288
+ ]);
289
+ return this.http.post(url, data);
290
+ }
291
+ /**
292
+ * Delete academic year mapping
293
+ * @param payload - Academic year mapping ID payload
294
+ * @returns Observable of the delete response
295
+ */
296
+ deleteAcademicYearMapping(payload) {
297
+ const query = generateStringFromObject(payload);
298
+ const url = cidePath.join([
299
+ hostManagerRoutesUrl.cideSuiteHost,
300
+ academicsRoutesUrl.module,
301
+ academicsRoutesUrl.academicYearMapping,
302
+ 'delete',
303
+ query
304
+ ]);
305
+ return this.http.delete(url);
306
+ }
307
+ /**
308
+ * Toggle academic year mapping status
309
+ * @param payload - Academic year mapping ID payload
310
+ * @returns Observable of the toggle response
311
+ */
312
+ toggleAcademicYearMappingStatus(payload) {
313
+ const url = cidePath.join([
314
+ hostManagerRoutesUrl.cideSuiteHost,
315
+ academicsRoutesUrl.module,
316
+ academicsRoutesUrl.academicYearMapping,
317
+ 'toggle-status'
318
+ ]);
319
+ return this.http.post(url, payload);
320
+ }
321
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideLytAcademicYearMappingService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
322
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideLytAcademicYearMappingService, providedIn: 'root' });
323
+ }
324
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: CideLytAcademicYearMappingService, decorators: [{
325
+ type: Injectable,
326
+ args: [{
327
+ providedIn: 'root'
328
+ }]
329
+ }] });
330
+
331
+ class AcademicYearCreateComponent {
332
+ // Modern Angular 20+ dependency injection
333
+ destroyRef = inject(DestroyRef);
334
+ fb = inject(FormBuilder);
335
+ academicYearService = inject(CideLytAcademicYearService);
336
+ academicYearMappingService = inject(CideLytAcademicYearMappingService);
337
+ entityService = inject(CideCoreEntityManagementService);
338
+ router = inject(Router);
339
+ route = inject(ActivatedRoute);
340
+ // Form and state management with proper typing
341
+ academicYearForm;
342
+ activeTab = signal('basic', ...(ngDevMode ? [{ debugName: "activeTab" }] : []));
343
+ loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
344
+ error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
345
+ // Academic year information from route
346
+ academicYearId = signal('', ...(ngDevMode ? [{ debugName: "academicYearId" }] : []));
347
+ isEditMode = signal(false, ...(ngDevMode ? [{ debugName: "isEditMode" }] : []));
348
+ originalMappings = signal([], ...(ngDevMode ? [{ debugName: "originalMappings" }] : [])); // Track original mappings for deletion
349
+ // Entity options for dropdown
350
+ entityOptions = signal([], ...(ngDevMode ? [{ debugName: "entityOptions" }] : []));
351
+ constructor() {
352
+ this.academicYearForm = this.fb.group({
353
+ // Basic Academic Year Information
354
+ acayr_code: ['', [Validators.required]],
355
+ acayr_name: ['', [Validators.required]],
356
+ acayr_description: [''],
357
+ acayr_from_date: ['', [Validators.required]],
358
+ acayr_to_date: ['', [Validators.required]],
359
+ acayr_isactive: [true],
360
+ acayr_iscurrent: [false],
361
+ acayr_islocked: [false],
362
+ // Academic Year Mapping Information
363
+ acayr_academic_year_mappings: this.fb.array([])
364
+ });
365
+ }
366
+ ngOnInit() {
367
+ this.initializeComponent();
368
+ }
369
+ /**
370
+ * Initialize component
371
+ */
372
+ initializeComponent() {
373
+ // Get academic year information from route (for edit mode)
374
+ this.route.params.pipe(takeUntilDestroyed(this.destroyRef)).subscribe(params => {
375
+ const queryParams = params['query'];
376
+ console.log(queryParams, 'queryParams');
377
+ if (queryParams) {
378
+ // Query parameters passed (following page-controls pattern)
379
+ const queryData = generateObjectFromString(queryParams);
380
+ if (queryData?.acayr_id) {
381
+ this.academicYearId.set(queryData.acayr_id);
382
+ this.isEditMode.set(true);
383
+ this.loadAcademicYearData(queryData.acayr_id);
384
+ }
385
+ }
386
+ else {
387
+ // Create mode
388
+ this.isEditMode.set(false);
389
+ }
390
+ // Always load dropdown options
391
+ this.loadDropdownOptions();
392
+ });
393
+ }
394
+ /**
395
+ * Load dropdown options
396
+ */
397
+ loadDropdownOptions() {
398
+ console.log('🏢 Loading dropdown options');
399
+ // Load entity options
400
+ this.entityService.getEntityList({}).subscribe({
401
+ next: (response) => {
402
+ if (response?.success) {
403
+ console.log(`🏢 entities:`, response.data);
404
+ this.entityOptions.set(response.data || []);
405
+ }
406
+ },
407
+ error: (error) => console.error('Error loading entities:', error)
408
+ });
409
+ }
410
+ /**
411
+ * Load academic year data for edit mode
412
+ */
413
+ loadAcademicYearData(academicYearId) {
414
+ this.loading.set(true);
415
+ this.error.set(null);
416
+ const payload = {
417
+ acayr_id: academicYearId
418
+ };
419
+ // Load academic year data
420
+ this.academicYearService.getAcademicYearById(payload).subscribe({
421
+ next: (response) => {
422
+ if (response?.success && response.data) {
423
+ console.log('📚 Academic year data loaded:', response.data);
424
+ this.populateFormWithAcademicYearData(response.data);
425
+ // Load existing mappings for this academic year
426
+ this.loadAcademicYearMappings(academicYearId);
427
+ }
428
+ else {
429
+ console.error('❌ Failed to load academic year data');
430
+ this.error.set('Failed to load academic year data');
431
+ this.loading.set(false);
432
+ }
433
+ },
434
+ error: (error) => {
435
+ console.error('❌ Error loading academic year data:', error);
436
+ this.error.set('Error loading academic year data');
437
+ this.loading.set(false);
438
+ }
439
+ });
440
+ }
441
+ /**
442
+ * Load academic year mappings for edit mode
443
+ */
444
+ loadAcademicYearMappings(academicYearId) {
445
+ const mappingPayload = {
446
+ acayrmp_academic_year_id_acayr: academicYearId
447
+ };
448
+ this.academicYearMappingService.getAcademicYearMappingList(mappingPayload).subscribe({
449
+ next: (response) => {
450
+ if (response?.success && response.data) {
451
+ console.log('🔗 Academic year mappings loaded:', response.data);
452
+ this.populateFormWithMappingsData(response.data);
453
+ }
454
+ else {
455
+ console.warn('⚠️ No mappings found for this academic year');
456
+ }
457
+ this.loading.set(false);
458
+ },
459
+ error: (error) => {
460
+ console.error('❌ Error loading academic year mappings:', error);
461
+ this.loading.set(false);
462
+ }
463
+ });
464
+ }
465
+ /**
466
+ * Populate form with academic year data
467
+ */
468
+ populateFormWithAcademicYearData(data) {
469
+ if (data) {
470
+ this.academicYearForm.patchValue({
471
+ acayr_code: data.acayr_code || '',
472
+ acayr_name: data.acayr_name || '',
473
+ acayr_description: data.acayr_description || '',
474
+ acayr_from_date: data.acayr_from_date ? new Date(data.acayr_from_date).toISOString().split('T')[0] : '',
475
+ acayr_to_date: data.acayr_to_date ? new Date(data.acayr_to_date).toISOString().split('T')[0] : '',
476
+ acayr_isactive: data.acayr_isactive ?? true,
477
+ acayr_iscurrent: data.acayr_iscurrent ?? false,
478
+ acayr_islocked: data.acayr_islocked ?? false
479
+ });
480
+ }
481
+ }
482
+ /**
483
+ * Populate form with mappings data
484
+ */
485
+ populateFormWithMappingsData(mappings) {
486
+ if (mappings && Array.isArray(mappings)) {
487
+ // Store original mappings for deletion tracking
488
+ this.originalMappings.set([...mappings]);
489
+ // Clear existing mappings
490
+ while (this.academicYearMappingsArray.length !== 0) {
491
+ this.academicYearMappingsArray.removeAt(0);
492
+ }
493
+ // Add existing mappings
494
+ mappings.forEach((mapping) => {
495
+ const mappingGroup = this.fb.group({
496
+ _id: [mapping._id || ''],
497
+ acayrmp_academic_year_id_acayr: [mapping.acayrmp_academic_year_id_acayr || this.academicYearId() || '', [Validators.required]],
498
+ acayrmp_entity_id_syen: [mapping.acayrmp_entity_id_syen || '', [Validators.required]],
499
+ acayrmp_islocked: [mapping.acayrmp_islocked ?? false],
500
+ acayrmp_iscurrent: [mapping.acayrmp_iscurrent ?? false]
501
+ });
502
+ this.academicYearMappingsArray.push(mappingGroup);
503
+ });
504
+ }
505
+ }
506
+ /**
507
+ * Tab configuration
508
+ */
509
+ academicYearTabs() {
510
+ return [
511
+ { id: 'basic', label: 'Academic Year Details', icon: 'school' },
512
+ { id: 'mapping', label: 'Mapping Configuration', icon: 'link' }
513
+ ];
514
+ }
515
+ onTabChange(tab) {
516
+ this.activeTab.set(tab.id);
517
+ }
518
+ onSubmit() {
519
+ // Clear any previous errors
520
+ this.error.set(null);
521
+ if (this.academicYearForm.valid && this.validateMappings()) {
522
+ this.loading.set(true);
523
+ const formData = this.academicYearForm.value;
524
+ // Prepare payload with mappings
525
+ const payload = {
526
+ ...formData,
527
+ _id: this.isEditMode() ? this.academicYearId() : undefined,
528
+ acayr_academic_year_mappings: this.getAcademicYearMappingsValue()
529
+ };
530
+ console.log('📤 Submitting academic year with mappings:', payload);
531
+ // First delete removed mappings, then save/update academic year
532
+ this.deleteRemovedMappings().pipe(switchMap(() => {
533
+ // After deleting removed mappings, save/update the academic year
534
+ return this.academicYearService.saveUpdateAcademicYearWithMappings(payload);
535
+ })).subscribe({
536
+ next: (response) => {
537
+ if (response?.success) {
538
+ console.log('✅ Academic year and mappings saved successfully:', response.data);
539
+ // Check if there were any mapping errors
540
+ if (response.mappingErrors && response.mappingErrors.length > 0) {
541
+ console.warn('⚠️ Some mappings failed:', response.mappingErrors);
542
+ this.error.set('Academic year saved but some mappings failed. Please check the mappings.');
543
+ }
544
+ else {
545
+ this.goBackToAcademicYearList();
546
+ }
547
+ }
548
+ else {
549
+ console.error('❌ Failed to save academic year');
550
+ this.error.set(response?.message || 'Failed to save academic year');
551
+ }
552
+ this.loading.set(false);
553
+ },
554
+ error: (error) => {
555
+ console.error('❌ Error saving academic year:', error);
556
+ this.error.set('Error saving academic year');
557
+ this.loading.set(false);
558
+ }
559
+ });
560
+ }
561
+ else {
562
+ console.warn('Form is invalid');
563
+ this.markFormGroupTouched();
564
+ }
565
+ }
566
+ /**
567
+ * Mark all form controls as touched to trigger validation display
568
+ */
569
+ markFormGroupTouched() {
570
+ Object.keys(this.academicYearForm.controls).forEach(key => {
571
+ const control = this.academicYearForm.get(key);
572
+ control?.markAsTouched();
573
+ });
574
+ }
575
+ resetForm() {
576
+ this.academicYearForm.reset({
577
+ acayr_isactive: true,
578
+ acayr_iscurrent: false,
579
+ acayr_islocked: false
580
+ });
581
+ // Clear the mappings array
582
+ while (this.academicYearMappingsArray.length !== 0) {
583
+ this.academicYearMappingsArray.removeAt(0);
584
+ }
585
+ // Reset academic year ID
586
+ this.academicYearId.set('');
587
+ this.isEditMode.set(false);
588
+ this.originalMappings.set([]);
589
+ // Reset entity options to show all entities again
590
+ this.updateEntityOptionsForMappings();
591
+ }
592
+ /**
593
+ * Go back to academic year list
594
+ */
595
+ goBackToAcademicYearList() {
596
+ this.router.navigate(['/control-panel/academic-year']);
597
+ }
598
+ /**
599
+ * Cancel form and optionally navigate back
600
+ */
601
+ cancelForm() {
602
+ if (this.isEditMode()) {
603
+ this.goBackToAcademicYearList();
604
+ }
605
+ else {
606
+ this.resetForm();
607
+ }
608
+ }
609
+ /**
610
+ * Get page title based on mode
611
+ */
612
+ getPageTitle() {
613
+ return this.isEditMode() ? 'Edit Academic Year' : 'Create New Academic Year';
614
+ }
615
+ /**
616
+ * Getter methods for form arrays
617
+ */
618
+ get academicYearMappingsArray() {
619
+ return this.academicYearForm.get('acayr_academic_year_mappings');
620
+ }
621
+ /**
622
+ * Add new academic year mapping
623
+ */
624
+ addAcademicYearMapping() {
625
+ // Check if there are any available entities to map
626
+ const availableEntities = this.getAvailableEntityCount(this.academicYearMappingsArray.length);
627
+ if (availableEntities === 0) {
628
+ console.warn('No available entities to map');
629
+ return;
630
+ }
631
+ // Generate a temporary ID for new mappings
632
+ const tempId = 'temp_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
633
+ const academicYearMapping = this.fb.group({
634
+ _id: [tempId],
635
+ acayrmp_academic_year_id_acayr: [this.academicYearId() || '', [Validators.required]],
636
+ acayrmp_entity_id_syen: ['', [Validators.required]],
637
+ acayrmp_islocked: [false],
638
+ acayrmp_iscurrent: [false]
639
+ });
640
+ this.academicYearMappingsArray.push(academicYearMapping);
641
+ }
642
+ /**
643
+ * Update academic year ID in all existing mappings when academic year ID changes
644
+ */
645
+ updateMappingsAcademicYearId(newAcademicYearId) {
646
+ const mappingsArray = this.academicYearMappingsArray;
647
+ mappingsArray.controls.forEach((control) => {
648
+ if (control.get('acayrmp_academic_year_id_acayr')) {
649
+ control.get('acayrmp_academic_year_id_acayr').setValue(newAcademicYearId);
650
+ }
651
+ });
652
+ }
653
+ /**
654
+ * Get form array value for validation or submission
655
+ */
656
+ getAcademicYearMappingsValue() {
657
+ return this.academicYearMappingsArray.value;
658
+ }
659
+ /**
660
+ * Check if mappings array has any invalid controls
661
+ */
662
+ isMappingsArrayValid() {
663
+ return this.academicYearMappingsArray.valid;
664
+ }
665
+ /**
666
+ * Set academic year ID for all existing mappings
667
+ * This is useful when the academic year ID is generated after creation
668
+ */
669
+ setAcademicYearIdForMappings(academicYearId) {
670
+ this.academicYearId.set(academicYearId);
671
+ this.updateMappingsAcademicYearId(academicYearId);
672
+ }
673
+ /**
674
+ * Validate that all mappings have required fields
675
+ */
676
+ validateMappings() {
677
+ const mappings = this.academicYearMappingsArray.controls;
678
+ // Check if there are any mappings
679
+ if (mappings.length === 0) {
680
+ return true; // No mappings is valid
681
+ }
682
+ // Validate each mapping
683
+ for (let i = 0; i < mappings.length; i++) {
684
+ const mapping = mappings[i];
685
+ // Check required fields
686
+ if (!mapping.get('acayrmp_entity_id_syen')?.value) {
687
+ console.warn(`Mapping ${i + 1} is missing entity selection`);
688
+ this.error.set(`Mapping ${i + 1} requires an entity selection`);
689
+ return false;
690
+ }
691
+ // Check for duplicate entity selections
692
+ const currentEntityId = mapping.get('acayrmp_entity_id_syen')?.value;
693
+ const duplicateIndex = this.findDuplicateEntitySelection(currentEntityId, i);
694
+ if (duplicateIndex !== -1) {
695
+ console.warn(`Mapping ${i + 1} has duplicate entity selection with mapping ${duplicateIndex + 1}`);
696
+ this.error.set(`Mapping ${i + 1} has duplicate entity selection with mapping ${duplicateIndex + 1}`);
697
+ return false;
698
+ }
699
+ }
700
+ return true;
701
+ }
702
+ /**
703
+ * Find duplicate entity selection in mappings
704
+ */
705
+ findDuplicateEntitySelection(entityId, currentIndex) {
706
+ const mappings = this.academicYearMappingsArray.controls;
707
+ for (let i = 0; i < mappings.length; i++) {
708
+ if (i !== currentIndex) {
709
+ const mapping = mappings[i];
710
+ if (mapping.get('acayrmp_entity_id_syen')?.value === entityId) {
711
+ return i;
712
+ }
713
+ }
714
+ }
715
+ return -1;
716
+ }
717
+ /**
718
+ * Remove academic year mapping
719
+ */
720
+ removeAcademicYearMapping(index) {
721
+ this.academicYearMappingsArray.removeAt(index);
722
+ // Update entity options after removal to re-enable previously selected entities
723
+ this.updateEntityOptionsForMappings();
724
+ }
725
+ /**
726
+ * Handle entity selection change for a specific mapping
727
+ */
728
+ onMappingEntityChange(event, mappingIndex) {
729
+ const selectedEntityId = event?.value || event;
730
+ if (selectedEntityId) {
731
+ console.log(`🏢 Entity selected for mapping ${mappingIndex}:`, selectedEntityId);
732
+ // Trigger change detection to update other mappings' entity options
733
+ this.updateEntityOptionsForMappings();
734
+ }
735
+ }
736
+ /**
737
+ * Get filtered entity options excluding already selected entities in other mappings
738
+ * @param currentMappingIndex The index of the current mapping being edited
739
+ * @returns Entity options excluding already selected entities
740
+ */
741
+ getFilteredEntityOptions(currentMappingIndex) {
742
+ const allEntities = this.entityOptions();
743
+ if (!allEntities || allEntities.length === 0) {
744
+ return [];
745
+ }
746
+ // Get all currently selected entity IDs from other mappings (excluding current mapping)
747
+ const selectedEntityIds = this.getSelectedEntityIds(currentMappingIndex);
748
+ console.log(`🔍 Filtering entities for mapping ${currentMappingIndex}:`, {
749
+ totalEntities: allEntities.length,
750
+ selectedEntityIds,
751
+ availableEntities: allEntities.length - selectedEntityIds.length
752
+ });
753
+ // Return only entities that are not already selected in other mappings
754
+ return allEntities.filter((entity) => {
755
+ const entityId = entity._id;
756
+ const isAlreadySelected = selectedEntityIds.includes(entityId);
757
+ if (isAlreadySelected) {
758
+ console.log(`🚫 Entity "${entity.syen_name}" (${entityId}) is already selected in another mapping - excluding from options`);
759
+ }
760
+ return !isAlreadySelected;
761
+ });
762
+ }
763
+ /**
764
+ * Get all currently selected entity IDs from other mappings
765
+ */
766
+ getSelectedEntityIds(excludeMappingIndex) {
767
+ const mappingsArray = this.academicYearMappingsArray;
768
+ const selectedIds = [];
769
+ mappingsArray.controls.forEach((control, index) => {
770
+ if (index !== excludeMappingIndex) {
771
+ const entityId = control.get('acayrmp_entity_id_syen')?.value;
772
+ if (entityId) {
773
+ selectedIds.push(entityId);
774
+ }
775
+ }
776
+ });
777
+ return selectedIds;
778
+ }
779
+ /**
780
+ * Update entity options for all mappings to prevent duplicates
781
+ */
782
+ updateEntityOptionsForMappings() {
783
+ // This method triggers change detection to update the UI
784
+ // The actual filtering is done in getEntityOptionsForMapping method
785
+ console.log('🔄 Updating entity options for all mappings');
786
+ }
787
+ /**
788
+ * Check if an entity is disabled for a specific mapping
789
+ */
790
+ isEntityDisabled(entityId, mappingIndex) {
791
+ const selectedEntityIds = this.getSelectedEntityIds(mappingIndex);
792
+ return selectedEntityIds.includes(entityId);
793
+ }
794
+ /**
795
+ * Get the count of available entities for a specific mapping
796
+ */
797
+ getAvailableEntityCount(mappingIndex) {
798
+ const availableEntities = this.getFilteredEntityOptions(mappingIndex);
799
+ return availableEntities.length;
800
+ }
801
+ /**
802
+ * Get the count of total entities
803
+ */
804
+ getTotalEntityCount() {
805
+ return this.entityOptions().length;
806
+ }
807
+ /**
808
+ * Check if all entities are already selected in other mappings
809
+ * @param currentMappingIndex The index of the current mapping being edited
810
+ * @returns True if all entities are already selected
811
+ */
812
+ isAllEntitiesSelected(currentMappingIndex) {
813
+ const allEntities = this.entityOptions();
814
+ const currentMappings = this.academicYearMappingsArray.value;
815
+ // Get all selected entity IDs from other mappings (excluding current mapping)
816
+ const selectedEntityIds = currentMappings
817
+ .map((mapping, index) => {
818
+ if (index !== currentMappingIndex && mapping.acayrmp_entity_id_syen) {
819
+ return mapping.acayrmp_entity_id_syen;
820
+ }
821
+ return null;
822
+ })
823
+ .filter((id) => id !== null && id !== '');
824
+ // Check if all entities are selected
825
+ return allEntities.length > 0 && selectedEntityIds.length >= allEntities.length;
826
+ }
827
+ /**
828
+ * Check if all entities are already mapped (for disabling Add Entity Mapping button)
829
+ * @returns True if all entities are already mapped
830
+ */
831
+ isAllEntitiesMapped() {
832
+ const allEntities = this.entityOptions();
833
+ const currentMappings = this.academicYearMappingsArray.value;
834
+ // Get all selected entity IDs
835
+ const selectedEntityIds = currentMappings
836
+ .map((mapping) => {
837
+ if (mapping.acayrmp_entity_id_syen) {
838
+ return mapping.acayrmp_entity_id_syen;
839
+ }
840
+ return null;
841
+ })
842
+ .filter((id) => id !== null && id !== '');
843
+ // Check if all entities are selected
844
+ return allEntities.length > 0 && selectedEntityIds.length >= allEntities.length;
845
+ }
846
+ /**
847
+ * Cleanup when component is destroyed
848
+ * This ensures that when the component is destroyed (e.g., tab closed),
849
+ * all state is properly reset to prevent old data from persisting
850
+ */
851
+ ngOnDestroy() {
852
+ console.log('🧹 AcademicYearCreateComponent: Cleaning up component state');
853
+ // Reset all signals to their initial state
854
+ this.activeTab.set('basic');
855
+ this.loading.set(false);
856
+ this.error.set(null);
857
+ this.academicYearId.set('');
858
+ this.isEditMode.set(false);
859
+ this.originalMappings.set([]);
860
+ this.entityOptions.set([]);
861
+ // Reset form to initial state
862
+ this.resetForm();
863
+ console.log('🧹 AcademicYearCreateComponent: Component state cleaned up');
864
+ }
865
+ /**
866
+ * Delete removed academic year mappings
867
+ */
868
+ deleteRemovedMappings() {
869
+ const originalMappings = this.originalMappings();
870
+ const currentMappings = this.getAcademicYearMappingsValue();
871
+ // Find mappings that were removed (exist in original but not in current)
872
+ const removedMappings = originalMappings.filter(originalMapping => {
873
+ return !currentMappings.some(currentMapping => currentMapping._id === originalMapping._id && originalMapping._id);
874
+ });
875
+ console.log('🗑️ Mappings to delete:', removedMappings);
876
+ if (removedMappings.length === 0) {
877
+ return of([]);
878
+ }
879
+ // Create delete observables for each removed mapping
880
+ const deleteObservables = removedMappings.map(mapping => {
881
+ return this.academicYearService.deleteAcademicYearMapping({
882
+ acayrmp_id: mapping._id || ''
883
+ }).pipe(catchError(error => {
884
+ console.error('❌ Error deleting mapping:', error);
885
+ return of({ success: false, error, mappingId: mapping._id });
886
+ }));
887
+ });
888
+ return forkJoin(deleteObservables);
889
+ }
890
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AcademicYearCreateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
891
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: AcademicYearCreateComponent, isStandalone: true, selector: "cide-core-academic-year-create", ngImport: i0, template: "<!-- \n ACADEMIC YEAR MASTER FORM\n \n Enterprise-Level Styling with Tailwind CSS\n Features: Responsive grids, proper typography, enhanced user experience\n-->\n\n<div class=\"tw-w-full tw-h-full\">\n <form class=\"tw-w-full tw-table tw-h-full tw-bg-transparent\" [formGroup]=\"academicYearForm\" [class.tw-opacity-60]=\"loading()\"\n (ngSubmit)=\"onSubmit()\">\n\n <!-- Simple Header Section -->\n <div class=\"tw-table-row tw-w-full tw-h-0\">\n <div class=\"tw-table-cell tw-w-full tw-px-6 tw-py-3 tw-border-b tw-border-gray-200 tw-bg-gray-50\">\n <div class=\"tw-flex tw-flex-col sm:tw-flex-row tw-justify-between tw-items-start sm:tw-items-center tw-space-y-3 sm:tw-space-y-0\">\n \n <!-- Title -->\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n <cide-ele-icon class=\"tw-text-blue-600 tw-w-5 tw-h-5\">school</cide-ele-icon>\n <h5 class=\"tw-text-base tw-font-medium tw-text-gray-900 tw-m-0\">\n {{ isEditMode() ? 'Edit Academic Year' : 'Create Academic Year' }}\n </h5>\n </div>\n\n <!-- Actions -->\n <div class=\"tw-flex tw-flex-col sm:tw-flex-row tw-items-start sm:tw-items-center tw-space-y-3 sm:tw-space-y-0 sm:tw-space-x-3\">\n <!-- Back button or other actions can be added here if needed -->\n </div>\n </div>\n </div>\n </div>\n\n <!-- Tab Navigation -->\n <div class=\"tw-table-row tw-h-0\">\n <div class=\"tw-table-cell tw-w-full tw-px-2 tw-py-0\">\n <cide-ele-tab [tabs]=\"academicYearTabs()\" [activeTabId]=\"activeTab()\" size=\"md\" variant=\"default\"\n (tabChange)=\"onTabChange($event)\">\n </cide-ele-tab>\n </div>\n </div>\n\n <!-- Tab Content -->\n <div class=\"tw-table-row\">\n <div class=\"tw-table-cell tw-w-full tw-px-6 tw-py-6\">\n <div class=\"tw-transition-opacity tw-duration-300\" [class.tw-opacity-60]=\"loading()\">\n @switch (activeTab()) {\n\n @case ('basic') {\n <!-- Basic Academic Year Information -->\n <div class=\"tw-space-y-6\">\n <div class=\"tw-grid tw-grid-cols-1 md:tw-grid-cols-2 tw-gap-6\">\n <cide-ele-input label=\"Academic Year Code *\" formControlName=\"acayr_code\"\n placeholder=\"e.g., AY2024-25\" size=\"md\" leadingIcon=\"code\">\n </cide-ele-input>\n\n <cide-ele-input label=\"Academic Year Name *\" formControlName=\"acayr_name\"\n placeholder=\"e.g., Academic Year 2024-2025\" size=\"md\" leadingIcon=\"school\">\n </cide-ele-input>\n </div>\n\n <div>\n <cide-ele-textarea label=\"Description\" formControlName=\"acayr_description\"\n placeholder=\"Enter detailed description of the academic year...\"\n rows=\"3\" size=\"md\">\n </cide-ele-textarea>\n </div>\n\n <div class=\"tw-grid tw-grid-cols-1 md:tw-grid-cols-2 tw-gap-6\">\n <cide-ele-input label=\"From Date *\" formControlName=\"acayr_from_date\" type=\"date\" size=\"md\"\n leadingIcon=\"calendar_today\" id=\"acayr_from_date\">\n </cide-ele-input>\n\n <cide-ele-input label=\"To Date *\" formControlName=\"acayr_to_date\" type=\"date\" size=\"md\"\n leadingIcon=\"calendar_today\" id=\"acayr_to_date\">\n </cide-ele-input>\n </div>\n\n <div class=\"tw-grid tw-grid-cols-1 md:tw-grid-cols-3 tw-gap-6\">\n <div class=\"tw-flex tw-items-center tw-gap-3 tw-p-4 tw-bg-gray-50 tw-rounded-lg tw-border tw-border-gray-200\">\n <cide-ele-input formControlName=\"acayr_isactive\" type=\"checkbox\" size=\"md\">\n </cide-ele-input>\n <div class=\"tw-flex tw-flex-col\">\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-700\">Active</span>\n <span class=\"tw-text-xs tw-text-gray-500\">Enable/disable this academic year</span>\n </div>\n </div>\n\n <div class=\"tw-flex tw-items-center tw-gap-3 tw-p-4 tw-bg-gray-50 tw-rounded-lg tw-border tw-border-gray-200\">\n <cide-ele-input formControlName=\"acayr_iscurrent\" type=\"checkbox\" size=\"md\">\n </cide-ele-input>\n <div class=\"tw-flex tw-flex-col\">\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-700\">Current</span>\n <span class=\"tw-text-xs tw-text-gray-500\">Mark as current academic year</span>\n </div>\n </div>\n\n <div class=\"tw-flex tw-items-center tw-gap-3 tw-p-4 tw-bg-gray-50 tw-rounded-lg tw-border tw-border-gray-200\">\n <cide-ele-input formControlName=\"acayr_islocked\" type=\"checkbox\" size=\"md\">\n </cide-ele-input>\n <div class=\"tw-flex tw-flex-col\">\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-700\">Locked</span>\n <span class=\"tw-text-xs tw-text-gray-500\">Prevent modifications</span>\n </div>\n </div>\n </div>\n </div>\n }\n\n @case ('mapping') {\n <!-- Academic Year Mapping Configuration -->\n <div class=\"tw-bg-gradient-to-r tw-from-blue-50 tw-to-indigo-50 tw-border tw-border-blue-200 tw-rounded-xl tw-p-6\">\n <div class=\"tw-flex tw-items-center tw-justify-between tw-mb-6\">\n <div class=\"tw-flex tw-items-center tw-gap-3\">\n <div class=\"tw-bg-blue-100 tw-p-2 tw-rounded-lg\">\n <cide-ele-icon class=\"tw-text-blue-600 tw-w-6 tw-h-6\">link</cide-ele-icon>\n </div>\n <div>\n <h6 class=\"tw-text-lg tw-font-semibold tw-text-blue-900 tw-m-0\">Academic Year Mapping</h6>\n <p class=\"tw-text-sm tw-text-blue-600 tw-m-0\">Configure entity mappings for this academic year</p>\n </div>\n </div>\n <button cideEleButton type=\"button\" variant=\"primary\" size=\"md\" leftIcon=\"add\"\n (click)=\"addAcademicYearMapping()\"\n class=\"tw-shadow-md hover:tw-shadow-lg tw-transition-shadow\">\n Add New Mapping\n </button>\n </div>\n \n @if (academicYearMappingsArray.length === 0) {\n <div class=\"tw-text-center tw-py-12 tw-text-gray-500\">\n <div class=\"tw-bg-white tw-p-4 tw-rounded-full tw-w-16 tw-h-16 tw-mx-auto tw-mb-4 tw-flex tw-items-center tw-justify-center\">\n <cide-ele-icon class=\"tw-w-8 tw-h-8 tw-text-gray-400\">link</cide-ele-icon>\n </div>\n <h6 class=\"tw-text-base tw-font-medium tw-text-gray-600 tw-mb-2\">No Mappings Yet</h6>\n <p class=\"tw-text-sm tw-text-gray-500\">Click \"Add New Mapping\" to start configuring entity relationships for this academic year.</p>\n </div>\n } @else {\n <div class=\"tw-space-y-4\" formArrayName=\"acayr_academic_year_mappings\">\n @for (mapping of academicYearMappingsArray.controls; track $index) {\n <div class=\"tw-bg-white tw-rounded-xl tw-border tw-border-gray-200 tw-shadow-sm hover:tw-shadow-md tw-transition-shadow tw-p-6\">\n <div class=\"tw-flex tw-items-start tw-justify-between tw-mb-4\">\n <div class=\"tw-flex tw-items-center tw-gap-2\">\n <div class=\"tw-bg-blue-100 tw-p-2 tw-rounded-lg\">\n <cide-ele-icon class=\"tw-text-blue-600 tw-w-5 tw-h-5\">settings</cide-ele-icon>\n </div>\n <h6 class=\"tw-text-sm tw-font-semibold tw-text-gray-900\">Mapping #{{ $index + 1 }}</h6>\n </div>\n <button cideEleButton type=\"button\" variant=\"danger\" size=\"sm\" leftIcon=\"delete\"\n (click)=\"removeAcademicYearMapping($index)\"\n class=\"tw-opacity-80 hover:tw-opacity-100 tw-transition-opacity\">\n Remove\n </button>\n </div>\n \n <div class=\"tw-grid tw-grid-cols-1 lg:tw-grid-cols-2 tw-gap-4\" [formGroupName]=\"$index\">\n <!-- Hidden Academic Year ID field (internal use only) -->\n <input type=\"hidden\" formControlName=\"acayrmp_academic_year_id_acayr\">\n \n <!-- Entity Selection -->\n <div class=\"tw-space-y-2\">\n <label class=\"tw-text-sm tw-font-medium tw-text-gray-700\">Select Entity *</label>\n <cide-ele-select \n [options]=\"entityOptions()\"\n formControlName=\"acayrmp_entity_id_syen\"\n placeholder=\"Choose entity for this mapping\" \n size=\"md\"\n valueKey=\"_id\"\n labelKey=\"syen_name\"\n class=\"tw-min-w-full\"\n (change)=\"onMappingEntityChange($event, $index)\">\n </cide-ele-select>\n </div>\n \n <!-- Status Controls -->\n <div class=\"tw-space-y-2\">\n <div class=\"tw-grid tw-grid-cols-1 tw-gap-3\">\n <div class=\"tw-flex tw-items-center tw-gap-2 tw-p-3 tw-bg-gray-50 tw-rounded-lg tw-border tw-border-gray-200\">\n <cide-ele-input \n formControlName=\"acayrmp_islocked\" \n type=\"checkbox\" \n size=\"sm\">\n </cide-ele-input>\n <div class=\"tw-flex tw-flex-col\">\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-700\">Locked</span>\n <span class=\"tw-text-xs tw-text-gray-500\">Prevent modifications</span>\n </div>\n </div>\n \n <div class=\"tw-flex tw-items-center tw-gap-2 tw-p-3 tw-bg-gray-50 tw-rounded-lg tw-border tw-border-gray-200\">\n <cide-ele-input \n formControlName=\"acayrmp_iscurrent\" \n type=\"checkbox\" \n size=\"sm\">\n </cide-ele-input>\n <div class=\"tw-flex tw-flex-col\">\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-700\">Current</span>\n <span class=\"tw-text-xs tw-text-gray-500\">Mark as current mapping</span>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Mapping Validation Messages -->\n @if (academicYearMappingsArray.length > 0 && !isMappingsArrayValid()) {\n <div class=\"tw-mt-4 tw-p-4 tw-bg-yellow-50 tw-border tw-border-yellow-200 tw-rounded-lg\">\n <div class=\"tw-flex tw-items-center tw-gap-2\">\n <cide-ele-icon variant=\"warning\" size=\"sm\">warning</cide-ele-icon>\n <span class=\"tw-text-sm tw-font-medium tw-text-yellow-800\">\n Please ensure all mappings have required fields filled in.\n </span>\n </div>\n </div>\n }\n </div>\n }\n }\n </div>\n\n <!-- Error Display -->\n @if (error()) {\n <div class=\"tw-mt-6 tw-p-4 tw-bg-red-50 tw-border tw-border-red-200 tw-rounded-lg\">\n <div class=\"tw-flex tw-items-center tw-gap-2\">\n <cide-ele-icon variant=\"red\" size=\"sm\">error</cide-ele-icon>\n <span class=\"tw-text-sm tw-font-medium tw-text-red-800\">{{ error() }}</span>\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- Form Actions -->\n <div class=\"tw-table-row tw-h-0\">\n <div class=\"tw-table-cell tw-w-full tw-px-6 tw-py-2 tw-bg-gray-50 tw-border-t tw-border-gray-200\">\n <div class=\"tw-flex tw-justify-end tw-gap-4\">\n <button cideEleButton type=\"button\" variant=\"secondary\" (click)=\"resetForm()\" leftIcon=\"refresh\"\n [disabled]=\"loading()\">\n Reset Form\n </button>\n\n <button cideEleButton type=\"button\" variant=\"secondary\" (click)=\"cancelForm()\" leftIcon=\"close\"\n [disabled]=\"loading()\">\n Cancel\n </button>\n\n <button cideEleButton type=\"submit\" variant=\"primary\" [disabled]=\"loading() || academicYearForm.invalid\"\n [loading]=\"loading()\" leftIcon=\"save\">\n {{ isEditMode() ? 'Update Academic Year' : 'Create Academic Year' }}\n </button>\n </div>\n </div>\n </div>\n </form>\n </div>", dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: i1.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "component", type: CideInputComponent, selector: "cide-ele-input", inputs: ["fill", "label", "labelHide", "disabled", "clearInput", "labelPlacement", "labelDir", "placeholder", "leadingIcon", "trailingIcon", "helperText", "helperTextCollapse", "hideHelperAndErrorText", "errorText", "maxlength", "minlength", "required", "autocapitalize", "autocomplete", "type", "width", "id", "ngModel", "option", "min", "max", "size"], outputs: ["ngModelChange"] }, { kind: "component", type: CideTextareaComponent, selector: "cide-ele-textarea", inputs: ["label", "labelHide", "placeholder", "helperText", "errorText", "required", "disabled", "minlength", "maxlength", "rows", "id", "ngModel", "size", "fill", "labelPlacement", "labelDir", "leadingIcon", "trailingIcon", "clearInput"], outputs: ["ngModelChange"] }, { kind: "component", type: CideEleButtonComponent, selector: "button[cideEleButton], a[cideEleButton]", inputs: ["label", "variant", "size", "type", "shape", "elevation", "disabled", "id", "loading", "fullWidth", "leftIcon", "rightIcon", "customClass", "tooltip", "ariaLabel", "testId", "routerLink", "routerExtras", "preventDoubleClick", "animated"], outputs: ["btnClick", "doubleClick"] }, { kind: "component", type: CideEleTabComponent, selector: "cide-ele-tab", inputs: ["tabs", "activeTabId", "size", "variant", "fullWidth", "disabled"], outputs: ["tabChange"] }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }, { kind: "component", type: CideSelectComponent, selector: "cide-ele-select", inputs: ["label", "labelHide", "placeholder", "helperText", "errorText", "required", "disabled", "id", "ngModel", "size", "fill", "labelPlacement", "labelDir", "leadingIcon", "trailingIcon", "clearInput", "options", "multiple", "searchable", "showSearchInput", "loading", "valueKey", "labelKey"], outputs: ["ngModelChange", "change", "searchChange"] }] });
892
+ }
893
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AcademicYearCreateComponent, decorators: [{
894
+ type: Component,
895
+ args: [{ selector: 'cide-core-academic-year-create', standalone: true, imports: [
896
+ CommonModule,
897
+ ReactiveFormsModule,
898
+ CideInputComponent,
899
+ CideTextareaComponent,
900
+ CideEleButtonComponent,
901
+ CideEleTabComponent,
902
+ CideIconComponent,
903
+ CideSelectComponent
904
+ ], template: "<!-- \n ACADEMIC YEAR MASTER FORM\n \n Enterprise-Level Styling with Tailwind CSS\n Features: Responsive grids, proper typography, enhanced user experience\n-->\n\n<div class=\"tw-w-full tw-h-full\">\n <form class=\"tw-w-full tw-table tw-h-full tw-bg-transparent\" [formGroup]=\"academicYearForm\" [class.tw-opacity-60]=\"loading()\"\n (ngSubmit)=\"onSubmit()\">\n\n <!-- Simple Header Section -->\n <div class=\"tw-table-row tw-w-full tw-h-0\">\n <div class=\"tw-table-cell tw-w-full tw-px-6 tw-py-3 tw-border-b tw-border-gray-200 tw-bg-gray-50\">\n <div class=\"tw-flex tw-flex-col sm:tw-flex-row tw-justify-between tw-items-start sm:tw-items-center tw-space-y-3 sm:tw-space-y-0\">\n \n <!-- Title -->\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n <cide-ele-icon class=\"tw-text-blue-600 tw-w-5 tw-h-5\">school</cide-ele-icon>\n <h5 class=\"tw-text-base tw-font-medium tw-text-gray-900 tw-m-0\">\n {{ isEditMode() ? 'Edit Academic Year' : 'Create Academic Year' }}\n </h5>\n </div>\n\n <!-- Actions -->\n <div class=\"tw-flex tw-flex-col sm:tw-flex-row tw-items-start sm:tw-items-center tw-space-y-3 sm:tw-space-y-0 sm:tw-space-x-3\">\n <!-- Back button or other actions can be added here if needed -->\n </div>\n </div>\n </div>\n </div>\n\n <!-- Tab Navigation -->\n <div class=\"tw-table-row tw-h-0\">\n <div class=\"tw-table-cell tw-w-full tw-px-2 tw-py-0\">\n <cide-ele-tab [tabs]=\"academicYearTabs()\" [activeTabId]=\"activeTab()\" size=\"md\" variant=\"default\"\n (tabChange)=\"onTabChange($event)\">\n </cide-ele-tab>\n </div>\n </div>\n\n <!-- Tab Content -->\n <div class=\"tw-table-row\">\n <div class=\"tw-table-cell tw-w-full tw-px-6 tw-py-6\">\n <div class=\"tw-transition-opacity tw-duration-300\" [class.tw-opacity-60]=\"loading()\">\n @switch (activeTab()) {\n\n @case ('basic') {\n <!-- Basic Academic Year Information -->\n <div class=\"tw-space-y-6\">\n <div class=\"tw-grid tw-grid-cols-1 md:tw-grid-cols-2 tw-gap-6\">\n <cide-ele-input label=\"Academic Year Code *\" formControlName=\"acayr_code\"\n placeholder=\"e.g., AY2024-25\" size=\"md\" leadingIcon=\"code\">\n </cide-ele-input>\n\n <cide-ele-input label=\"Academic Year Name *\" formControlName=\"acayr_name\"\n placeholder=\"e.g., Academic Year 2024-2025\" size=\"md\" leadingIcon=\"school\">\n </cide-ele-input>\n </div>\n\n <div>\n <cide-ele-textarea label=\"Description\" formControlName=\"acayr_description\"\n placeholder=\"Enter detailed description of the academic year...\"\n rows=\"3\" size=\"md\">\n </cide-ele-textarea>\n </div>\n\n <div class=\"tw-grid tw-grid-cols-1 md:tw-grid-cols-2 tw-gap-6\">\n <cide-ele-input label=\"From Date *\" formControlName=\"acayr_from_date\" type=\"date\" size=\"md\"\n leadingIcon=\"calendar_today\" id=\"acayr_from_date\">\n </cide-ele-input>\n\n <cide-ele-input label=\"To Date *\" formControlName=\"acayr_to_date\" type=\"date\" size=\"md\"\n leadingIcon=\"calendar_today\" id=\"acayr_to_date\">\n </cide-ele-input>\n </div>\n\n <div class=\"tw-grid tw-grid-cols-1 md:tw-grid-cols-3 tw-gap-6\">\n <div class=\"tw-flex tw-items-center tw-gap-3 tw-p-4 tw-bg-gray-50 tw-rounded-lg tw-border tw-border-gray-200\">\n <cide-ele-input formControlName=\"acayr_isactive\" type=\"checkbox\" size=\"md\">\n </cide-ele-input>\n <div class=\"tw-flex tw-flex-col\">\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-700\">Active</span>\n <span class=\"tw-text-xs tw-text-gray-500\">Enable/disable this academic year</span>\n </div>\n </div>\n\n <div class=\"tw-flex tw-items-center tw-gap-3 tw-p-4 tw-bg-gray-50 tw-rounded-lg tw-border tw-border-gray-200\">\n <cide-ele-input formControlName=\"acayr_iscurrent\" type=\"checkbox\" size=\"md\">\n </cide-ele-input>\n <div class=\"tw-flex tw-flex-col\">\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-700\">Current</span>\n <span class=\"tw-text-xs tw-text-gray-500\">Mark as current academic year</span>\n </div>\n </div>\n\n <div class=\"tw-flex tw-items-center tw-gap-3 tw-p-4 tw-bg-gray-50 tw-rounded-lg tw-border tw-border-gray-200\">\n <cide-ele-input formControlName=\"acayr_islocked\" type=\"checkbox\" size=\"md\">\n </cide-ele-input>\n <div class=\"tw-flex tw-flex-col\">\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-700\">Locked</span>\n <span class=\"tw-text-xs tw-text-gray-500\">Prevent modifications</span>\n </div>\n </div>\n </div>\n </div>\n }\n\n @case ('mapping') {\n <!-- Academic Year Mapping Configuration -->\n <div class=\"tw-bg-gradient-to-r tw-from-blue-50 tw-to-indigo-50 tw-border tw-border-blue-200 tw-rounded-xl tw-p-6\">\n <div class=\"tw-flex tw-items-center tw-justify-between tw-mb-6\">\n <div class=\"tw-flex tw-items-center tw-gap-3\">\n <div class=\"tw-bg-blue-100 tw-p-2 tw-rounded-lg\">\n <cide-ele-icon class=\"tw-text-blue-600 tw-w-6 tw-h-6\">link</cide-ele-icon>\n </div>\n <div>\n <h6 class=\"tw-text-lg tw-font-semibold tw-text-blue-900 tw-m-0\">Academic Year Mapping</h6>\n <p class=\"tw-text-sm tw-text-blue-600 tw-m-0\">Configure entity mappings for this academic year</p>\n </div>\n </div>\n <button cideEleButton type=\"button\" variant=\"primary\" size=\"md\" leftIcon=\"add\"\n (click)=\"addAcademicYearMapping()\"\n class=\"tw-shadow-md hover:tw-shadow-lg tw-transition-shadow\">\n Add New Mapping\n </button>\n </div>\n \n @if (academicYearMappingsArray.length === 0) {\n <div class=\"tw-text-center tw-py-12 tw-text-gray-500\">\n <div class=\"tw-bg-white tw-p-4 tw-rounded-full tw-w-16 tw-h-16 tw-mx-auto tw-mb-4 tw-flex tw-items-center tw-justify-center\">\n <cide-ele-icon class=\"tw-w-8 tw-h-8 tw-text-gray-400\">link</cide-ele-icon>\n </div>\n <h6 class=\"tw-text-base tw-font-medium tw-text-gray-600 tw-mb-2\">No Mappings Yet</h6>\n <p class=\"tw-text-sm tw-text-gray-500\">Click \"Add New Mapping\" to start configuring entity relationships for this academic year.</p>\n </div>\n } @else {\n <div class=\"tw-space-y-4\" formArrayName=\"acayr_academic_year_mappings\">\n @for (mapping of academicYearMappingsArray.controls; track $index) {\n <div class=\"tw-bg-white tw-rounded-xl tw-border tw-border-gray-200 tw-shadow-sm hover:tw-shadow-md tw-transition-shadow tw-p-6\">\n <div class=\"tw-flex tw-items-start tw-justify-between tw-mb-4\">\n <div class=\"tw-flex tw-items-center tw-gap-2\">\n <div class=\"tw-bg-blue-100 tw-p-2 tw-rounded-lg\">\n <cide-ele-icon class=\"tw-text-blue-600 tw-w-5 tw-h-5\">settings</cide-ele-icon>\n </div>\n <h6 class=\"tw-text-sm tw-font-semibold tw-text-gray-900\">Mapping #{{ $index + 1 }}</h6>\n </div>\n <button cideEleButton type=\"button\" variant=\"danger\" size=\"sm\" leftIcon=\"delete\"\n (click)=\"removeAcademicYearMapping($index)\"\n class=\"tw-opacity-80 hover:tw-opacity-100 tw-transition-opacity\">\n Remove\n </button>\n </div>\n \n <div class=\"tw-grid tw-grid-cols-1 lg:tw-grid-cols-2 tw-gap-4\" [formGroupName]=\"$index\">\n <!-- Hidden Academic Year ID field (internal use only) -->\n <input type=\"hidden\" formControlName=\"acayrmp_academic_year_id_acayr\">\n \n <!-- Entity Selection -->\n <div class=\"tw-space-y-2\">\n <label class=\"tw-text-sm tw-font-medium tw-text-gray-700\">Select Entity *</label>\n <cide-ele-select \n [options]=\"entityOptions()\"\n formControlName=\"acayrmp_entity_id_syen\"\n placeholder=\"Choose entity for this mapping\" \n size=\"md\"\n valueKey=\"_id\"\n labelKey=\"syen_name\"\n class=\"tw-min-w-full\"\n (change)=\"onMappingEntityChange($event, $index)\">\n </cide-ele-select>\n </div>\n \n <!-- Status Controls -->\n <div class=\"tw-space-y-2\">\n <div class=\"tw-grid tw-grid-cols-1 tw-gap-3\">\n <div class=\"tw-flex tw-items-center tw-gap-2 tw-p-3 tw-bg-gray-50 tw-rounded-lg tw-border tw-border-gray-200\">\n <cide-ele-input \n formControlName=\"acayrmp_islocked\" \n type=\"checkbox\" \n size=\"sm\">\n </cide-ele-input>\n <div class=\"tw-flex tw-flex-col\">\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-700\">Locked</span>\n <span class=\"tw-text-xs tw-text-gray-500\">Prevent modifications</span>\n </div>\n </div>\n \n <div class=\"tw-flex tw-items-center tw-gap-2 tw-p-3 tw-bg-gray-50 tw-rounded-lg tw-border tw-border-gray-200\">\n <cide-ele-input \n formControlName=\"acayrmp_iscurrent\" \n type=\"checkbox\" \n size=\"sm\">\n </cide-ele-input>\n <div class=\"tw-flex tw-flex-col\">\n <span class=\"tw-text-sm tw-font-medium tw-text-gray-700\">Current</span>\n <span class=\"tw-text-xs tw-text-gray-500\">Mark as current mapping</span>\n </div>\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- Mapping Validation Messages -->\n @if (academicYearMappingsArray.length > 0 && !isMappingsArrayValid()) {\n <div class=\"tw-mt-4 tw-p-4 tw-bg-yellow-50 tw-border tw-border-yellow-200 tw-rounded-lg\">\n <div class=\"tw-flex tw-items-center tw-gap-2\">\n <cide-ele-icon variant=\"warning\" size=\"sm\">warning</cide-ele-icon>\n <span class=\"tw-text-sm tw-font-medium tw-text-yellow-800\">\n Please ensure all mappings have required fields filled in.\n </span>\n </div>\n </div>\n }\n </div>\n }\n }\n </div>\n\n <!-- Error Display -->\n @if (error()) {\n <div class=\"tw-mt-6 tw-p-4 tw-bg-red-50 tw-border tw-border-red-200 tw-rounded-lg\">\n <div class=\"tw-flex tw-items-center tw-gap-2\">\n <cide-ele-icon variant=\"red\" size=\"sm\">error</cide-ele-icon>\n <span class=\"tw-text-sm tw-font-medium tw-text-red-800\">{{ error() }}</span>\n </div>\n </div>\n }\n </div>\n </div>\n\n <!-- Form Actions -->\n <div class=\"tw-table-row tw-h-0\">\n <div class=\"tw-table-cell tw-w-full tw-px-6 tw-py-2 tw-bg-gray-50 tw-border-t tw-border-gray-200\">\n <div class=\"tw-flex tw-justify-end tw-gap-4\">\n <button cideEleButton type=\"button\" variant=\"secondary\" (click)=\"resetForm()\" leftIcon=\"refresh\"\n [disabled]=\"loading()\">\n Reset Form\n </button>\n\n <button cideEleButton type=\"button\" variant=\"secondary\" (click)=\"cancelForm()\" leftIcon=\"close\"\n [disabled]=\"loading()\">\n Cancel\n </button>\n\n <button cideEleButton type=\"submit\" variant=\"primary\" [disabled]=\"loading() || academicYearForm.invalid\"\n [loading]=\"loading()\" leftIcon=\"save\">\n {{ isEditMode() ? 'Update Academic Year' : 'Create Academic Year' }}\n </button>\n </div>\n </div>\n </div>\n </form>\n </div>" }]
905
+ }], ctorParameters: () => [] });
906
+
907
+ var academicYearCreate_component = /*#__PURE__*/Object.freeze({
908
+ __proto__: null,
909
+ AcademicYearCreateComponent: AcademicYearCreateComponent
910
+ });
911
+
912
+ class AcademicYearListComponent {
913
+ // Modern Angular 20+ dependency injection using inject()
914
+ destroyRef = inject(DestroyRef);
915
+ academicYearService = inject(CideLytAcademicYearService);
916
+ router = inject(Router);
917
+ appState = inject(AppStateHelperService);
918
+ confirmationService = inject(ConfirmationService);
919
+ notificationService = inject(NotificationService);
920
+ // Modern ViewChild signals for template renderers (Angular 20 approach)
921
+ academicYearDetailsRendererTemplate = viewChild.required('academicYearDetailsRendererTemplate');
922
+ dateRangeRendererTemplate = viewChild.required('dateRangeRendererTemplate');
923
+ statusRendererTemplate = viewChild.required('statusRendererTemplate');
924
+ actionsDropdownRendererTemplate = viewChild.required('actionsDropdownRendererTemplate');
925
+ // Modern Angular 20+ state management using signals with proper typing
926
+ academicYears = signal([], ...(ngDevMode ? [{ debugName: "academicYears" }] : []));
927
+ loading = signal(false, ...(ngDevMode ? [{ debugName: "loading" }] : []));
928
+ error = signal(null, ...(ngDevMode ? [{ debugName: "error" }] : []));
929
+ // Server-side pagination state using signals
930
+ currentPage = signal(1, ...(ngDevMode ? [{ debugName: "currentPage" }] : []));
931
+ pageSize = signal(10, ...(ngDevMode ? [{ debugName: "pageSize" }] : []));
932
+ totalItems = signal(0, ...(ngDevMode ? [{ debugName: "totalItems" }] : []));
933
+ totalPages = signal(0, ...(ngDevMode ? [{ debugName: "totalPages" }] : []));
934
+ // Server-side search state using signals
935
+ searchQuery = signal('', ...(ngDevMode ? [{ debugName: "searchQuery" }] : []));
936
+ // Server-side sorting state using signals
937
+ sortColumn = signal('acayr_name', ...(ngDevMode ? [{ debugName: "sortColumn" }] : []));
938
+ sortDirection = signal('asc', ...(ngDevMode ? [{ debugName: "sortDirection" }] : []));
939
+ // Server-side filtering state using signals
940
+ selectedStatusFilter = signal('', ...(ngDevMode ? [{ debugName: "selectedStatusFilter" }] : []));
941
+ // Filter options using computed signal for better performance
942
+ statusFilterOptions = computed(() => [
943
+ { value: '', label: 'All Status' },
944
+ { value: 'active', label: 'Active Years' },
945
+ { value: 'inactive', label: 'Inactive Years' },
946
+ { value: 'current', label: 'Current Year' },
947
+ { value: 'locked', label: 'Locked Years' }
948
+ ], ...(ngDevMode ? [{ debugName: "statusFilterOptions" }] : []));
949
+ // Modern Angular 20+ grid configuration using computed signal with proper typing
950
+ gridConfig = computed(() => ({
951
+ id: 'academic-year-list-grid',
952
+ columns: [
953
+ {
954
+ key: 'acayr_code',
955
+ header: 'Academic Year Code',
956
+ type: 'text',
957
+ width: '140px',
958
+ truncate: true,
959
+ align: 'left',
960
+ sortable: true
961
+ },
962
+ {
963
+ key: 'details',
964
+ header: 'Academic Year Details',
965
+ type: 'custom',
966
+ width: 'auto',
967
+ truncate: true,
968
+ align: 'left',
969
+ renderer: 'academicYearDetailsRenderer',
970
+ sortable: true
971
+ },
972
+ {
973
+ key: 'date_range',
974
+ header: 'Date Range',
975
+ type: 'custom',
976
+ width: '200px',
977
+ truncate: false,
978
+ align: 'left',
979
+ renderer: 'dateRangeRenderer',
980
+ sortable: true
981
+ },
982
+ {
983
+ key: 'acayr_isactive',
984
+ header: 'Status',
985
+ type: 'custom',
986
+ width: '140px',
987
+ truncate: false,
988
+ align: 'center',
989
+ renderer: 'statusRenderer'
990
+ },
991
+ {
992
+ key: 'actions',
993
+ header: 'Actions',
994
+ type: 'custom',
995
+ width: '80px',
996
+ truncate: false,
997
+ align: 'center',
998
+ renderer: 'actionsDropdownRenderer'
999
+ }
1000
+ ],
1001
+ data: this.academicYears(),
1002
+ trackBy: '_id',
1003
+ pagination: {
1004
+ enabled: true,
1005
+ pageSize: this.pageSize(),
1006
+ pageSizeOptions: [10, 25, 50, 100],
1007
+ showQuickJump: true,
1008
+ showPageInfo: true,
1009
+ showRefresh: true
1010
+ },
1011
+ search: {
1012
+ enabled: true,
1013
+ placeholder: 'Search by code, name, or description...',
1014
+ searchableColumns: ['acayr_code', 'acayr_name', 'acayr_description'],
1015
+ debounceMs: 300,
1016
+ showClearButton: true
1017
+ },
1018
+ loading: {
1019
+ useDefer: true,
1020
+ skeletonRows: 5,
1021
+ showOverlay: this.loading()
1022
+ },
1023
+ scroll: {
1024
+ enabled: true,
1025
+ maxHeight: '',
1026
+ minHeight: '',
1027
+ stickyHeader: true,
1028
+ virtualScroll: false,
1029
+ rowHeight: 50
1030
+ },
1031
+ export: {
1032
+ enabled: true,
1033
+ formats: ['csv', 'excel', 'pdf'],
1034
+ filename: 'academic-years',
1035
+ includeHeaders: true
1036
+ },
1037
+ responsive: true,
1038
+ striped: false,
1039
+ bordered: true,
1040
+ compact: false,
1041
+ tableClass: 'tw-table-fixed tw-w-full tw-rounded-none'
1042
+ }), ...(ngDevMode ? [{ debugName: "gridConfig" }] : []));
1043
+ ngOnInit() {
1044
+ console.log('🎓 Academic Year List Component initialized');
1045
+ this.loadAcademicYears();
1046
+ }
1047
+ ngOnDestroy() {
1048
+ // Cleanup if needed
1049
+ }
1050
+ /**
1051
+ * Load academic years from API
1052
+ */
1053
+ loadAcademicYears() {
1054
+ this.loading.set(true);
1055
+ this.error.set(null);
1056
+ const payload = {
1057
+ pageIndex: this.currentPage(),
1058
+ pageSize: this.pageSize(),
1059
+ query: this.searchQuery() || "",
1060
+ sort: {
1061
+ key: this.sortColumn(),
1062
+ order: this.sortDirection()
1063
+ }
1064
+ };
1065
+ this.academicYearService.getAcademicYearList(payload)
1066
+ .pipe(takeUntilDestroyed(this.destroyRef))
1067
+ .subscribe({
1068
+ next: (response) => {
1069
+ if (response?.success && response.data) {
1070
+ console.log('📚 Academic years loaded:', response.data);
1071
+ console.log('📊 Pagination info:', {
1072
+ total: response.total,
1073
+ pageIndex: response.pageIndex,
1074
+ pageSize: response.pageSize
1075
+ });
1076
+ this.academicYears.set(response.data);
1077
+ this.totalItems.set(response.total || 0);
1078
+ this.totalPages.set(Math.ceil((response.total || 0) / this.pageSize()));
1079
+ // Show success notification if data was loaded successfully
1080
+ if (response.data.length > 0) {
1081
+ this.notificationService.success(`Loaded ${response.data.length} academic year(s) successfully.`);
1082
+ }
1083
+ }
1084
+ else {
1085
+ console.warn('⚠️ No academic year data received');
1086
+ this.academicYears.set([]);
1087
+ this.totalItems.set(0);
1088
+ this.totalPages.set(0);
1089
+ this.notificationService.warning('No academic years found.');
1090
+ }
1091
+ this.loading.set(false);
1092
+ },
1093
+ error: (error) => {
1094
+ console.error('❌ Error loading academic years:', error);
1095
+ this.error.set('Failed to load academic years. Please try again.');
1096
+ this.academicYears.set([]);
1097
+ this.totalItems.set(0);
1098
+ this.totalPages.set(0);
1099
+ this.notificationService.error('Failed to load academic years. Please try again.');
1100
+ this.loading.set(false);
1101
+ }
1102
+ });
1103
+ }
1104
+ // Computed template renderers for grid
1105
+ templateRenderers = computed(() => ({
1106
+ academicYearDetailsRenderer: this.academicYearDetailsRendererTemplate(),
1107
+ dateRangeRenderer: this.dateRangeRendererTemplate(),
1108
+ statusRenderer: this.statusRendererTemplate(),
1109
+ actionsDropdownRenderer: this.actionsDropdownRendererTemplate()
1110
+ }), ...(ngDevMode ? [{ debugName: "templateRenderers" }] : []));
1111
+ /**
1112
+ * Handle grid events
1113
+ */
1114
+ onGridEvent(event) {
1115
+ switch (event.type) {
1116
+ case 'pageChange':
1117
+ if (event.data && typeof event.data === 'object' && 'pageIndex' in event.data && 'pageSize' in event.data) {
1118
+ this.currentPage.set(event.data['pageIndex']);
1119
+ this.pageSize.set(event.data['pageSize']);
1120
+ this.loadAcademicYears();
1121
+ }
1122
+ break;
1123
+ case 'search':
1124
+ if (event.data && typeof event.data === 'string') {
1125
+ this.searchQuery.set(event.data);
1126
+ this.currentPage.set(1);
1127
+ this.loadAcademicYears();
1128
+ }
1129
+ break;
1130
+ case 'refresh':
1131
+ this.loadAcademicYears();
1132
+ break;
1133
+ case 'action':
1134
+ // Handle action events if needed
1135
+ console.log('Action event:', event);
1136
+ break;
1137
+ case 'rowClick':
1138
+ // Handle row click events if needed
1139
+ console.log('Row click event:', event);
1140
+ break;
1141
+ case 'sort':
1142
+ if (event.data && typeof event.data === 'object' && 'column' in event.data && 'direction' in event.data) {
1143
+ this.sortColumn.set(event.data['column']);
1144
+ this.sortDirection.set(event.data['direction']);
1145
+ this.currentPage.set(1); // Reset to first page when sorting
1146
+ this.loadAcademicYears();
1147
+ }
1148
+ break;
1149
+ case 'export':
1150
+ if (event.data && typeof event.data === 'string') {
1151
+ this.handleExport(event.data);
1152
+ }
1153
+ break;
1154
+ default:
1155
+ console.log('🔄 Unhandled grid event:', event.type);
1156
+ }
1157
+ }
1158
+ // Filter handlers
1159
+ onStatusFilterChange() {
1160
+ console.log('🔍 Status filter changed:', this.selectedStatusFilter());
1161
+ this.currentPage.set(1); // Reset to first page when filtering
1162
+ this.loadAcademicYears();
1163
+ }
1164
+ clearFilters() {
1165
+ console.log('🧹 Clearing filters');
1166
+ this.selectedStatusFilter.set('');
1167
+ this.searchQuery.set('');
1168
+ this.currentPage.set(1);
1169
+ this.loadAcademicYears();
1170
+ }
1171
+ // Academic Year actions with proper typing
1172
+ createAcademicYear() {
1173
+ console.log('➕ Navigating to create academic year');
1174
+ this.notificationService.info('Opening form to create a new academic year.');
1175
+ this.router.navigate(['/control-panel/academic_year/create']);
1176
+ }
1177
+ viewAcademicYear(academicYear) {
1178
+ console.log('👁️ Viewing academic year:', academicYear);
1179
+ this.notificationService.info(`Opening academic year "${academicYear.acayr_name}" for viewing.`);
1180
+ const queryParams = generateStringFromObject({ acayr_id: academicYear._id });
1181
+ this.router.navigate(['/control-panel/academic_year/view', queryParams]);
1182
+ }
1183
+ editAcademicYear(academicYear) {
1184
+ console.log('✏️ Editing academic year:', academicYear);
1185
+ // Check if the academic year is locked
1186
+ if (academicYear.acayr_islocked) {
1187
+ this.notificationService.error(`Cannot edit "${academicYear.acayr_name}" - This academic year is locked and cannot be modified. Please contact an administrator to unlock it first.`);
1188
+ return;
1189
+ }
1190
+ this.notificationService.info(`Opening academic year "${academicYear.acayr_name}" for editing.`);
1191
+ const queryParams = generateStringFromObject({ acayr_id: academicYear._id });
1192
+ this.router.navigate(['/control-panel/academic_year/edit', queryParams]);
1193
+ }
1194
+ /**
1195
+ * Get dropdown configuration
1196
+ */
1197
+ getDropdownConfig() {
1198
+ return {
1199
+ triggerIcon: 'more_vert',
1200
+ triggerSize: 'sm',
1201
+ // menuPosition: 'right' as const,
1202
+ // forcePosition: 'bottom' as const,
1203
+ // offsetX: 0,
1204
+ // offsetY: 1,
1205
+ // usePortal: true,
1206
+ // showArrow: false
1207
+ };
1208
+ }
1209
+ /**
1210
+ * Get action dropdown items
1211
+ */
1212
+ getActionDropdownItems(academicYear) {
1213
+ console.log('🔽 Generating dropdown items for:', academicYear.acayr_name);
1214
+ console.log('🔽 Academic year data:', {
1215
+ isCurrent: academicYear.acayr_iscurrent,
1216
+ isLocked: academicYear.acayr_islocked,
1217
+ isActive: academicYear.acayr_isactive
1218
+ });
1219
+ const items = [
1220
+ {
1221
+ id: 'view',
1222
+ label: 'View Details',
1223
+ icon: 'visibility',
1224
+ iconColor: 'tw-text-gray-400',
1225
+ textColor: 'tw-text-gray-700',
1226
+ hoverBgColor: 'hover:tw-bg-gray-100'
1227
+ },
1228
+ {
1229
+ id: 'edit',
1230
+ label: 'Edit',
1231
+ icon: 'edit',
1232
+ iconColor: academicYear?.acayr_islocked ? 'tw-text-gray-300' : 'tw-text-blue-400',
1233
+ textColor: academicYear?.acayr_islocked ? 'tw-text-gray-400' : 'tw-text-blue-600',
1234
+ hoverBgColor: academicYear?.acayr_islocked ? 'hover:tw-bg-gray-50' : 'hover:tw-bg-blue-50'
1235
+ },
1236
+ {
1237
+ id: 'toggle-status',
1238
+ label: academicYear.acayr_isactive ? 'Deactivate' : 'Activate',
1239
+ icon: academicYear.acayr_isactive ? 'toggle_off' : 'toggle_on',
1240
+ iconColor: academicYear?.acayr_islocked ? 'tw-text-gray-300' : 'tw-text-gray-400',
1241
+ textColor: academicYear?.acayr_islocked ? 'tw-text-gray-400' : 'tw-text-gray-700',
1242
+ hoverBgColor: academicYear?.acayr_islocked ? 'hover:tw-bg-gray-50' : 'hover:tw-bg-gray-100'
1243
+ }
1244
+ ];
1245
+ return items;
1246
+ }
1247
+ /**
1248
+ * Handle dropdown item click
1249
+ */
1250
+ onDropdownItemClick(item, academicYear) {
1251
+ console.log('🖱️ Dropdown item clicked:', item.id, 'for academic year:', academicYear.acayr_name);
1252
+ // Check if item is disabled
1253
+ if (item.disabled) {
1254
+ console.log('⚠️ Item is disabled, ignoring click');
1255
+ return;
1256
+ }
1257
+ switch (item.id) {
1258
+ case 'view':
1259
+ this.viewAcademicYear(academicYear);
1260
+ break;
1261
+ case 'edit':
1262
+ this.editAcademicYear(academicYear);
1263
+ break;
1264
+ case 'toggle-status':
1265
+ this.toggleAcademicYearStatus(academicYear);
1266
+ break;
1267
+ default:
1268
+ console.log('❓ Unknown dropdown item clicked:', item.id);
1269
+ }
1270
+ }
1271
+ /**
1272
+ * Toggle academic year status (active/inactive)
1273
+ */
1274
+ toggleAcademicYearStatus(academicYear) {
1275
+ console.log('🔄 Toggling academic year status:', academicYear);
1276
+ // Check if the academic year is locked
1277
+ if (academicYear.acayr_islocked) {
1278
+ this.notificationService.error(`Cannot ${academicYear.acayr_isactive ? 'deactivate' : 'activate'} "${academicYear.acayr_name}" - This academic year is locked and its status cannot be changed. Please contact an administrator to unlock it first.`);
1279
+ return;
1280
+ }
1281
+ const action = academicYear.acayr_isactive ? 'deactivate' : 'activate';
1282
+ const actionText = academicYear.acayr_isactive ? 'Deactivate' : 'Activate';
1283
+ // Show confirmation dialog
1284
+ console.log('🔔 Showing confirmation dialog for toggle status');
1285
+ this.confirmationService.ask({
1286
+ title: `${actionText} Academic Year`,
1287
+ message: `Are you sure you want to ${action} the academic year "${academicYear.acayr_name}"?${!academicYear.acayr_isactive ? '\n\nThis will also deactivate all associated mappings.' : ''}`,
1288
+ confirmText: actionText,
1289
+ cancelText: 'Cancel',
1290
+ type: academicYear.acayr_isactive ? 'warning' : 'info'
1291
+ }).then((confirmed) => {
1292
+ console.log('🔔 Confirmation dialog result:', confirmed);
1293
+ if (confirmed) {
1294
+ this.loading.set(true);
1295
+ // First toggle the academic year status
1296
+ console.log('🔄 Calling toggleAcademicYearStatus with ID:', academicYear._id);
1297
+ this.academicYearService.toggleAcademicYearStatus({ acayr_id: academicYear._id }).subscribe({
1298
+ next: (response) => {
1299
+ console.log('🔄 Toggle academic year status response:', response);
1300
+ if (response?.success) {
1301
+ console.log('✅ Academic year status toggled successfully');
1302
+ // If deactivating, also toggle status of all academic year mappings
1303
+ if (!academicYear.acayr_isactive) { // If we're deactivating (was active, now inactive)
1304
+ this.toggleAcademicYearMappingsStatus(academicYear._id || '', false);
1305
+ }
1306
+ else {
1307
+ // If activating, just reload the list
1308
+ this.notificationService.success(`Academic year "${academicYear.acayr_name}" has been activated successfully.`);
1309
+ this.loadAcademicYears();
1310
+ }
1311
+ }
1312
+ else {
1313
+ console.error('❌ Failed to toggle academic year status');
1314
+ this.notificationService.error(response?.message || 'Failed to toggle academic year status');
1315
+ this.loading.set(false);
1316
+ }
1317
+ },
1318
+ error: (error) => {
1319
+ console.error('❌ Error toggling academic year status:', error);
1320
+ console.error('❌ Error details:', {
1321
+ status: error.status,
1322
+ message: error.message,
1323
+ error: error.error
1324
+ });
1325
+ this.notificationService.error('Failed to toggle academic year status. Please try again.');
1326
+ this.loading.set(false);
1327
+ }
1328
+ });
1329
+ }
1330
+ });
1331
+ }
1332
+ /**
1333
+ * Toggle status of all academic year mappings for a specific academic year
1334
+ */
1335
+ toggleAcademicYearMappingsStatus(academicYearId, isActive) {
1336
+ console.log(`🔄 Toggling mappings status for academic year ${academicYearId} to ${isActive ? 'active' : 'inactive'}`);
1337
+ // Get all mappings for this academic year and toggle their status
1338
+ // For now, we'll reload the academic year data to get the mappings
1339
+ // In a real implementation, you might want to get mappings separately
1340
+ this.academicYearService.getAcademicYearById({ acayr_id: academicYearId }).subscribe({
1341
+ next: (response) => {
1342
+ if (response?.success && response.data && response.data.acayr_academic_year_mappings) {
1343
+ const mappings = response.data.acayr_academic_year_mappings;
1344
+ console.log('📋 Found mappings to toggle:', mappings);
1345
+ // Toggle each mapping's status
1346
+ const toggleObservables = mappings.map((mapping) => {
1347
+ return this.academicYearService.toggleAcademicYearMappingStatus({
1348
+ acayrmp_id: mapping._id
1349
+ }).pipe(catchError(error => {
1350
+ console.error('❌ Error toggling mapping status:', error);
1351
+ return of({ success: false, error, mappingId: mapping._id });
1352
+ }));
1353
+ });
1354
+ if (toggleObservables.length > 0) {
1355
+ forkJoin(toggleObservables).subscribe((results) => {
1356
+ console.log('✅ All mapping statuses toggled:', results);
1357
+ const resultsArray = results;
1358
+ const failedCount = resultsArray.filter((result) => !result.success).length;
1359
+ if (failedCount === 0) {
1360
+ this.notificationService.success(`Academic year and all ${resultsArray.length} associated mappings have been deactivated successfully.`);
1361
+ }
1362
+ else {
1363
+ this.notificationService.warning(`Academic year deactivated, but ${failedCount} out of ${resultsArray.length} mappings failed to update.`);
1364
+ }
1365
+ this.loadAcademicYears(); // Reload the list
1366
+ }, (error) => {
1367
+ console.error('❌ Error toggling mapping statuses:', error);
1368
+ this.notificationService.error('Academic year deactivated, but failed to update some mappings.');
1369
+ this.loadAcademicYears(); // Still reload the list
1370
+ });
1371
+ }
1372
+ else {
1373
+ this.notificationService.success('Academic year has been deactivated successfully.');
1374
+ this.loadAcademicYears(); // No mappings to toggle, just reload
1375
+ }
1376
+ }
1377
+ else {
1378
+ this.notificationService.success('Academic year has been deactivated successfully.');
1379
+ this.loadAcademicYears(); // No mappings found, just reload
1380
+ }
1381
+ },
1382
+ error: (error) => {
1383
+ console.error('❌ Error getting academic year mappings:', error);
1384
+ this.notificationService.error('Academic year deactivated, but failed to update mappings.');
1385
+ this.loadAcademicYears(); // Still reload the list
1386
+ }
1387
+ });
1388
+ }
1389
+ // Export handler
1390
+ handleExport(format) {
1391
+ console.log(`📤 Exporting academic years as ${format}`);
1392
+ const data = this.academicYears().map(item => ({
1393
+ 'Academic Year Code': item.acayr_code,
1394
+ 'Academic Year Name': item.acayr_name,
1395
+ 'Description': item.acayr_description,
1396
+ 'From Date': this.formatDate(item?.acayr_from_date || ""),
1397
+ 'To Date': this.formatDate(item?.acayr_to_date || ""),
1398
+ 'Duration': this.getDuration(item?.acayr_from_date || "", item?.acayr_to_date || ""),
1399
+ 'Status': this.getStatusText(item),
1400
+ 'Is Current': item.acayr_iscurrent ? 'Yes' : 'No',
1401
+ 'Is Active': item.acayr_isactive ? 'Yes' : 'No',
1402
+ 'Is Locked': item.acayr_islocked ? 'Yes' : 'No'
1403
+ }));
1404
+ // Create filename with timestamp
1405
+ const timestamp = new Date().toISOString().split('T')[0];
1406
+ const filename = `academic-years-${timestamp}`;
1407
+ switch (format.toLowerCase()) {
1408
+ case 'csv':
1409
+ this.exportToCSV(data, filename);
1410
+ break;
1411
+ case 'excel':
1412
+ this.exportToExcel(data, filename);
1413
+ break;
1414
+ case 'pdf':
1415
+ this.exportToPDF(data, filename);
1416
+ break;
1417
+ default:
1418
+ console.warn(`Unsupported export format: ${format}`);
1419
+ }
1420
+ }
1421
+ exportToCSV(data, filename) {
1422
+ const headers = Object.keys(data[0] || {});
1423
+ const csvContent = [
1424
+ headers.join(','),
1425
+ ...data.map(row => headers.map(header => `"${row[header] || ''}"`).join(','))
1426
+ ].join('\n');
1427
+ this.downloadFile(csvContent, `${filename}.csv`, 'text/csv');
1428
+ }
1429
+ exportToExcel(data, filename) {
1430
+ // For Excel export, you might want to use a library like xlsx
1431
+ // For now, we'll export as CSV with .xlsx extension
1432
+ this.exportToCSV(data, filename);
1433
+ }
1434
+ exportToPDF(data, filename) {
1435
+ // For PDF export, you might want to use a library like jsPDF
1436
+ // For now, we'll export as CSV
1437
+ this.exportToCSV(data, filename);
1438
+ }
1439
+ downloadFile(content, filename, mimeType) {
1440
+ const blob = new Blob([content], { type: mimeType });
1441
+ const url = window.URL.createObjectURL(blob);
1442
+ const link = document.createElement('a');
1443
+ link.href = url;
1444
+ link.download = filename;
1445
+ document.body.appendChild(link);
1446
+ link.click();
1447
+ document.body.removeChild(link);
1448
+ window.URL.revokeObjectURL(url);
1449
+ }
1450
+ // Utility methods with proper typing
1451
+ formatDate(date) {
1452
+ if (!date)
1453
+ return '-';
1454
+ return new Date(date).toLocaleDateString('en-US', {
1455
+ year: 'numeric',
1456
+ month: 'short',
1457
+ day: 'numeric'
1458
+ });
1459
+ }
1460
+ getDuration(fromDate, toDate) {
1461
+ if (!fromDate || !toDate)
1462
+ return '';
1463
+ const from = new Date(fromDate);
1464
+ const to = new Date(toDate);
1465
+ const diffTime = Math.abs(to.getTime() - from.getTime());
1466
+ const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
1467
+ const months = Math.floor(diffDays / 30);
1468
+ const years = Math.floor(months / 12);
1469
+ if (years > 0) {
1470
+ return `${years} year${years > 1 ? 's' : ''}`;
1471
+ }
1472
+ else if (months > 0) {
1473
+ return `${months} month${months > 1 ? 's' : ''}`;
1474
+ }
1475
+ else {
1476
+ return `${diffDays} day${diffDays > 1 ? 's' : ''}`;
1477
+ }
1478
+ }
1479
+ getStatusBadgeClass(academicYear) {
1480
+ if (academicYear.acayr_iscurrent)
1481
+ return 'tw-bg-green-100 tw-text-green-800';
1482
+ if (academicYear.acayr_islocked)
1483
+ return 'tw-bg-red-100 tw-text-red-800';
1484
+ if (academicYear.acayr_isactive)
1485
+ return 'tw-bg-blue-100 tw-text-blue-800';
1486
+ return 'tw-bg-gray-100 tw-text-gray-800';
1487
+ }
1488
+ getActiveStatusBadgeClass(academicYear) {
1489
+ if (academicYear.acayr_isactive) {
1490
+ return 'tw-bg-green-100 tw-text-green-800';
1491
+ }
1492
+ else {
1493
+ return 'tw-bg-orange-100 tw-text-orange-800';
1494
+ }
1495
+ }
1496
+ getStatusText(academicYear) {
1497
+ if (academicYear.acayr_iscurrent)
1498
+ return 'Current';
1499
+ if (academicYear.acayr_islocked)
1500
+ return 'Locked';
1501
+ if (academicYear.acayr_isactive)
1502
+ return 'Active';
1503
+ return 'Inactive';
1504
+ }
1505
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AcademicYearListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1506
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.1.7", type: AcademicYearListComponent, isStandalone: true, selector: "cide-core-academic-year-list", viewQueries: [{ propertyName: "academicYearDetailsRendererTemplate", first: true, predicate: ["academicYearDetailsRendererTemplate"], descendants: true, isSignal: true }, { propertyName: "dateRangeRendererTemplate", first: true, predicate: ["dateRangeRendererTemplate"], descendants: true, isSignal: true }, { propertyName: "statusRendererTemplate", first: true, predicate: ["statusRendererTemplate"], descendants: true, isSignal: true }, { propertyName: "actionsDropdownRendererTemplate", first: true, predicate: ["actionsDropdownRendererTemplate"], descendants: true, isSignal: true }], ngImport: i0, template: "<!-- Academic Year Container -->\n<div class=\"tw-table tw-w-full tw-h-full\">\n\n <!-- Header Section with Filters -->\n <div class=\"tw-table-row tw-h-0\">\n <div class=\"tw-table-cell tw-px-6 tw-py-3 tw-border-b tw-border-gray-200 tw-bg-gray-50\">\n <div\n class=\"tw-flex tw-flex-col sm:tw-flex-row tw-justify-between tw-items-start sm:tw-items-center tw-space-y-3 sm:tw-space-y-0\">\n\n <!-- Title -->\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n <cide-ele-icon class=\"tw-text-blue-600 tw-w-5 tw-h-5\">school</cide-ele-icon>\n <h5 class=\"tw-text-base tw-font-medium tw-text-gray-900 tw-m-0\">Academic Year Management</h5>\n </div>\n\n <!-- Actions -->\n <div\n class=\"tw-flex tw-flex-col sm:tw-flex-row tw-items-start sm:tw-items-center tw-space-y-3 sm:tw-space-y-0 sm:tw-space-x-3\">\n <button cideEleButton variant=\"primary\" size=\"sm\" leftIcon=\"add\" (click)=\"createAcademicYear()\">\n Create Academic Year\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Main Content Area -->\n <div class=\"tw-table-row\">\n <div class=\"tw-table-cell tw-h-full tw-relative\">\n\n <!-- Data Grid Component -->\n <div class=\"tw-h-full tw-overflow-auto\">\n <cide-ele-data-grid [config]=\"gridConfig()\" [templateRenderers]=\"templateRenderers()\"\n (gridEvent)=\"onGridEvent($event)\">\n </cide-ele-data-grid>\n </div>\n\n </div>\n </div>\n\n</div>\n\n<!-- Template Renderers -->\n<ng-template #academicYearDetailsRendererTemplate let-row=\"row\">\n <div class=\"tw-flex tw-flex-col tw-w-full\">\n <div class=\"tw-font-medium tw-text-gray-900\">{{ row.acayr_name || 'N/A' }}</div>\n <div class=\"tw-text-sm tw-text-gray-500 tw-truncate\">{{ row.acayr_description || 'No description' }}</div>\n </div>\n</ng-template>\n\n<ng-template #dateRangeRendererTemplate let-row=\"row\">\n <div class=\"tw-flex tw-flex-col tw-text-sm\">\n <span class=\"tw-text-gray-900 tw-font-medium\">{{ formatDate(row.acayr_from_date) }}</span>\n <span class=\"tw-text-gray-500\">to {{ formatDate(row.acayr_to_date) }}</span>\n <span class=\"tw-text-xs tw-text-gray-400\">{{ getDuration(row.acayr_from_date, row.acayr_to_date) }}</span>\n </div>\n</ng-template>\n\n<ng-template #statusRendererTemplate let-row=\"row\">\n <div class=\"tw-flex tw-flex-col tw-gap-1 tw-items-center\">\n <!-- Current Year Badge (Priority) -->\n @if (row.acayr_iscurrent) {\n <span\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-px-2.5 tw-py-1 tw-rounded-full tw-text-xs tw-font-medium tw-bg-yellow-100 tw-text-yellow-800 tw-text-center\">\n <cide-ele-icon size=\"2xs\" class=\"tw-mr-1\">star</cide-ele-icon>\n Current Year\n </span>\n }\n \n <!-- Active/Inactive Status Badge -->\n <span class=\"tw-inline-flex tw-items-center tw-justify-center tw-px-2.5 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium tw-text-center\"\n [ngClass]=\"getActiveStatusBadgeClass(row)\">\n <cide-ele-icon size=\"2xs\" class=\"tw-mr-1\">\n {{ row.acayr_isactive ? 'check_circle' : 'cancel' }}\n </cide-ele-icon>\n {{ row.acayr_isactive ? 'Active' : 'Inactive' }}\n </span>\n \n <!-- Locked Status Badge (Additional info) -->\n @if (row.acayr_islocked) {\n <span\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-px-2 tw-py-1 tw-rounded-full tw-text-xs tw-font-medium tw-bg-red-100 tw-text-red-800 tw-text-center\">\n <cide-ele-icon size=\"2xs\" class=\"tw-mr-1\">lock</cide-ele-icon>\n Locked\n </span>\n }\n </div>\n</ng-template>\n\n<ng-template #actionsDropdownRendererTemplate let-row=\"row\" let-value=\"value\">\n <cide-ele-dropdown \n [items]=\"getActionDropdownItems(row)\"\n [config]=\"{ triggerIcon: 'more_vert', triggerSize: 'sm' }\"\n (itemClick)=\"onDropdownItemClick($event, row)\">\n </cide-ele-dropdown>\n</ng-template>", styles: [".academic-year-listing-container{@apply tw-w-full tw-h-full;}:host{@apply tw-w-full tw-h-full tw-flex tw-flex-col;}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "ngmodule", type: FormsModule }, { kind: "component", type: CideIconComponent, selector: "cide-ele-icon", inputs: ["size", "type", "toolTip"] }, { kind: "component", type: CideEleButtonComponent, selector: "button[cideEleButton], a[cideEleButton]", inputs: ["label", "variant", "size", "type", "shape", "elevation", "disabled", "id", "loading", "fullWidth", "leftIcon", "rightIcon", "customClass", "tooltip", "ariaLabel", "testId", "routerLink", "routerExtras", "preventDoubleClick", "animated"], outputs: ["btnClick", "doubleClick"] }, { kind: "component", type: CideEleDataGridComponent, selector: "cide-ele-data-grid", inputs: ["config", "templateRenderers", "customFormatters", "actionHandlers", "serverSidePagination", "totalServerItems", "currentServerPage", "currentServerPageSize", "dragDropEnabled"], outputs: ["gridEvent"] }, { kind: "component", type: CideEleDropdownComponent, selector: "cide-ele-dropdown", inputs: ["items", "config", "triggerTemplate", "menuTemplate"], outputs: ["itemClick", "dropdownToggle"] }] });
1507
+ }
1508
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.7", ngImport: i0, type: AcademicYearListComponent, decorators: [{
1509
+ type: Component,
1510
+ args: [{ selector: 'cide-core-academic-year-list', standalone: true, imports: [
1511
+ CommonModule,
1512
+ FormsModule,
1513
+ CideIconComponent,
1514
+ CideEleButtonComponent,
1515
+ CideEleDataGridComponent,
1516
+ CideEleDropdownComponent
1517
+ ], template: "<!-- Academic Year Container -->\n<div class=\"tw-table tw-w-full tw-h-full\">\n\n <!-- Header Section with Filters -->\n <div class=\"tw-table-row tw-h-0\">\n <div class=\"tw-table-cell tw-px-6 tw-py-3 tw-border-b tw-border-gray-200 tw-bg-gray-50\">\n <div\n class=\"tw-flex tw-flex-col sm:tw-flex-row tw-justify-between tw-items-start sm:tw-items-center tw-space-y-3 sm:tw-space-y-0\">\n\n <!-- Title -->\n <div class=\"tw-flex tw-items-center tw-space-x-2\">\n <cide-ele-icon class=\"tw-text-blue-600 tw-w-5 tw-h-5\">school</cide-ele-icon>\n <h5 class=\"tw-text-base tw-font-medium tw-text-gray-900 tw-m-0\">Academic Year Management</h5>\n </div>\n\n <!-- Actions -->\n <div\n class=\"tw-flex tw-flex-col sm:tw-flex-row tw-items-start sm:tw-items-center tw-space-y-3 sm:tw-space-y-0 sm:tw-space-x-3\">\n <button cideEleButton variant=\"primary\" size=\"sm\" leftIcon=\"add\" (click)=\"createAcademicYear()\">\n Create Academic Year\n </button>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Main Content Area -->\n <div class=\"tw-table-row\">\n <div class=\"tw-table-cell tw-h-full tw-relative\">\n\n <!-- Data Grid Component -->\n <div class=\"tw-h-full tw-overflow-auto\">\n <cide-ele-data-grid [config]=\"gridConfig()\" [templateRenderers]=\"templateRenderers()\"\n (gridEvent)=\"onGridEvent($event)\">\n </cide-ele-data-grid>\n </div>\n\n </div>\n </div>\n\n</div>\n\n<!-- Template Renderers -->\n<ng-template #academicYearDetailsRendererTemplate let-row=\"row\">\n <div class=\"tw-flex tw-flex-col tw-w-full\">\n <div class=\"tw-font-medium tw-text-gray-900\">{{ row.acayr_name || 'N/A' }}</div>\n <div class=\"tw-text-sm tw-text-gray-500 tw-truncate\">{{ row.acayr_description || 'No description' }}</div>\n </div>\n</ng-template>\n\n<ng-template #dateRangeRendererTemplate let-row=\"row\">\n <div class=\"tw-flex tw-flex-col tw-text-sm\">\n <span class=\"tw-text-gray-900 tw-font-medium\">{{ formatDate(row.acayr_from_date) }}</span>\n <span class=\"tw-text-gray-500\">to {{ formatDate(row.acayr_to_date) }}</span>\n <span class=\"tw-text-xs tw-text-gray-400\">{{ getDuration(row.acayr_from_date, row.acayr_to_date) }}</span>\n </div>\n</ng-template>\n\n<ng-template #statusRendererTemplate let-row=\"row\">\n <div class=\"tw-flex tw-flex-col tw-gap-1 tw-items-center\">\n <!-- Current Year Badge (Priority) -->\n @if (row.acayr_iscurrent) {\n <span\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-px-2.5 tw-py-1 tw-rounded-full tw-text-xs tw-font-medium tw-bg-yellow-100 tw-text-yellow-800 tw-text-center\">\n <cide-ele-icon size=\"2xs\" class=\"tw-mr-1\">star</cide-ele-icon>\n Current Year\n </span>\n }\n \n <!-- Active/Inactive Status Badge -->\n <span class=\"tw-inline-flex tw-items-center tw-justify-center tw-px-2.5 tw-py-0.5 tw-rounded-full tw-text-xs tw-font-medium tw-text-center\"\n [ngClass]=\"getActiveStatusBadgeClass(row)\">\n <cide-ele-icon size=\"2xs\" class=\"tw-mr-1\">\n {{ row.acayr_isactive ? 'check_circle' : 'cancel' }}\n </cide-ele-icon>\n {{ row.acayr_isactive ? 'Active' : 'Inactive' }}\n </span>\n \n <!-- Locked Status Badge (Additional info) -->\n @if (row.acayr_islocked) {\n <span\n class=\"tw-inline-flex tw-items-center tw-justify-center tw-px-2 tw-py-1 tw-rounded-full tw-text-xs tw-font-medium tw-bg-red-100 tw-text-red-800 tw-text-center\">\n <cide-ele-icon size=\"2xs\" class=\"tw-mr-1\">lock</cide-ele-icon>\n Locked\n </span>\n }\n </div>\n</ng-template>\n\n<ng-template #actionsDropdownRendererTemplate let-row=\"row\" let-value=\"value\">\n <cide-ele-dropdown \n [items]=\"getActionDropdownItems(row)\"\n [config]=\"{ triggerIcon: 'more_vert', triggerSize: 'sm' }\"\n (itemClick)=\"onDropdownItemClick($event, row)\">\n </cide-ele-dropdown>\n</ng-template>", styles: [".academic-year-listing-container{@apply tw-w-full tw-h-full;}:host{@apply tw-w-full tw-h-full tw-flex tw-flex-col;}\n"] }]
1518
+ }] });
1519
+
1520
+ var academicYearList_component = /*#__PURE__*/Object.freeze({
1521
+ __proto__: null,
1522
+ AcademicYearListComponent: AcademicYearListComponent
1523
+ });
1524
+
1525
+ // Academic Year Management Components
1526
+
1527
+ /*
1528
+ * Public API Surface of cloud-ide-academics
1529
+ */
1530
+
1531
+ /**
1532
+ * Generated bundle index. Do not edit.
1533
+ */
1534
+
1535
+ export { AcademicYearCreateComponent, AcademicYearListComponent, CideLytAcademicYearMappingService, CideLytAcademicYearService, academicsRoutes };
1536
+ //# sourceMappingURL=cloud-ide-academics.mjs.map