07 — Component Library (Academics Module)
- 1. HierarchyTree
- 2. SectionChips
- 3. SubjectAssignmentRow
- 4. YearStatusBadge
- 5. EntityStatusBadge (grade/section/class)
- 6. CascadingEntityPicker
- 7. MarksSummaryTiles
- 8. RosterTable (desktop)
- 9. ConflictBanner
- 10. ReferenceCacheProvider (infra widget)
Module-specific widgets. Shared components (00-shared/03) are referenced by name and used as-is (AppCard, AppListTile, AppDropdown, AppChips, AppSearchBar, AppSkeleton, AppEmptyState, AppErrorState, AppBanner, AppSnackbar, AppDialog, AppDatePicker, AppTextField, AppMenu, AppFAB). All names are proposals; the server API facts they bind to are exact.
1. HierarchyTree
- Purpose: renders the Year → Grade → Section → Class structure as an expandable tree (Hierarchy Explorer, 06 §12).
- Props:
nodes(nested model: year → grades → sections → classes),expandedIds: Set<String>,onToggle(id),onNodeTap(node),onNodeMenu(node)(admin),highlightEmpty: bool. - States: loading (
AppSkeletonrows), empty (rootAppEmptyState), error (AppErrorState), expanded/collapsed. - Row anatomy: leading expand icon (rotates
Mo.m-fast), depth indentSp.8× level, entity icon by type, titleTy.titleMedium, subtitle countsbodySmall onSurfaceVariant("4 sections · 8 classes"), trailing status badge / warning dot (empty node,tertiary). - A11y: each node
Semantics(expanded:)+ label "Grade 10, expanded, 4 sections"; keyboard arrows expand/collapse (00-shared/09 §6). - Tokens: node hover
surfaceVariant; expanded child indent guidesoutlineVariant1 px; Flutter: customExpansionTile-freeAnimatedSizelist (keep N expanded ≤ 50 for perf, lazy-load children).
2. SectionChips
- Purpose: dense list of a grade's sections (Grade detail, 06 §4; class rows).
- Props:
sections[{id,name,capacity,status}],selectedId?,onTap,maxChips(default 8 → "+N more"). - States: default, selected (
secondaryContainer), inactive (status≠'active'→ outlined + strike-through name, OQ-2), overflow. - A11y:
Semantics(label: "Section A, capacity 40"); chip group labeled. - Flutter:
WrapofFilterChip/InputChip(00-shared/04 §2).
3. SubjectAssignmentRow
- Purpose: one row in the roster / matrix (06 §6, §11).
- Props:
subject{code,name,shortName},teacherName,conflict: bool,onReplace,onRemove,readOnly. - Anatomy: leading subject avatar (
Icons.menu_book,primaryContainerwhen conflict →tertiaryContainer), titlesubject.name, subtitlecodemono+ shortName, trailing teacher namebodyMedium+AppMenu(Replace/Remove). - Conflict state:
tertiaryleft border 2 px + warning icon + tooltip "Assigned more than once" (client-computed — server allows duplicates, OQ-4). - A11y: whole row one
Semantics(button)with subject + teacher label. - Flutter:
ListTile-custom withAnimatedContainerborder.
4. YearStatusBadge
- Purpose: status chip for
academic_years. - Props:
status: 'upcoming'|'active'|'archived',isCurrent: bool. - Mapping:
upcoming→ outline chip;active→ tonal (primaryContainer);archived→ neutral (surfaceVariant);isCurrentadds a filledprimarypill "Current" (rendered separately, see 06 §1). - Source: enum
AcademicYearStatus(academic-year.schema.ts:7-11).
5. EntityStatusBadge (grade/section/class)
- Purpose: status chip for entities whose schema status is a free string
(default
'active'). - Behaviour:
status === 'active'→ no badge (default, reduces noise) or subtle tonal "Active"; anything else → neutral "Archived"-style badge with the raw value truncated. - Source:
grade.schema.ts:21-22,section.schema.ts:24-25,class.schema.ts:33-34; flag OQ-2 (01).
6. CascadingEntityPicker
- Purpose: Year → Grade → Section cascading dropdown group (Class create form, 06 §5; also Grade → Section standalone).
- Props:
scopedYearId,onYearChanged,grades,sections(already filtered by the parent selection via the ReferenceRepository, 13 §2),onChanged(selection),errorText,readOnly. - Behaviour: parent cleared → children reset + disabled; selections emit a
complete
{academicYearId, gradeId, sectionId}or null. - A11y: group
Semantics; each dropdown role "combobox"; errors announced. - Flutter: three
AppDropdownin a column withAnimatedSizevalidation reveal.
7. MarksSummaryTiles
- Purpose: read-only 4-tile grid for subject marks config (Subject detail, 06 §10).
- Props:
maximumMarks, passingMarks, theoryMarks, practicalMarks. - Anatomy: 2×2 grid of
AppCard-tiles; labellabelMedium, numeralTy.titleMediumwithtabularFigures(). - A11y: one group labeled "Marks configuration", each value announced.
- Source:
subject.schema.ts:21-32defaults (100/33/80/20).
8. RosterTable (desktop)
- Purpose: table variant of the assignment matrix ≥ 840 dp (06 §6).
- Props:
rows,onReplace,onRemove,readOnly. - Anatomy: columns Subject | Code | Teacher | Actions; sticky header
(
surfaceContainerLow), column headers announced; row hoversurfaceVariant. - Flutter:
DataTablewrapped withScrollbar(always visible on desktop, 00-shared/04 §8).
9. ConflictBanner
- Purpose: inline warning for client-computed conflicts (duplicate assignment, empty class, orphan node).
- Props:
kind: duplicate|empty|orphan|teacherOverlap,message,action?. - Tokens:
tertiaryContainerbackground,onTertiaryContainertext,r-md; slide-down + fadeMo.m-base; live region. - Flutter: custom
MaterialBanner-styleAnimatedContainer(notAppBannererror— conflicts are warnings, not errors).
10. ReferenceCacheProvider (infra widget)
- Purpose: top-of-tree widget exposing the 24 h reference cache (13 §3) to all Academics screens.
- Props:
tenantId,yearIdscope; exposesyears, grades, sections, classes, subjects+refreshAll(). - Behaviour: invisible; drives
RefreshIndicatorcascades; emitsReferenceChangedso timetable/attendance screens re-fetch their scoped lists (13 §5). - Flutter:
InheritedNotifierover theReferenceCubit.
Dependency map
| Component | Shared base | Data source |
|---|---|---|
| HierarchyTree | AppCard, AppSkeleton | 4 list endpoints (client join) |
| SectionChips | AppChips | GET /sections/by-grade/:gradeId |
| SubjectAssignmentRow | AppListTile, AppMenu | GET /subject-assignments/by-class/:c?academicYearId= |
| YearStatusBadge | AppChips | GET /academic-years |
| EntityStatusBadge | AppChips | GET /grades / /sections / /classes |
| CascadingEntityPicker | AppDropdown | year/grades/sections lists |
| MarksSummaryTiles | AppCard | GET /subjects/:id |
| RosterTable | DataTable | roster endpoint |
| ConflictBanner | AppBanner pattern | client-computed |