Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

15 — Flutter Implementation Guide (Teachers Module)

Extends 00-shared/11. Forward-looking: no client repo exists (shared ledger A1). Everything below derives from src/modules/teachers/**, src/modules/academics/** (subject-assignments), src/modules/staff/** (catalogs), src/modules/timetable/**.


1. Module folder

lib/features/teachers/
├── data/
│   ├── dto/teacher_dto.dart            # envelope payload mapper
│   ├── dto/subject_assignment_dto.dart
│   ├── dto/teacher_draft_dto.dart      # create/update payloads (F1/F2)
│   ├── models/teacher.dart             # + EmploymentStatus enum
│   ├── models/subject_assignment.dart
│   └── repositories/teacher_repository.dart
├── domain/
│   └── teachers_service.dart           # optional: catalog-join helper
└── presentation/
    ├── cubit/teachers_list_cubit.dart
    ├── cubit/teacher_detail_cubit.dart
    ├── cubit/teacher_form_cubit.dart
    ├── cubit/assignment_editor_cubit.dart
    ├── pages/teachers_list_page.dart
    ├── pages/teacher_detail_page.dart
    ├── pages/teacher_form_page.dart
    └── widgets/status_chip.dart, teacher_list_tile.dart,
        teacher_card.dart, assignment_card.dart,
        assignment_matrix_header.dart, subject_badge.dart

2. Domain models

enum EmploymentStatus { active, inactive, onLeave, terminated }

class Teacher {
  final String id;
  final String userId;               // identity link (teacher.schema.ts:16-17)
  final String employeeNumber;       // mono display
  final String? departmentId, designationId;
  final DateTime? joiningDate;
  final EmploymentStatus employmentStatus;
  final String? qualification;
  final int experienceYears;         // default 0 (teacher.schema.ts:41-42)
  final List<String> subjects;       // ObjectIds (teacher.schema.ts:44-45)
  final List<String> classTeacherFor;
  final Map<String, dynamic>? metadata;
}

class SubjectAssignment {
  final String id, teacherId, subjectId, classId, academicYearId;
}
  • DTO→model: fromJson with strict types; enums via EnumByName with unknown → active fallback (proposed) (server enum not validated on input, but stored values are enum-safe, teacher.schema.ts:31-36).
  • Ref-name resolution: TeachersService.joinNames(teacher, catalogs) — catalogs (departments/designations/subjects/classes/years) loaded once, cached 24 h (13 §3).

3. Repository

class TeacherRepository {
  TeacherRepository(this._dio);            // AppDio (00-shared/11 §5)
  Future<Page<Teacher>> list({int page = 1, int limit = 20});
  Future<Teacher> byId(String id);
  Future<Teacher> create(TeacherDraftDto dto);          // POST /teachers
  Future<Teacher> update(String id, TeacherPatchDto dto);// PATCH /teachers/:id
  Future<void> remove(String id);                       // DELETE /teachers/:id
  Future<List<SubjectAssignment>> assignmentsByTeacher(String teacherId, String academicYearId);
  Future<SubjectAssignment> createAssignment(Map<String, String> triple); // POST /subject-assignments
  Future<void> removeAssignment(String id);             // DELETE /subject-assignments/:id
}
  • Errors: interceptor maps envelope → ApiException(code, status, fieldDetails) (00-shared/11 §5); 409 exposes conflict flavor for banner copy.
  • Pagination: Page<T> helper (items + meta) shared with other modules.

4. Cubits

  • TeachersListCubitPaginatedListMixin<Teacher> (00-shared/06 §3.2); local filter application for status/dept/designation/q (server ignores sort/q, teacher.service.ts:66-78); RefreshTeachers bypasses cache.
  • TeacherDetailCubit — parallel loads: profile + assignments + timetable (three repos) with per-tab LoadState; ChangeYear emits token, drops stale responses.
  • TeacherFormCubit — mirrors TeacherDraftDto/TeacherPatchDto; on 400 maps fieldDetails; on 409 sets ConflictType; dirty guards back-navigation.
  • AssignmentEditorCubit — duplicate check against current matrix (13 §2 state); submit disabled while pending.

5. Router

// go_router additions (00-shared/05 §4 + 00-shared/11 §6)
GoRoute(path: '/staff/teachers', builder: TeachersListPage.new),
GoRoute(path: '/staff/teachers/new', builder: TeacherFormPage(mode: create)),
GoRoute(path: '/staff/teachers/:id', builder: TeacherDetailPage.new),
GoRoute(path: '/staff/teachers/:id/edit', builder: TeacherFormPage(mode: edit)),
// (planned) teacher self
GoRoute(path: '/my/teaching', builder: MyTeachingPage.new),
  • Guards: permissionGuard('staff.read') client-side mirror of permissions.constants.ts:19 (server is JWT-only today — 01 §5, OQ-1).
  • Deep link: studylyon://teachers/:id.

6. Theme & components

  • All tokens via AppTheme (00-shared/04); module components in 07; status colors only inside StatusChip (11 §7).
  • employeeNumber rendered with mono + tabularFigures (02 §2).

7. i18n keys

teachers.title, teachers.search.hint, teachers.add,
teachers.status.{active,inactive,on_leave,terminated},
teachers.create.conflict.user, teachers.create.conflict.employeeNumber,
teachers.notFound, teachers.deactivate.title, teachers.deactivate.body,
teachers.deactivate.done, teachers.empty.title, teachers.empty.filtered,
assignments.title, assignments.add, assignments.duplicate,
assignments.remove.confirm, assignments.notFound, assignments.empty.{teacher,year},
schedule.empty, teachers.self.banner.onLeave, errors.server, errors.rateLimited

8. Tests

LayerCases
UnitTeachersListCubit pagination merge + local filters; TeacherFormCubit 400/409 mapping; AssignmentEditorCubit duplicate guard; DTO↔model mappers
WidgetS1 3-state (skeleton/error/empty), S5 409 banner, S7 duplicate block, StatusChip golden ×4×2
Integrationcreate→assign→edit→deactivate journey; 404 detail handling
E2EP0: full teacher lifecycle + cross-tenant 404 (per 00-shared/10 §9)

9. Known backend gaps to coordinate (from 14-QA)

  • Duplicate-assignment guard (QA-1), PATCH employeeNumber 409 (QA-9), deactivate guard (QA-12), server search/sort (QA-18)
  • TeacherCreated email intent + in-app notification enum mismatch (QA-25/26)
  • Search-index title for teacher events (QA-27)
  • GET /teachers/me for teacher self-view (OQ-2)