Commit graph

116 commits

Author SHA1 Message Date
DEls
6cdcb4fa9f feat(fuel): POST/DELETE /api/vehicles/[id]/attention-dismiss
Sets or clears vehicles.attention_dismissed_at via pbAdmin().
15-char PB id pattern, 404 when PB reports the record is missing,
500 on any other write failure. Test mocks pbAdmin() so no live PB
needed.
2026-08-28 12:31:23 +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
2204989a3c docs(plan): plant hours weekly log implementation plan
Bite-sized TDD task decomposition covering 13 tasks:
1. PocketBase schema (vehicles.unit_type + plant_hours_log)
2. Type updates (UnitType, PlantHoursLogRecord, BatchType)
3. Shared import-parse helpers + parseTime
4. Per-row validator + derived fields
5. Template generator + seed script
6. GET /api/plant-hours/template
7. Exception-report CSV builder + route
8. POST /api/plant-hours/import
9. DELETE /api/plant-hours/[id]
10. PATCH /api/vehicles/[id] accepts unit_type
11. ImportPlantHoursButton component
12. /dashboard/plant page extension
13. End-to-end smoke test

Each task has files, failing tests, implementation code, run commands,
and commit step. Follows the approved design spec.
2026-08-28 12:26:35 +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
c5230190d0 docs(plan): fuel attention dismissal implementation plan
7-task TDD plan: PB migration, API route + tests, isAlertAcknowledged
helper + tests, server mapper + filter, Dismiss button + optimistic
overlay, Dismissed pill + Restore on detail page, full verification
with Agent Browser. Steps use checkbox syntax for subagent-driven
or inline execution.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-28 12:24:14 +02:00
DEls
cc0abfdc2c docs(plant-hours): weekly log import + exception report design
Adds a design doc for capturing weekly plant-equipment hours via an Excel
template against the existing /dashboard/plant page. One new
plant_hours_log collection, one vehicles.unit_type field, three new
routes (import, template download, exception report CSV), and an
extension to PlantView.

Spec covers: lenient import with per-row errors, downloadable exception
report for fix-and-retry, snapshot of vehicle.unit_type on each row,
over-budget flag against a per-week budget, and a symmetric attention
panel on /dashboard/plant for type mismatches.
2026-08-28 12:20:58 +02:00
DEls
0e13720fa7 docs(spec): fuel attention dismissal design
Per-vehicle 'I've seen this' control on the Fuel dashboard. Stamps
attention_dismissed_at on the vehicles collection; auto-undismisses when
a flagged fuel log with date_logged > dismissal arrives. Timestamp only,
no reason field. Tiny POST/DELETE route + filter line in page mapper +
Dismiss button on alert rows + Dismissed/Restore pill on vehicle detail.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-08-28 12:19:59 +02:00
DEls
3f1ef9dab7 feat(purchasing): collapse received items in DeliveryNoteCard
Wrap the received-items list in <details>/<summary>. Default
collapsed; click to expand. Hidden when dn.receivedItems is
empty (preserved). The 'Raw OCR data' <details> lower in the
same card is independent and unaffected.
2026-08-27 00:58:58 +02:00
DEls
caa5a1261b feat(purchasing): wire SupplierEditor into PO detail view
PoDetailView now mirrors its detail prop into local state and
passes a handleSupplierSaved callback that updates state.supplier
and triggers the existing refresh(). SupplierEditor replaces the
static Badge when the supplier is unknown.
2026-08-27 00:58:24 +02:00
DEls
c687af49d9 feat(purchasing): SupplierEditor component (not wired yet)
Badge-as-button + Input + Save/Cancel client component. Only
renders when isUnknownSupplier(currentSupplier) is true. Save
POSTs to /api/purchase-orders/{poId}/supplier and invokes
onSaved(next). Esc cancels; Enter saves when valid.

