Dynamic KPI analytics dashboard for industrial safety monitoring with real-time safety metrics, close-call detection, and customizable analytics.
One command to run the full stack (backend + frontend):
docker compose up --build -dThis will:
- Build and start the backend (auto-seeds SQLite with 100k rows on first run)
- Start the frontend (production preview)
- Expose API at http://localhost:3001 and UI at http://localhost:8080
Verify:
curl http://localhost:3001/api/health
open http://localhost:8080See TESTING.md for scenario-wise curl examples.
Useful Docker commands:
docker compose ps
docker compose logs -f backend
docker compose down # stop services
docker compose down -v # stop and reset database volumeRun services locally only if you are not using Docker.
Check health:
curl http://localhost:3001/api/healthkpi-builder-sprint/
βββ backend/ # Express API server
β βββ src/
β β βββ server.ts # Main server
β β βββ db.ts # Database connection & queries
β β βββ routes.ts # API endpoints
β βββ data/
β β βββ seed_sqlite.py # Python seeding script
β β βββ work-package-raw-data.csv # 100k detection records
β βββ Dockerfile
β βββ package.json
βββ frontend/ # React frontend
β βββ src/
β β βββ components/ # UI components
β β βββ lib/
β β βββ api.ts # Backend API client
β βββ package.json
βββ TESTING.md # Scenario-wise curl examples
βββ docker-compose.yml # Docker orchestration
βββ Dockerfile # Docker build configuration
βββ start.sh # Docker startup script
βββ package.json # Root package.json with workspaces
βββ README.md
GET /api/health
POST /api/detections
Body: { filters: {...}, limit: 100, offset: 0 }
POST /api/aggregate
Body: { metric: 'count', filters: {...}, groupBy: 'hour' }
POST /api/close-calls
Body: { filters: { timeRange: {...} }, distance: 2.0 }
GET /api/vest-violations?from=2025-01-01&to=2025-01-07
GET /api/overspeed?from=2025-01-01&to=2025-01-07&threshold=1.5
The project includes comprehensive test suites for both frontend and backend:
- Backend Unit Tests: Jest-based API endpoint and business logic tests
- Backend Ground Truth Tests: Validates against real dataset (100k records)
- Frontend Tests: React component testing with Vitest and Testing Library
# Backend unit tests
cd backend && npm test
# Backend ground truth tests (uses real database)
cd backend && npx jest --config jest.groundtruth.config.js
# Frontend tests
cd frontend && npm test
# All tests (if configured)
npm run test:all- β Ground Truth Tests: 4/4 passing (validates vest filter logic)
- β Frontend Tests: 20/20 passing (all UI components)
β οΈ Backend Unit Tests: Some failures due to test database setup (non-critical)
docker run --rm -it \
-v "$PWD/backend":/app -w /app \
-e NODE_ENV=test -e TEST_SQLITE_PATH=./test_kpi_builder.sqlite \
node:18-alpine sh -lc "npm ci && npm test"# Health check
curl -s http://localhost:3001/api/health | jq .
# Get sample data
curl -s -X POST http://localhost:3001/api/detections \
-H 'Content-Type: application/json' \
-d '{"limit":5}' | jq .
# Test aggregation
curl -s -X POST http://localhost:3001/api/aggregate \
-H 'Content-Type: application/json' \
-d '{"metric":"count","groupBy":"hour"}' | jq .- Health Check:
{"status": "healthy", "recordCount": 100000} - Detections: Array of detection objects with coordinates, timestamps, etc.
- Aggregations: Time-series data for charts and KPIs
The dataset contains 100,000 detection records from April 2, 2025 (15:42:06 to 16:00:18 UTC).
- Total Human Detections: 28,323
- Total Vehicle Detections: 71,677
- Vest Violations (human, vest=0): 10,312
- Overspeed Events (speed > 1.5 m/s): 16,599
- Human: 15,013
- Vehicle: 1,586
- Close Calls (distance β€ 2.0m, time β€ 250ms): 0
# Test vest violations
curl -X POST http://localhost:3001/api/aggregate \
-H 'Content-Type: application/json' \
-d '{
"metric": "vest_violations",
"filters": {
"timeRange": {"from": "2025-04-02T15:42:06Z", "to": "2025-04-02T16:00:19Z"}
},
"groupBy": "day"
}' | jq '.meta.filteredRecords'
# Expected: 10312
# Test overspeed by class
curl -X POST http://localhost:3001/api/aggregate \
-H 'Content-Type: application/json' \
-d '{
"metric": "overspeed",
"filters": {
"timeRange": {"from": "2025-04-02T15:42:06Z", "to": "2025-04-02T16:00:19Z"},
"classes": ["human", "vehicle"]
},
"groupBy": "class"
}' | jq '.series'
# Expected: [{"label": "human", "value": 15013}, {"label": "vehicle", "value": 1586}]
# Test total overspeed events
curl -X POST http://localhost:3001/api/aggregate \
-H 'Content-Type: application/json' \
-d '{
"metric": "overspeed",
"filters": {
"timeRange": {"from": "2025-04-02T15:42:06Z", "to": "2025-04-02T16:00:19Z"},
"classes": ["human", "vehicle"]
},
"groupBy": "day"
}' | jq '.meta.filteredRecords'
# Expected: 16599
# Test close calls (should be 0)
curl -X POST http://localhost:3001/api/close-calls \
-H 'Content-Type: application/json' \
-d '{
"filters": {
"timeRange": {"from": "2025-04-02T15:42:06Z", "to": "2025-04-02T16:00:19Z"}
},
"distance": 2.0
}' | jq '.series | map(.value) | add'
# Expected: 0Install all dependencies:
npm run install:allStart backend:
./scripts/start-backend.sh
# or
npm run dev:backendStart frontend:
./scripts/start-frontend.sh
# or
npm run dev:frontendStart both (development):
npm run devUse the commands above. Avoid running the local backend on port 3001 while Docker is up to prevent EADDRINUSE.
- β 100k+ detection records auto-seeded with Python
- β SQLite database with optimized indexing
- β RESTful API with flexible filtering and aggregation
- β Close-call detection (< 2m proximity algorithm)
- β Vest violation tracking for safety compliance
- β Overspeed monitoring with configurable thresholds
- β Dynamic KPI builder with custom metrics
- β Interactive charts (line, bar, area)
- β Real-time filtering by class, area, time range
- β Dockerized for easy deployment
- β Comprehensive test suites
- β Separate startup scripts for development
"Database not seeded"
# Check if database file exists
ls -la backend/data/kpi_builder.sqlite
# Re-run seeding script
cd backend/data && python3 seed_sqlite.py"Backend won't start"
# Check if port is in use
lsof -i:3001
# Kill existing process
pkill -f "node.*server"
# Restart backend
./scripts/start-backend.shIf you see EADDRINUSE: address already in use :::3001 when running locally:
# Find and kill the process holding 3001
lsof -i:3001
kill -9 <PID>
# Or run on another port temporarily
PORT=3002 npm run dev:backendThe server is guarded to not auto-start under tests (NODE_ENV=test). Avoid running the backend locally while Docker is up (both bind port 3001).
"Frontend can't reach API"
# Check if backend is running
curl http://localhost:3001/api/health
# Check frontend environment
cat frontend/.env"Docker issues"
# Check Docker status
docker-compose ps
# View logs
docker-compose logs backend
# Reset everything
docker-compose down -v
./start.shMIT
The project includes comprehensive test suites with the following status:
β Working Test Suites:
- Ground Truth Tests: 4/4 passing - Validates vest filter logic against real dataset
- Frontend Tests: 20/20 passing - All React components and UI interactions
- API Integration Tests: Core endpoints working correctly
- Backend Unit Tests: Some failures due to test database setup (non-critical for demo)
Test Coverage:
- β Vest filter logic (vest=0, vest=1, vest=2)
- β Ground truth validation (10,312 vest violations, 18,011 vest worn, 28,323 total)
- β API endpoints with various filters
- β Frontend component rendering and interactions
- β Error handling and edge cases
- β Data validation and type checking
Run Tests:
# Ground truth tests (recommended for demo)
cd backend && npx jest --config jest.groundtruth.config.js
# Frontend tests
cd frontend && npm test
# Backend unit tests (has some failures)
cd backend && npm test