174 Commits

Author SHA1 Message Date
0db53bfd9c test: add admin route guard coverage
Some checks failed
app-ci-deploy / test-build-deploy (push) Has been cancelled
2026-05-27 23:24:26 +01:00
e68eef8865 chore: consolidate app compose dev and prod 2026-05-27 22:55:00 +01:00
77282893f3 chore: add .gitignore for React frontend project 2026-05-27 21:56:23 +01:00
6642d5fa22 docs: add .env.example with all app environment variables 2026-05-27 21:55:27 +01:00
b2973dc6ad feat: add error boundary to prevent full app crashes 2026-05-27 21:44:59 +01:00
b69c4d3e4f fix: remove 4 empty stub files (dead code) 2026-05-27 21:40:17 +01:00
1c9ed4bd65 fix: remove last classItem.class.academic_year reference in MyClassesPage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 13:50:27 +01:00
de21c56543 fix: navigate scope in CCGraphNavPanel, class.code -> class_code in MyClassesPage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 13:45:12 +01:00
7a1032a3bd fix(classes): flat shape for MyClassesPage, SubjectClass nav -> class detail page
- 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>
2026-05-27 13:30:38 +01:00
63d6d1bbca fix(nav): trim burger menu, fix MyClasses for teachers, fix NotFound crash
- 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>
2026-05-27 12:58:33 +01:00
98a4212e46 feat(nav): timetable view toggle (By Class / By Term), fix cc-section-node crash
- Fix: skip canvas navigation for Section-type nodes (was crashing on cc-section-node)
- Add By Class / By Term toggle to My Timetable section (mirrors calendar Generic/Academic)
- By Class: pre-loaded SubjectClass children shown immediately on expand
- By Term: lazy-loads AcademicTerms -> Weeks -> TaughtLessons when switched to term view
- displayChildren respects timetableView context for the timetable section

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 12:12:59 +01:00
510bef02b6 feat(timetable): add assign lesson plan dialog to TaughtLessonsPage
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>
2026-05-27 11:25:55 +01:00
9438e17f88 feat(nav): add launch buttons for My Timetable and My Classes sections
Add useNavigate + Launch icon to TreeItem; timetable section shows a
launch button routing to /my-lessons, classes section routes to /my-classes.
Both buttons only appear when the section status is populated.
Exclude classes section from the unlinked/pending icon fallback.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 11:22:26 +01:00
61ef95a35e fix(nav): register Journal/Planner shapes, fix headerColor crash, enable academic week expansion
Bugs fixed:
- 'cc-teacher-timetable-node' missing from NODE_TYPE_THEMES caused
  Cannot read properties of undefined (reading 'headerColor') crash
  when clicking My Timetable
- 'cc-journal-node' and 'cc-planner-node' had no shape utils registered,
  causing 'No shape util found for type' error on Journal/Planner click
- Added null safety (?? fallback) to getNodeTheme to prevent future crashes
  from any other unmapped type
- Removed AcademicWeek from canExpand exclusion so weeks can be expanded
  to show individual academic days

Added:
- CCJournalNodeShapeUtil and CCPlannerNodeShapeUtil (stub shapes)
- CCJournalNodeProps and CCPlannerNodeProps types
- Journal/Planner added to CCNodeTypes, ccGraphShapeProps, NODE_TYPE_THEMES
- 'cc-department-structure-node' mapping added to NODE_TYPE_THEMES

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 10:55:40 +01:00
0f5dbd12bf feat(phase-c): lesson plans library — browse, create, and edit lesson plans
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>
2026-05-27 03:59:44 +01:00
6ac5ab7b5c feat(phase-b): student enrollment UI, teacher/student management pages, lesson views
- ClassDetailPage: full rewrite with MUI tabs (students, enrollment requests, teachers),
  add/remove students, approve/reject enrollment requests, AddStudentDialog