Not yet rendered — Task 3 wires it into PoDetailView.
2026-08-27 00:57:06 +02:00
DEls
b004121360 feat(purchasing): POST /api/purchase-orders/[id]/supplier route
Validates { supplier } (trimmed, 1-200 chars) and PATCHes the
purchase_orders.supplier field via pbAdmin(). 400/404/500 with
explicit error codes; mirrors the retry-route pattern.
2026-08-27 00:53:07 +02:00
DEls
58f2ca6a7b docs(plan): PO supplier edit + DN collapse implementation
5 tasks: API route (TDD), SupplierEditor component, wire into
PoDetailView with state, wrap received items in <details>,
final verification with Agent Browser.
2026-08-27 00:50:44 +02:00
DEls
6c00676343 docs(spec): PO detail supplier edit + DN items collapse
Two scoped follow-ups to the supplier-unknown flag (9897f47):
- Edit supplier on detail page when flagged as unknown
- Collapse received items per delivery note by default
2026-08-27 00:49:42 +02:00
DEls
9897f477b6 feat(purchasing): show 'Supplier unknown' badge when OCR couldn't extract supplier 2026-08-27 00:41:10 +02:00
DEls
e3c2e0396d refactor(purchasing): include orphan IDs in console.warn, single rollup pass 2026-08-27 00:15:39 +02:00
DEls
cb17760e5a fix(purchasing): use getFullList for received_items to avoid silent 500-row cap 2026-08-27 00:12:33 +02:00
DEls
b6fccac5a7 fix(po-detail): scope SlipPreview URL to correct PB collection 2026-08-27 00:10:45 +02:00
DEls
80a270d4be feat(po-detail): add View PO PDF button using existing SlipPreview 2026-08-27 00:07:28 +02:00
DEls
2f3505b5f8 fix(purchasing): DnStatusPill checks actual completed statuses (auto-verified/manual-verified) 2026-08-27 00:05:41 +02:00
DEls
85f3bdcbab feat(purchasing): amber treatment for POs/DNs with pending status 2026-08-27 00:01:41 +02:00
DEls
c9f3d61fc6 feat(purchasing): shorten upload button labels (Upload PO / Upload DN) 2026-08-26 23:59:21 +02:00
DEls
a8e67faf3d docs(plans): purchasing UI tweaks — 4 small tasks with TDD-friendly bite-sized steps 2026-08-26 23:58:30 +02:00
DEls
7020449853 docs(specs): purchasing UI tweaks — shorter upload labels, unlinked-DN styling, view PO PDF, fulfillment bug 2026-08-26 23:56:34 +02:00
DEls
6558b3b24e docs(specs): update URL examples and clarify wording in specifications 2026-08-26 23:34:57 +02:00
DEls
417723628f docs(specs): fix stale URL example, 'above' wording, and add recordId notes 2026-08-26 23:34:10 +02:00
DEls
0c95243ecf docs(specs): screenshot of compact needs-review modal with working PDF 2026-08-26 23:33:33 +02:00
DEls
d2a4efa6e8 feat(needs-review): shrink modal to max-w-md for glance-and-pick flow 2026-08-26 23:26:26 +02:00
DEls
aa76470446 fix: pass recordId to SlipPreview from both callsites 2026-08-26 23:24:39 +02:00
DEls
ce3de2f9fb fix(slip-preview): build PB URL from recordId (was parsing filename) 2026-08-26 23:22:33 +02:00
DEls
aded006b9f docs(plans): PDF URL fix + modal compact 2026-08-26 23:21:26 +02:00
DEls
5e86fadc38 docs(specs): PDF URL fix + compact needs-review modal 2026-08-26 23:20:08 +02:00
DEls
0c2e092a3a feat(needs-review): compact modal with sticky header above slip 2026-08-26 23:10:05 +02:00
DEls
9ed7e55139 feat(purchasing): enhance purchasing page with received items tracking
Updated the PurchasingPage component to include received items data, allowing for accurate fulfillment percentage calculations based on authoritative data. The implementation now sums received quantities per purchase order item, replacing the previous denormalized field. Additionally, the needs-review delivery notes component has been improved with a modal for reviewing slips and assigning purchase orders, enhancing user interaction. The SlipPreview component has been integrated for better visual representation of delivery slips.
2026-08-26 23:05:05 +02:00
DEls
20d12f46d9 feat(slip-preview): render PDFs in an iframe instead of download-only 2026-08-26 23:03:41 +02:00
DEls
6df78d8ccd docs(plans): needs-review modal resize + inline PDF viewer 2026-08-26 23:00:10 +02:00
DEls
988ffc6377 docs(specs): needs-review modal resize + inline PDF viewer 2026-08-26 22:58:11 +02:00
DEls
d645a39d9d feat(ocr-result): enhance PO validation and tier resolution logic
Updated the OCR result processing to include validation of matched PO items before determining the verification tier. This change ensures that if the local PO items fetch is empty but n8n supplied matched IDs, the tier will downgrade to draft, requiring user review. Additionally, the `resolveOcrTier` function has been modified to account for item verifiability, improving the accuracy of tier assignments based on confidence levels and PO resolution status. Unit tests have been added to cover these new scenarios.
2026-08-26 22:07:42 +02:00
DEls
d03df384ea feat(ocr-result, po-items): enhance PO matching logic with fuzzy search
Updated the OCR result processing to include a more robust PO matching mechanism. The new implementation prioritizes exact matches, followed by normalized comparisons, and finally employs a Levenshtein-based fuzzy match for improved accuracy. This change ensures that delivery notes are correctly linked to purchase orders, even when OCR introduces minor errors. Additionally, the behavior for handling confidence levels and PO resolution has been clarified in the documentation, enhancing overall processing reliability.
2026-08-26 21:39:59 +02:00
DEls
2fcd7550de feat(po-items): server-side Levenshtein fuzzy match against all POs
OCR is noisy. Instead of exact-then-fuzzy-fallback, always fetch
all ~15 POs from PocketBase, normalise both sides (strip non-
alphanumeric, lowercase), score each with Levenshtein similarity,
and return the best match above 0.75 threshold. Single-char
insert/delete (MEGCP09239 vs MEGCPO9239) scores ~0.9 — matches.
Completely different POs (similarity <0.5) don't match.

