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

05 — Screen Inventory (Students Module)

Every screen of the Students module with intent, route, composition, states and permissions. Shared components come from 00-shared/03; module-specific components are defined in 07_Component_Library.md. States legend: idle / loading / success / empty / error / disabled / permission. Analytics events (proposed).


1. Students List (Roster) — /students

FieldDetail
PurposeFind, filter, open and batch-manage the roster
EntryBottom nav "Students"; deep link; after import/create returns
SourceGET /students (paginated data[] + meta) — student.controller.ts:41-43; find() ignores q/sort (OQ-2)
CompositionAppSearchBar, status/class/year filter chips (client-side), list of StudentListTile (avatar from user, name, admission number, class label, status badge), infinite scroll (meta.hasNext)
FAB+ Add student (visible iff student.create)
Row menuView, Edit, Transfer, Graduate, Archive, Restore, Delete (contextual by status: Restore only when archived; Graduate/Transfer only when active)
Statesloading → AppSkeleton list; empty → AppEmptyState ("No students yet — add or import"); error → AppErrorState with retry; offline → cached list + banner
Keyboard (desktop)Ctrl+F focus search, N new, Esc clears filters
Analyticsstudents.list.view, students.list.search, students.list.filter, students.list.open (proposed)
a11ySearch results count live-region; "item X of Y" list announcements (00-shared/09 §5)

2. Student Detail — /students/:id

FieldDetail
PurposeOne student: identity + status + per-domain tabs
SourceGET /students/:id (student.controller.ts:44-46); tabs lazy-load their own endpoints
CompositionHeader card (avatar, name, admission number mono, status badge, class + academic year chips, campus/house if set), action bar (Edit / Transfer / Graduate / Archive / Restore / Delete), AppTabs: Profile · Attendance · Fees · Results · Documents · History
Tab sourcesProfile = GET /students/:id; Documents = GET /students/:id/documents (createdAt desc); History = GET /students/:id/academic-history (joinedAt desc) + GET /students/:id/enrollments (active); Attendance/Fees/Results = other modules (forward-looking)
Statesdetail skeleton → content; tab-level skeletons; tab error isolated (tab shows AppErrorState compact, rest of screen intact)
a11yTabs announced with badges; header actions labelled
Analyticsstudents.detail.view, students.detail.tab.{profile,documents,history,…} (proposed)

3. Create Student Wizard — /students/add

FieldDetail
PurposeAdmission in one guided flow (identity → academics → extras → confirm)
SourcePOST /users then POST /students (auto-enrolls — student.service.ts:71-78)
Steps1) Identity: search/create user (firstName, lastName, email; middleName/phone optional) · 2) Academics: academic year, grade, section, class (cascading dropdowns), rollNumber, admissionType · 3) Extras: campus, house, admissionDate (default today), transportRequired, hostelRequired, medicalNotes · 4) Review + submit (shows admission number + auto-enroll note)
CTA per stepNext / Back; final step "Create student"
Statesstep validation errors inline; submit loading; 409 duplicate admissionNumber → jump to field; 400 → field mapping; offline → blocked
Duplicate guardadmission number pre-check against loaded roster client-side; server 409 is authoritative
Analyticsstudents.create.start, students.create.step_{n}, students.create.success, students.create.failure(code) (proposed)

4. Bulk Import Wizard — /students/import

FieldDetail
PurposeCSV admissions batch with template + report
SourceGET /bulk/export/students (template), POST /bulk/import/students (multipart file)
Steps1) Template: download CSV (columns: admissionNumber, rollNumber, status, admissionDate — students-import.adapter.ts:87-98); show required column list · 2) File pick: accept .csv; client pre-parse preview (isolate — 00-shared/11 §13) · 3) Upload: progress bar (request is synchronous server-side — UI simulates progress; OQ-12) · 4) Report: {totalRows, imported, failed, errors[{rowNumber, errors}]} — grouped failure list, "Export failures" not available server-side (client can re-download template), retry with fixed CSV
Guardsmissing file → 400; malformed/empty CSV → 400 with message
Statesstep states; upload loading; report success/failure mixed is normal (partial success)
Analyticsstudents.import.template, students.import.upload, students.import.report{imported,failed} (proposed)

5. Transfer Form — /students/:id/transfer

