src/lib/agent/images.ts's generateImage posts MiniMax image-01's
documented request shape (verified against the installed mmx-cli SDK:
POST {baseUrl}/v1/image_generation with
{model, prompt, aspect_ratio, n, response_format}) and reads
data.image_base64[0]. A base_resp.status_code of 1028/1030/2061 (the
same codes the CLI itself maps to a "needs Plus plan" hint for this
endpoint) surfaces as a clear, non-retried error rather than a generic
failure. generate_image (tools-actions.ts) stores the result as its own
append-only ask_messages row (kind: "image") and returns its url.
extractGeneratedImages splits a tool result the way extractCharts
already splits chart numbers — the browser gets the full image, the
model keeps only imagesAvailable: [{id}] so it can place
[[image:<id>]] without ever seeing pixels or a URL. answer-parts.ts's
splitAnswer now takes an imageIds set alongside chartIds (existing
chart-only callers keep working via a default empty set), and
AnswerMarkdown renders the image inline.
The pb-files proxy (src/app/api/pb-files/.../route.ts) now also serves
ask_messages (previously only delivery_notes/purchase_orders): since
that collection's rules are all null by design (Task 1), this one
collection mints a short-lived admin file token server-side rather
than exposing the underlying PB rule.
Live check skipped (both the chat flow and MiniMax call): reaching
generate_image needs a thread, which needs ask_threads (not yet
applied). Covered by unit tests against the documented request/response
shape and error codes instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
||
|---|---|---|
| .claude | ||
| .freebuff | ||
| .opencode/openwork/inbox/chat-attachments | ||
| .playwright-mcp | ||
| .superpowers/sdd/2026-09-18-hybrid-delivery-note-ingestion | ||
| docs | ||
| public | ||
| RAW_DOCS | ||
| scripts | ||
| src | ||
| .dockerignore | ||
| .env.coolify | ||
| .env.local.example | ||
| .gitignore | ||
| AGENTS.md | ||
| cedar_gate_fuel_logs_sep2026.csv | ||
| cedar_gate_summary_sep2026.csv | ||
| CLAUDE.md | ||
| cooler.env | ||
| COOLIFY.md | ||
| Copy of THE HILLS WAGES HRS AUGUST 2026.xlsx | ||
| deployment-vptid1uo0jnz2q4t1mcbqjj9-all-logs-2026-08-19-00-23-06.txt | ||
| docker-compose.yml | ||
| Dockerfile | ||
| eslint.config.mjs | ||
| fuel_logs_duplicates_2026-09-10.csv | ||
| next.config.js | ||
| package-lock.json | ||
| package.json | ||
| postcss.config.mjs | ||
| README.md | ||
| tsconfig.json | ||
| Vehicle Fuel Summary for Mega Civils- The Hills- 01_09_2026 to 22_09_2026.csv | ||
| vitest.config.ts | ||
MEGA INFRASTRUCTURE — Operations Dashboard
A Next.js 16 + TypeScript + Tailwind v4 operations dashboard for a South African civil construction company, backed by PocketBase. Tracks, per construction site: contracts, employees & timesheets, vehicles (fuel + plant), fuel logs, plant hour meters, fixed costs, income claims, purchase orders + line items, and delivery notes with OCR-driven receipting. Includes an LLM chat ("Ask") with a read-only tool layer over the same data, and a read-only MCP server.
Default login PIN is
2468. ChangeNEXT_PUBLIC_APP_PINin production. There is no PIN bypass — the old?demo=1shortcut was removed on 2026-09-26.
Quick start
cp .env.local.example .env.local
# edit NEXT_PUBLIC_POCKETBASE_URL, POCKETBASE_ADMIN_*, N8N_*
npm install
npm run dev # next dev -H 0.0.0.0 -p 6004 --webpack
Production build / start:
npm run build # icons + og-image + next build --webpack
npm run start # next start -H 0.0.0.0 -p 3000
Docker / Coolify: see COOLIFY.md and the
Dockerfile.
MCP server (stdio, read-only):
npm run mcp # uses the PocketBase creds in .env.local
Modules
The sidebar (src/components/layout/sidebar.tsx) has four top-level
pages and three groups. Each is documented in
docs/modules.md.
| Path | Module | What it shows |
|---|---|---|
/dashboard |
Overview | KPI tiles, fuel-attention card grouped by site, 7-day rain outlook, every site |
/dashboard/sites, /dashboard/sites/[id] |
Sites | Per-site profitability, burn, P&L, payroll, weekly & monthly trends |
/dashboard/workforce |
Workforce | Landing: people cost this week |
/dashboard/workforce/workers[/[employeeId]] |
Workers | Every worker, hours, cost, printable QR badge |
/dashboard/workforce/timesheets |
Timesheets | Every entry from every source; spreadsheet import |
/dashboard/workforce/on-site |
On site now | Today's MEGA Clock attendance + Needs attention (missed scan-outs) |
/dashboard/plant |
Plant | Landing: fuel pumped, hours run, cost |
/dashboard/plant/vehicles[/[vehicleId]] |
Vehicles (L/km) | Fleet fuel efficiency, variance, dismissable attention |
/dashboard/plant/equipment[/[vehicleId]] |
Equipment (L/h) | Plant hour-meters, L/h, billable value |
/dashboard/plant/fuel-logs |
Fuel logs | Every fuel log across both buckets |
/dashboard/plant/map |
Map | Leaflet view of each vehicle's last scanned position |
/dashboard/financial |
Financial | Landing: open PO value, slips received, uploads waiting |
/dashboard/financial/purchase-orders[/[poId]] |
Purchase Orders | Every PO, fulfilment %, line editing, verify |
/dashboard/financial/delivery-notes (+ /inbox, /work-queue, /document/[id]) |
Delivery Notes | Upload → OCR → match → post pipeline and its review queues |
/dashboard/financial/suppliers[/[supplierId]] |
Suppliers | Ordered / delivered / outstanding per supplier |
/dashboard/chat |
Ask | Read-only LLM chat with the same metrics the dashboard uses |
/clock |
MEGA Clock | Installable phone PWA: worker badge scan in/out + vehicle hour-meter scan |
Old paths (/dashboard/employees, /dashboard/fuel[/id],
/dashboard/purchasing[/id], /dashboard/plant/[id]) still work — they
redirect to the pages above and keep ?site=.
Documentation
| Document | What's in it |
|---|---|
docs/architecture.md |
Runtime topology, the two PB clients, rendering model, auth model, build/deploy |
docs/data-model.md |
Every PocketBase collection, fields, relations, the project_code denormalisation, indexes, API rules |
docs/modules.md |
One section per dashboard module — what it reads, computes, lets the user do |
docs/data-flows.md |
End-to-end ingestion pipelines with sequence diagrams (timesheets, fuel, plant, PO, DN) |
docs/api-reference.md |
Every API route — method, auth, request/response, error cases |
docs/operations.md |
Env vars, scripts, tests, feature flags, deploy / smoke test |
docs/multi-site-assessment.md |
The main deliverable — will this app hold up at 50 sites? Where does it break, and what to fix in what order? |
docs/clock-daily-exceptions-rollout.md |
MEGA Clock new-day scans and the missed scan-out correction flow |
docs/pwa-install-verification.md |
Checking the dashboard / clock PWAs install correctly on iOS + Android |
docs/n8n/ |
The n8n PO-ingest and DN-OCR workflows |
docs/setup/env-and-mcp.md |
Local env + MCP client setup |
The docs/superpowers/ directory has older design notes; treat as
historical, not authoritative.
Tech stack
- Next.js 16 (App Router, modified build — see
AGENTS.md) - React 19 + TypeScript 5.7
- Tailwind v4 (via
@tailwindcss/postcss) - PocketBase 0.27 (PocketBase v0.22+ schema shape, see
scripts/pb-bootstrap-schema.py) - SheetJS (xlsx) + PapaParse (csv) for imports
- Recharts for charts
- Vitest for tests
@ducanh2912/next-pwa(Webpack-only —next dev/buildpass--webpackto support it)- Anthropic or MiniMax as the chat LLM provider
API surface (summary)
| Route group | Auth | Notes |
|---|---|---|
GET /api/health |
none | Docker healthcheck |
POST /api/sites, PATCH /api/sites/[id], POST /api/sites/[id]/fixed-costs |
server-only | Sites CRUD |
POST / PATCH / DELETE /api/vehicles[ /[id]] |
server-only | Vehicles CRUD (unified fuel + plant) |
POST /api/plant-hours/import, GET /api/plant-hours/template, GET /api/plant-hours/exception-report/[sheetId], DELETE /api/plant-hours/[id] |
server-only | Plant hours ingest + reporting |
POST /api/timesheets/import |
server-only | Timesheet ingest (three formats auto-detected) |
POST /api/fuel-logs/import |
server-only | Fuel log ingest |
POST /api/purchase-orders/upload, POST /api/purchase-orders/process-batch (n8n), GET /api/purchase-orders/by-number/[poNumber]/items, PATCH /api/purchase-orders/[id]/supplier |
server-only / x-ingest-secret |
PO ingest pipeline |
POST /api/delivery-notes/upload, GET /api/delivery-notes/[id]/slip (n8n), POST /api/delivery-notes/[id]/ocr-result (n8n), POST /api/delivery-notes/[id]/assign-po |
server-only / x-ingest-secret |
DN pipeline + manual fallback |
GET / POST /api/data-import-batches/[id]/status|retry |
server-only | Batch lifecycle |
POST /api/records/[collection]/[id]/verify |
server-only | Verify toggle for POs and DNs |
POST /api/purchase-orders/ingest, GET …/[id]/status, POST …/[id]/retry, GET …/[id]/document, GET …/[id]/pages/[n], POST …/[id]/items, PATCH / DELETE …/[id]/items/[itemId] |
server-only / x-ingest-secret (ingest) |
PO queue, source document, and manual line edits (blocked once verified) |
POST /api/dn-batches/tick (cron), …/[id]/rerun, …/[id]/dismiss, …/[id]/assembly, GET …/[id]/pages/[n], …/pages/[n]/dismiss |
x-ingest-secret (tick) / server-only |
Staged delivery-note batches |
PATCH /api/dn-batches/[id]/notes/[noteId], …/resolve-entities, …/resolve-item, …/post, …/dismiss, …/correct-receipt |
server-only / RECEIPT_CORRECTION_SECRET (correct-receipt) |
Correct, match, post, or stand down one candidate note |
POST /api/suppliers, PATCH /api/suppliers/[id] |
server-only | Supplier create / rename |
POST /api/vehicles/import-summary |
server-only | Sync the Buildsite Vehicle Fuel Summary CSV into vehicles |
POST /api/vehicles/[id]/merge |
server-only | Merge a duplicate vehicle into another (dry run unless confirm) |
GET /api/pb-files/[collection]/[recordId]/[...filename] |
server-only | Same-origin proxy for PocketBase files (PDF slips in iframes) |
POST /api/chat |
ENABLE_CHAT=true (no user auth) |
SSE chat stream |
GET /api/clock/employee-lookup, POST /api/clock/scan, POST /api/clock/vehicle-lookup, POST /api/clock/vehicle-scan |
server-only | MEGA Clock PWA: badge clock-in/out + vehicle hour-meter scan |
POST /api/clock/events/[id]/correct |
same-origin + x-app-pin = APP_PIN |
Record the real leaving time for a missed scan-out |
Full reference: docs/api-reference.md.
Tests
npm run typecheck # tsc --noEmit
npm run test:run # vitest run
npm run lint # eslint . — not yet clean, see docs/operations.md §4.1
The metric helpers under src/lib/metrics/*.test.ts pin every
computation the dashboard exposes. Run after any change to the
financial roll-up.
License
Proprietary. Internal use only.