- StudentLessonsPage: new — student's weekly lesson view with week navigation
- TaughtLessonsPage: teacher's taught lesson week view
- SchoolSettingsPage, StaffManagerPage, StudentManagerPage: school admin management pages
- PlatformAdminPage: platform admin reset/seed controls
- Header: expanded nav menu (student lessons, school management, platform admin items)
- AppRoutes: routes for all new pages
- SchoolCalendarWizard, TeacherTimetableWizard: week_cycle support and improvements
- CCGraphNavPanel: updated navigation integration
- index.ts: export all new timetable pages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 02:56:04 +01:00
138dfb1531 chore: add .dockerignore to exclude node_modules from build context
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 02:03:17 +01:00
9d78f06c97 feat(phase-b): school onboarding wizard + GAIS school search UI
- SchoolOnboardingWizard.tsx: new 3-step wizard (GAIS search → confirm →
  calendar setup) triggered from the nav panel school section when no school
  is linked. Calls GET /school/search for live school lookup and
  POST /school/register to create the institute record.
- CCGraphNavPanel.tsx: add showOnboard button for no_school state,
  onOnboardSchool context action, wire up SchoolOnboardingWizard state.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 01:56:37 +01:00
b0b2a7f2c3 feat(phase-a+b): merge clean auth + Supabase navigation to master
Merges Phase A (Neo4j removed from startup) and Phase B (Supabase navigation,
school/timetable setup, graph nav panel) into master.

Phase A: NeoUserProvider removed, CCUser simplified, snapshotService null-safe
Phase B: navigationStore → Supabase whiteboard_rooms, snapshotService → Supabase Storage,
  CCGraphNavPanel, SchoolCalendarWizard, TeacherTimetableWizard, CIS auth fixes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 01:25:41 +01:00
83adcce951 feat(phase-b): school/timetable wizards, graph nav panel, UI updates
New components:
- CCGraphNavPanel: Supabase-driven navigation tree (school/timetable/calendar/classes sections),
  role-aware setup buttons, lazy child loading, academic/generic calendar toggle
- SchoolCalendarWizard: 3-step admin-only school setup (details → term dates → daily periods)
- TeacherTimetableWizard: period grid with existing slot pre-loading, edit-mode title

Updated:
- CCNodeSnapshotPanel: saves via Supabase storage path + accessToken
- BasePanel: nav panel tab wired to CCGraphNavPanel
- CCFilesPanelEnhanced: auth context fixes
- CCDocumentIntelligence suite: accessToken threading, Supabase storage integration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-26 01:25:29 +01:00
b0c7758135 feat(phase-b): Supabase navigation store, snapshot service, auth wiring
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>
2026-05-26 01:25:15 +01:00
3a65cf436b fix(panels): eliminate getSession() from transcription store and cabinets panel
Same GoTrueClient lock contention fix as CCFilesPanel:
- transcriptionStore: add _accessToken/_userId state + setAuthInfo() action;
  replace all 6 getSession() calls (startSession, flushCanvasEvents, loadSessions,
  loadKeywordWatches, addKeywordWatch, deleteKeywordWatch, checkSegmentForKeywords)
  with stored values — zero getSession() calls remain in the store
- CCTranscriptionPanel: destructure accessToken from useAuth; sync both values into
  store via setAuthInfo() on every auth change; gate loadSessions on authUser.id
- CCCabinetsPanel: same pattern as CCFilesPanel — useAuth for token, useCallback on
  apiFetch/loadCabinets, gate effect on authUser.id

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 15:13:37 +01:00
6bd85671d2 fix(panels): bypass GoTrueClient lock — expose accessToken via AuthContext, gate panels on auth
Root cause: apiFetch called supabase.auth.getSession() on every API request, acquiring
the GoTrueClient internal lock. On refresh, concurrent mount of panels caused lock
contention — the loadCabinets/loadSessions fetch awaits hung, loading spinner never
cleared.

- AuthContext: add accessToken state, set/clear it alongside user on all auth events
- CCFilesPanel: apiFetch reads accessToken from AuthContext (no lock), loadCabinets
  effect gated on authUser.id (fires only after SIGNED_IN, session guaranteed valid)