Plus the existing 'return 200 with empty items' fallback if no
match — keeps the workflow loop alive even when OCR is bad.
2026-08-26 18:32:54 +02:00
DEls
901d7f2841 fix(po-items): return 200 with empty items on PO not found
OCR sometimes mis-reads PO numbers (e.g. 'MEGCP09239' instead of
'MEGCPO9239') — when that happens the PO lookup 404'd, which
terminated the n8n workflow loop even with onError set on the
HTTP node.

Return 200 with {po: null, items: [], note: 'po_not_found'} instead.
The downstream 'Match items to PO' Code node already treats an
empty items array as 'no PO to match against' and leaves every
OCR item unmatched. The OCR result route then marks the DN as
needs-review and the user can fix the PO link manually.
2026-08-26 17:54:51 +02:00
DEls
32049a67cc fix(po-items): sanitise poNumber param (strip leading # from OCR)
OCR sometimes prefixes PO numbers with '#' from the slip header
(e.g. "#MEGCPO9165"). The workflow URL would otherwise break on
the # being a URL fragment separator, and the exact-match PB
filter would also fail (the fuzzy fallback worked, but a clean
sanitise is cheaper).
2026-08-26 17:23:21 +02:00
DEls
a129affe38 fix(po-items): use double cast for RecordModel -> typed po
TS strict-mode rejects direct cast from RecordModel because the
two types don't sufficiently overlap. The fix is the standard
'as unknown as' double-cast. Was previously caught only by the
Docker build's stricter typecheck, not by the local dev
typecheck (which was polluted by .next/dev/types stale cache).
2026-08-26 16:53:04 +02:00
DEls
187d2a44ee feat(po-items): add route for n8n to fetch PO line items by number
GET /api/purchase-orders/by-number/[poNumber]/items
- Auth via shared x-ingest-secret
- Returns { po: {id, purchase_order_number, supplier}, items: [{id, description, qty, qty_outstanding, unit}] }
- Exact lookup first, then fuzzy fallback (strip non-alphanumeric + lowercase)
- Used by the Delivery Note OCR workflow to enrich the LLM extraction
  with PO context so each line item can be linked back to a
  purchase_order_items row.
2026-08-26 16:41:55 +02:00
DEls
a100f742ac fix(ocr-result): use && not AND in batch filter
PB's filter parser only accepts && as the boolean AND operator; the
literal word AND returns HTTP 400 'Something went wrong while processing
your request'. The rest of the codebase already uses && (see
purchase-orders/upload/route.ts:81).

Caused updateBatchRow to silently no-op after every successful OCR
run — the batch row never transitioned to 'processed'.
2026-08-26 16:34:54 +02:00
DEls
1606bd2cc7 feat(delivery-notes): own batch lifecycle in OCR pipeline
- upload: create data_import_batches row when DN is uploaded
  (batch_type=delivery_notes, processed_record_id=<dnId>)
- ocr-result: mark the batch processed on success, failed/dead on
  failure (uses nextBatchState from batch-state.ts for retries)
- new /api/delivery-notes/[id]/slip proxy route: lets n8n fetch the
  slip file via shared secret instead of embedding the PocketBase
  admin JWT in workflow headers (which was leaking in the workflow JSON)

Backfill: existing pending DN ekko6xkhynoekas needs a manually
created data_import_batches row until it's reprocessed.
2026-08-26 15:50:44 +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
2ace4365e6 feat(timesheets): enhance import functionality with site linking and employee creation
- Updated the timesheet import route to support optional metadata fields for site linking and auto-creation of missing employees.
- Enhanced parsing to detect multiple file formats, including Hills Casual Matrix and long-format CSV/XLSX.
- Modified response structure to include detected format and created employees in the JSON response.
- Improved UI components to allow project selection and options for creating new workers during import.
- Refactored import logic to ensure a seamless user experience when handling timesheet uploads.
2026-08-26 12:44:02 +02:00
DEls
f5c063765d feat(fuel-view): enhance inline editing and UI components
- Integrated inline editing functionality for vehicle descriptions, allowing users to edit directly within the FuelView.
- Added a Topbar with EditModeToggle and ImportFuelLogsButton for improved user interaction.
- Implemented EditModeBanner to provide feedback during editing sessions.
- Updated FleetMobileCard and FleetRow components to support inline editing, enhancing the overall user experience.
- Refactored FuelView to accept title and subtitle props for better contextual information.
2026-08-26 08:54:57 +02:00
DEls
2158538782 fix(employee-details): enhance detail drawer scrolling and layout
- Improved the layout of the detail drawer using flexbox for better responsiveness.
- Implemented a scrollable container for the timesheet table to prevent overflow.
- Ensured a clean and organized appearance for displaying employee details.
2026-08-25 23:38:35 +02:00
DEls
ac21f8a214 fix(employees-view): improve detail drawer layout and scrolling behavior
- Adjusted the DialogContent to use flex layout and set a maximum height for better responsiveness.
- Wrapped the timesheet table in a scrollable div to prevent overflow and enhance user experience.
- Ensured the layout maintains a clean and organized appearance while displaying employee details.
2026-08-25 22:47:26 +02:00