Commit graph

25 commits

Author SHA1 Message Date
DEls
b1d06c1aa7 feat(schema): add file_hash and queue fields to DNs and POs 2026-09-11 10:29:10 +02:00
DEls
08ed89d9c8 feat(dashboard): enhance operations dashboard with rain outlook and coordinate support
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.
2026-09-06 19:59:52 +02:00
DEls
602c62ba06 refactor(plant): enhance PlantPage and PlantView for fuel log integration
Updated PlantPage to aggregate vehicle data from fuel_logs, including hours worked and total value calculations based on odo readings. Adjusted PlantView to support navigation to vehicle details and improved log display with new data structure. Removed legacy code related to plant_hours_log and refined data handling for better performance and clarity.
2026-09-02 12:36:10 +02:00
DEls
658d83dcff fix(pb): allow PDF and image MIME on data_import_batches.original_file
The PO upload route (and the delivery-notes and xlsx import
paths) writes the uploaded file into the DATA_IMPORT_BATCHES.
original_file field. That field was bootstrapped with a CSV/Excel
allowlist only:

  text/csv, text/plain, application/vnd.ms-excel, application/csv

When the upload route was rewritten on 2026-08-19 (commit f0f7c82)
to write directly to PB instead of forwarding the file to n8n, every
PDF and image upload started failing PB field validation with
validation_invalid_mime_type, surfacing in the API as a 500 with
error='batch_create_failed' and the real cause buried in
pb_data.original_file. The 2026-08-24 migration that should have
added xlsx was also never applied to prod (verified in PB:
xlsx missing too).

Migration: scripts/migrations/2026-08-31-data-import-batches-allow-pdf-images.py
- Strictly additive: unions the new mimeTypes onto the existing
  allowlist, exits no-op if already applied (verified idempotent).
- Adds application/pdf, image/jpeg, image/png, image/webp (PDF /
  image support for the PO and delivery-note flows) and
  application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
  (xlsx, completing the missed 2026-08-24 migration).
- No code change in the upload routes required - they were already
  validating against ALLOWED_MIME in purchase-order-schema.ts and
  expecting PB to accept the same set.

Verification on https://pb.mega.infra.apphub.co.za:
  Before: POST /api/purchase-orders/upload (PDF) -> HTTP 500,
    pb_data.original_file.code = 'validation_invalid_mime_type'.
  After:  same POST -> HTTP 202 with batch_id, PB row created
    (batch_type=purchase_orders, status=pending, file_hash
    populated, original_file stored).
2026-08-31 12:20:41 +02:00
DEls
6ce3c24f12 refactor(pwa): address code review on build-icons.mjs 2026-08-30 22:18:25 +02:00
DEls
5a7d2931f9 feat(pwa): render chat icon set + per-route apple-touch-icons 2026-08-30 22:15:15 +02:00
DEls
2d36e9b1cd fix(vehicles): handle empty unit_type during updates
Updated the vehicles API to allow PATCH requests with an empty unit_type. This change ensures that existing records with unit_type='' can be updated without validation errors, while maintaining the default value of 'fuel' for new inserts. This adjustment resolves issues with updating pre-existing vehicle records that lack a unit_type field.
2026-08-29 19:17:14 +02:00
DEls
9ea0a1f65e fix(pb): make vehicles.unit_type optional
53 vehicles predate the unit_type field and have unit_type=''. PB
validates the WHOLE record on any PATCH, so as long as unit_type was
required, no record with an empty value could be updated (Dismiss,
inline-edit, anything calling .update()). Making the field optional
unblocks all 53 immediately.

The default='fuel' still applies to new inserts.

Note: the live PB schema lost its 'default' value during the PATCH
(see review below) — only 'required' was actually changed in the
stored schema. Fresh records will be inserted with unit_type='' (or
null) until the application sets it explicitly. The bootstrap
definition still has default='fuel' so fresh environments get it.

Idempotent migration: scripts/migrations/2026-08-28-vehicles-unit-type-optional.py
applied successfully; re-run is a no-op.
2026-08-28 15:26:33 +02:00
DEls
0290ced51a fix(schema): ensure is_recurring=false writes are preserved
The live PB fixed_costs collection never received the is_recurring field
added in 87bb179 — the bootstrap script is collection-create-only and
errors out on already-existing collections, and no migration added the
field. Smoke-test rows sent is_recurring=false and got it silently
dropped (PB ignores unknown fields on write), then the partition helper
treated the missing value as monthly (its !=== false fallback).

Two changes:

1. Drop 'default: true' from the bootstrap schema definition. A bool
   default only applies when the field is absent from the write payload,
   so this is belt-and-braces — but it removes any doubt that a default
   could ever silently override an explicit false. The partition helper
   still treats absent values as monthly, and the backfill migration
   patches pre-existing rows to true.

2. Make the backfill migration self-contained: if the field is missing
   from the collection, add it (bool, required=false, no default) before
   patching rows. Idempotent — no-op if already present.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-28 13:43:38 +02:00
DEls
c36eaeed51 fix(plant-hours-template): ESM-safe __dirname + freeze note
Two follow-up fixes from the Task 5 code-quality review:

- scripts/generate-plant-hours-template.ts uses
  fileURLToPath(import.meta.url) instead of __dirname. The
  project's tsconfig has module: esnext, so the moment the
  package flips to ESM the original would throw
  ReferenceError: __dirname is not defined.
