Mega-Tracker/README.md
Dave 471cf6fb3c Project tracker seeded from the Mega Infrastructure Candy barchart
App for the Mega Infrastructure AI Integration programme (A000) and Mega
Plant (B110). 100 activities across 2 programmes, 06 Oct 2026 - 31 Aug 2027,
parsed from the client PDF and frozen as an immutable baseline.

Views: Dashboard, Timeline (month grid + week grid matching the PDF's
Friday-anchored week numbering), Todo list, WBS, Data.

Notes on the source data:
- Hierarchy is derived from the PDF's indentation, not the activity code.
  Codes do not sort structurally (A490 sits under Workforce, A510 under
  Financials), so a code-ordered import would produce a wrong tree.
- The schedule is built from the dates, not the PDF's "Durn" column, which
  disagrees with its own dates for 13 of the 100 activities.
- Spec issues found in the PDF are surfaced in-app under Data: 2 unresolved
  "?" placeholders, 6 activities sharing one stretched 6-week window while
  declared 15 days, 13 duration mismatches, 17 spelling corrections
  (original wording preserved and viewable).

Progress (status, % complete, note) is stored behind a pluggable adapter so
the site can be static today and point at a shared JSON store later without
touching the UI.
2026-10-01 08:48:51 +02:00

136 lines
6.7 KiB
Markdown