- CCTranscriptionPanel: loadSessions effect gated on authUser.id for same reason

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 15:06:51 +01:00
5284d30f84 fix(panels): resolve sidebar refresh bugs — getUser→getSession, files double-call, hardcoded IP
- transcriptionStore: replace all supabase.auth.getUser() with getSession() so session
  restoration on page refresh does not race against GoTrue network validation
- CCFilesPanel: remove selectedCabinet from loadCabinets useCallback deps; use
  initialSelectionDone ref to prevent double-call on first mount
- CCTranscriptionPanel: replace hardcoded LAN IP with VITE_API_URL env var

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 13:41:25 +00:00
fb1795fd2b fix(phase-a): actually remove Neo provider JSX from App.tsx
Python heredoc string replacement missed the JSX wrapper lines due to
indentation mismatch. Rewrote App.tsx directly. Previous commit removed
the imports but left NeoUserProvider/NeoInstituteProvider in the JSX,
causing ReferenceError at runtime (Vite builds without tsc so it
compiled clean despite the undefined references).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 13:22:24 +00:00
7b546c933e feat(phase-a): remove Neo4j from startup chain, clean auth flow
- 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>
2026-05-25 13:06:39 +00:00
4139eb8fd3 fix: audio pipeline — 16 kHz AudioContext, 4096-sample buffering, SERVER_READY handshake
Root causes of disconnection and slow transcription:
- AudioWorklet was firing every 128 native samples (~48 kHz), sending
  ~375 tiny WebSocket messages/sec. Server flooded with tiny frames
  during silence → keepalive ping timed out → connection dropped.
- JS resampling 48 kHz → 16 kHz added CPU overhead on every chunk.
- Audio started on ws.onopen before server sent SERVER_READY, so early
  frames were dropped.

Fixes:
- audioWorklet.js: accumulate 4096 samples before posting (256 ms/chunk
  at 16 kHz, ~4 messages/sec), transfer ArrayBuffer zero-copy.
- transcriptionService: AudioContext({ sampleRate: 16000 }) — browser
  handles native resampling, no JS resampling needed. Remove
  resampleTo16kHZ entirely.
- Wait for SERVER_READY message before calling setupAudioProcessing().
- Send 'END_OF_AUDIO' string on stop so server can finalise last segment.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 07:26:20 +00:00
308889937c fix: transcription segment dedup, store finalisation, and panel colours
- transcriptionService: track finalizedSegmentCount so only newly-final
  segments are emitted per WS message (was re-processing full array each
  time, causing the live segment to freeze in the completed list)
- transcriptionStore: saveSegment isFinal branch now appends the passed
  text directly instead of currentSegment (currentSegment was stale
  relative to the incoming final)
- CCTranscriptionPanel: record button colour changed from var(--color-text)
  to explicit #2563eb so it is visible in dark mode; completed segment
  backgrounds changed from hardcoded #fff to var(--color-muted) so text
  is readable in both themes; keyword Add button gets same blue fix

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 07:08:53 +00:00
0345258247 fix: transcription getUserMedia called before enumerateDevices
The old startTranscription() called enumerateDevices() first to find a
device ID, then getUserMedia. Without microphone permission, enumerateDevices()
returns devices with deviceId="" (empty string, falsy). This caused the
!this.selectedDeviceId check to bail out early, never calling getUserMedia,
never prompting the user for mic permission, and never creating the WebSocket.

Result: user clicks Start Recording → isRecording=true → button changes to
Stop Recording → but no mic prompt, no WebSocket, no transcription.

Fix: call getUserMedia directly (with optional echoCancellation/noiseSuppression
if no device pre-selected). getUserMedia triggers the browser permission prompt
automatically. Device selection is still honoured via exact deviceId constraint
when one has been explicitly chosen.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 19:32:43 +00:00
83f17c9ab6 fix: UserContext fast-path initialization — no spinner on page refresh
Before: UserContext waited for a Supabase profiles table query (~200ms)
before setting isInitialized=true, causing FullContextRoutes to show
a spinner on every page refresh even with a valid stored session.