- src/lib/plant-hours-template.ts documents that the frozen
  header row is a best-effort request — SheetJS Community
  Edition silently drops the !freeze property (only Pro emits
  frozen-pane XML). The function still produces the canonical
  13-column workbook, but the doc comment no longer claims a
  behaviour that doesn't happen.
2026-08-28 12:55:08 +02:00
DEls
1e212a7339 feat(plant-hours): template workbook generator + seed file
buildPlantHoursTemplateWorkbook() emits the canonical 13-column xlsx
with a frozen header row, typed cells (date/time/number), a sample
row using the supplied vehicle, and a Notes column widened for
readability. Used by both /api/plant-hours/template and the seed
script that writes RAW_DOCS/Plant_Hours_Template_2.xlsx.
2026-08-28 12:51:14 +02:00
DEls
70522ed1ae feat(migrations): backfill is_recurring=true on fixed_costs 2026-08-28 12:43:25 +02:00
DEls
87bb1790fe feat(schema): add is_recurring bool to fixed_costs collection
Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-28 12:40:00 +02:00
DEls
535649bf54 fix(pb): default vehicles.unit_type to fuel
The plan spec called for default "fuel" on the new select so existing
vehicles rows (which would otherwise fail the required=True
constraint) stay valid. PocketBase applies the default at the field
level when adding it to an existing collection.
2026-08-28 12:32:52 +02:00
DEls
ffd840eac1 feat(pb): vehicles.unit_type + plant_hours_log collection
Adds the schema needed for the weekly plant-equipment hours log:
- vehicles gets a unit_type select (fuel|plant), defaults to fuel
  for backward-compat with existing rows
- plant_hours_log collection with the 13 plant-hours fields plus
  vehicle_unit_type_at_import snapshot and composite unique index
  (vehicle_qr, start_date)
- data_import_batches.batch_type gains plant_hours
2026-08-28 12:31:17 +02:00
DEls
fb7d3d51b1 feat(fuel): PB schema migration for attention_dismissed_at
Adds the optional date field to VEHICLES via a one-shot idempotent
migration, and keeps scripts/pb-bootstrap-schema.py in sync so freshly
bootstrapped environments also get it. No data backfill — existing
rows have the field set to null, which the UI treats as 'not yet
dismissed'.
2026-08-28 12:26:23 +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
472be99fd8 feat(dashboard): add job start date to site records and enhance site management
- Introduced a new optional field `job_start_date` in the site schema to track project start dates.
- Updated the SitesPage component to utilize `job_start_date` for determining site status and fallback logic.
- Enhanced the SitesView component to display the new status "upcoming" for sites with future start dates.
- Modified the SiteDetailPage to include `job_start_date` in the site details and added an EditSiteButton for site management.
- Updated the site templates to include `job_start_date` for better pre-filling in the site creation form.
2026-08-25 19:01:56 +02:00
DEls
1d06baafee fix(data-import-batches): allow xlsx MIME on original_file
When the new fuel-import route started accepting .xlsx uploads, the
real xlsx MIME type (application/vnd.openxmlformats-officedocument
.spreadsheetml.sheet) hit PB's mimeTypes allowlist on data_import_
batches.original_file and got rejected with:

    validation_invalid_mime_type: mime type must be one of:
    application/pdf, image/jpeg, image/png, image/webp, text/csv,
    text/plain, application/vnd.ms-excel, application/csv.

The old CSV-only route never hit this (text/csv was already on the
list), so the audit-trail batch attach worked fine.

Adds the OOXML xlsx MIME to the allowlist via the same migration
pattern as 2026-08-19 and 2026-08-24-fuel-logs. Idempotent: re-run
is a no-op once applied. No app-code changes - the route was
already sending the correct MIME for the file extension.

Verified: batch create succeeds against live PB with the exact
xlsx payload the route sends.
2026-08-24 22:17:45 +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
c534572590 feat(migration): add async-queue fields to data_import_batches
Extends the data_import_batches collection for the PO/DN async-import
queue redesign (see spec docs/superpowers/specs/2026-08-19-po-import-queue-redesign.md
and plan docs/superpowers/plans/2026-08-19-po-import-queue-implementation.md).

Adds 8 fields:
  content_type, status (select: pending|processing|processed|failed|dead),
  attempts, last_error, last_attempt_at, next_retry_at, processed_at,
  processed_record_id

Extends the batch_type enum to include purchase_orders and delivery_notes.

Adds index idx_batches_pending on (status, batch_type, next_retry_at) for
the cron query.

Idempotent: safe to re-run.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-19 19:19:17 +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
7d2684f321 Fix Coolify build: skip postinstall in deps stage
The postinstall hook runs scripts/patch-next-dev-blocker.cjs, but at
that point in the Docker build only package.json and package-lock.json
have been copied — the scripts/ directory isn't there yet, so the
hook fails with MODULE_NOT_FOUND.

The patch is dev-only (it mutates Next 16's dev-only cross-site
guard, which is never loaded in production), so it's safe to skip
during the Docker build. Two changes:

1. Dockerfile: use 'npm ci --ignore-scripts' in the deps stage so
   the postinstall hook doesn't run before the scripts/ directory
   is available.
2. scripts/patch-next-dev-blocker.cjs: bail out cleanly with exit 0
   when node_modules/next is missing, so the script is also safe to
   run in any intermediate state.
2026-08-09 02:29:48 +02:00
SJCoetz
eeea5b7592 Initial commit: MEGA INFRASTRUCTURE dashboard + Coolify Dockerfile 2026-08-09 01:37:40 +02:00