# Frontend Architecture Technical architecture overview of the B12 SIS Angular frontend. ## Technology Stack | Technology | Version | Purpose | |------------|---------|---------| | Angular | 19 | Core framework | | Angular Material | 19 | UI components | | Bootstrap | 5 | Layout utilities | | ngx-translate | Latest | Internationalization | | ngx-charts | Latest | Data visualization | | FullCalendar | Latest | Calendar views | | RxJS | 7+ | Reactive programming | ## Application Architecture ``` ┌─────────────────────────────────────────────────────────────┐ │ App Component │ ├─────────────────────────────────────────────────────────────┤ │ ┌─────────────────┐ ┌─────────────────────────────────┐ │ │ │ Auth Layout │ │ Main Layout │ │ │ │ (Login pages) │ │ ┌─────────────────────────┐ │ │ │ │ │ │ │ Header │ │ │ │ │ │ │ ├─────────────────────────┤ │ │ │ │ │ │ │ Sidebar │ Content │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ └─────────────────────────┘ │ │ │ └─────────────────┘ └─────────────────────────────────┘ │ └─────────────────────────────────────────────────────────────┘ ``` ## Module Architecture ### Lazy Loading Strategy All feature modules are lazy-loaded to optimize initial bundle size: ```typescript // app.routes.ts { path: 'module', canActivate: [AuthGuard], loadChildren: () => import('./features/features.routes').then((m) => m.ROUTE), } // features/features.routes.ts { path: 'students', loadChildren: () => import('./students/routes').then((m) => m.ROUTE), } ``` ### Feature Modules 75+ feature modules covering all SIS functionality: **Academic Management** - `academic_sessions` - Academic years/terms - `education_levels` - Education level definitions - `grade_levels` - Grade/year levels - `subjects` - Subject catalog - `courses` - Course management - `classes` - Class sections - `timetables` - Scheduling **People Management** - `students` - Student records - `teachers` - Teacher records - `guardians` - Parent/guardian records - `users` - System users **Enrollment** - `student_enroll_classes` - Class enrollment - `student_enroll_courses` - Course enrollment - `student_enroll_programs` - Program enrollment **Grading & Assessment** - `gradebooks` - Gradebook management - `assessments` - Assessment definitions - `assessment_results` - Student scores - `grade_scales` - Grading scales - `report_card_templates` - Report card templates - `student_report_cards` - Generated report cards **Attendance & Behavior** - `attendances` - Attendance records - `attendance_configs` - Attendance settings - `behaviors` - Behavior incidents **Medical & Health** - `medical_cares` - Medical visits - `medical_care_histories` - Medical history - `medical_timeslots` - Appointment scheduling **Administration** - `roles` - Role management - `teams` - Multi-tenant teams - `import_datas` - Data import - `system_settings` - System configuration ## Core Module The `core/` module provides application-wide services and utilities: ### Services ``` core/services/ ├── api.service.ts # HTTP client wrapper ├── auth.service.ts # Authentication state ├── backend.service.ts # Higher-level API operations ├── broadcast.service.ts # Cross-component communication ├── language.service.ts # Language management ├── lti-auth.service.ts # LTI authentication ├── modal.service.ts # Modal dialogs ├── notification.service.ts # Toast notifications └── oidc-auth.service.ts # OIDC/SSO authentication ``` ### HTTP Interceptors ``` core/interceptor/ ├── jwt.interceptor.ts # Bearer token injection ├── team.interceptor.ts # X-Team-ID header ├── academic-session.interceptor.ts # Academic session context └── error.interceptor.ts # Global error handling ``` **Request Flow:** ``` Request → JwtInterceptor → TeamInterceptor → AcademicSessionInterceptor → API Response ← ErrorInterceptor ← HTTP Response ``` ### Route Guards ```typescript // auth.guard.ts - Protects authenticated routes canActivate(route: ActivatedRouteSnapshot): boolean { const currentUser = this.authService.currentUserValue; if (currentUser && currentUser.token?.access_token) { return true; } this.router.navigate(['/authentication/signin']); return false; } // module-import.guard.ts - Controls module access canActivate(route: ActivatedRouteSnapshot): boolean { // Check user permissions for module return this.hasModuleAccess(route.data['module']); } ``` ## Base Component Pattern Abstract base components provide consistent behavior: ### Component Hierarchy ``` BaseComponent ├── BaseListComponent (list views) ├── BaseDetailComponent (detail/edit views) └── BaseCreateComponent (create forms) ``` ### BaseComponent ```typescript @Component({ template: '' }) export abstract class BaseComponent implements OnDestroy { protected destroy$ = new Subject(); ngOnDestroy(): void { this.destroy$.next(); this.destroy$.complete(); } } ``` ### BaseListComponent ```typescript @Component({ template: '' }) export abstract class BaseListComponent extends BaseComponent { abstract listConfig: GenericListConfig; @Output() extraActionClick = new EventEmitter<{ action: any; model: any }>(); @Output() headerExtraActionClick = new EventEmitter<{ action: any }>(); protected setupTranslations(): void { // Auto-translate list configuration } onExtraActionClicked(event: { action: any; model: any }): void { // Override in child components } } ``` ## Shared Module 113+ reusable components in `shared/`: ### Component Categories **Data Display** - `generic-list` - Configurable data table with sorting, filtering, pagination - `generic-form` - Dynamic form generator - `editable-field-renderer` - Field type renderer **Layout** - `breadcrumb` - Navigation breadcrumbs - `chart-card` variants - Dashboard chart cards **Inputs** - `file-upload` - S3 file upload - `fc-*` components - Form control wrappers **Custom Widgets** - `attendance-chart` - Attendance visualization - `calendar` - Event calendar - `timetable` - Schedule display ## State Management Service-based state with RxJS observables: ```typescript @Injectable({ providedIn: 'root' }) export class AuthService { private currentUserSubject: BehaviorSubject; public currentUser: Observable; constructor() { this.currentUserSubject = new BehaviorSubject( JSON.parse(localStorage.getItem('currentUser') || '{}') ); this.currentUser = this.currentUserSubject.asObservable(); } public get currentUserValue(): Auth { return this.currentUserSubject.value; } } ``` ### State Flow ``` User Action → Component → Service → BehaviorSubject → Subscribers ↓ API Call → Backend ↓ Response → Update Subject → UI Update ``` ## Multi-Tenancy Team-based data isolation: ```typescript // TeamInterceptor adds header to all requests request = request.clone({ setHeaders: { 'X-Team-ID': `${teamId}`, }, }); ``` **Team Selection:** ```typescript // AuthService.setActiveTeam() setActiveTeam(teamId: string): Observable { this.currentUserValue.active_team_id = teamId; this.currentUserSubject.next(this.currentUserValue); localStorage.setItem('currentUser', JSON.stringify(this.currentUserValue)); return of(this.currentUserValue); } ``` ## Authentication Flow ### Standard Login ``` 1. User submits credentials 2. AuthService.login() calls POST /api/auth/login 3. Server returns JWT tokens 4. AuthService stores tokens in localStorage 5. AuthService calls GET /api/auth/me for user details 6. Permissions loaded into auth state 7. JwtInterceptor adds token to subsequent requests ``` ### Token Storage ```typescript // In AuthService localStorage.setItem('currentUser', JSON.stringify({ token: { access_token, refresh_token }, user: { ... }, permissions: [...], active_team_id: '...' })); // In JwtInterceptor localStorage.setItem('access_token', currentUser.token.access_token); ``` ### LTI Authentication ```typescript // LtiAuthService handles LTI 1.3 launches // Tokens provided by LMS platform localStorage.setItem('lti_mode', 'true'); ``` ### OIDC/SSO Authentication ```typescript // OidcAuthService handles OIDC flows // Redirects to identity provider // Processes callback with tokens ``` ## Internationalization ### ngx-translate Setup ```typescript // app.config.ts TranslateModule.forRoot({ loader: { provide: TranslateLoader, useFactory: httpLoaderFactory, deps: [HttpClient] } }) ``` ### Translation Keys ```typescript // Component this.translate.instant('students.list.title') // Template {{ 'students.list.title' | translate }} // With parameters {{ 'students.enrolled' | translate:{ count: studentCount } }} ``` ### Language Switching ```typescript // LanguageService setLanguage(lang: string) { this.translate.use(lang); localStorage.setItem('lang', lang); } ``` ## Error Handling ### ErrorInterceptor ```typescript intercept(request: HttpRequest, next: HttpHandler) { return next.handle(request).pipe( catchError((error: HttpErrorResponse) => { if (error.status === 401) { // Token expired - logout this.authService.logout(); this.router.navigate(['/authentication/signin']); } // Show notification this.notification.showError(error.error?.message || 'Error'); return throwError(() => error); }) ); } ``` ### Component-Level Handling ```typescript this.api.post('endpoint', data).subscribe({ next: (response) => { this.notification.showSuccess('Saved successfully'); }, error: (error) => { // ErrorInterceptor handles common cases // Custom handling if needed console.error('Specific error handling:', error); } }); ``` ## Performance Optimization ### Lazy Loading All feature modules loaded on-demand reduces initial bundle. ### Change Detection Use `OnPush` strategy where applicable: ```typescript @Component({ changeDetection: ChangeDetectionStrategy.OnPush }) ``` ### Subscription Management Always cleanup subscriptions: ```typescript this.api.get('endpoint') .pipe(takeUntil(this.destroy$)) .subscribe(...); ``` ### TrackBy Functions Optimize ngFor loops: ```html ```