Tldraw 3.6.1 Tldraw component already registers defaultBindingUtils
internally; passing them explicitly via the bindingUtils prop caused
"Binding type 'arrow' is defined more than once" runtime crash.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Select + Furniture tool buttons used color='default', which MUI v5 resolves to
theme.palette.default (undefined) -> TypeError reading 'main'/'dark' at render,
tripping the ErrorBoundary ('Template setup canvas crashed'). Use 'inherit'.
Caught by live browser-verify t_11f5a049; model-level tests never rendered the toolbar.
Co-Authored-By: Claude Opus 4.8 <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>
Re-fetch classes when NeoInstitute context changes. Catch fetchMyClasses rejection with
logger.warn so unhandled promise rejections don't break the component boundary silently.
Use CircularProgress (MUI) for loading skeleton.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
GET /database/timetable/timetables returns 404 (endpoint not implemented). Treat any
fetch error as empty state so the page renders gracefully rather than showing error UI.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
timetableOnlyService.getMyTimetables is referenced but never defined; calling it throws
'is not a function' at runtime. fetchTimetables calls the implemented listTimetables endpoint.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bare /timetable routed to TimetablePage which called useParams<{timetableId}>() and
immediately hit !currentTimetable → "Error Loading Timetable". New TimetableListPage
uses fetchMyTimetables() from timetableStore. AppRoutes now: /timetable →
TimetableListPage, /timetable/:timetableId → TimetablePage.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Root cause: disposing store while React's async state update (storeReady=false) hadn't
unmounted Tldraw yet caused tldraw's reactive signals to read currentPageId on a disposed store.
Fix: Effect 1 creates store once per user.id (never recreated on node navigation).
Effect 2 loads/reloads snapshots when context.node changes, store stays alive.
Store is only disposed when user changes or component unmounts — after setStoreReady(false)
has had time to unmount Tldraw cleanly via React's synchronous cleanup.
- Added import for ErrorBoundary from src/components/ErrorBoundary.tsx
- Wrapped {store && <Tldraw>} block in ErrorBoundary with 'Canvas failed to load — Reload' fallback
- ErrorBoundary.tsx already existed with full error/reload UI
- Timetable pages (J2) already have loading states via timetableStore loading flags
- tsc: 177 (master baseline) → 173 on this branch (no regression)
F3: authenticated unknown routes now redirect to /dashboard instead of
rendering private NotFound.
F2: Header shows a warning Chip with the current onboarding next_step
when the user has not completed onboarding (next_step != 'ready').
Chip navigates to /dashboard on click. Hidden on xs screens.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
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
- MyClassesPage: fix all classItem.class?.* references to flat field access (class_code, name, description etc)
- MyClassesPage: fix class detail link /timetable/classes/:id -> /classes/:id (actual route)
- CCGraphNavPanel: SubjectClass click navigates to /classes/:id since no Neo4j SubjectClass nodes exist yet
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>
Each lesson card gets a LibraryAdd button that opens a searchable
plan picker. Falls back gracefully to empty state when no plans exist,
with a link to /lesson-plans to create one.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds LessonPlansPage (card grid with search/filter, create dialog) and
LessonPlanDetailPage (structured editor with objectives, activities, Bloom
taxonomy tags, per-field AI suggest via ✨ button, and auto-save).
Routes: /lesson-plans and /lesson-plans/:planId wired into AppRoutes.
Nav: Lesson Plans item added to Header menu under Timetable & Classes.
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>
Three changes to singlePlayerPage:
1. Store creation no longer waits for isEditorReady. The editor ref
is already passed as optional to NavigationSnapshotService and
loadNodeSnapshotFromDatabase, so we can create and populate the
store without needing the editor to be mounted first.
2. <Tldraw> only renders when store is defined: {store && <Tldraw>}.
Previously it rendered immediately with store={undefined}, which
caused TLDraw to initialize with no backing store and crash
reading topOffset on an unresolved internal object.
3. Loading guard also checks !user so the component never renders
with a null profile before the redirect effect fires.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
singlePlayerPage was using useUser().user which is always null (no setter
in UserContext). This caused the redirect effect to always fire, making
the workspace completely inaccessible. Fixed by destructuring profile as
user, which IS properly set from the Supabase profiles query.
Also added 8s timeout to NeoUserContext.switchContext call to match the
NeoInstituteContext timeout fix, preventing infinite spinner if the
navigation API call hangs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Header.tsx: Use <MenuIcon /> instead of <Menu /> component
- NotFoundPublic.tsx: Use <ErrorOutlineIcon /> instead of <ErrorOutline />
Resolves: Menu open prop error and ErrorOutline undefined error