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

Module-specific components; all reference the shared library (00-shared/03). Every component listed here is a client composition over the implemented API — none invent backend data.


A. Module components

TimetableGrid (hero component, extends AppScrollContainer)

  • Props: entries: List<TimetableEntry>, scope (class | teacher | room), catalogs (subject/teacher/room/class name resolvers), readOnly, onCellTap, onSlotTap, onSlotDrag (desktop), onLongPress.
  • Structure: pinned TimeGutter (left) + 6 DayHeaders (Mon–Sat, from DayOfWeek, timetable.schema.ts:7-14) + row of GridCells; rows = union of startTime values, ascending (server sort contract, timetable.service.ts:51,58).
  • Slot placement: absolute-positioned TimetableSlotCards within each day column, height ∝ endTime − startTime (min 1 row).
  • Perf: row/cell widgets built via ListView.builder-style lazy construction; RepaintBoundary per slot card; scroll controllers shared gutter/header (15 §9).
  • Desktop: each slot wrapped in Draggable<TimetableEntry>; cells are DragTarget<TimetableEntry> (10 §2, 15 §8). Mobile: LongPressDraggable off — long-press opens actions instead.
  • A11y: single Semantics node per slot with full label (06 §S1).

TimetableSlotCard (extends AppCard)

  • Props: entry (subjectLabel, teacherLabel, roomLabel, startTime, endTime), conflict: bool (client-derived same-grid overlap), onTap, onLongPress, onDrag (desktop).
  • Content: {subject shortName} (titleSmall), {teacher} · {room} (bodySmall); conflict → errorContainer border + event_busy icon + label "double-booked".
  • Room label shows room code (room.schema.ts:21-22) — absent room → no chip.
  • Tokens: surfaceContainerLow, e-1; hover (desktop) e-2 + tint (10 §2).

GridCell

  • Props: day, startTime, endTime implicit row, onTap (empty cell → S4), acceptsDrop (desktop).
  • Empty state: dashed outline (outlineVariant), subtle hover fill.
  • A11y: cell tap targets ≥ 44 dp; announced "Monday, 08:00, free".

DayHeader / TimeGutter

  • DayHeader: day label (short) + date-of-current-week (client computed); pinned top, sticky.
  • TimeGutter: HH:MM labels (mono, tabularFigures, 02 §2), pinned left.
  • A11y: announced as column/row headers (06 §S1).

ConflictBanner (extends AppBanner)

  • Props: kind: teacher | room, clash (day, startTime, endTime, otherPartyLabel), onDismiss, onChangeTime.
  • Copy: "Teacher already booked Tue 08:00–09:00 (R. Kumar)" / "Room already in use Tue 08:00–09:00 (Room 201)".
  • Tokens: errorContainer/onErrorContainer (11 §2); live-region announce.

WeekNavigator

  • Props: offset, onPrev, onNext, onToday.
  • Content: [Week of {date} · {day range}] + "Today" text button.
  • Pure client state (06 §S6); disabled state: none (always navigable).
  • Keyboard: ←/→ when grid focused (desktop).

EntryEditorSheet (extends AppBottomSheet / AppDialog)

  • Props: prefill (classId, day, startTime, endTime, subject/teacher/room from duplicate), onSaved(entry), onConflict(409).
  • Hosts the F1 form (08) + ConflictBanner; phone sheet r-xl top corners, desktop dialog e-4.

SlotDetailPopover (extends AppMenu surface)

  • Props: entry with resolved names; actions: Duplicate (→ editor), Edit/Delete (planned) — hidden unless timetable.create + coordinator role.

TeacherScheduleTab (composition)

  • Wraps TimetableGrid with teacher scope (GET /timetable?teacherId=, timetable.controller.ts:26) in the Teachers module's detail screen — same grid, read-only for teacher role.

B. Reused shared components

Shared componentUsed in
AppDropdownscope picker (S1/S2), class/day/year pickers (S4)
AppSearchBar-style searchable pickersubject/teacher/room pickers (S4)
AppSkeletongrid loading ghost
AppEmptyStateS1/S2/S3 empty
AppErrorStategrid/editor errors (envelope code)
AppBanneroffline reads, conflict (via ConflictBanner)
AppSnackbar"Slot added", "Slot removed (planned)"
AppFABS1 "Add slot" (hidden for teacher role)
AppButtonSave (filled), Cancel (text), Change time (outlined)
AppTimePicker-style fieldstart/end time inputs (S4)
AppChipsday picker quick-select (S4)
AppOfflineBannermodule-wide offline reads
AppSectionHeadergrid section titles, editor groups
AppDialogdestructive confirm (planned), editor on desktop

C. Composition rules

  1. No screen renders raw ObjectIds — every ref resolved via catalogs (subjects, teachers, rooms, classes, years) or "—" fallback (04 §6).
  2. DayOfWeek display order fixed: Mon→Sat (timetable.schema.ts:7-14) — never locale-reordered.
  3. Conflict styling centralized in TimetableSlotCard/ConflictBanner — no ad-hoc red in screens (11 §2).
  4. Times rendered mono + tabularFigures everywhere (gutter, cards, editor).
  5. New components only if shared library lacks the need (YAGNI — check shared first).
  6. Golden tests: TimetableSlotCard (normal/conflict × light/dark), TimeGutter, ConflictBanner, WeekNavigator (00-shared/03 §G).