ULagos 360° is built on a strict two-tier architecture. The frontend is a fully static React 19 single-page application compiled by Vite and deployable to any static host (Vercel, Netlify, etc.). The backend is a separate Socket.IO server running on Railway. The two tiers never communicate over HTTP REST — every state change, user registration, and real-time broadcast flows through a single persistent WebSocket connection managed by theDocumentation Index
Fetch the complete documentation index at: https://mintlify.com/Pewiz/ulagos360/llms.txt
Use this file to discover all available pages before exploring further.
useSocketConnection hook.
Component Tree
The rendered UI is composed of a shallow, predictable hierarchy rooted at<App>:
<App> owns no local React state (useState/useRef). It destructures spaces, loading, connected, currentUser, and all action handlers from useRealTimeSpaces, and separately calls usePersistentBackup() to obtain recoverFromBackup for the emergency recovery handler wired to <Header>.
Hook Layer
Three custom hooks form the application’s logic layer, each with a clearly bounded responsibility.useRealTimeSpaces
Primary orchestration hook consumed by
App.jsx. Composes the Zustand store with useSocketConnection, wires optimistic updates to socket emissions, and sets up the 120-second forceSync interval plus beforeunload / visibilitychange flush handlers.useSocketConnection
Owns the Socket.IO client lifecycle: creates the socket with
io(), handles connect / disconnect / connect_error, registers all server-to-client event listeners, and exposes sendSpaceUpdate, forceServerSync, loginUser, and disconnectSocket to callers.usePersistentBackup
Runs a
setInterval every 15 seconds to snapshot spaces into two localStorage keys: ulagos360_spaces_backup (raw spaces map) and ulagos360_emergency_backup (timestamped envelope). Also flushes on visibilitychange (tab hidden) and beforeunload. Exposes recoverFromBackup() for the emergency recovery action in <Header>.Data Flow
Store initialisation
On mount,
useRealTimeSpaces calls initializeSpaces(). If the spaces-storage localStorage key is empty (first visit or after logout), the store is seeded from the SPACES_DATA constant catalogue, setting every space to disponible. If persisted data already exists, this step is a no-op and loading is set to false immediately.Socket connection and registration
useSocketConnection opens a Socket.IO connection to https://ulagos360-backend-production.up.railway.app (polling → WebSocket upgrade). On connect, if a user is found in localStorage, the hook emits register_user followed immediately by get_all_spaces. The server responds with a full state snapshot on one of its aliased state events (spaces_state, all_spaces_state, etc.), which hydrates the Zustand store via updateSpace.Optimistic space update
When a tutor taps an action button on a
SpaceCard, handleUpdateSpaceStatus in useRealTimeSpaces fires in two steps — first calling updateSpaceStatus() on the Zustand store (UI updates instantly), then passing the returned updates object to sendSpaceUpdate(), which emits the update_space Socket.IO event to the backend.Server broadcast to other clients
The backend rebroadcasts the change as a
space_updated event. Every other connected client’s useSocketConnection instance receives it and calls updateSpace(spaceId, updates), keeping all panels in sync without polling.Fallback sync
If the server has not responded with full state within 5 seconds of registration,
registerUserAndRecoverState emits sync_spaces to upload the client’s local state. Additionally, forceServerSync is called 1 second after a user becomes connected, and the 120-second interval in useRealTimeSpaces calls forceSync to re-write the Zustand persist snapshot.Repository Structure
ULagos 360° ships as two independent repositories. This document covers the frontend only. The backend Socket.IO server lives in a separate repo and is deployed exclusively on Railway. To point the frontend at a different backend, update the server URL constant inside
src/hooks/useSocketConnection.js.Key Dependencies
| Package | Version | Role |
|---|---|---|
react | 19.1.0 | UI framework |
socket.io-client | 4.8.1 | WebSocket + polling transport |
zustand | 5.0.7 | Global state with persist middleware |
tailwindcss | 4.1.11 | Utility-first styling |
vite | 7.0.4 | Build tool and dev server |
lucide-react | 0.536.0 | Icon library |