115 lines
3.6 KiB
Markdown
115 lines
3.6 KiB
Markdown
# MEGA INFRASTRUCTURE — Operations Dashboard
|
||
|
||
A **Next.js 16 + TypeScript + Tailwind v4** dashboard for a construction company.
|
||
Tracks fuel consumption per hour/day/week, delivery notes & outstanding items
|
||
per purchase order, and weekly workforce hours with attendance flags.
|
||
|
||
The project is **frontend-first with mock data**, ready to be wired up to
|
||
PocketBase (or any backend) for live data.
|
||
|
||
---
|
||
|
||
## Modules
|
||
|
||
| Path | What it does |
|
||
| --- | --- |
|
||
| `/login` | Numeric PIN gate. PIN lives in `NEXT_PUBLIC_APP_PIN`. Append `?demo=1` to any URL to skip the PIN during development. |
|
||
| `/dashboard` | Operations overview — KPI cards, fuel chart, order status donut, "needs attention" rail, top suppliers. |
|
||
| `/dashboard/fuel` | Per-vehicle consumption, theft detection, CSV ingest (papaparse), anomaly log. |
|
||
| `/dashboard/orders` | Purchase-order table with filters, pagination, sort. Click any PO to see line items, delivery notes, outstanding items. |
|
||
| `/dashboard/employees` | Weekly hours per employee with progress bar, overtime flag, role/site/attendance filters, mini daily-hours chart. |
|
||
|
||
---
|
||
|
||
## APIs
|
||
|
||
- `GET /api/health` — counts and site list.
|
||
- `GET /api/mcp?module=all|fuel|orders|employees` — compact JSON for an AI
|
||
agent / MCP server / n8n workflow.
|
||
- `POST /api/upload` — multipart upload of PDF / image / CSV (delivery notes,
|
||
fuel attachments). 20 MB limit per file.
|
||
|
||
---
|
||
|
||
## PWA
|
||
|
||
The app is installable on phone and desktop. Service worker is generated by
|
||
`@ducanh2912/next-pwa` at build time (`public/sw.js`), manifest at
|
||
`public/manifest.json`, icons at `public/icons/`.
|
||
|
||
> **Note:** `@ducanh2912/next-pwa` uses the **Webpack** plugin, so `next dev`
|
||
> and `next build` must run with `--webpack`. Next 16 ships Turbopack by
|
||
> default; the `package.json` scripts already pass `--webpack`.
|
||
|
||
---
|
||
|
||
## Run
|
||
|
||
```bash
|
||
cp .env.local.example .env.local # change NEXT_PUBLIC_APP_PIN
|
||
npm install
|
||
npm run dev # http://localhost:3000
|
||
```
|
||
|
||
Default PIN is **`2468`**.
|
||
|
||
### Production
|
||
|
||
```bash
|
||
npm run build
|
||
npm run start
|
||
```
|
||
|
||
---
|
||
|
||
## How the theft detection works
|
||
|
||
`src/lib/fuel.ts` defines `EXPECTED_LITRES_TOLERANCE_PCT = 12`. For every
|
||
reading we compute:
|
||
|
||
```
|
||
expectedLitres = hoursWorked * expectedConsumptionPerHour
|
||
deltaPct = (litresFilled - expectedLitres) / expectedLitres * 100
|
||
```
|
||
|
||
Severity buckets:
|
||
|
||
| Over expected | Severity |
|
||
| --- | --- |
|
||
| ≤ 12 % | `ok` |
|
||
| 12 – 24 % | `watch` |
|
||
| 24 – 48 % | `alert` |
|
||
| > 48 % | `critical` |
|
||
|
||
Adjust `EXPECTED_LITRES_TOLERANCE_PCT` to suit on-site practice.
|
||
|
||
---
|
||
|
||
## CSV format for fuel upload
|
||
|
||
Required columns:
|
||
|
||
```
|
||
vehicleRegistration,date,hoursWorked,odometer,litresFilled,unitPrice,expectedConsumptionPerHour
|
||
MI-EX-01,2026-08-01,9.5,12480,90,24.50,9
|
||
```
|
||
|
||
The "Template" button on the Fuel page downloads a populated example.
|
||
|
||
---
|
||
|
||
## Wiring a backend
|
||
|
||
Replace the imports in `src/app/dashboard/*/page.tsx` and
|
||
`src/app/api/mcp/route.ts` with calls into PocketBase / your API. The shape
|
||
of every type is in `src/lib/types.ts`.
|
||
|
||
Recommended PocketBase collections:
|
||
|
||
- `vehicles` (registration, type, model, tankCapacityL, assignedSite)
|
||
- `fuel_readings` (vehicleId, date, hoursWorked, odometer, litresFilled, unitPrice, expectedConsumptionPerHour, attachment?)
|
||
- `orders` (reference, supplier, site, status, priority, placedAt, expectedAt, totalValue, notes)
|
||
- `order_lines` (orderId, description, quantityOrdered, quantityDelivered, unit, unitPrice)
|
||
- `delivery_notes` (orderId, scannedBy, file, mimeType, receivedAt)
|
||
- `employees` (fullName, role, site, status, phone, hourlyRate, weeklyTargetHours)
|
||
- `daily_hours` (employeeId, date, hours)
|