3.8 KiB
2026-07-17 — Class Room page (P1-UX-7 / PDF §9) + VC/VM buttons
Branch: feat/p1-ux-7-course-scheme
Worktree: .worktrees/feat-p1-ux-7-course-scheme
Why
PDF §9 (Academic Structure / Course Scheme/Plan) calls for a teacher/admin landing page with three sub-tabs and a "Virtual Class Room" button on every lesson row. PDF §3 (Virtual Classroom Integration) requires the same lesson to expose Google Meet / Google Classroom links.
The two are tightly coupled: the VC/VM button reads meet_url /
classroom_url, columns added in P1-UX-3 (feat/p1-ux-3-meet-classroom,
commit 64d6389). This PR lands the front-end slice plus a small
backend endpoint and a forward-port of the P1-UX-3 migration so dev
picks the columns up too.
Changes
server/src/database/migrations/knex/2026071700000009_lesson_plan_virtual_classroom.js (new)
Identical schema work as …0008_… (committed on
feat/p1-ux-3-meet-classroom), but forwarded onto dev so the
endpoint can query the columns today. Idempotent; same try/catch
swallow as 0008.
server/src/controllers/class-room.controller.js (new)
GET /api/class-room returns an aggregated payload the UI can render
without further joining:
{
"lesson_plans": [...],
"total": 12,
"with_live_link": 4,
"upcoming": 2
}
Visibility: privileged (admin / principal / systems_admin) sees every
row; teachers see rows they authored; student / parent return an empty
list with a "not yet exposed" note (the student-facing scope is a
follow-up that needs the parent's parent_students join — not in this
slice).
server/src/index.js (modified)
Mounts the new controller at /api/class-room.
client/src/pages/teacher/ClassRoom.tsx (new)
Three sub-tabs per PDF §9:
- Learning Material — every lesson plan with date / subject /
teacher. Renders a "VC" button (Google Meet) if
meet_urlis set, a "VM" button (Google Classroom) ifclassroom_urlis set. Color-coded matching the action. - Export Scheme —
Export Schemebutton (wasExport Intel) downloads a CSV of all rows in the active filter. - Learner — placeholder summary pointing to the parent-side AcademicProgress page for the full subject-scoped rollup.
Header shows three stat cards: Total / With Live Link / Upcoming.
client/src/App.tsx (modified)
Adds /class-room route under both principal and teacher arms of
the role-aware getRoutes() map. Gated via <ProtectedRoute allowedRoles={…}>.
client/src/components/Nav.tsx (modified)
Adds a "Class Room" entry to both the teacher and principal nav
entries (label is terminology-aware via the new T.PAGE_TITLE_COURSE_SCHEME
key in lib/terminology.ts).
server/scripts/smoke-class-room.js (new)
Smoke-test script that boots the server, calls /api/class-room as
admin and as student, and prints status + body. Run via
node scripts/smoke-class-room.js.
Out of scope (follow-ups)
- Student-side "Join Class" surface (
/api/class-roomis gated to teacher/principal for this slice). The map of student → subjects already exists viaenrollments; surfacing it is a small follow-up. - Per-learner rollup (the "Learner" tab content). Currently a
placeholder pointing to
AcademicProgress.tsx. - Tier-aware switch (PDF §5).
ACTIVE_TIERinlib/terminology.tsis hard-codedtertiary; tier wiring is P1-UX-5b's other half.
Verification
node src/database/init.jsagainst a fresh DB → 8 + 1 migrations applied, demo users seeded, bothmeet_urlandclassroom_urlcolumns present.node scripts/smoke-class-room.js→ admin 200 (empty rows), student 200 (note returned).npx vitest run→ 2 / 3 spec files green; 1 pre-existing failure inpaynow-webhook.test.js("paid_amount undefined" — same failure as ondev, not caused by this PR).