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 (Academics Module)

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 (AppSkeleton rows), empty (root AppEmptyState), error (AppErrorState), expanded/collapsed.
  • Row anatomy: leading expand icon (rotates Mo.m-fast), depth indent Sp.8 × level, entity icon by type, title Ty.titleMedium, subtitle counts bodySmall 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 guides outlineVariant 1 px; Flutter: custom ExpansionTile-free AnimatedSize list (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: Wrap of FilterChip/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, primaryContainer when conflict → tertiaryContainer), title subject.name, subtitle code mono + shortName, trailing teacher name bodyMedium + AppMenu (Replace/Remove).
  • Conflict state: tertiary left 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 with AnimatedContainer border.

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); isCurrent adds a filled primary pill "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 AppDropdown in a column with AnimatedSize validation 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; label labelMedium, numeral Ty.titleMedium with tabularFigures().
  • A11y: one group labeled "Marks configuration", each value announced.
  • Source: subject.schema.ts:21-32 defaults (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 hover surfaceVariant.
  • Flutter: DataTable wrapped with Scrollbar (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: tertiaryContainer background, onTertiaryContainer text, r-md; slide-down + fade Mo.m-base; live region.
  • Flutter: custom MaterialBanner-style AnimatedContainer (not AppBanner error — 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, yearId scope; exposes years, grades, sections, classes, subjects + refreshAll().
  • Behaviour: invisible; drives RefreshIndicator cascades; emits ReferenceChanged so timetable/attendance screens re-fetch their scoped lists (13 §5).
  • Flutter: InheritedNotifier over the ReferenceCubit.

Dependency map

ComponentShared baseData source
HierarchyTreeAppCard, AppSkeleton4 list endpoints (client join)
SectionChipsAppChipsGET /sections/by-grade/:gradeId
SubjectAssignmentRowAppListTile, AppMenuGET /subject-assignments/by-class/:c?academicYearId=
YearStatusBadgeAppChipsGET /academic-years
EntityStatusBadgeAppChipsGET /grades / /sections / /classes
CascadingEntityPickerAppDropdownyear/grades/sections lists
MarksSummaryTilesAppCardGET /subjects/:id
RosterTableDataTableroster endpoint
ConflictBannerAppBanner patternclient-computed