System Events
These are built-in Socket.IO events.
Event Direction Description connectINBOUND Fired when connection is established disconnectINBOUND Fired when connection is lost errorINBOUND Fired on system error
Room Events
Client sends these to join or leave rooms. All room events support an acknowledgment callback that returns { ok: true, room: "..." }.
Join Events
Join a room
Join personal room
socket . emit ( 'join_dashboard' , ( ack ) => {
console . log ( ack ); // { ok: true, room: "dashboard" }
});
Event Direction Room joined Description join_meOUTBOUND user:<id>Join personal room join_dashboardOUTBOUND dashboardJoin dashboard page join_inventoryOUTBOUND inventoryJoin inventory page join_stationOUTBOUND stationJoin station page join_logOUTBOUND logJoin material log page join_requestOUTBOUND requestJoin request page join_withdrawalOUTBOUND withdrawalJoin withdrawal page join_orderOUTBOUND orderJoin order page join_claimOUTBOUND claimJoin claim page join_paneOUTBOUND paneJoin pane page join_productionOUTBOUND productionJoin production tracking page join_pricingOUTBOUND pricingJoin pricing settings page join_station_roomOUTBOUND station:<stationId>Join a specific station’s work view leave_station_roomOUTBOUND station:<stationId>Leave a specific station’s work view
Station Room Events
Join or leave a specific station’s room to receive real-time check-in/check-out events for that station only.
Join a station room
Leave a station room
socket . emit ( 'join_station_room' , { stationId: '69a98ee25bb1d35efb8cc1ff' }, ( ack ) => {
console . log ( ack ); // { ok: true, room: "station:69a98ee..." }
});
Event Direction Room joined/left Description join_station_roomOUTBOUND station:<stationId>Join a specific station’s room leave_station_roomOUTBOUND station:<stationId>Leave a specific station’s room
Payload: { stationId: "<station _id>" }
These are dynamic rooms scoped to individual stations, unlike join_station which joins a single shared room for the stations page. stationId is required — the callback returns { ok: false, error: "stationId is required" } if omitted.
QR Check-in Events
Used by the QR check-in system for worker presence at stations. The station screen joins with join-station and receives scan-confirmed when a mobile device emits mobile-scan.
Station screen — join and listen
Mobile device — send scan
socket . emit ( 'join-station' , 'cutting' , ( ack ) => {
console . log ( ack ); // { ok: true, room: "station:cutting" }
});
socket . on ( 'scan-confirmed' , ({ worker , time }) => {
console . log ( ` ${ worker } checked in at ${ time } ` );
});
Event Direction Description join-stationOUTBOUND Station screen joins station:<stationId>. Receives replayed scan-confirmed if a scan occurred within the last 15 seconds. mobile-scanOUTBOUND Mobile sends { stationId, worker? }. Server broadcasts scan-confirmed to the station room. worker defaults to the authenticated user’s name if omitted. scan-confirmedINBOUND Sent to station:<stationId> with { worker, time } when a worker checks in.
join-station uses a hyphenated name (not underscore) to distinguish it from join_station which joins the shared stations page room. Both require JWT authentication.
Leave Events
socket . emit ( 'leave_dashboard' , ( ack ) => {
console . log ( ack ); // { ok: true, room: "dashboard" }
});
Event Direction Room left Description leave_dashboardOUTBOUND dashboardLeave dashboard page leave_inventoryOUTBOUND inventoryLeave inventory page leave_stationOUTBOUND stationLeave station page leave_logOUTBOUND logLeave material log page leave_requestOUTBOUND requestLeave request page leave_withdrawalOUTBOUND withdrawalLeave withdrawal page leave_orderOUTBOUND orderLeave order page leave_claimOUTBOUND claimLeave claim page leave_paneOUTBOUND paneLeave pane page leave_productionOUTBOUND productionLeave production tracking page leave_pricingOUTBOUND pricingLeave pricing settings page leave_station_roomOUTBOUND station:<stationId>Leave a specific station’s work view
Data Events
These events are emitted by the server when data changes via the REST API. You receive them only if you’ve joined the corresponding room.
Resource Updated Events
All resource events include an action field (created, updated, or deleted) and a data field with the affected record.
socket . on ( 'order:updated' , ( payload ) => {
console . log ( payload . action ); // "created" | "updated" | "deleted"
console . log ( payload . data ); // the order object
});
Event Direction Emitted to rooms Triggered by material:updatedINBOUND dashboard, inventoryMaterial create / update / delete inventory:updatedINBOUND dashboard, inventoryInventory create / update / delete / move; Withdrawal approve / reject order:updatedINBOUND dashboard, orderOrder create / update / delete; QR scan scan_out or qc_pass; qc_fail remake / paneCount log:updatedINBOUND dashboard, logMaterial log create / update / delete; QR scan (all actions) request:updatedINBOUND dashboard, requestRequest create / update / delete withdrawal:updatedINBOUND dashboard, withdrawalWithdrawal create / update / delete. Payload data is the withdrawal document (includes withdrawalNumber ) claim:updatedINBOUND dashboard, claimClaim create / update / delete station:updatedINBOUND dashboard, stationStation create / update / delete pane:updatedINBOUND dashboard, pane, production, station:<name>Pane create / update / delete; QR scan (all actions) production-log:updatedINBOUND dashboard, productionProduction log create / update / delete station-template:updatedINBOUND dashboard, stationStation template create / update / delete sticker-template:updatedINBOUND dashboardSticker template create / update / delete jobType:updatedINBOUND dashboardJob type create / update / delete pricing:updatedINBOUND pricingPricing settings update station:check_inINBOUND station:<stationId>Order update with station changes station:pane_arrivedINBOUND station:<nextStation>, stationQR scan with scan_out or qc_pass (pane advances); remake queue (isRemake: true) laminate:readyINBOUND dashboard, pane, production, station:<id>Sheet scan_in at lamination station (all sheets present) laminate:waitingINBOUND station:<id>Sheet scan_in at lamination station (sheets still missing) pane:laminatedINBOUND dashboard, pane, production, station:<id>laminate scan — survivor pane + retired sheet numbers
Payload format:
{
"action" : "created" ,
"data" : {
"_id" : "69a98ee25bb1d35efb8cc1ff" ,
"..." : "..."
}
}
Action values
Most events use created, updated, or deleted. Some events have additional action values:
Event Extra actions Description pane:updatedscannedEmitted on every QR scan action (scan_in, start, complete, scan_out, qc_pass, laminate) pane:updatedqc_failedEmitted on qc_fail with the defected pane payload pane:updatedcreatedEmitted when a remake pane is created (qc_fail auto-remake or claim-approved remake) inventory:updatedadjustedEmitted when a withdrawal is approved/rejected (inventory quantity changes) inventory:updatedmovedEmitted on inventory move/split. Payload: { action: "moved", data: { source, target, quantity } } log:updatedpane_scannedEmitted on every QR scan. Payload: { action: "pane_scanned", data: { paneLog, material } }
Station Check-In
Emitted to a specific station’s room when an order enters or exits that station (triggered by order updates that modify stationHistory or currentStationIndex).
socket . on ( 'station:check_in' , ( payload ) => {
console . log ( payload . orderId ); // "69a98ee25bb1d35efb8cc1ff"
console . log ( payload . stationId ); // "69b12cd35bb1d35efb8cc200"
console . log ( payload . action ); // "entered" | "exited"
});
Event Direction Emitted to Triggered by station:check_inINBOUND station:<stationId>Order update with station changes
Payload:
{
"orderId" : "69a98ee25bb1d35efb8cc1ff" ,
"stationId" : "69b12cd35bb1d35efb8cc200" ,
"action" : "entered"
}
Field Description orderIdThe order that moved stationIdThe station where it happened actionentered (order arrived) or exited (order completed/left)
Pane Arrived (QR Scan)
Emitted when a pane is scanned with scan_out or qc_pass and advances to the next station. Also emitted when a remake pane is queued at a station (payload may include isRemake: true). Sent to the next station’s room and the general station room.
socket . on ( 'station:pane_arrived' , ( payload ) => {
console . log ( payload . paneNumber ); // "PNE-0001"
console . log ( payload . fromStation ); // "cutting"
console . log ( payload . toStation ); // "edging"
console . log ( payload . orderId ); // "69a98ee25bb1d35efb8cc1ff"
});
Event Direction Emitted to Triggered by station:pane_arrivedINBOUND station:<nextStation>, stationPOST /api/panes/{paneNumber}/scan with action: "scan_out" or "qc_pass"; remake arrival
Payload:
{
"paneNumber" : "PNE-0001" ,
"paneId" : "69a98ee25bb1d35efb8cc1ff" ,
"fromStation" : "cutting" ,
"toStation" : "edging" ,
"orderId" : "69b12cd35bb1d35efb8cc200"
}
Field Description paneNumberThe pane that moved (e.g. PNE-0001) paneIdThe pane’s _id fromStationThe station the pane just completed toStationThe station the pane moved to orderIdThe associated order ID (if any)
Pane Arrived (Station Notification)
In addition to station:pane_arrived, the scan endpoint also emits a notification event directly to the next station’s room when a pane moves via scan_out or qc_pass , and when a QC remake pane arrives at a station. This allows station screens to show an alert.
socket . on ( 'notification' , ( payload ) => {
if ( payload . type === 'pane_arrived' ) {
console . log ( payload . title ); // "มีกระจกเข้าสถานี"
console . log ( payload . message ); // "กระจก PNE-0001 เข้าสถานีนี้แล้ว"
}
});
Event Direction Emitted to Triggered by notificationINBOUND station:<nextStation>QR scan scan_out / qc_pass (pane moves); QC remake arrival
Payload:
{
"type" : "pane_arrived" ,
"title" : "มีกระจกเข้าสถานี" ,
"message" : "กระจก PNE-0001 เข้าสถานีนี้แล้ว"
}
Laminate Pairing Events
Emitted during the laminate flow when child sheets arrive at a lamination station and when laminate runs (one survivor sheet keeps its QR; see Laminate Merge ).
laminate:ready
Emitted when all active child sheets have arrived at the lamination station. The frontend can use this to enable the “merge” button.
socket . on ( 'laminate:ready' , ( payload ) => {
console . log ( payload . parentPaneNumber ); // "PNE-0100"
console . log ( payload . sheetsPresent ); // 2
console . log ( payload . sheetsTotal ); // 2
});
Event Direction Emitted to Triggered by laminate:readyINBOUND dashboard, pane, production, station:<stationId>scan_in of the last active sheet at its lamination station
Payload:
Field Description parentPaneNumberThe parent pane number (e.g. PNE-0100) parentPaneIdThe parent pane’s _id sheetsPresentNumber of sheets currently at the station sheetsTotalTotal active sheets expected
laminate:waiting
Emitted when a sheet arrives at the lamination station but other sheets are still missing.
socket . on ( 'laminate:waiting' , ( payload ) => {
console . log ( ` ${ payload . sheetsPresent } / ${ payload . sheetsTotal } sheets arrived` );
});
Event Direction Emitted to Triggered by laminate:waitingINBOUND station:<stationId>scan_in of a sheet at its lamination station (when siblings are still missing)
Payload: Same shape as laminate:ready.
pane:laminated
Emitted when the laminate scan merges sheets: one sheet survives with the same QR; the dormant parent and other sheets are retired (merged_into).
socket . on ( 'pane:laminated' , ( payload ) => {
console . log (( payload . survivor || payload . parent ). paneNumber ); // survivor QR
console . log ( payload . sheets ); // ["PNE-0100-A", "PNE-0100-B"]
});
Event Direction Emitted to Triggered by pane:laminatedINBOUND dashboard, pane, production, station:<stationId>POST /api/panes/{paneNumber}/scan with action: "laminate"
Payload:
Field Description survivorThe surviving pane (populated), awaiting_scan_out at lamination, laminateRole: "single" parentAlias of survivor (backwards compatibility) retiredPaneNumbersPane numbers retired to merged_into (non-survivor sheets and the dormant parent’s number) sheetsArray of merged sheet pane numbers (e.g. ["PNE-0100-A", "PNE-0100-B"])
Pricing Updated
Emitted to the pricing room when pricing settings are updated via PUT /api/pricing-settings.
socket . on ( 'pricing:updated' , ( settings ) => {
console . log ( settings . glassPrices );
console . log ( settings . holePriceEach );
console . log ( settings . notchPrice );
});
Event Direction Emitted to Triggered by pricing:updatedINBOUND pricingPUT /api/pricing-settings
Payload: The full pricing settings object (same as GET response).
Order Arrived (Station Notification)
Emitted to a specific station’s room when a new order is created with stations, or when an existing order advances to a new station. This notifies the station screen that work is incoming.
socket . on ( 'notification' , ( payload ) => {
if ( payload . type === 'order_arrived' ) {
console . log ( payload . title ); // "มีออเดอร์ใหม่เข้า" or "มีออเดอร์เข้า"
console . log ( payload . message ); // "ออเดอร์ ORD-0001 เข้าสถานีนี้แล้ว"
}
});
Event Direction Emitted to Triggered by notificationINBOUND station:<stationId>Order create (first station) or order update (station advancement)
Payload:
{
"type" : "order_arrived" ,
"title" : "มีออเดอร์ใหม่เข้า" ,
"message" : "ออเดอร์ ORD-0001 เข้าสถานีนี้แล้ว" ,
"referenceId" : "69a98ee25bb1d35efb8cc1ff" ,
"referenceType" : "Order" ,
"priority" : "high" ,
"readStatus" : false
}
Field Description typeAlways order_arrived titleThai text — “มีออเดอร์ใหม่เข้า” (new order) or “มีออเดอร์เข้า” (advanced order) messageIncludes the order number referenceIdThe order’s _id referenceTypeAlways Order priorityAlways high
This notification is emitted directly to the station room (not to user:<id>), so it’s received by any client that has joined station:<stationId> via join_station_room or join-station.
Notification
Sent to a specific user’s personal room when a notification is created via the REST API or the QR scan endpoint.
socket . on ( 'notification' , ( notification ) => {
console . log ( notification . title );
console . log ( notification . message );
});
Event Direction Emitted to Triggered by notificationINBOUND user:<recipientId>POST /api/notifications; QR scan scan_out / qc_pass; qc_remake after qc_fail when the order has assignedTo
Payload: The full populated notification object.
System Alert
Broadcast to all connected clients. Only admin users can emit this event. Non-admin users will receive an error.
// Sending (admin only)
socket . emit ( 'system_alert' , { message: 'Server maintenance in 10 minutes' });
// Receiving (all users)
socket . on ( 'system_alert' , ( data ) => {
console . log ( data . message );
});
Event Direction Emitted to Triggered by system_alertINBOUND All connected clients Admin socket client only
If a non-admin user attempts to emit system_alert, they will receive an error event with { message: "Not authorized to send system alerts" }.
Full Example
import { io } from 'socket.io-client' ;
const socket = io ( 'http://localhost:3000' , {
path: '/api/socket-entry' ,
auth: { token: 'your-jwt-token' },
});
socket . on ( 'connect' , () => {
// Join the rooms you need
socket . emit ( 'join_me' );
socket . emit ( 'join_dashboard' );
socket . emit ( 'join_order' );
});
// Listen for data changes
socket . on ( 'order:updated' , ({ action , data }) => {
if ( action === 'created' ) console . log ( 'New order:' , data );
if ( action === 'updated' ) console . log ( 'Order changed:' , data );
if ( action === 'deleted' ) console . log ( 'Order removed:' , data );
});
// Listen for personal notifications
socket . on ( 'notification' , ( notif ) => {
console . log ( `[ ${ notif . priority } ] ${ notif . title } : ${ notif . message } ` );
});
// Listen for system alerts
socket . on ( 'system_alert' , ( alert ) => {
console . log ( 'ALERT:' , alert . message );
});
// When navigating away from orders page
socket . emit ( 'leave_order' );