FieldDetail
PurposeMove student to a new class (same or next academic year)
SourcePOST /students/:id/transfer — guard: status must be active else 409 (student.service.ts:175-179)
CompositionTarget class dropdown (filtered by academic year), academic year dropdown, optional grade/section (default = class's own), optional rollNumber, info banner "Current enrollment will be marked transferred"
CTA"Transfer student" (confirm dialog first)
Statespre-fill from current class; submit loading; 409 → status banner + block; success → back to detail, history tab shows old enrollment transferred
Analyticsstudents.transfer.submit, `students.transfer.success

6. Enroll Form (sheet) — from detail

FieldDetail
PurposeEnroll into a class for an academic year (history preserved)
SourcePOST /students/:id/enroll — deactivates current actives, creates new ACTIVE (student.service.ts:125-140)
CompositionAppBottomSheet: classId (required), academicYearId (required), rollNumber (optional)
CTA"Enroll" — warning text: existing active enrollments become transferred
NoteSame backend as transfer minus profile fields; used for re-enrollment/next-year rollover

7. Documents List + Upload — detail tab + sheet

FieldDetail
PurposePer-student files: birth certificate, reports, transfers
SourceGET /students/:id/documents (createdAt desc — student.service.ts:273-279); upload POST /students/:id/documents multipart file + optional category
CompositionList of AppAttachmentTile (fileName, size, mimeType, category chip, uploadedBy label, createdAt); FAB/sheet + Upload: file picker + category AppDropdown (free values — no enum server-side; upload-student-document.dto.ts:4-9)
Statesempty → "No documents yet"; upload progress tile; error 400/5xx
a11yAttachment labels with type + size
NotesNo download endpoint returns bytes today (only fileId metadata; local provider exposes /api/v1/files/<tenant>/<uuid>--<name> path — OQ-9). Preview/Download actions are (planned).

8. Profile Photo Upload (identity) — via Users module

FieldDetail
PurposeStudent avatar (stored on the user record, not the student)
SourcePOST /users/:id/avatar multipart file (users.controller.ts:95-102) — surfaced inside student detail header (forward-looking)
Compositionavatar tap → picker → upload tile → header refresh
Statesuploading spinner; error snackbar
FieldDetail
PurposeManage guardians
SourceGET /parents/link/student/:studentId (list links — parent.controller.ts:53-57); create parent POST /parents (parent.controller.ts:29-31); link POST /parents/link/:studentId (parent.controller.ts:47-52); unlink DELETE /parents/link/:linkId (parent.controller.ts:58-60)
CompositionLink cards: parent name (via parent→user), relationship chip (mother/father/guardian/grandparent/relative/foster_parent), primary-guardian toggle badge, financial responsibility + pickup flags, emergency priority number
Link sheetPick existing parent (search) or create new (user + parent profile: occupation, company, annualIncome, relationshipNotes, pickupAuthorization — create-parent.dto.ts); required relationship selector; optional toggles
Guards409 duplicate parent-per-user; 404 unknown student; parent existence not checked at link time (OQ-11)

10. Graduate / Archive / Restore / Delete dialogs

FieldDetail
GraduateConfirm → POST /students/:id/graduate; 409 if already graduated; result: status badge graduated
ArchiveConfirm ("hidden from roster, can restore later") → POST /students/:id/archive; 409 if already archived
RestorePOST /students/:id/restore → status active; item reappears in list
DeleteConfirm ("removes from roster; academic records retained") → DELETE /students/:id soft delete (student.service.ts:157-168); 404 if already gone
a11yDialogs trap focus; destructive actions use error colors

Shared components used

AppSearchBar, AppChips, AppListTile, AppAvatar, AppBadge, AppTabs, AppSkeleton, AppEmptyState, AppErrorState, AppOfflineBanner, AppFAB, AppButton, AppDialog, AppBottomSheet, AppMenu, AppTextField, AppDropdown, AppDatePicker, AppSwitch, AppAttachmentTile, AppStepper, AppSnackbar, AppInfoRow, AppCard.

Module-specific components (07_Component_Library.md)

StudentListTile, StatusBadge, StudentHeader, AcademicHistoryTimeline, ImportReportCard, RelationshipChip, GuardianLinkCard.

Analytics events (proposed)

students.list.{view,search,filter,open}, students.create.{start,step,success,failure}, students.detail.{view,tab}, students.transfer.{submit,success,failure}, students.enroll.{submit,success,failure}, students.document.{upload,open}, students.import.{template,upload,report}, students.graduate/archive/restore/delete.{confirm,success}.