04 — Information Architecture (Students Module)
- 1. Placement in the app shell
- 2. Route tree
- 3. Screen relationships (master-detail)
- 4. Navigation & actions per screen
- 5. Filter & search model (list screen)
- 6. Status vocabulary (UI badge set)
- 7. Cross-cutting ownership
Where Students lives in the global shell (00-shared/05) and how its screens nest. Routes are module-level; base shell routes in 00-shared/05 §4. Permission-gating per 00-shared/05 §9 (client mirrors
permissions.constants.ts; server enforcement(planned)— 01 §12 OQ-4).
1. Placement in the app shell
AppShell
├─ NavigationBar / Rail / Drawer
│ └─ "Students" destination (route prefix /students)
│ roles (default): admin, staff, teacher (read) — 00-shared/05 §2
│
└─ /students branch (StatefulShellBranch — tab state survives nav switches)
- Hidden for parent/student roles today (they have no
student.read; self-view is(planned)OQ-1). If a parent/student opens a shared student link, the guard rejects → 403 screen (00-shared/05 §8).
2. Route tree
/students Students list (roster)
├── /students/add Create wizard (user identity → academic → extras → confirm)
├── /students/import Bulk CSV import wizard (+ result report)
├── /students/:id Student detail (tabs)
│ ├── profile Tab: profile summary + identity + status actions
│ ├── attendance Tab: (other module — forward-looking read)
│ ├── fees Tab: (other module — forward-looking read)
│ ├── results Tab: (other module — forward-looking read)
│ ├── documents Tab: document list + upload
│ └── history Tab: academic history + enrollments (server-backed)
├── /students/:id/edit Profile edit form (PATCH)
├── /students/:id/transfer Transfer form (POST transfer)
└── (sheets, not routes)
├── Enroll form (POST /students/:id/enroll)
├── Link parent (POST /parents + /parents/link/:studentId)
├── Document upload (multipart POST documents)
├── Graduate / Archive / Restore / Delete confirmations
└── Status filter sheet (client-side filter — OQ-8)
Deep links (per 00-shared/05 §4 convention):
studylyon://students/:id → detail (documents tab); studylyon://students/:id/documents/:docId (planned) — requires file-serving endpoint (OQ-9).
3. Screen relationships (master-detail)
List ──tap──► Detail ──actions──► Forms (edit/transfer/enroll/graduate/archive)
│ │
│ └──► Documents (tab) ──upload──► confirm → tab refresh
│ └──► Parents (tab) ──link sheet──► refresh
│ └──► History (tab) — server-sorted joinedAt desc
└──menu──► Import wizard ──result report──► back to list (refresh)
- Phone: push-on-top; tablet/desktop ≥ 840 dp: master-detail two-pane
(
00-shared/05 §6,00-shared/04 §6). - Edit/transfer return to detail with refresh (never a stale cache).
- List refresh after: create (new student first page), import (count delta), restore (item reappears), delete (item leaves).
4. Navigation & actions per screen
| Screen | AppBar | FAB / primary CTA | Row actions (AppMenu) | Swipe (phone) |
|---|---|---|---|---|
| List | Title "Students", global search icon | + Add student (student.create) | View / Edit / Transfer / Archive / Restore / Delete | — (roster rows have menu instead) |
| Import | Title, close | "Import CSV" (submit) | — | — |
| Detail | Student name, back, overflow (Edit, Transfer, Graduate, Archive, Delete) | Contextual per tab | Tab-dependent | — |
| Documents tab | — | + Upload (file.upload+student.update) | Preview (planned) / Remove (planned) | delete (planned) |
Rule: destructive/irreversible actions need AppDialog confirm (00-shared/05 §5):
Delete (typed confirm not needed — soft delete), Graduate, Archive. Transfer is
reversible via re-transfer but confirm anyway (state change).
5. Filter & search model (list screen)
- Search box (
AppSearchBar): client filters loaded page(s) by name/ admission number/roll number today (qignored server-side — OQ-2). - Filter chips (client-side until server filters land):
- Status: all / active / graduated / archived (+ inactive/transferred when data exists — OQ-6)
- Class: pick class → filter
- Academic year
- Sort (client-side on loaded page): name, admission number, admission date.
- Empty states: no students ("Add your first student"), no results for filters ("No students match — clear filters").
6. Status vocabulary (UI badge set)
| Status (enum) | Badge | Meaning | Source |
|---|---|---|---|
active | ✓ success | Current enrollment ACTIVE | student.schema.ts:8 |
inactive | neutral | Enum exists; no writer today (OQ-6) | student.schema.ts:9 |
graduated | tertiary | Completed; guarded 409 | student.schema.ts:10 |
transferred | neutral | Enum exists; enrollment rows use it, student status doesn't | student.schema.ts:11 |
archived | error-ish (muted) | Withdrawn/left; restorable | student.schema.ts:12 |
Enrollment statuses (class-enrollment.schema.ts:7-11): active, inactive,
transferred — shown inside history tab timeline, not as student badge.
7. Cross-cutting ownership
| Concern | Owner | Note |
|---|---|---|
| Identity fields (name, email, phone, avatar) | Users module (/users/:id) | Students UI reads via linked userId; avatar edit → POST /users/:id/avatar (users.controller.ts:95-102) |
| Attendance / fees / results tabs | Those modules | Forward-looking; read APIs from those modules |
| Global search | /search (00-shared/05 §3) | GET /api/v1/search?q= (planned) (docs/IMPLEMENTATION_PLAN.md:174) |
| Notifications | in-app queue | StudentCreated/ParentCreated → notification docs (event-queue-map.ts:28,37) |