07 — Component Library (Students Module)
- 1. StudentListTile
- 2. StatusBadge
- 3. StudentHeader (detail)
- 4. AcademicHistoryTimeline
- 5. ImportReportCard
- 6. RelationshipChip
- 7. GuardianLinkCard
- 8. ClassSelectCascade (wizard helper)
- 9. DocumentUploadTile
- 10. RosterFilterBar
- Component reuse map
- Golden-test expectation
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 (
secondaryContainerindicator) / 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(bodySmallmono for the admission number), trailing =StatusBadge+ chevron/menu (AppMenu). - Tokens: list row ≥ 56 (00-shared/02 §9),
surface,e-0. - Flutter:
ListTilewrapped withMenuAnchor;Herotag =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):
| Status | Icon | Color role | Label |
|---|---|---|---|
active | ✓ check_circle (filled) | secondary/success | Active |
inactive | remove_circle_outline | onSurfaceVariant | Inactive |
graduated | school (filled) | tertiary | Graduated |
transferred | swap_horiz (filled) | onSurfaceVariant | Transferred |
archived | archive (filled) | error-muted / outline | Archived |
- Flutter:
Badge/customContainer+ icon;AppBadgewhen 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 withoutstudent.update/student.delete). - Layout: avatar 64 (
AppAvatar), nametitleLarge, admission numbermono,StatusBadge, chips for class path (Grade 6 · B · 2025-26), campus/house when set; action row:FilledButton.tonalEdit/Transfer (≤ 2), rest inAppMenu. - 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 sortedjoinedAt descbystudent.service.ts:281-287. - Node: year + class label (
titleMedium),joinedAt → leftAtdates (bodySmall,Intl-formatted),EnrollmentStatusbadge; connector lineoutlineVariant; active node dotprimary(pulsingm-fast), past nodesonSurfaceVariant. - States: empty → inline
AppEmptyStatecompact ("No enrollment history yet — created students are auto-enrolled once"). - A11y: timeline as list semantics; each node one row.
- Flutter: custom
ColumnofRows withSemantics.
5. ImportReportCard
- Purpose: bulk-import result summary (from
ImportReport—import-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:
surfaceVariantfill,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" (filledprimaryContainer) whenisPrimaryGuardian, flag rows (financial responsibility ✓, pickup allowed ✓/✗, priority n) viaAppInfoRow, trailingAppMenu(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 — noteclass.repository/controller only index by year server-side;academics/controllers/class.controller.ts:30-33). - States: per-level loading skeleton (
AppSkeletonline), 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 toAppAttachmentTile) → 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
AppDropdownchips; count linebodySmall;Clearappears when any filter active. Client-side only until server filters land (OQ-8).
Component reuse map
| Shared component (00-shared/03) | Used by |
|---|---|
AppStepper | Create wizard, import wizard |
AppAttachmentTile | Documents tab |
AppTabs | Student detail |
AppStatTile | Import report |
AppSlider | Emergency priority (link sheet) |
AppBottomSheet | Enroll, upload, link-parent |
AppDialog | Graduate/Archive/Restore/Delete/Transfer confirm |
AppMenu | Row + header overflow actions |
Golden-test expectation
Each module component: phone/tablet/desktop + dark mode (00-shared/03 G, 00-shared/10 §9).