Adds src/lib/agent/attachments.ts (parseAttachment/previewForModel/
summarise for xlsx, xls, csv, pdf — text-layer or scanned, via
pdftotext — image and plain text) with fixtures under
src/lib/agent/__fixtures__/, POST /api/chat/attachments (multipart
upload into an append-only ask_messages row, rasterising a scanned
PDF's first pages so they ride along as vision images the same way a
photographed slip does), and the read_attachment / send_to_po_pipeline
tools in tools-actions.ts. send_to_po_pipeline mirrors
/api/purchase-orders/upload's own hash-dedupe-then-create logic
directly against PocketBase rather than a same-process HTTP call (no
reliable base URL to call itself with from a server-side tool handler).
chat-view.tsx gained a second attach control (FileUp icon) alongside
the existing PO-upload paperclip, wired to the new route; pending
attachment chips ride along on the next message the same way pending
photos already do.
Deviation: send_to_po_pipeline takes { id, site } (the plan's { id }
alone can't resolve which site's purchase_orders row to create,
since /api/purchase-orders/upload always required one).
Live browser check skipped: ask_messages doesn't exist yet in the
target PocketBase (pending the user's --apply of Task 1's schema).
Covered by mocked unit tests and real fixture files instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds src/lib/agent/memory.ts (remember/forget/listMemories, dedupe by
normalised text, formatMemoriesForPrompt) and wires it in: a
"## What you remember" system-prompt section, the remember/forget/
list_memories tools (src/lib/agent/tools-actions.ts, a per-request
factory since these need the actor unlike the static read-only tool
set), a REST view for the UI (/api/ask/memories), and a Memory drawer
in chat-view.tsx (Mine / Shared with everyone, add, delete).
Adjusted src/lib/agent/read-only.test.ts: its blanket "no mutation
calls under src/lib/agent" contract now allowlists the specific write
seams the plan designates (memory.ts here; tools-actions.ts, gateway.ts
and images.ts to follow) instead of forbidding every file, since
memories/requests are specified to bypass the gateway and write
directly.
Live browser check skipped: ask_log doesn't exist yet in the target
PocketBase (pending the user's --apply of Task 1's schema). Covered by
mocked unit tests instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds the "Who's using Ask?" picker (GET /api/ask/users), actor
resolution via x-ask-user (src/lib/agent/context.ts), and thread
persistence (src/lib/chat/threads.ts, /api/chat/threads routes).
/api/chat now accepts { threadId?, message, display?, images? } and
persists each turn as its own append-only ask_messages row; the old
{ history } body still works for one release. chat-view.tsx gained the
picker, a thread sidebar (new/select/rename/archive), and ?thread=<id>
in the URL.
Live browser check skipped: ask_threads/ask_messages/ask_log don't
exist yet in the target PocketBase (Task 1's schema is dry-run only,
pending the user's --apply). Covered by mocked unit tests instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Additive, dry-run-by-default script creating ask_threads, ask_messages
and ask_log (change/memory/request), following the
pb-add-dn-staged-ingestion.py pattern. --dry-run confirms all three
collections are new; --apply is a human step.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Green when a supplier's ordered > 0 and outstanding === 0 -- the whole
row, not just the number, so a fully-paid-up supplier stands out at a
glance.
Period filter next to the search:
All time, This week, This month,
Week 1-4 (of current calendar month, last absorbs any remainder),
Month 1 (current) / Month 2 (last) / Month 3 (month before),
Custom range with two date inputs.
Window anchors on delivery_notes.date_received -- liability accrues when
the goods arrive, not when the PO is uploaded ('we only pay what we
received this month'). computeSupplierBalances gains lastDeliveryISO per
row; a pure helper in supplier-period.ts resolves windows in UTC so the
boundary does not drift with the server's timezone.
Four KPI tiles at the top of the page:
- Total ordered / delivered / outstanding (cumulative, always).
- Payable this period (delivery value inside the active window).
Subtitle is honest: 'subtract anything already paid' -- we do not
track supplier payments, so this is the upper bound.
ui/select.tsx exports a SelectLabel so the period dropdown can group
options.
The query string skipped the PIN gate on any URL. The PIN is now the
only way in. Docs updated; the login plan notes there is no fixed
cutover date while the app is in on-site testing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A live /findings run showed the model adding per-site and per-item
counts itself. Rules that span sites now return one finding with the
total in the title and the per-site split in the detail, and the prompt
says not to add counts across findings.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
list_sites now reads computeSiteSummaries (moved verbatim, output pinned
by snapshot). The read-only guard also scans src/lib/reports.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 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>
Introduced a new secret key for receipt correction in the environment configuration. Enhanced the API for handling item edits in delivery notes, allowing for more granular updates and preserving existing manual overrides. Updated the document detail view to display additional supplier and customer information, and integrated a toggle for skipping customer identity checks. Improved validation for item entries and added tests to ensure robust handling of various edge cases in item editing.
Updated the purchase order processing to include enhanced validation for item edits, ensuring that all required fields are checked and properly formatted. Introduced new functions for computing item amounts, taking into account discounts and VAT. Refactored the PO detail view to streamline item management, including a new ItemsCard component for better organization of line items. Additionally, improved the handling of PO number sanitization to strip leading and trailing characters, enhancing matching accuracy. This update aims to improve the overall robustness and user experience of the purchase order system.
Updated the on-site dashboard to provide a clearer overview of employee statuses. Renamed sections for better clarity, changing "On site now" to "On site today" and updated descriptions to reflect current employee activity. Refactored the data handling to categorize employees into three distinct groups: those currently on site, those clocked out today, and those who have not arrived. This change improves the visibility of employee attendance and enhances the overall user experience on the dashboard.
Removed the DialogFooter component and restructured the ConfirmDialog layout for improved usability. Increased AUTO_DISMISS_MS from 5 seconds to 30 seconds and adjusted DOUBLE_CONFIRM_WINDOW_MS from 4 seconds to 6 seconds to enhance user experience during the scanning process. Updated button styles for better visibility and interaction. This change aims to streamline the confirmation flow and reduce accidental dismissals.
Enhanced the PersonScanner component to support a two-step flow for scanning employee badges. The new process includes a lookup phase to identify the employee and their last scan status, followed by a confirmation phase where the operator can verify the scan before recording the clock event. This change addresses issues with accidental double scans and improves user feedback with warnings for recent scans. Updated state management and error handling to reflect the new flow, ensuring a smoother user experience.
Introduced a new function to infer vehicle unit type from descriptions, improving the auto-creation process for vehicles in the fuel logs. Enhanced error handling for missing identifiers during vehicle creation, providing clearer feedback on the reasons for failures. Updated the SheetJS parsing to prevent date misinterpretation and refined the PlantPage to display all plant vehicles, regardless of cost per hour, ensuring better visibility of available resources.
Leaflet's internal panes use z-index 200..1000 (tile, overlay,
marker, popup, controls). The map wrapper had no z-index of its
own, so those values leaked up to the dashboard layout and buried
the Topbar's `All Sites` dropdown — which sits at z-50 by default.
The Topbar itself is in a non-overlapping zone so it looked fine,
but the dropdown's content (portalled into document.body) dropped
into the map area and got hidden behind the tile pane.
Add `isolation: isolate` to the map wrapper so Leaflet's z-index
ladder is contained inside the wrapper as a single atomic unit.
External UI at z-50+ now floats above the map as expected. The
legend and layer switcher inside the wrapper are unchanged (still
z-[400]) — they remain above the tile pane and below the map
controls, which is where they were before.
No-op for every other page; the wrapper is only on /dashboard/plant/map.
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.