# Mega Infrastructure — Project Tracker
A project tracker seeded from `Mega Infrastructure AI intergration system.pdf` (a Candy 2.05p / P6
barchart printed 30 Sep 2026). **100 activities, 2 programmes, 06 Oct 2026 → 31 Aug 2027.**
```bash
npm install
npm run dev # http://localhost:4004
npm run build # -> dist/ (static, drop on any host)
```
Port **4004** on purpose — 3000 is taken by another local app.
## What's in it
| View | What it does |
|---|---|
| **Dashboard** | Weighted % complete, counts by status, overdue, starting-this-week, workstream health, build-load-by-month chart |
| **Timeline** | Collapsible WBS Gantt. **Month** grid for reading, **Week** grid matches the PDF (Friday-anchored, the SA convention Candy uses). Today-line, status colours, progress fill. |
| **Todo list** | The 78 deliverables as a checklist. Filter by window (open / next 7d / next 30d / in-flight / overdue / unconfirmed), status, workstream, free text. Group by workstream, status or start month. Inline status + % slider. |
| **WBS** | Full 100-row hierarchy matching the PDF, with rollup % and status. Toggle corrected names ↔ original PDF wording. |
| **Data** | Source provenance, sharing config, export/import, and the spec issues found in the PDF. |
Click any activity (bar, row or todo) to open the edit drawer: status, % complete, note, plus the
plan facts and any flags attached to that activity.
## Appearance
Dark/light mode plus four accent colours, set from the topbar.
- **Mode** — sun/moon toggle, **dark by default**. An explicit choice sticks from then on.
- **Accent** — Blue, Violet, Emerald, Amber. Each has a lighter variant tuned for dark mode
(`#2563eb` light / `#60a5fa` dark, and so on) so contrast holds in both.
Both persist to `localStorage`. An inline script in `index.html` resolves the theme *before* first
paint, so there's no flash of the wrong colours on reload — it mirrors `src/lib/appearance.ts`
(`DEFAULT_MODE` / `DEFAULT_ACCENT`), so keep the two in sync if you change the defaults or the key names.
All colour is token-driven from the top of `src/styles.css`. To add a fifth accent, add one entry
to `ACCENTS` in `src/lib/appearance.ts`, a `[data-accent="..."]` rule and its
`[data-theme="dark"][data-accent="..."]` counterpart in `styles.css`, and the value to the inline
script's whitelist.
## Deploying to Coolify
```bash
docker build -t mega-tracker .
```
Multi-stage: `node:22-alpine` builds, `nginx:alpine` serves the static output. Final image is nginx
plus ~215 KB of files — no Node runtime, no `node_modules`.
In Coolify: **New Resource → Dockerfile**, point it at this repo/directory. It finds the Dockerfile
automatically.
- **Port**: `80` (the Dockerfile `EXPOSE`s it; Coolify/Traefik routes to it)
- **Build args**: none needed
- **Domain**: set it on the resource; Coolify generates the Traefik route and TLS for you
- **Health check**: baked into the image (`wget --spider` every 30s)
**Use a subdomain, not a path.** The build emits absolute asset URLs (`/assets/…`). That is correct
for a dedicated hostname like `tracker.mega.co.za`, and wrong for a path mount like
`example.com/mega/`, which would 404 on every asset. If you ever need a subpath, set
`base: '/mega/'` in `vite.config.ts` and rebuild.
**Caching is deliberate.** Hashed assets under `/assets/` get `max-age=31536000, immutable`; the
app shell gets `no-store`. A new deploy emits new asset filenames, so this is always correct, and it
prevents the classic "the update didn't work" browser cache bug.
### If you ever need build-time config
Vite inlines `VITE_*` variables into the bundle at **build** time, so they must be set as build
arguments in Coolify, not runtime environment variables. There is currently nothing to configure —
no API base URL, no feature flags. If you add the shared progress store (below) and need a default
endpoint, add a `--build-arg VITE_PROGRESS_URL=…` and read it via `import.meta.env`.
## Data model
The plan (codes, names, dates, hierarchy) is baked into `src/data/baseline.ts` and is **never
edited by the app**. Only progress is recorded: status, % complete, and a note, keyed by activity
code.
Parent rollups are duration-weighted from the children, using `spanDays` — the working days the
activity's own dates actually span — rather than the PDF's `Durn` column, which disagrees with the
dates for 13 of the 100 activities.
## Storage, and the honest limitation
Progress defaults to `localStorage`. **That means a static deploy shows the project owner the plan
but not your progress** — the plan is in the bundle, the progress is in your browser. The owner
would see every activity at 0%.
To share, open **Data → Share progress with the project owner** and point it at any endpoint that
accepts `GET` and `PUT` of a JSON body. A single-row PocketBase record works and needs no schema
knowledge in the app:
```
GET https://your-host/api/collections/tracker_progress/records/<RECORD_ID>
PATCH https://your-host/api/collections/tracker_progress/records/<RECORD_ID>
```
The adapter is endpoint-agnostic (`src/lib/storage.ts` → `remoteBlobAdapter`), so it also works
against a tiny hand-rolled endpoint or a KV store. The auth token is kept in the browser only and
is never bundled into the deployed files.
Export/import JSON works regardless of adapter, so you always have a portable backup.
## Spec issues found in the PDF
Flagged in-app under **Data → Spec issues**, and derived at runtime from the baseline rather than
hardcoded:
- **2 activities still contain a `?`** — `B220 Payments ?`, `B340 Per Bank?`. Unfinished spec text.
- **6 activities share one identical 6-week window** (`A490, A140, A180, A220, A260, A320`, all
08 Dec 26 → 19 Jan 27) while each is declared 15 working days but occupies 31. Reads as a bulk
edit or a scheduling-constraint artefact in Candy, not a plan.
- **13 activities** have a duration that disagrees with their own dates.
- **17 spelling corrections** applied for display; the original wording is preserved and viewable
in the WBS tab.
- `A000` and `A010` cover the identical window — worth confirming whether "Site Dashboard" is a
module or the whole programme.
- The plan is heavily front-loaded: 33 of 78 deliverables start in the first 8 weeks, peaking at
10 concurrent in the week of 13 Oct 2026.
## Regenerating from a new PDF
```bash
pdftotext -layout "new-plan.pdf" /tmp/mega.txt
python3 scripts/parse_pdf.py # -> /tmp/mega_acts.json
python3 scripts/gen_baseline.py # -> src/data/baseline.ts
```
**Hierarchy is taken from the PDF's indentation, not the activity code.** Codes do not sort
structurally — `A490` sits under Workforce, `A510` under Financials, `A580`/`A590` under Health &
Safety. Re-importing from a spreadsheet that sorts by code will produce a wrong tree.