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

07 — Component Library (Students Module)

Module-specific widgets built on 00-shared/03 components. Each entry: purpose, props, states, tokens, Flutter mapping. Analytics events follow the (proposed) convention from 00-shared/10 §8.


1. StudentListTile

  • Purpose: roster row (list screen + master-detail pane).
  • Props: student (id, admissionNumber, rollNumber, status, class/grade/section labels, academicYear label), user (name, avatarUrl), onTap, menuItems, selected (desktop master-detail).
  • States: default / selected (secondaryContainer indicator) / disabled (none needed — no student is un-openable).
  • Layout: avatar 40 (AppAvatar, fallback initials), title = display name (titleMedium), subtitle = ADM-2026-0142 · Grade 6 · B (bodySmall mono for the admission number), trailing = StatusBadge + chevron/menu (AppMenu).
  • Tokens: list row ≥ 56 (00-shared/02 §9), surface, e-0.
  • Flutter: ListTile wrapped with MenuAnchor; Hero tag = student-{id} (avatar → detail header, 00-shared/08 §4).
  • A11y: combined label "Meera Sharma, active, Grade 6 B"; row tap semantics.

2. StatusBadge

  • Purpose: student + enrollment status chips; never color-only (00-shared/09 §9).
  • Props: status (StudentStatus | EnrollmentStatus), size (sm 20 / md 24).
  • Mapping (from student.schema.ts:7-13, class-enrollment.schema.ts:7-11):
StatusIconColor roleLabel
active✓ check_circle (filled)secondary/successActive
inactiveremove_circle_outlineonSurfaceVariantInactive
graduatedschool (filled)tertiaryGraduated
transferredswap_horiz (filled)onSurfaceVariantTransferred
archivedarchive (filled)error-muted / outlineArchived
  • Flutter: Badge/custom Container + icon; AppBadge when icon+text too wide.

3. StudentHeader (detail)

  • Purpose: detail hero block (avatar, identity, class path, status, actions).
  • Props: student, user, status, onEdit, onTransfer, onGraduate, onArchive, onRestore, onDelete, permissions (hide actions without student.update/student.delete).
  • Layout: avatar 64 (AppAvatar), name titleLarge, admission number mono, StatusBadge, chips for class path (Grade 6 · B · 2025-26), campus/house when set; action row: FilledButton.tonal Edit/Transfer (≤ 2), rest in AppMenu.
  • Tokens: surfaceContainerLow, e-1, r-lg, padding 16.
  • Flutter: Card.filled + Row/Wrap (wraps on phone width).

4. AcademicHistoryTimeline

  • Purpose: enrollment history visualization (append-only trail, COLLECTIONS.md:1674-1677).
  • Props: enrollments[] (classId, academicYearId, rollNumber, joinedAt, leftAt, status) — already sorted joinedAt desc by student.service.ts:281-287.
  • Node: year + class label (titleMedium), joinedAt → leftAt dates (bodySmall, Intl-formatted), EnrollmentStatus badge; connector line outlineVariant; active node dot primary (pulsing m-fast), past nodes onSurfaceVariant.
  • States: empty → inline AppEmptyState compact ("No enrollment history yet — created students are auto-enrolled once").
  • A11y: timeline as list semantics; each node one row.
  • Flutter: custom Column of Rows with Semantics.

5. ImportReportCard

  • Purpose: bulk-import result summary (from ImportReportimport-adapter.interface.ts:14-25).
  • Props: report {entity, totalRows, imported, failed, errors[]}.
  • Layout: 3 AppStatTile (Imported / Failed / Total) + collapsible failed-rows list: "Row 42 — Missing required column "section"." (each error string verbatim from server).
  • States: all-imported (success tint), partial (warning tint), all-failed (error tint) — icons + text, not color alone.
  • A11y: live-region announces "997 of 1000 imported".

6. RelationshipChip

  • Purpose: parent-link relationship tag (enum values).
  • Props: relationship — mother | father | guardian | grandparent | relative | foster_parent (student-parent-link.schema.ts:7-14).
  • Rendering: InputChip-style with icon (mother = female icon, father = male, guardian = shield, grandparent = family, relative = link, foster_parent = home); text label capitalized.
  • Tokens: surfaceVariant fill, labelMedium.

7. GuardianLinkCard

  • Purpose: one linked parent (Parents tab).
  • Props: link {parentId, relationship, isPrimaryGuardian, financialResponsibility, pickupAllowed, emergencyPriority} + parent/user names + onUnlink, onEditLink (planned) — no link-update endpoint exists; edit = unlink + re-link.
  • Layout: AppCard: avatar + name (titleMedium), RelationshipChip, badge "Primary guardian" (filled primaryContainer) when isPrimaryGuardian, flag rows (financial responsibility ✓, pickup allowed ✓/✗, priority n) via AppInfoRow, trailing AppMenu (Unlink).
  • Empty: "No guardians linked — add a parent or guardian".

8. ClassSelectCascade (wizard helper)

  • Purpose: year → grade → section → class cascading pickers with capacity info.
  • Props: onChanged(ClassSelection{academicYearId, gradeId, sectionId, classId}), initial?, loading.
  • Sources (called through repository): GET /academic-years, GET /grades, GET /sections/by-grade/:gradeId, GET /classes/by-year/:academicYearId (and client-side grade+section filtering for class list when by-year is coarse — note class.repository/controller only index by year server-side; academics/controllers/class.controller.ts:30-33).
  • States: per-level loading skeleton (AppSkeleton line), empty level ("No classes yet for this year — set up Academics first").
  • Tokens: stacked AppDropdowns, gap 16.

9. DocumentUploadTile

  • Purpose: upload-in-progress/error attachment row.
  • Props: fileName, size, progress?, error?, onRetry, onCancel.
  • States: queued → uploading (determinate LinearProgressIndicator) → done (swap to AppAttachmentTile) → error (error text + Retry/Cancel).
  • A11y: progress announced via live region.

10. RosterFilterBar

  • Purpose: status/class/year filter chips + result count.
  • Props: statusFilter, classFilter, yearFilter, onChanged, resultCount, loading.
  • Behaviour: status chips fixed set (All/Active/Graduated/Archived); class/year are AppDropdown chips; count line bodySmall; Clear appears when any filter active. Client-side only until server filters land (OQ-8).

Component reuse map

Shared component (00-shared/03)Used by
AppStepperCreate wizard, import wizard
AppAttachmentTileDocuments tab
AppTabsStudent detail
AppStatTileImport report
AppSliderEmergency priority (link sheet)
AppBottomSheetEnroll, upload, link-parent
AppDialogGraduate/Archive/Restore/Delete/Transfer confirm
AppMenuRow + header overflow actions

Golden-test expectation

Each module component: phone/tablet/desktop + dark mode (00-shared/03 G, 00-shared/10 §9).