04 — Information Architecture (Timetable Module)
- 1. Placement in the app shell
- 2. Sitemap (client, this module)
- 3. Screen relationships
- 4. Navigation details
- 5. Role → surface gating (client-side; server enforces JWT only —
01 §5) - 6. Information units (primary entities)
- 7. Empty-state strategy
Fits the shared shell (00-shared/05_Global_Information_Architecture.md). Timetable is a scheduling workspace over the Academics/Teachers/Rooms catalogs: routes live under an
Academics/Schedulingdestination, keyed by the entity being scheduled (class, teacher, room).
1. Placement in the app shell
- Primary home:
Academicsdestination (admin/coordinator role-gated) — the class grid is the entry surface, per-class navigation following the 05_Global_Information_Architecture.md:39 shell pattern. - Teacher schedule: secondary entry from Teacher detail · Schedule tab
(
GET /timetable?teacherId=,timetable.controller.ts:26); and a teacher-self "My Schedule" surface(planned). - Room schedule: tertiary, from Rooms module detail — client composition today,
native
(planned)(see 03 §J5). - Cross-links: Academics (classes, subjects, academic years), Teachers,
Rooms, Notifications (
TimetableEntryCreatedconsumers(planned)).
2. Sitemap (client, this module)
Academics (Scheduling)
└── Timetable
├── Class Timetable /academics/timetable?classId=:id
│ ├── Week view (default, current week)
│ ├── Today view (filtered)
│ └── Entry Editor (sheet/dialog) → /academics/timetable/new
├── Teacher Timetable /academics/timetable/teacher/:teacherId
│ └── (same grid, read-only for teacher role)
└── Room Timetable (planned) /academics/timetable/room/:roomId
Teacher (self)
└── My Schedule (planned) /my/schedule
Rooms
└── Room Detail → Schedule tab /rooms/:id/schedule
3. Screen relationships
- Class grid → tap empty slot → Entry Editor (bottom sheet on phone, dialog on
desktop); tap filled slot → detail popover → "Edit"
(planned)/ "Duplicate". - Class grid ↔ Teacher grid: switch scope via segmented control on the same route —
data source swaps between
?classId=and?teacherId=(timetable.controller.ts:23-26). - Grid → Room view: open from a slot's room chip (composed view,
03 §J5). - Conflict banner is inline state of the editor, not a page.
4. Navigation details
| Aspect | Rule |
|---|---|
| Route params | classId / teacherId / roomId = Mongo ObjectIds (timetable.schema.ts:18-28) |
| Grid scope state | current scope (class | teacher | room) + week offset — kept in shell state across navigation (05 §3) |
| Deep link | studylyon://timetable?classId=:id; studylyon://timetable/teacher/:teacherId |
| Breadcrumbs | desktop: Academics › Timetable › {Class name} |
| FAB | Grid screens: "Add slot" opens the editor prefilled with the current class |
| Context menu | Slot long-press / right-click: Edit (planned), Duplicate, Delete (planned) |
| Keyboard | + / N = new slot; ←/→ = week navigation (desktop) |
5. Role → surface gating (client-side; server enforces JWT only — 01 §5)
| Surface | org_admin / coordinator (timetable.create) | teacher (self) | read-only staff |
|---|---|---|---|
| Class grid | full | — | read (timetable.read, permissions.constants.ts:44) |
| Teacher grid | full | own only (planned) | read |
Room view (planned) | full | — | read |
| Entry editor (create) | full | — | — |
Edit / delete (planned) | full | — | — |
Server reality: no endpoint carries RBAC metadata (timetable.controller.ts:9);
cross-tenant access is still structurally blocked via scopedFilter
(base.repository.ts:20-30).
6. Information units (primary entities)
| Entity | Collection | Key fields | Client model |
|---|---|---|---|
| TimetableEntry | timetable_entries | classId, subjectId, teacherId, roomId?, dayOfWeek, startTime, endTime, academicYearId | TimetableEntry |
| Class (ref) | classes | name, gradeId, sectionId, academicYearId, roomId (string, distinct) | Class (reference) |
| Subject (ref) | subjects | code, name, shortName | Subject (reference) |
| Teacher (ref) | teachers | employeeNumber, employmentStatus, userId | Teacher (reference) |
| Room (ref) | rooms | name, code, capacity, type | Room (reference) |
| AcademicYear (ref) | academic_years | name, startDate, endDate, isCurrent | AcademicYear (reference) |
Server returns raw ObjectIds for all refs (timetable.schema.ts:18-40) — the
client joins names from catalogs; missing ref id renders "—".
7. Empty-state strategy
- Grid with zero entries →
AppEmptyState"No classes scheduled" + CTA "Add slot". - Single empty day → dashed empty cells, no empty-state page.
- Teacher grid empty → "No classes scheduled" (no editor CTA for teacher role).
- Room view with no matches → "No bookings for this room".
- Conflict submit → inline
AppBanner, never an empty state.