Updated the operations dashboard to include a rain outlook feature, providing users with a 7-day forecast based on site coordinates. Added latitude and longitude fields to the site form for better weather data integration. Enhanced the fuel log and site management functionalities to utilize dynamic fuel pricing based on historical data. Improved overall data handling and user experience across various dashboard components.
277 lines
14 KiB
Markdown
277 lines
14 KiB
Markdown
# Prompt for MiniMax agent — Document the app + assess multi-site readiness
|
||
|
||
> Paste everything below the line into the MiniMax agent. It assumes the agent has
|
||
> read/write access to this repository.
|
||
|
||
---
|
||
|
||
## Role
|
||
|
||
You are a senior engineer joining the **MEGA INFRASTRUCTURE** codebase. Your job has two
|
||
parts, in this order:
|
||
|
||
1. **Write the missing technical documentation** for the app as it exists today.
|
||
2. **Deliver a multi-site readiness assessment**: can this app genuinely run many
|
||
construction sites at once right now, and what specifically has to change for it to do
|
||
so reliably at scale?
|
||
|
||
You are producing **documentation and analysis only**. Do **not** change application code,
|
||
schema, or config. The only files you create or modify are the deliverables listed at the
|
||
bottom.
|
||
|
||
---
|
||
|
||
## Ground rules
|
||
|
||
- **Evidence over assumption.** Every factual claim you make must cite a real
|
||
`path/to/file.ts:line`. If you cannot find evidence for something, write "unverified" and
|
||
say what you looked at.
|
||
- **The `README.md` at the repo root is stale and actively wrong.** It describes an earlier
|
||
mock-data version. It references `src/lib/fuel.ts`, `src/lib/types.ts`,
|
||
`src/app/api/mcp/`, `src/app/api/upload/`, and `/dashboard/orders` — **none of these
|
||
exist**. Do not use it as a source. Treat the source code as the only truth.
|
||
- **Read `AGENTS.md` first.** This is a modified build of Next.js 16; conventions may differ
|
||
from what you expect. Authoritative docs live in `node_modules/next/dist/docs/`. Read the
|
||
relevant guide there before making any claim about Next.js behaviour (caching, dynamic
|
||
rendering, route handlers, server components).
|
||
- **Do not invent severity.** Distinguish clearly between: (a) a bug that breaks correctness
|
||
today, (b) a limit that will break at a known threshold, (c) a design choice that is fine
|
||
now but will cost more to change later.
|
||
- Verify claims by reading code. Where useful, run `npm run typecheck` and `npm run test:run`
|
||
to confirm the suite's current state, and report the actual output.
|
||
|
||
---
|
||
|
||
## What the app is
|
||
|
||
A Next.js 16 (App Router, React 19, Tailwind v4) operations dashboard for a South African
|
||
civil construction company, backed by **PocketBase**. It tracks, per construction site:
|
||
sites/contracts, employees & timesheets, vehicles (fuel and plant units), fuel logs, plant
|
||
hour meters, fixed costs, income claims, purchase orders + line items, and delivery notes
|
||
with OCR-driven receipting. There is also an LLM chat ("Ask") surface with a read-only tool
|
||
layer over the same data, and an MCP server.
|
||
|
||
Key entry points to orient yourself:
|
||
|
||
| Area | Path |
|
||
| --- | --- |
|
||
| PocketBase clients (`pb` browser, `pbAdmin` superuser) | `src/lib/pb.ts` |
|
||
| Full collection type definitions | `src/lib/pb-types.ts` |
|
||
| Dashboard pages (server components) | `src/app/dashboard/**/page.tsx` |
|
||
| Route handlers | `src/app/api/**/route.ts` |
|
||
| Pure metric/calculation functions | `src/lib/metrics/` |
|
||
| Import parsers (timesheets, fuel, plant hours, POs) | `src/lib/*-parse.ts`, `src/lib/*-import.ts` |
|
||
| Chat/agent tool layer | `src/lib/agent/`, `src/lib/chat/` |
|
||
| Auth (PIN gate) | `src/hooks/use-auth.tsx`, `src/components/auth-gate.tsx` |
|
||
| Schema bootstrap / rules scripts | `scripts/pb-bootstrap-schema.py`, `scripts/pb-relax-rules.py` |
|
||
| Existing design/plan docs | `docs/superpowers/specs/`, `docs/superpowers/plans/` |
|
||
|
||
---
|
||
|
||
## Part 1 — Documentation to write
|
||
|
||
Write these as Markdown into `docs/`. Aim for documents a new engineer could use to become
|
||
productive without reading every file, and that a non-technical owner could use to
|
||
understand what the system does.
|
||
|
||
### 1. `docs/architecture.md`
|
||
|
||
- Runtime topology: Next.js app → PocketBase → n8n webhooks (PO ingest, delivery-note OCR)
|
||
→ LLM providers. Draw it as a Mermaid diagram.
|
||
- Rendering model: which pages are server components, what `export const dynamic` settings
|
||
are in use, where data fetching happens, and what is client-side.
|
||
- The two PocketBase clients and **when each is used**. Be explicit about the fact that
|
||
server routes and pages authenticate as the PocketBase **superuser** via `pbAdmin()`
|
||
(`src/lib/pb.ts`) and what that implies.
|
||
- Auth model as it actually is today: a shared numeric PIN from `NEXT_PUBLIC_APP_PIN`
|
||
(default `2468`), held in `sessionStorage`, with a `?demo=1` query-string bypass
|
||
(`src/hooks/use-auth.tsx`). State plainly that there is **no per-user identity** in the
|
||
application.
|
||
- Build/deploy: `Dockerfile`, `docker-compose.yml`, `COOLIFY.md`, the `--webpack` requirement
|
||
for the PWA plugin, and the icon/OG-image prebuild scripts.
|
||
|
||
### 2. `docs/data-model.md`
|
||
|
||
- Every PocketBase collection from `src/lib/pb-types.ts`: fields, types, relations, and
|
||
which are optional/legacy.
|
||
- An entity-relationship Mermaid diagram.
|
||
- For **each** collection, state explicitly whether it carries a `site` relation, a
|
||
`project_code` string, both, or neither. Flag the ones that carry neither and explain how
|
||
those records are attributed to a site (if at all).
|
||
- Document the `project_code` denormalisation: it is copied onto child records rather than
|
||
read through the `site` relation. Describe the drift risk and where it is written.
|
||
- Document known unique indexes and constraints you can find (e.g. the composite unique index
|
||
on `plant_hours_log` referenced in `src/app/api/plant-hours/import/route.ts`), and what
|
||
happens on collision.
|
||
- Document PocketBase API rules as configured by `scripts/pb-relax-rules.py`
|
||
(list/view/create = any authenticated user; update/delete = superuser only) and what that
|
||
means for who can read what.
|
||
|
||
### 3. `docs/modules.md`
|
||
|
||
One section per dashboard module (Overview, Sites, Workforce, Plant & Equipment, Fleet &
|
||
Fuel, Purchasing, Ask). For each: purpose, route, which collections it reads, which metric
|
||
functions it uses, what the user can do there, and which API routes it calls.
|
||
|
||
### 4. `docs/data-flows.md`
|
||
|
||
Trace each ingestion pipeline end to end, with a sequence diagram:
|
||
|
||
- Timesheet import (`/api/timesheets/import`) — including the auto-create-employees path.
|
||
- Fuel log import (`/api/fuel-logs/import`).
|
||
- Plant hours import (`/api/plant-hours/import`) + exception report.
|
||
- Purchase order upload → n8n → `/api/purchase-orders/ingest` → `process-batch`.
|
||
- Delivery note upload → OCR → `/api/delivery-notes/[id]/ocr-result` → received items.
|
||
|
||
For each pipeline, answer precisely: **how is the target site determined for each imported
|
||
row?** Name the exact matching strategy and the failure behaviour when no match is found.
|
||
|
||
### 5. `docs/api-reference.md`
|
||
|
||
Every route under `src/app/api/`: method, path, auth/secret requirements, request shape,
|
||
response shape, error cases. Note which are protected by `N8N_INGEST_SECRET` and which have
|
||
no protection at all.
|
||
|
||
### 6. `docs/operations.md`
|
||
|
||
Environment variables (from `.env.local.example`), what each does, which are
|
||
client-exposed (`NEXT_PUBLIC_`), the scripts in `scripts/`, how to run tests, and the
|
||
existing feature flags (e.g. `ENABLE_CHAT`).
|
||
|
||
---
|
||
|
||
## Part 2 — Multi-site readiness assessment
|
||
|
||
Write this as `docs/multi-site-assessment.md`. This is the deliverable that matters most.
|
||
|
||
### The question to answer
|
||
|
||
**Multiple construction sites** — The Hills, Cosmo City, Castle Gate, Autumns Gate, Pitsana
|
||
Mills, West Park, and more to come. This is *not* a question about multi-tenancy or serving
|
||
multiple client companies. It is: **does the existing per-site model actually hold up as the
|
||
number of sites and the volume of historical data grows, and where does it break?**
|
||
|
||
Structure your answer in three parts.
|
||
|
||
### Part A — What works today
|
||
|
||
The data model is already site-aware. Verify and document this properly: which collections
|
||
carry a `site` relation, which UI surfaces group or filter by site, how per-site financials
|
||
are computed (`src/lib/metrics/site-spend.ts`, `site-projection.ts`,
|
||
`income-vs-expenses.ts`), and how the chat tool layer resolves a site from free text
|
||
(`src/lib/agent/resolve.ts`). Give a plain verdict: **at what scale does the app work
|
||
correctly as-is?**
|
||
|
||
### Part B — Where it breaks
|
||
|
||
Investigate each of the following. **These are leads I have partially verified — confirm or
|
||
refute each one yourself with file:line evidence, correct me where I am wrong, and add
|
||
anything I missed.** Do not simply restate this list; test each claim.
|
||
|
||
1. **Hard 50-site cap on the Overview page.**
|
||
`src/app/dashboard/page.tsx:64` calls
|
||
`pb.collection("sites").getList<SiteRecord>(1, 50, { sort: "name" })`. Determine what
|
||
happens to site 51+ — does it silently vanish from KPI tiles and the "Attention required"
|
||
rail? Search the whole codebase for other capped `getList(page, perPage)` calls and list
|
||
every one with its cap and its blast radius. There is a related comment at
|
||
`src/app/dashboard/purchasing/page.tsx:29` about a previous cap that silently truncated
|
||
data — treat that as evidence this class of bug has already bitten once.
|
||
|
||
2. **Load-everything-then-filter-in-memory.** Most dashboard pages call `getFullList()` on
|
||
entire collections and filter in JavaScript — e.g.
|
||
`src/app/dashboard/page.tsx`, `src/app/dashboard/sites/page.tsx`,
|
||
`src/app/dashboard/purchasing/page.tsx`, `src/app/dashboard/fuel/page.tsx`,
|
||
`src/app/dashboard/employees/page.tsx`. Note especially
|
||
`src/app/dashboard/sites/[id]/page.tsx:50-56`, which filters *some* collections by
|
||
`site='<id>'` server-side but pulls **all** timesheets and **all** employees regardless.
|
||
Quantify: with N sites × M months of fuel logs, timesheets, POs and received items, how
|
||
does page cost grow? Estimate the row counts at which pages become slow, and identify
|
||
which single page falls over first.
|
||
|
||
3. **No global site scope.** The sidebar (`src/components/layout/sidebar.tsx`) has no site
|
||
selector; each module handles site filtering independently, mostly client-side. Assess
|
||
what this costs a user who works on one site: how many clicks/filters to see just their
|
||
data, and which modules offer no site filter at all.
|
||
|
||
4. **Site resolution by name string.** Importers match incoming rows to sites by
|
||
case-insensitive `sites.name` comparison (see `src/app/api/fuel-logs/import/route.ts`
|
||
around lines 214-224 and `src/app/api/plant-hours/import/route.ts` around lines 237-245),
|
||
and PO matching goes through `src/lib/purchase-order-schema.ts`. Evaluate the failure
|
||
modes as site count grows: near-identical names, renamed sites, whitespace/casing drift,
|
||
two sites sharing a name, source spreadsheets using a different label than PocketBase.
|
||
What happens to a row that matches nothing — is it dropped, orphaned, or rejected? Is
|
||
`project_code` a more reliable key, and is it consistently present?
|
||
|
||
5. **Vehicles, plant and employees moving between sites.** A vehicle or worker is bound to
|
||
one `site` at a time. Determine what happens to historical records when that assignment
|
||
changes: do past fuel logs and timesheets stay attributed to the old site, or do they
|
||
follow the current assignment and silently rewrite history? Check whether logs snapshot
|
||
the site at import time or read it through the relation. Note that
|
||
`plant_hours_log` stores `vehicle_unit_type_at_import`, which suggests the codebase
|
||
already understands point-in-time snapshotting — does the same discipline apply to `site`?
|
||
Also check the `(vehicle_qr, start_date)` unique index for cross-site collisions.
|
||
|
||
6. **Access control per site.** With a single shared PIN and no user records, no user can be
|
||
restricted to their own site — everyone who knows the PIN sees every site's contract
|
||
values, margins and payroll. Confirm this, and confirm that server-side data access runs
|
||
as PocketBase superuser (`pbAdmin()`), meaning collection-level API rules do not constrain
|
||
it. State whether per-site access control is achievable without introducing real user
|
||
accounts.
|
||
|
||
7. **The chat/agent layer.** Tools in `src/lib/agent/tools.ts` load portfolio-wide bundles
|
||
(`list_sites` and friends). Assess: cost and latency growth as sites multiply, whether
|
||
ambiguous site names are handled safely (`src/lib/agent/resolve.ts`), and whether the
|
||
agent can leak one site's financials to a user who should only see another's. Note that
|
||
`.env.local.example` already warns `ENABLE_CHAT` must stay off in production until real
|
||
authentication exists — verify whether that warning is still accurate.
|
||
|
||
8. **Anything else you find.** Cross-site data leakage, missing indexes, N+1 queries, unbounded
|
||
`getFullList()` calls in route handlers, aggregates that double-count when a record has a
|
||
stale `project_code`, and the `$autoCancel: false` workarounds in `src/lib/pb.ts` and the
|
||
dashboard pages — do those indicate a concurrency problem that worsens with more sites?
|
||
|
||
For every finding, give: **what breaks, at what threshold, what the user sees when it breaks,
|
||
and the file:line evidence.**
|
||
|
||
### Part C — What to change
|
||
|
||
A prioritised remediation plan. For each item: the problem, the proposed change, the files
|
||
affected, rough effort (S/M/L), the risk of doing it, and the risk of *not* doing it. Group
|
||
into:
|
||
|
||
- **Tier 1 — Correctness.** Things that silently produce wrong numbers or hide data today.
|
||
These are not optional.
|
||
- **Tier 2 — Scale.** Query patterns, pagination, indexes, and server-side filtering needed
|
||
before the site count or data volume grows further. Say explicitly what the app should do
|
||
instead of `getFullList()` and where PocketBase filters/indexes belong.
|
||
- **Tier 3 — Product.** A global site scope/selector, per-site access control (and the user
|
||
model it requires), site archiving for completed contracts, and point-in-time site
|
||
attribution for moved assets.
|
||
|
||
End with a one-paragraph **bottom line** in plain English, written for the business owner,
|
||
not an engineer: can this app run many sites today, how many, and what is the single most
|
||
important thing to fix first.
|
||
|
||
---
|
||
|
||
## Deliverables
|
||
|
||
Create exactly these files:
|
||
|
||
```
|
||
docs/architecture.md
|
||
docs/data-model.md
|
||
docs/modules.md
|
||
docs/data-flows.md
|
||
docs/api-reference.md
|
||
docs/operations.md
|
||
docs/multi-site-assessment.md
|
||
```
|
||
|
||
Then **replace the stale root `README.md`** with an accurate short overview that links to the
|
||
documents above. Keep it under 100 lines; the detail belongs in `docs/`.
|
||
|
||
Do not modify any file under `src/`, `scripts/`, or any config file.
|
||
|
||
Finish with a short summary in chat covering: your multi-site verdict, the top three things
|
||
that must be fixed, and any claim in this prompt that turned out to be wrong.
|