A real-time sales operations dashboard. Sales stream in over a WebSocket and the panel updates instantly — live KPIs, a revenue trend, product performance, a regional bubble map, a conversion gauge and a streaming order feed. Built with a Node.js + Express + WebSocket backend, PostgreSQL (with a zero-config in-memory fallback) and a React + Vite + Chart.js frontend dressed in the "Pulse" teal-cyan theme.
Stack: Node.js (ESM) · Express ·
ws· PostgreSQL / pg-mem · React 18 · Vite · Chart.js Port:5252· Theme: Pulse (dark + light) · Backend default: pg-mem (zero-config)
- Live overview — Revenue, Orders, Average Order Value and Conversion Rate KPIs, each with a period-over-period delta and an inline sparkline.
- Real-time updates — a server-side sale simulator and every manually recorded sale broadcast over a WebSocket; the dashboard feed, the live badge and (debounced) the aggregate charts refresh the instant a sale lands.
- Revenue trend — area line chart, switchable between day / week / month grouping and 7 / 30 / 90-day ranges.
- Sales volume — orders-per-period bar chart with rich hover tooltips.
- Product performance — top 5 products by revenue with share bars.
- Regional sales — custom SVG bubble map; bubble size encodes revenue per region, with on-hover detail.
- Conversion gauge — semicircular gauge of orders ÷ visits.
- Channel mix — revenue doughnut across web / mobile / marketplace / in-store.
- Order stream — paginated, resizable orders table; record a sale from a modal and watch it propagate live.
- Product catalog — 220 seeded products, searchable, category-filterable, paginated, with drag-to-resize columns.
- System log monitor — in-app live log tail with level filtering.
- Exports — real
.xlsx(ExcelJS) and.pdf(PDFKit) report downloads. - Dark / light theme — persisted to
localStorage, initialised fromprefers-color-scheme, toggled from the top bar. - Collapsible sidebar — a floating edge-chevron collapses the rail to icons.
No database, no Docker. The server boots an in-memory PostgreSQL (pg-mem), seeds 220 products / 8 regions / 3,000+ orders automatically, and starts the live simulator.
npm run setup # install server + client deps, then build the client
npm start # → http://localhost:5252npm run setup is shorthand for: install root deps, build the client
(cd client && npm install && npm run build). After it finishes, npm start
serves the built SPA and the API on the same port.
Run the API and the Vite dev server side by side:
npm install
npm run dev # API + simulator on :5252
cd client && npm install && npm run dev # Vite on :5300, proxies /api and /ws → :5252Open http://localhost:5300 for the hot-reloading client.
docker compose up -d
export DATABASE_URL=postgres://sales:sales@localhost:5432/salesrealtime # PowerShell: $env:DATABASE_URL="..."
npm run seed # one-time: populate the database
npm startWhen DATABASE_URL is set the app uses the real pg driver; otherwise it falls
back to pg-mem. The SQL is identical either way.
| Script | What it does |
|---|---|
npm start |
Run the server (serves API, WebSocket and built client) |
npm run dev |
Run the server with --watch |
npm run seed |
Reseed the database (mainly for real PostgreSQL) |
npm run build |
Install client deps and build the SPA into client/dist |
npm run setup |
Install everything and build the client |
SalesRealtime Dashboard/
├── server/
│ ├── index.js # Express app, HTTP server, WS init, static SPA, bootstrap
│ ├── db.js # pg / pg-mem abstraction + schema
│ ├── seed.js # 220 products, 8 regions, 3k orders, 60d traffic
│ ├── sales.js # recordSale() — insert order + broadcast live
│ ├── simulator.js # periodic random sales → broadcast (the "real-time" engine)
│ ├── analytics.js # dashboard aggregations (SQL roll-ups + JS time bucketing)
│ ├── export.js # ExcelJS + PDFKit report streams
│ ├── ws.js # WebSocket broadcast hub
│ ├── logger.js # fire-and-forget log writer
│ └── routes/ # dashboard · products · sales · logs · export
└── client/
└── src/
├── App.jsx # app shell: sidebar + topbar + WS-driven live badge
├── views/ # Dashboard · Products · Sales · Logs
├── components/ # Charts · RegionalMap · ConversionGauge · ResizableTable · …
├── useWebSocket.js # auto-reconnecting WS hook
├── theme.jsx # dark/light context
└── styles.css # the "Pulse" design system
simulator.jsfires every 4–9s, inserts a random order viarecordSale(), and bumps today's visit count so conversion stays realistic.recordSale()(also used by thePOST /api/salesendpoint) writes the order and callsbroadcast('sale', …)inws.js.- The browser's
useWebSockethook receives the frame;App.jsxincrements the live badge and hands the sale to the dashboard, which prepends it to the feed instantly and debounces a full refresh so the KPIs and charts catch up.
- The dashboard reads ranged roll-ups via SQL
GROUP BY; time-series bucketing (day/week/month) is done in JS so it behaves identically on pg-mem and PostgreSQL without depending ondate_trunc/to_char. - PostgreSQL returns
NUMERIC/BIGINTaggregates as strings, so values are coerced withNumber()on the way out.
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/health |
Status + active backend |
| GET | /api/dashboard?range=&group= |
Full dashboard payload (KPIs, charts, feed) |
| GET | /api/dashboard/timeseries |
Revenue/order time-series only |
| GET | /api/products?page=&search=&category= |
Paginated catalog |
| GET | /api/products/categories |
Category list |
| GET | /api/sales?page= |
Paginated orders |
| GET | /api/sales/meta |
Products + regions for the record-sale form |
| POST | /api/sales |
Record a sale → broadcasts live |
| GET | /api/logs?limit=&level= |
Recent log entries |
| GET | /api/export/excel?range= |
.xlsx report download |
| GET | /api/export/pdf?range= |
.pdf report download |
| WS | /ws |
Live sale broadcast channel |
Responses follow a consistent envelope: { success, data, meta? }.
- The bundled pg-mem backend is in-memory: data resets on each restart and is re-seeded automatically. Use the PostgreSQL path for durable storage.
- The
esbuildadvisory surfaced bynpm auditaffects Vite's dev toolchain only; the shipped artifact is static HTML/CSS/JS served by Express, so esbuild is not part of the runtime.
Apache License 2.0 — see LICENSE.