Commit graph

22 commits

Author SHA1 Message Date
DEls
05f6d551f2 feat(sites): import payment certificates into Inflows with cumulative chart
Inflows (claims) now shows only Certified invoice (VAT incl) and Retention
(ex VAT), fed by an Import certificate dialog that reads the PDF (INVOICE
TOTAL, this month's Less RETENTION, valuation number, date) for review
before saving. Adds a monthly + cumulative chart and a certificates table
linking each stored PDF.

- income_claims gains retention_amount and certificate (migration applied)
- pdftotext with an unpdf fallback for machines without poppler

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 10:53:54 +02:00
DEls
a96f35366d feat(management): store planning data in PocketBase with editable income, add job and forecast vs actual chart
Move the Management planning snapshots into PocketBase collections
(plans, projects, per-year project plans, retention, edit log), loaded
with a bundled-JSON fallback. Admins can double-click an income cell to
enter forecast and actual, edit awarded jobs' planned amounts, change
status and costs, and add jobs, with revision checks and an audit trail.
Income chart now shows forecast and invoiced bars with not-yet-started
money stacked in red; Income is in the sidebar. Adds schema and import
scripts, and jsdom/Testing Library dev dependencies for UI tests.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-20 03:04:01 +02:00
DEls
0a7138f60c fix: harden workforce, plant and financials
Improve workforce scanning, site transfers and timesheet exports.
Audit plant imports, duplicate identities, rates and dashboard totals.
Repair delivery PO selection, periodic rechecks and receiving guards.
Align financial ledgers, supplier periods and mobile layouts.
Add the site map preview, shared theme controls and audit evidence.

Verify 3,455 application tests, six fuel export tests, TypeScript and
an optimized production build. Clean up a leaked test timer and keep
local worktrees and generated reports out of ESLint traversal.

Changed-file lint passes; repository-wide lint retains 33 existing
errors in unchanged files.
2026-10-19 13:18:31 +02:00
DEls
220ca7bc45 feat(site-map): three.js scene engine, navigation and road layers 2026-10-14 07:12:32 +02:00
DEls
3e06a78fcb feat(reports): add a real, server-side PDF builder
Replaces Ask's screenshot-then-wrap PDFs with an actual PDF pipeline
that runs in plain Node (no Chromium/Puppeteer, Alpine-safe): pdfmake
draws real text, real tables (zebra rows, right-aligned numbers, a
repeated header row across a page break) and vector charts (bar,
stacked bar, line, area, donut) built from ChartSpec as hand-rolled
SVG, using pdfkit's standard 14 fonts so no font files ship at all.
Chosen over @react-pdf/renderer for its native repeated-table-header
support, which that library only gets via manual pagination.

- chart-svg.ts: ChartSpec -> drawing plan (bar count, axis max) -> SVG.
- markdown-blocks.ts: GFM markdown -> headings/paragraphs/lists/tables.
- table-content.ts / kpi-content.ts / chart-content.ts / image-content.ts:
  the pdfmake content nodes for each piece.
- chrome.ts: masthead + running header + "Page n of N" footer.
- resolve-section-table.ts: a report section's table, preferring the
  cached query_records result (table: { resultId }) so figures are
  never hand-copied.
- build.ts: assembles either an answer's markdown or a make_pdf_report
  structured document into one PDF.

Verified against real pdftotext/pdftoppm output, including the header
repeating across a page break.

Adds pdfmake, unified, remark-parse (+ @types/pdfmake, @types/pdfkit);
drops html-to-image, whose only caller is removed in a later commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 09:16:20 +02:00
DEls
93d8fef17d feat(ask): download answers as PDF
src/app/dashboard/chat/answer-pdf.ts: slicePages (the pure page-break
maths — packs consecutive top-level blocks under an A4 content height,
breaking only between blocks, an oversized single block gets its own
page rather than being split) and answerToPdf (clones the answer at a
fixed 794px/A4 width in light theme, renders it with html-to-image at
pixelRatio 2, and embeds one page per slice via pdf-lib with a header
and "Page n of N" footer). A new "PDF" button sits next to the
existing "Save as PDF" print button (relabelled "Print" for clarity —
it was always window.print()), filename
ask-<thread-title-slug>-<date>.pdf.

Live browser check skipped: reaching an answer at all now goes through
the picker -> threaded /api/chat flow, which needs ask_threads (not
yet applied). Covered by unit tests on the page-slicing maths and
slugify instead; the render/embed path itself is browser-only DOM code
that can't be meaningfully unit-tested under vitest's node environment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-26 19:02:17 +02:00
DEls
e83a0d302c fix: overview counts, wrong-unit fuel limits, meter guard, en-ZA numbers, vehicle merge, docs
- Overview/Plant tiles and pages share one vehicle bucket rule (blank unit_type = fleet)
- Plant landing equipment figures were always 0; hour diffs now reject 0 baselines and >24h/day jumps
- km/L limits on plant (and L/h on vehicles) report unit-mismatch instead of false alerts
- formatPercent/formatDecimal replace display toFixed (en-ZA)
- POST /api/vehicles/[id]/merge + Merge duplicate dialog (dry run first)
- Attention links go to equipment vs vehicles by unit type; PO upload view link fixed
- Conditional hook fixed in DN document view; ESLint flat config (next lint removed in 16)
- Docs: current modules/routes/env vars; login + Ask reports plans

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-25 22:04:08 +02:00
DEIs
87f75dd98f feat(plant): fleet map page with GPS-denormalised vehicles, clustering, and tile switcher
Adds /dashboard/plant/map: a Leaflet view of every vehicle scanned from
the MEGA Clock kiosk, plotted at its last known position.

Schema
- vehicles.last_latitude / last_longitude / last_scanned_at /
  last_location_accuracy_m denormalised off fuel_logs so the map page
  renders every vehicle in a single query. Same best-effort pattern as
  attention_dismissed_at; updated by /api/clock/vehicle-scan after every
  successful scan. PB init defaults new number cols to 0 (not null) —
  filter uses 'last_latitude > 0 || last_latitude < 0' to mean 'real fix'.
- Indexed via idx_vehicles_last_scanned.

Map UI
- Custom SVG markers per unit_type: car silhouette for fuel, tractor
  silhouette for plant, 24x24, drop-shadowed.
- Clustering via react-leaflet-cluster over Leaflet.markercluster.
  Cluster icons colour-ramp with size (green->sky->amber->rose) and
  scale 32->54px.
- Three-button base-layer switcher: Street (OSM), Dark (ESRI Dark
  Gray Canvas — CartoDB watermarks tiles without a key), Satellite
  (ESRI World Imagery). Last choice persists in localStorage.
- Site pins: small purple map-marker divIcon, click flies the map to
  zoom level 15.
- Empty-state is a floating chip on the map rather than a full-page
  card so the map is always visible.

APIs
- /api/clock/vehicle-scan: stores the GPS fix on the fuel_logs row
  AND denormalises onto the vehicle when present; missing-coord
  scan leaves last_* untouched.

Sidebar: new Map entry under Plant group.
2026-09-22 21:39:25 +02:00
DEls
81c8f78602 feat(dn): normalise source documents into canonical pages 2026-09-18 19:07:52 +02:00
DEls
667c513de9 build: add poppler-utils and pdf-lib for delivery note page splitting 2026-09-17 13:42:11 +02:00
DEls
56bdbd66b7 chore(clock): add jsqr dependency for QR decoding 2026-09-12 00:13:13 +02:00
DEls
045b6fe469 chore(workforce): add qrcode dependency for worker badges 2026-09-11 20:56:29 +02:00
DEls
95be557983 build(pwa): wire build-icons.mjs into the build pipeline 2026-08-30 22:22:42 +02:00
DEls
b69900fc1b feat(chat): render answers as dashboard-grade markdown
The models return GFM — tables of vehicles, bold figures, lists — and
the view rendered it as preformatted text, so users saw raw pipes and
asterisks.

Answers now render through react-markdown with every element mapped to
this app's own conventions: tables get the borders, muted header and
hover of components/ui/table.tsx, and numeric columns get tabular-nums
so rand figures line up as they do on the fuel and sites pages.

Alignment is computed per COLUMN, not per cell — the table inspects its
own rows, decides which columns are numeric, and publishes that through
context so a header never ranges left above a right-ranged figure.

Layout: questions are compact chips, answers are full-width documents
with a thin amber rule. Bubbles fight tables. Each answer carries a
provenance strip naming the tools it used — for a tool whose value is
that its numbers reconcile with the dashboard, the source is part of the
answer.

Motion is limited to a streaming caret and the running-tool pulse, both
disabled under motion-reduce.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 21:00:35 +02:00
DEls
84cf8759e8 build(chat): add Anthropic and OpenAI SDKs, document chat env gate
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 20:41:30 +02:00
DEls
dee027aeb9 build(mcp): add MCP SDK, zod and tsx for the agent tool layer
zod was previously only a transitive dependency; declared explicitly so
it cannot vanish under a dependency bump. tsx rather than Node's native
type stripping because the metrics modules import via the @/* alias,
which Node does not resolve.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-29 20:25:39 +02:00
DEls
8dbf393446 feat(delivery-notes): enhance delivery note upload and processing
- Updated the delivery note upload API to include site ID for better project association.
- Modified the delivery note schema to support additional fields such as processing status, OCR confidence, and received by name.
- Enhanced the UI components for uploading delivery notes to allow file drop functionality and improved user feedback during file selection.
- Refactored purchasing page to integrate delivery notes that require review, providing better visibility and management of pending items.
- Added new KPIs to track active purchase orders and fulfillment percentages, improving overall dashboard insights.
2026-08-26 15:33:30 +02:00
DEls
7bce0a4e80 feat(fuel): drop-in xlsx/csv import for pump sheets
The Aug 2026 pump sheet from The Hills (and any future sheet from
the same source) uses different column names and a different date
format than the legacy CSV the import route was built for. Adapts
the route so the same endpoint accepts either Excel (.xlsx) or CSV,
maps the new column names onto the existing FUEL_LOGS schema, and
persists their 'Latest Consumption' column verbatim.

What's new
  • SheetJS (xlsx) replaces PapaParse for parsing. Single code path
    handles both .xlsx and .csv uploads.
  • Resolves columns case-insensitively against an alias table so
    legacy CSVs (vehicle_description, odo_reading) still work
    alongside the new shape (Date Logged, Vehicle QR, Km's/ Hours,
    Litres Pumped, Current Site, Latest Consumption).
  • Date parser: DD/MM/YYYY first (with leading-space tolerance),
    falls back to YYYY-MM-DD, then to Excel serial numbers.
  • Vehicle match: QR code first (clean identifier in the source
    data), falls back to description text for legacy CSV.
  • Site match: by sites.name (one round-trip, cached for the
    import). project_code is derived from the resolved site; falls
    back to the vehicle's project_code.
  • Latest Consumption persisted as a new FUEL_LOGS field
    (latest_consumption, number, optional). Empty source cells
    stay empty rather than getting coerced to 0.

PocketBase change
  • scripts/migrations/2026-08-24-fuel-logs-latest-consumption.py
    adds the new field. Idempotent; follows the pattern of the
    2026-08-19 migration. Run it with PB_ADMIN_EMAIL +
    PB_ADMIN_PASSWORD before importing the new sheets, or the
    import will fail with 'latest_consumption is not a field'.

UI
  • Import button now accepts .xlsx and CSV; SAMPLE_HEADERS +
    SAMPLE_OPTIONAL show the new column expectations and the
    DD/MM/YYYY date format hint.
  • fuel-view.tsx shows a 'Consumption (src)' column on the row
    table so operators can eyeball what their fuel team sent
    against the app's recomputed variance.

Out of scope (intentionally untouched)
  • vehicles, sites, data_import_batches and every other
    collection — no schema changes there.
  • PO / timesheets / delivery-notes imports.
  • fuel/page.tsx business logic (variance calc, vehicle summaries).

Verified
  • Dry-run against 'The Hills Diesel 1-24 August.xlsx': 134/134
    rows parse cleanly, 0 errors. 12/134 rows correctly carry an
    empty Latest Consumption (matches the source).
  • vitest: 20/20 fuel-import-parse + 35/35 PO-schema tests pass.
  • tsc --noEmit clean.
  • One pre-existing failure in
    src/app/api/purchase-orders/process-batch/route.test.ts
    unrelated to fuel; confirmed failing on main before this change.
2026-08-24 22:03:08 +02:00
DEls
716fa45bf6 Add Vitest + n8n env var documentation 2026-08-19 02:21:36 +02:00
DEls
c5b1acfc7f Update package dependencies and remove unused API routes
- Added `pocketbase` as a dependency in `package.json` and `package-lock.json`.
- Updated the `dev` script in `package.json` to run on port 6004 instead of 3000.
- Removed unused API routes: `src/app/api/health/route.ts`, `src/app/api/mcp/route.ts`, `src/app/api/upload/route.ts`, and their associated logic to streamline the codebase.
- Cleaned up `package-lock.json` by removing unnecessary peer dependencies and ensuring consistency in the dependency tree.
2026-08-19 00:14:47 +02:00
DEls
4b03125002 Add Open Graph image + social link preview metadata
When someone shares a link to the dashboard in WhatsApp, Facebook,
LinkedIn, Discord, etc., the platform now shows a custom preview
instead of a plain blank card.

  - public/og-image.png    1200x630 PNG with the brand mark, the
                            MEGA INFRASTRUCTURE wordmark, and a
                            tagline over a dark gradient with the
                            brand accent bars.
  - public/fonts/          Inter ExtraBold (woff) used by the OG image
                            generator so the rendered text is identical
                            on every machine.
  - scripts/build-og-image.mjs
                            Generates the image from an SVG template
                            using sharp. The font is loaded from the
                            local file (or Google Fonts CDN as fallback)
                            and embedded as base64 inside the SVG so
                            the renderer doesn't depend on system
                            fonts.
  - src/app/layout.tsx     Adds the openGraph + twitter metadata
                            pointing at /og-image.png so the preview
                            appears in link-unfurl contexts.
  - package.json           'npm run og-image' runs the generator;
                            'npm run build' chains it via prebuild so
                            the image is regenerated on every CI build.
2026-08-09 20:01:13 +02:00
SJCoetz
eeea5b7592 Initial commit: MEGA INFRASTRUCTURE dashboard + Coolify Dockerfile 2026-08-09 01:37:40 +02:00