7.7 KiB
Live Dashboard — Real-Time Event Specification
Mechanism
PHP engine files emit named events to a Node.js process over HTTP after a successful DB commit. Node.js broadcasts the event to all browser tabs in the same company room via Socket.IO. Each dashboard page listens for relevant events and reloads only the sections that changed — without a full page refresh.
PHP (engine file)
└─ notify_node(event, payload, company_id) // fire-and-forget POST to Node
└─ Node.js /emit endpoint
└─ io.to('company_{id}').emit(event, payload)
└─ Browser (dashboard JS)
└─ targeted section reload + flash effect
notify_node() (app/assets/utils/notify_node.php)
Fire-and-forget HTTP POST from PHP to the local Node.js /emit endpoint. Called after $pdo->commit() so the event is never sent for rolled-back transactions.
notify_node(string $event, array $payload, int $company_id): void
Failure is silently ignored — the DB write is authoritative; the live update is a UX enhancement only.
Socket.IO Room
Each company gets its own room: company_{company_id}. The browser joins this room on page load via window._socket (initialized in include_ending.php, always before page <script> blocks).
Events
stock_updated
Fired when stock inventory changes.
| Trigger file | Action |
|---|---|
ics/api/engine/approve_stock.php |
Stock In / Out / Transfer approved |
Payload: { warehouse_id, type }
Dashboard sections reloaded: stock-in total, stock-out total, low stock count, stock movement chart, stock health donut, most-moved list, low-stock list, recent activity list.
stock_overview.phpdoes not listen — it is an analytical report page, not a live monitor.
order_updated
Fired when a sales order is created, confirmed, cancelled, or deleted.
| Trigger file | Action |
|---|---|
order/api/engine/manage_order.php |
Order saved (create or update) |
order/api/engine/confirm_order.php |
Order confirmed |
order/api/engine/cancel_order.php |
Order cancelled |
order/api/engine/delete_order.php |
Order soft-deleted |
Payload: {}
Dashboard sections reloaded: total orders card.
invoice_updated
Fired when any invoice-type document changes status or is created/deleted. Also fired as a side-effect from confirm_order when auto_invoice = true, and from confirm_return when auto_cn = true.
| Trigger file | Action |
|---|---|
order/api/engine/proceed_to_invoice.php |
Invoice created from order |
order/api/engine/manage_invoice.php |
Invoice saved (create or update) |
order/api/engine/void_invoice.php |
Invoice voided |
order/api/engine/delete_invoice.php |
Invoice soft-deleted |
order/api/engine/update_invoice_status.php |
Invoice status updated |
order/api/engine/confirm_order.php |
Auto-invoice on order confirm (conditional) |
order/api/engine/confirm_return.php |
Auto credit note on return confirm (conditional) |
revenue/api/engine/proceed_to_invoice.php |
Revenue invoice created |
revenue/api/engine/delete_credit_note.php |
Credit note deleted |
expense/api/engine/delete_supplier_credit_note.php |
Supplier credit note deleted |
Payload: {}
Dashboard sections reloaded: total revenue card, unpaid invoices card.
return_updated
Fired when a customer return is created, confirmed, cancelled, or deleted.
| Trigger file | Action |
|---|---|
order/api/engine/manage_return.php |
Return saved (create or update) |
order/api/engine/confirm_return.php |
Return confirmed |
order/api/engine/cancel_return.php |
Return cancelled |
order/api/engine/delete_return.php |
Return soft-deleted |
Payload: {}
Dashboard sections reloaded: pending returns card.
gl_posted
Fired when a GL entry is posted, replaced, or saved as a manual journal.
| Trigger file | Action |
|---|---|
accounting/api/engine/post_gl_entry.php |
Source document GL posted or replaced |
accounting/api/engine/save_manual_journal.php |
Manual journal saved or replaced |
order/api/engine/issue_invoice.php |
Invoice issued and posted to GL |
finance/api/engine/manage_receipt.php |
Receipt saved and posted to GL |
finance/api/engine/manage_payment.php |
Payment saved and posted to GL |
Payload is built by gl_posted_payload() for most emitters and includes document type, source id, action, and journal lines where available.
Accounting dashboard sections reloaded: dashboard cards and charts handled by ac_dashboard/index.php.
receipt_posted and payment_posted
Fired as finance-specific signals immediately after receipt or payment creation.
| Event | Trigger file | Payload |
|---|---|---|
receipt_posted |
finance/api/engine/manage_receipt.php |
{ id } |
payment_posted |
finance/api/engine/manage_payment.php |
{ id } |
The current accounting dashboard refresh is driven by gl_posted; these finance events are available for targeted listeners if a page adds them later.
WMS Dashboard Section Map (dashboard/index.php)
| Event | Sections updated | DOM IDs | Reload function |
|---|---|---|---|
stock_updated |
Stock in/out cards, low stock card, movement chart, health donut, most-moved list, low-stock list, activity list | stat_total_in, stat_total_out, stat_low_stock_count, #ics_movementChart, #overallChart, #list_most_moved, #list_low_stock, #list_recent_activity |
retrieve_stock_sections() |
order_updated |
Total orders card | stat_total_orders |
retrieve_order_sections() |
invoice_updated |
Revenue + unpaid invoices cards | stat_total_revenue, stat_unpaid_invoices |
retrieve_invoice_sections() |
return_updated |
Pending returns card | stat_pending_returns |
retrieve_return_sections() |
All four section functions call the same API endpoint (dashboard/api/engine_report/reports_stats.php) but update only the relevant DOM elements.
Accounting Dashboard (ac_dashboard/index.php)
The accounting dashboard listens for gl_posted and reloads its accounting KPI sections. It does not listen to the WMS events unless a future page explicitly adds those listeners.
Flash Card Effect
When a silent socket-triggered section reload completes, each updated card receives a 1-second yellow glow animation to signal the change without disrupting the user.
CSS:
@keyframes card-flash {
0% { box-shadow: 0 0 0 3px rgba(250,204,21,0.8); }
100% { box-shadow: none; }
}
.card-flash { animation: card-flash 1s ease-out; }
JS helper (flash_card):
function flash_card(el) {
if (!el) return;
el.classList.remove('card-flash');
void el.offsetWidth; // force reflow so re-trigger works
el.classList.add('card-flash');
setTimeout(function() { el.classList.remove('card-flash'); }, 1000);
}
Called immediately after the overlay .hide() for each updated card element. Flash does not fire on the initial full-page load — only on silent socket-triggered background refreshes.
Pages That Do NOT Listen
| Page | Reason |
|---|---|
ics/stock_overview.php |
Analytical report — user loads on demand, no live monitoring needed |
| All other non-dashboard pages | Point-in-time views; manual refresh is sufficient |
Key Files
| File | Role |
|---|---|
app/assets/utils/notify_node.php |
PHP → Node.js HTTP POST helper |
nodejs/server.js |
Node.js Socket.IO server, /emit endpoint, room broadcast |
app/include_ending.php |
Initializes window._socket before page scripts run |
app/dashboard/index.php |
WMS dashboard: 4 socket listeners, 4 section-reload functions, flash_card() |
app/ac_dashboard/index.php |
Accounting dashboard: own listener pattern, flash_card() |