Mini internal dashboard that shows near real-time game analytics with basic CRUD-ish behavior (create + list + summary). Uses an in-memory backend store with a periodic mock data mutator so the UI changes without manual input.
- Short polling (~4s) for list + summary
- Debounced filters (~350ms) so typing doesn’t trigger request-per-keystroke
- Chart demo (Recharts): value-over-time line chart (last ~30 events)
- Nice-to-have: drag-and-drop reorder for chart cards within the Analytics Charts section (dnd-kit)
- Light/Dark mode: system + manual toggle (persisted)
- Node.js: latest LTS (Node 22+ recommended)
- npm: comes with Node
cd backend-express
npm install
npm run devBackend starts on http://localhost:4000.
cd backend
npm install
npm run dev- Defaults to
http://localhost:4000(same as Express). - If
backend/is already running, either stop it or run Nest on another port:
cd backend
PORT=4001 npm run dev- Swagger UI:
http://localhost:<PORT>/api-docs - OpenAPI JSON:
http://localhost:<PORT>/api-json - Scalar UI:
http://localhost:<PORT>/api-reference
cd frontend
npm install
npm run devFrontend starts on http://localhost:5173.
PORT: defaults to4000CORS_ORIGIN: defaults to*MUTATE_EVERY_MS: defaults to3500(randomly appends/mutates data for polling demo)
VITE_API_URL: defaults tohttp://localhost:4000
Example:
# frontend/.env
VITE_API_URL=http://localhost:4000Returns analytics entries (newest first). Supports simple filtering:
game: exact matcheventType: exact matchq: substring search overgame,eventType,userIdpage: 1-based page index (default1)pageSize: items per page (default25, max100)
Examples:
GET /analytics?game=SpaceRacerGET /analytics?eventType=purchaseGET /analytics?q=u_042
Response:
{
"data": [ { "id": "...", "timestamp": "...", "game": "...", "eventType": "...", "value": 123.45 } ],
"meta": { "page": 1, "pageSize": 25, "total": 123, "totalPages": 5 }
}Creates a new analytics entry. Backend auto-generates id + timestamp.
Body:
{ "game": "SpaceRacer", "eventType": "level_complete", "value": 100, "userId": "u_042" }Response:
{ "data": { "id": "...", "timestamp": "...", "game": "...", "eventType": "...", "value": 100, "userId": "u_042" } }Aggregated stats over the in-memory store:
totalCountaverageValue
Response:
{ "data": { "totalCount": 123, "averageValue": 456.78 } }Returns chart-ready datasets computed on the backend from the full in-memory store (so the frontend does not do aggregation).
Response (shape):
{
"data": {
"trend": { "points": [ { "timestamp": "...", "value": 12.34, "game": "...", "eventType": "..." } ] },
"eventsByGame": [ { "game": "SpaceRacer", "count": 42 } ],
"eventsByType": [ { "eventType": "purchase", "count": 10 } ],
"avgValueByGame": [ { "game": "PixelWars", "avg": 123.45 } ]
}
}Video Youtube link (use FSCapture): https://youtu.be/rY-uYpipD_s




