- Add /exam-marker/:templateId/marks route with MarkSchemePage
- Per-Part mark scheme editor: points/levels/parts/checklist/free forms
- SpecPoint picker via GET /api/exam/specs/{spec_code}/points (falls back to manual spec_ref when endpoint 404s)
- Manual neo4j-sync button; ASSESSES edge verified in cc.public.exams
- Edit marks button on each template card in dashboard
- Merge-resolved: AppRoutes, index.ts, exam.types.ts, ExamDashboardPage (kept grouped UI + added Edit marks button)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New examRepository (R2.1 seam): the only module talking to /api/exam (Supabase
JWT → Bearer, axios to API_BASE). list/get/create/archive templates.
- ExamDashboardPage (/exam-marker): lists institute templates, create dialog,
archive; cards link to setup (S4-9). Wrapped in ErrorBoundary (R6.4).
- exam.types.ts mirrors the API contract.
- Dashboard 'Exam Marker' quick action (top-level discovery, R1.3/R6.1).
Note: the in-canvas TeacherNavigation is unsuitable for a nav section (it's
the worker prev/next tab bar) — flagged; used the dashboard entry instead.
- R1.1 removal: deleted src/pages/tldraw/CCExamMarker/ (old 3-PDF viewer) and
the now-dead CCExamMarkerPanel + its wiring in CCPanel/BasePanel (examMarkerProps
was only ever passed by the deleted page).
- Fixed pre-existing broken AppRoutes test (missing TimetableListPage mock export).
Build green (vite); AppRoutes route tests pass; typecheck clean for new files.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Pre-load: if session.currentPageId not in snapshot pages, reset to first available page
- Post-load: if store instance.currentPageId still invalid, clear store for fresh start
- On loadSnapshot error: clear store instead of silently failing with corrupt state
- Root cause: teacher1 had a saved snapshot with currentPageId pointing to a deleted page
Replaces JSON.stringify snapshot comparison with a store.listen() dirty flag.
Eliminates 5-15ms main-thread serialize on every poll tick when canvas is idle.
Use GET /me/bootstrap as the primary authenticated state source per ADR-0003. AuthContext now fetches and exposes typed bootstrapData, Header and class/detail admin checks use bootstrap permissions/roles, dashboard surfaces onboarding/calendar/timetable/graph status, and graph navigation derives school setup state from bootstrap instead of /school/status.
Refs: t_44353587
- Header: trim menu to My Work section (My Lessons, My Classes, Lesson Plans), School Admin (gated), Platform Admin (gated)
- MyClassesPage: fix loading/error state destructure (classesLoading not myClassesLoading)
- NotFound: fix ErrorOutline -> ErrorOutlineIcon to prevent 404 page crash
- timetableService: getMyClasses now calls both /me/teacher and /me/student, merges with role annotation
- timetableStore: myClasses type updated to ClassWithRole[]
- timetable.types: add ClassWithRole interface and code/institute_id optional fields to Class
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
navigationStore: rewritten off Neo4j db names — Supabase whiteboard_rooms table,
setAuthInfo(token, userId) pattern, auto-creates default room per context on first use
snapshotService: rewritten to Supabase Storage REST (/storage/v1/object/authenticated/cc.users/…),
setAccessToken() instance method, static methods take accessToken not dbName
AuthContext/NeoUserContext: auth injected into nav store, no Neo4j db names required
singlePlayerPage: loadNodeData no longer calls Neo4j; snapshot wired via accessToken
navigation types: NeoGraphNode updated for Supabase-backed tree structure
transcriptionStore/Service: getSession() removed, accessToken via AuthContext
LLMConfigModal: auth context wiring fixes
GraphNavigator/GraphSidebar: updated nav components
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove NeoUserProvider + NeoInstituteProvider from App.tsx startup chain
- Strip user_db_name/school_db_name from CCUser; add school_id (Phase B wires it to Supabase)
- Remove DatabaseNameService from AuthContext and UserContext
- Remove provisionUser() call from login path; API endpoint preserved for Phase B decision
- Simplify UserContext.resolveProfile: fast-path JWT metadata then background Supabase fetch
- Replace user.user_db_name reads in singlePlayerPage + snapshotService with null-safe guards
- Add useDeviceContext hook (desktop/tablet/phone/iwb, persists to localStorage)
App now loads to dashboard without any Neo4j dependency at startup.
Canvas opens to blank TLDraw state; Phase B rebuilds navigation on Supabase.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Reads VITE_TLSYNC_SECRET from env and appends ?token=... to the
/connect/:roomId WebSocket URI so tlsync server can authenticate connections.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- timetableOnlyService: timetable-specific methods (line 134)
- timetableService: combined export with class/lesson/enrollment methods (line 328)
- This fixes 'getClasses is not a function' and 'getMyClasses is not a function' errors