After: UserContext immediately builds a profile from auth metadata +
localStorage (synchronous, zero network calls) and sets isInitialized=true
before the Supabase query. The Supabase profiles table is still queried
in the background and the profile is updated when it returns with
authoritative user_db_name/school_db_name data.

On refresh with a stored session: auth metadata is available
synchronously via INITIAL_SESSION, so the page renders with no visible
spinner.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 19:16:02 +00:00
5e21a2c7fc fix: add missing transcription entry to PANEL_DIMENSIONS
BasePanel reads PANEL_DIMENSIONS[currentPanelType] to get the panel
dimensions (topOffset, bottomOffset, width). The transcription panel
type was added to the panel list and render switch during CIS Phase 3C
but was never added to PANEL_DIMENSIONS. This caused a crash whenever
the BasePanel rendered with the transcription tab active:

  PANEL_DIMENSIONS["transcription"] === undefined
  undefined.topOffset → TypeError: Cannot read properties of undefined

This crash appeared as a topOffset error anywhere a Tldraw canvas was
rendered (singlePlayerPage, multiplayerUser, etc.).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 19:05:51 +00:00
e27d1f5c8d fix: prevent TLDraw topOffset crash — decouple store init from editor ready
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>
2026-05-21 18:17:52 +00:00
b1681d86fb fix: rewrite AuthContext to use canonical Supabase onAuthStateChange pattern
The previous implementation had two concurrent session recovery paths:
1. loadInitialSession() calling supabase.auth.getSession()
2. onAuthStateChange handling INITIAL_SESSION/SIGNED_IN

These raced unpredictably causing setLoading(false) to never fire in
certain interleavings, leaving the app permanently stuck on the spinner.

