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

04 — Information Architecture (Students Module)

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

ScreenAppBarFAB / primary CTARow actions (AppMenu)Swipe (phone)
ListTitle "Students", global search icon+ Add student (student.create)View / Edit / Transfer / Archive / Restore / Delete— (roster rows have menu instead)
ImportTitle, close"Import CSV" (submit)
DetailStudent name, back, overflow (Edit, Transfer, Graduate, Archive, Delete)Contextual per tabTab-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 (q ignored 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)BadgeMeaningSource
active✓ successCurrent enrollment ACTIVEstudent.schema.ts:8
inactiveneutralEnum exists; no writer today (OQ-6)student.schema.ts:9
graduatedtertiaryCompleted; guarded 409student.schema.ts:10
transferredneutralEnum exists; enrollment rows use it, student status doesn'tstudent.schema.ts:11
archivederror-ish (muted)Withdrawn/left; restorablestudent.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

ConcernOwnerNote
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 tabsThose modulesForward-looking; read APIs from those modules
Global search/search (00-shared/05 §3)GET /api/v1/search?q= (planned) (docs/IMPLEMENTATION_PLAN.md:174)
Notificationsin-app queueStudentCreated/ParentCreated → notification docs (event-queue-map.ts:28,37)