Skip to main content

Documentation 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.

All real-time communication in ULagos 360° uses Socket.IO events over a persistent WebSocket connection — with long-polling as an automatic fallback — to the backend at https://ulagos360-backend-production.up.railway.app. The client initiates the transport as HTTP polling and upgrades to WebSocket once the handshake completes. There are no REST endpoints; every piece of state change flows through the events documented on this page.

Client-Emitted Events

These are the events that the browser sends to the server, all emitted from useSocketConnection.js.

register_user

Sent on socket connect if a user already exists in localStorage, or when a tutor submits the identification modal for the first time. Establishes the tutor’s identity on the server and begins session recovery.
{
  "id": "string",
  "name": "string",
  "sessionId": "string",
  "persistentSession": true,
  "timestamp": 1234567890,
  "userId": "string",
  "userName": "string"
}
id and userId carry the same value; name and userName likewise duplicate. Both forms are sent to maximise compatibility with different versions of the backend.

get_all_spaces

Requests a full snapshot of all space states from the server. It is emitted in two distinct situations with slightly different payloads. On connect — emitted inside registerUserAndRecoverState immediately after register_user, carrying the full identity context:
{
  "userId": "string",
  "sessionId": "string",
  "timestamp": 1234567890,
  "immediate": true
}
immediate: true signals to the server that the client cannot wait for any debounce or batching delay — it needs the state right away. On forceServerSync() call — emitted with a minimal payload (no userId, no immediate flag):
{
  "sessionId": "string",
  "timestamp": 1234567890
}
forceServerSync is called 1 second after the socket connects (when a user is present) and whenever the 120-second interval in useRealTimeSpaces fires. Because the server already knows the user’s identity from the initial registration, the abbreviated payload is sufficient.

update_space

Fired whenever a tutor changes a space’s status via a SpaceCard action button. This is the primary write event in the system.
{
  "updateId": "string",
  "spaceId": "string",
  "spaceData": {
    "status": "string",
    "occupiedBy": "string | null",
    "updatedBy": "string"
  },
  "updates": {
    "status": "string",
    "occupiedBy": "string | null",
    "updatedBy": "string"
  },
  "userId": "string",
  "userName": "string",
  "sessionId": "string",
  "timestamp": 1234567890
}
spaceData and updates carry identical content. The updateId is a client-generated unique string (update_<timestamp>_<random>) used for deduplication on the server side.

sync_spaces

Uploads the client’s entire local state to the server. Triggered 5 seconds after register_user if the server has not yet responded with a full-state event, or on manual sync trigger. Acts as a conflict-resolution fallback when the server has stale or missing data.
{
  "spaces": {
    "[spaceId]": { "...space object" }
  },
  "userId": "string",
  "sessionId": "string",
  "timestamp": 1234567890
}

user_logout

Emitted when the tutor clicks the logout button, immediately before the socket disconnects. Allows the server to clean up session state.
{
  "userId": "string",
  "sessionId": "string",
  "timestamp": 1234567890,
  "isRealLogout": true,
  "reason": "user_action"
}
The isRealLogout: true flag distinguishes intentional logouts from accidental disconnections (network drop, browser close). The backend should use this to decide whether to release reserved spaces.

pong

Sent in direct response to a ping event from the server. Part of the application-level heartbeat mechanism that keeps the connection alive through proxies and load balancers.
{
  "userId": "string",
  "sessionId": "string",
  "timestamp": 1234567890
}

Server-to-Client Events

These are the events the browser listens for, registered inside setupSocket() in useSocketConnection.js.

Full-State Response Events

The server may broadcast a complete snapshot of all spaces under any of the following event names. The client registers a listener for all of them and handles each identically.
Event nameNotes
spaces_statePrimary state response
all_spaces_stateAlternative full-state name
full_state_responseUsed by some backend versions
spaces_sync_responseResponse to sync_spaces
spaces_dataGeneric data push
state_updateIncremental bulk update
sync_responseSynchronisation acknowledgement
user_state_responseState scoped to the authenticated user
All of these share the same payload shape:
{
  "spaces": {
    "[spaceId]": {
      "id": "string",
      "name": "string",
      "status": "string",
      "occupiedBy": "string | null",
      "reservedBy": "string | null",
      "lastUpdate": "2025-01-01T00:00:00.000Z",
      "updatedBy": "string | null",
      "category": "string",
      "type": "string"
    }
  },
  "reservations": [
    {
      "spaceId": "string",
      "userId": "string",
      "userName": "string",
      "timestamp": 1234567890
    }
  ]
}
On receipt, the persistent spaceStateEvents listener checks first for a reservations array. If present, it applies each reservation individually via updateSpace and skips the spaces map. If there are no reservations, it falls back to iterating data.spaces (or the raw data object) and calling updateSpace(spaceId, spaceData) for each entry.
The client listens to all eight event names to maintain backward and forward compatibility as the backend evolves. Adding a new alias on the server requires no frontend changes.

space_updated

Broadcast by the server when any connected client emits update_space. This is how changes made by one tutor propagate to all other open panels instantly.
{
  "spaceId": "string",
  "updates": {
    "status": "string",
    "occupiedBy": "string | null",
    "reservedBy": "string | null",
    "updatedBy": "string",
    "lastUpdate": "2025-01-01T00:00:00.000Z"
  }
}
The client calls updateSpace(data.spaceId, data.updates) to merge the delta into the Zustand store.

User Confirmation Events

Fired by the server to acknowledge a successful registration or login. The client normalises the payload and persists the resolved user object to both the Zustand store and localStorage.
Event namePayload shape
user_registered{ user: { userId, userName, ... } }
register_response{ user: { userId, userName, ... } }
login_success{ id, name, ... } or { userId, userName, ... }
user_confirmed{ user: { id, name, ... } }
The client tries three payload shapes in order:
  1. data.user — extracts userId/id and userName/name
  2. data.userId + data.userName at the top level
  3. data.id + data.name at the top level

ping

Sent by the server with no payload as a keep-alive probe. The client must respond immediately with a pong event (see above). If the client fails to respond, Socket.IO’s engine will eventually detect the stale connection and trigger a reconnect cycle.

Build docs developers (and LLMs) love