Key Technical Points
5.1 Authentication & API Client
Auth: localStorage.auth_token drives useAuth() / checkAuth() and protected-route redirects. 401 responses clear the token and route to /auth.
API client: zenspaceApi.request() (src/common/helpers/api.ts) returns a parsed ApiResponse<T>; non-OK HTTP responses are returned (not thrown) — call sites check res?.success.
The public magic-link page is the exception — it uses raw fetch via joinBackendUrl().
5.2 Organization Context
OrganizationProvider reads ?orgId= and loads org into Redux (organization slice).
Protected pages read the org from Redux. All API calls, breadcrumbs, and timezone formatting use this context.
5.3 Redux Slices
| Slice | Purpose |
|---|---|
| common | Global flags; hardRefresh signal that triggers list re-fetches |
| meetingRoomDisplay | Meeting Room Display list + detail state |
| webhookLogs | Webhook log lists for both displays and physical spaces |
| organization | Currently selected org loaded by OrganizationProvider |
| IoTGateway | IoT Adapter list + detail state |
| physicalSpace | Physical Space list + detail state |
| bookingAccessCredential | Booking-access credential attempt log state |
common.hardRefresh is the cross-feature invalidation signal — ListViewTable refetches whenever it changes.
5.4 List Views
Most lists use ListViewTable with a shared URL contract: page, limit, sortBy, sortOrder, search, plus direct filter params.
5.5 Plaintext API Keys
Security Requirement
Plaintext API keys are shown exactly once in OrgApiKeySecretModal after create or rotate. Do not log, store, or surface them anywhere else.
5.6 Magic Link
Raw fetch to /magic-links/:token/context; iframe sandbox is allow-scripts only.
The route must remain unauthenticated.
5.7 Backends
ZenCore — REST API at VITE_BACKEND_URL.
ZenEdge — Realtime WebSocket at VITE_WEBSOCKET_SERVICE_URL.
See CLAUDE.md for full terminology and API contracts.