Fix: Remove loadInitialSession() entirely. Start loading=true. Rely
solely on onAuthStateChange — INITIAL_SESSION fires immediately with
the current session state, eliminating the race. One path, one
setLoading(false) call, no interleaving possible.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 18:05:51 +00:00
d789586fca fix: nginx serves index.html with no-cache headers
Vite hashes all JS/CSS assets, so those can be cached for 30 days.
But index.html must never be cached — it references the current hashes.
Caching index.html meant users ran old JS after deploys.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 17:54:49 +00:00
3c0fd4f647 fix: FullContextRoutes no longer blocks on Neo4j contexts
Removed NeoUserContext and NeoInstituteContext from the gateway
spinner. They were blocking ALL authenticated routes until Neo4j
graph data was loaded, which took 1-8 seconds and caused persistent
spinners. Now only UserContext (Supabase profile, ~200ms) must be
ready before routes render. Each page handles its own graph data
loading state independently.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 17:52:31 +00:00
679653c118 fix: workspace spinner — use profile (not null user) and add 8s context timeouts
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>
2026-05-21 17:43:38 +00:00
ab1f8111f6 fix: add 8s timeout to NeoInstituteContext school node fetch
Races the SchoolNeoDBService.getSchoolNode() call against an 8-second
timeout. If Neo4j is slow or unavailable the workspace now loads within
seconds rather than waiting for the full axios 120s timeout. The context
degrades gracefully — workspace opens without institute data, error is
logged as a warning not an exception.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 17:28:57 +00:00
d3c2a9bdff security: pass TLSYNC_SECRET token in sync WebSocket URI
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>
2026-05-21 17:09:39 +00:00
06f761e750 feat(cis): add Keywords tab — watches, real-time detection, match log (Phase 3C)
- KeywordWatch and KeywordMatch interfaces in transcriptionStore
- loadKeywordWatches, addKeywordWatch, deleteKeywordWatch actions via API with JWT auth
- checkSegmentForKeywords: client-side detection on each final segment, logs events to backend
- clearKeywordMatches: resets session-scoped match list
- Keywords tab in CCTranscriptionPanel: add/delete watches, match log with timestamp
- Match count badge on Keywords tab when hits exist during recording
- Also fixes missing Close import that was present in summary modal
2026-05-21 12:21:17 +00:00
4d10d75003 fix(cis): fix JSX syntax errors and import paths for Phase 3 build
- Fix broken template literal in transcriptionStore.ts export function
- Fix broken regex in Content-Disposition parser
- Fix broken character class in filename sanitization
- Fix LLMConfigModal import path (5 levels up, not 6)
- Export button JSX properly nested in CCTranscriptionPanel
- Build now passes cleanly
2026-05-21 06:35:09 +00:00
cb8c2dab74 feat(cis): add LLM config modal, summary generation, keywords tab, and export button (Phase 3)
- LLMConfigModal: provider dropdown, model field, API key input (localStorage only)
- Summary generation: modal with 5 summary types, calls API, displays result
- Keywords tab: add/delete watches, real-time detection, event logging
- Export button: SRT/TXT/JSON download via API endpoint
- All Phase 3 frontend tasks complete
2026-05-20 22:57:06 +00:00
6bbed42f55 feat(cis): add Supabase integration, canvas event logger, and sessions tab (Phase 2)
- Connect transcriptionStore to Supabase (start/stop session, save segments)
- Add CanvasEventLogger for silent TLDraw activity tracking
- Add Sessions tab to CCTranscriptionPanel with past sessions list
- Auto-detect timetable context on panel mount
- Flush canvas events to API every 5 seconds during recording
2026-05-20 22:06:31 +00:00
2ee4e4afe7 feat(cis): add CCTranscriptionPanel Live tab to sidebar (Phase 1)
- Add CCTranscriptionPanel component with Live tab
- Add Zustand transcriptionStore for session state management
- Wire panel into BasePanel sidebar system
- Fix merged switch cases in getIconForPanel, getDescriptionForPanel, renderCurrentPanel
- Add VITE_WHISPERLIVE_URL to .env
2026-05-20 21:34:21 +00:00
Agent Zero
0f4956d4a4 fix: correct enrollment service method aliases
- Fix getEnrollmentRequests to use listEnrollmentRequests
- Fix respondToEnrollmentRequest to use respondToEnrollment
2026-02-26 17:47:37 +00:00
Agent Zero
d17acffd41 fix: add method aliases to timetableService for store compatibility
- Added getClasses, getClass, createClass, updateClass, deleteClass aliases
- Added getMyClasses, getMyTeachingClasses aliases
- Added timetable and lesson method aliases
- Added enrollment request method aliases
- Fixes 'getClasses is not a function' error in timetableStore
2026-02-26 07:49:53 +00:00
Agent Zero
fc6b6c3d10 fix: rename timetableServiceExports to timetableService to fix store imports
- 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
2026-02-26 07:44:34 +00:00
Agent Zero
51c0bfaeaf fix: correct MUI icon usage in Header and NotFoundPublic
- Header.tsx: Use <MenuIcon /> instead of <Menu /> component
- NotFoundPublic.tsx: Use <ErrorOutlineIcon /> instead of <ErrorOutline />

Resolves: Menu open prop error and ErrorOutline undefined error
2026-02-26 07:38:05 +00:00
Agent Zero
856816c36a fix: TimetablePage fetchTimetableDetail -> fetchTimetable and add null check for currentLessons 2026-02-26 07:36:40 +00:00
Agent Zero
067df34c50 fix: correct Material UI icon naming in Header.tsx and update timetable components
- Fix icon naming: remove 'Icon' suffix from MUI icon components in Header.tsx
  (AccessTime, Close, Person, School, Schedule, Class, Book, Settings, Student, Login, Logout)
- Update timetable components to use UserContext instead of ProfileContext
- Fix timetableService naming collision and circular reference
- Update various components for consistency
2026-02-26 07:28:47 +00:00
Agent Zero
00a6f941c7 fix(contexts): replace ProfileContext with UserContext in timetable pages 2026-02-26 07:09:43 +00:00