Frontend State Management
The frontend stores all application state in multiple Zustand stores:
- usePlanningWorkflowStore
- useLandmarkStore
- useCutStore
- useAlignPointStore
When a save is triggered, the application gathers state data from all stores, removes non-serializable objects, and combines the remaining data into a schema-safe JSON payload. This payload is then stored in the backend session_data column.
Key Files
src/modules/planningWorkflow/session/autoSave.ts —
Manages a debounce timer (triggerAutoSave) and subscribes to step changes in the workflow to force immediate saves when the user moves to the next phase.
src/modules/planningWorkflow/session/sessionSerializer.ts — The Compiler
Exposes buildSessionDataPayload(), which manually plucks data from all Zustand stores and packages it into a schema-safe JSON blob
src/modules/planningWorkflow/session/sessionResume.ts — The Hydrator
Exposes loadProcedureSession(), which fetches the JSON blob from the backend and calls .setState() on all Zustand stores to repopulate the UI.
src/services/caseStateService.ts — The Network Layer
Provides saveCaseState and fetchCaseState wrappers around the fetch API.
src/pages/planning/planning.tsx — The Entry Point
on-mount resumption flow and re-downloads heavy binary data (DICOM/STLs) that could not be JSON serialized.
Backend
src/controllers/caseStateController.ts
Handles the incoming payload, resolves the workflow status (e.g., deriving "in_progress" if the user is in an active step), and uses the Sequelize CaseSession model to execute a PostgreSQL/MySQL
UPSERT (update if exists, else create).
APIs Used
Operation | Method | Endpoint
Save | POST | /api/case/:caseId/session
Fetch | GET | /api/case/:caseId/session
Auto-Save & Restoration Flow
autoSave.ts subscribes to the workflow store. If autoSaveEnabled is true, moving to a new step immediately calls saveStepChange.
flushAutoSave() is hooked into the browser's beforeunload and pagehide events to catch pending saves if the user closes the tab before the 3-second timer fires.
(Refresh / Resume)
Step 1 — Mounting
When planning.tsx mounts with a caseId, it calls loadProcedureSession(caseId).
Step 2 — Hydration
sessionResume.ts fetches the JSON, checks the version, and pushes the data back into the
Zustand stores.
Step 3 — Binary Rehydration
Because raw File objects (STLs) and Zarr volumes (DICOM) cannot be saved in JSON,
planning.tsx uses custom useEffect hooks to detect files. It then:
- Triggers autoLoadDicom(lastSelectedDicomPath) to re-download DICOM volumes
- Fetches patientData from WebCeph APIs to re-download and re-mount the 3D meshes based on
the saved layout configuration
Align Backend Integration
Completed backend integration for alignment persistence and restoration
Added alignment state saving into session payload
Implemented alignment restoration during session resume/refresh
Ensured aligned meshes, alignment landmarks, and alignment workflow states are correctly rehydrated from backend session data
Added support for restoring alignment-related UI and viewport state after refresh/resume