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

Module-specific components built from shared App* primitives (00-shared/03). Props, states, animation, a11y, tokens, Flutter mapping. Reuse over new — only components missing from the shared library are defined here. All data fields map exactly to parent.schema.ts, student-parent-link.schema.ts, and user.schema.ts.


1. ParentListTile (list row)

FieldSpec
PurposeOne guardian in /parents list
PropsparentId, displayName, photoUrl?, occupation?, company?, linkedChildrenCount, onTap, menu?
RenderingAppListTile 56+: AppAvatar 40 leading; title = displayName titleMedium (from joined User); subtitle = "occupation · company" bodyMedium onSurfaceVariant (omit empties); trailing = AppBadge "N children" primaryContainer + AppMenu (Open, Edit)
Data sourceGET /parents + GET /parents/:id/students count (client join, OQ-10); children count can be lazy-fetched on demand
a11ySemantics(button, label: "Guardian <name>, <N> linked children")
Tokenssurface, e-0; avatar initials primaryContainer
FlutterListTile in Card(elevation: 0) with MenuAnchor

2. LinkedChildCard (detail-screen child row)

FieldSpec
PurposeOne parent–student link with relationship metadata
Propslink (link doc: relationship, isPrimaryGuardian, financialResponsibility, pickupAllowed, emergencyPriority, student-parent-link.schema.ts:18-40), student (joined or loading), onTap (→ student detail), onUnlink, onSetPrimary, isPrimaryWarning
RenderingAppCard e-1: leading AppAvatar (student), title student name + mono admissionNumber, subtitle line: RelationshipChip + PrimaryGuardianBadge; meta line bodySmall: "Pickup ✓/✗ · Financial ✓/✗ · Priority N" (icon+label, never color-only — 09 §9); trailing AppMenu: "Set as primary" (when not primary), "Unlink"
Statesstudent == null → inline AppSkeleton; student 404 → "Student no longer active" (bodySmall warning) with unlink still available
a11ycard button + menu; badge text "Primary guardian"
FlutterCard + ListTile + Row of Icon+Text; MenuAnchor

3. RelationshipChip

FieldSpec
PurposeDisplay the relationship enum value
Propsrelationship (one of `mother
Mappinglabel = i18n key parent.relationship.<value>; icon map: mother → female, father → male, guardian → verified_user, grandparent → elderly, relative → family_restroom, foster_parent → home_work
TokensAppChips read-only: surfaceVariant fill, labelMedium
a11ychip label + tooltip; Semantics(label: "Relationship: <value>")
FlutterChip (disabled) with Icon

4. PrimaryGuardianBadge

FieldSpec
PurposeMark isPrimaryGuardian === true per link
Propsactive (bool), compact?
Renderingactive → AppBadge tonal primaryContainer, icon star + "Primary"; inactive → nothing (or outline "Secondary" only in edit contexts)
a11yannounced "Primary guardian" when active
FlutterBadge/custom Container with Icon + Text

5. EmergencyFlagsRow (detail meta line)

FieldSpec
PurposeCompact per-link flags line: pickup, financial responsibility, priority
PropspickupAllowed, financialResponsibility, emergencyPriority
RenderingRow of icon+label pairs bodySmall: directions_walk ✓/✗ "Pickup", payments "Billing" (only when true), priority_high "N" (when > 0)
a11yeach pair Semantics(label); never color-only (icon + text) — 09 §9
FlutterWrap of Icon+Text

6. UserPickerField (create-form)

FieldSpec
PurposeChoose the User a new parent profile belongs to
PropsselectedUser?, onPicked(UserSummary), enabled
Behaviorread-only AppTextField + Icons.search → opens AppBottomSheet with AppSearchBar over GET /users?q= (users.service.ts:90-115); results = displayName + email; picking pops sheet with the user; clear button available
a11yfield label "User account"; results list live region
FlutterInkWell field + showModalBottomSheet

7. ChildSwitcherBar

FieldSpec
PurposeMulti-child context switcher (parent zone, (forward-looking))
Propschildren[{id, name, avatarUrl?}], selectedId, onChanged(id), loading?
Renderinghorizontal scrollable row of avatar chips (40 dp, initials fallback); selected = primaryContainer fill + primary border; AppSkeleton when loading
Behaviortap → ChildSwitcherCubit.switchChild(id); announce "Switched to "; persist last child in memory (per session)
a11ychips Semantics(button, toggled: selected)
FlutterSingleChildScrollView + custom FilterChip-style widgets

8. GuardianSummaryCard (student-detail embed)

FieldSpec
PurposeGuardian list inside student detail (reverse direction)
Propslinks, parents (joined), onOpenParent(id), onAdd
RenderingAppCard section: per link — RelationshipChip + name + PrimaryGuardianBadge; TextButton "Add guardian" → link sheet; empty → "No guardians linked"
SourceGET /parents/link/student/:studentId (parent.controller.ts:53-57) + parent join
FlutterCard + ListTile rows

9. Flutter widget map (module)

ComponentFlutter widgets
ParentListTileListTile + CircleAvatar + Badge + MenuAnchor
LinkedChildCardCard + ListTile + Chip + MenuAnchor
RelationshipChipChip (disabled)
PrimaryGuardianBadgecustom Container + Icon
EmergencyFlagsRowWrap of Icon + Text
UserPickerFieldInkWell + TextField(readOnly) + bottom sheet
ChildSwitcherBarListView horizontal of chips
GuardianSummaryCardCard + ListTile

10. Golden tests (base 00-shared/03 G)

Every module component: golden at phone/tablet/desktop + dark mode; state permutations for LinkedChildCard (primary/not, pickup on/off, student-missing) and ChildSwitcherBar (1 child, 4 children, loading).