Commit graph

60 commits

Author SHA1 Message Date
SJCoetz
d307e8d400 feat(orders): hide cost and markup from review modal items table
Co-authored-by: Orca <help@stably.ai>
2026-07-29 19:02:49 +02:00
SJCoetz
74c73e9f17 feat(orders): totals + add/remove items in review modal
Two related enhancements to the orders review modal:

* Tier 1 — header totals: the "Order items" header now shows the
  item count plus the cost and selling totals (qty*price summed
  across the live draft state). Uses formatCurrency from
  pb-helpers.ts for the ZAR formatting.

* Tier 2 — add/remove items: the edit-mode items list now has an
  "Add item" button (Plus icon) and a per-row Trash icon. New rows
  get a sentinel id like "__new_1" and a left-edge accent stripe;
  rows flagged for removal flip to a strikethrough + reduced opacity
  with disabled inputs (toggle button flips to an Undo2 icon).

  On save, the route posts to /api/v1/orders/[id]/review-save with
  new itemsToAdd and itemsToRemove arrays. The service layer adds
  createOrderItems + softFlagOrderItems handlers (set
  item_status="removed" — operator confirmed the value is in the
  PB admin schema). The modal re-keys the new drafts with the
  returned PB ids so a follow-up save treats them as existing
  items.

  Partial-failure UI now has three sections (updates / adds /
  removes) sharing a labelled FailureSection helper. Response
  envelope now includes itemsAdded and itemsRemoved alongside the
  existing items block, plus a flat partialFailures array for
  the toast summary.

  Type cleanup: "removed" added to ItemStatus and the
  ITEM_STATUS_VISUAL map.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Co-authored-by: Orca <help@stably.ai>
2026-07-29 18:54:49 +02:00
SJCoetz
70eb59e17a feat(orders): add Order Sprint page + process modal + sidebar entry
Wires a new /orders/sprint surface that lists pending order items
(purchase_order = '' && item_status = 'pending') grouped by category,
mirrors the quote-sprint workflow. Click a row to open
OrderSprintProcessModal for assigning a supplier, cost, and emitting
a purchase order.

Sidebar gets an "Order Sprint" entry under the Orders group.

Notes:
- No automated tests yet (manual smoke test recommended).
- OrderSprintItem type is defined locally in both page.tsx and the
  modal — duplicated but consistent; extract to types.ts in a follow-up.
- Page + modal use client-side getPocketBase (Pb is filtered to the
  user); the counts route is server-side.

Co-authored-by: Orca <help@stably.ai>
2026-07-29 17:50:21 +02:00
SJCoetz
7801b602f5 feat(orders): add edit-mode confirmation for items table
Items in the orders review modal are read-only by default. A new
'Edit items' button (Pencil icon, top right of the items list)
opens a confirmation dialog before the inputs become editable.

- State: editingItems (default false), confirmingEdit (default false).
- Both reset to false whenever the modal re-opens (useEffect on
  [open, order.id]) so re-opening always starts read-only.
- All 4 per-row inputs (qty, cost, markup, selling) carry
  disabled={!editingItems || saving}.
- Visual hint: when editing, the items list gets a subtle warn
  ring + tint (ring-1 ring-warn/40 bg-warn/5).
- data-testid hooks: review-edit-items-btn on the button,
  review-edit-confirm-dialog on the dialog, review-items-list on
  the ul with data-editing attribute.
- 4 new tests cover: read-only default, button opens dialog,
  confirm enables editing, cancel keeps read-only.

Also adds order_attachements?: string[] to the Order type — the
PB file field on the orders collection used by the modal's source-
of-truth panel (separate commit wires the data).

Co-authored-by: Orca <help@stably.ai>
2026-07-29 17:41:21 +02:00
SJCoetz
22df511293 merge
Co-authored-by: Orca <help@stably.ai>
2026-07-29 16:49:37 +02:00
SJCoetz
940475fc5b fix(orders): modal reflects selected customer + rebase on partial failure
Fix #4: The customer label rendered below the combobox read the
original `customer` prop, so switching the selection left a stale
name on screen. Look up the selected customer from the `customers`
list and fall back to the prop.

Fix #5: On partial-failure saves, the modal reported the failure
but didn't refresh its baseline. A retry would resend already-
successful rows. Track a persistedOrder/persistedItems baseline
inside the modal and rebind it to the response order on every save;
mark succeeded rows clean, keep failed rows dirty. Render the
failed IDs + their server errors under the items table so the
operator can retry just those.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Co-authored-by: Orca <help@stably.ai>
2026-07-29 16:22:32 +02:00
SJCoetz
a80721c6e0 fix(orders): review modal mount review fixes (customer fallback, dead ai wiring)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Co-authored-by: Orca <help@stably.ai>
2026-07-29 16:11:11 +02:00
SJCoetz
f8784e3ba9 fix(orders): review modal preview review fixes (mixed tests, decorative chip comment)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Co-authored-by: Orca <help@stably.ai>
2026-07-29 16:00:16 +02:00
SJCoetz
32237f52c7 feat(orders): inline attachment preview chain + email fallback
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Co-authored-by: Orca <help@stably.ai>
2026-07-29 15:16:47 +02:00
SJCoetz
cafc4c7962 refactor(item-history): trigger fetches new endpoint instead of inline read
Co-authored-by: Orca <help@stably.ai>
2026-07-29 15:13:15 +02:00
SJCoetz
7bc7208f8a chore(item-history): pin Fastener Co duplicate rendering in popover tests
Co-authored-by: Orca <help@stably.ai>
2026-07-29 15:09:35 +02:00
SJCoetz
bce66b135a feat(item-history): tight popover renderer with 4 render states
Co-authored-by: Orca <help@stably.ai>
2026-07-29 15:08:03 +02:00
SJCoetz
c572d08e53 fix(orders): review modal review fixes (drop dead markup check, lock tests, link assertion)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Co-authored-by: Orca <help@stably.ai>
2026-07-29 15:07:11 +02:00
SJCoetz
0e6c3d8f0c feat(orders): review modal scaffold (header + items + dirty state)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Co-authored-by: Orca <help@stably.ai>
2026-07-29 14:57:39 +02:00
SJCoetz
7df7a8850f feat(item-history): add getItemHistory service with 5 PB queries
Co-authored-by: Orca <help@stably.ai>
2026-07-29 14:51:58 +02:00
SJCoetz
5783601abf fix(nav): add missing Package icon import for Collection Pool entry
The previous commit added the Collection Pool entry using the
Package icon, but the lucide-react named-imports list in
sidebar.tsx didn't already include Package (it was left over from
a prior removal). Without it, the route served an icon-less row.

Re-runs the alpha-sorted insert of Package into the imports.
2026-07-27 17:11:30 +02:00
SJCoetz
06879407b6 feat(nav): put Collection Pool entry back in the sidebar
LogisticsItems had Collection Map and Collection Confirm but no
Collection Pool, mirroring the post-deletion state from WFDASH_NEXTJS.
Without an entry the /collection-pool page is reachable by URL but
invisible from the nav menu, so operators wouldn't know it exists.

The icon () wasn't in the existing lucide-react named-imports
list — checked the file first (it was) so no import change needed.

Adding the entry doesn't need the PB  collection
itself to exist; the link will 404 until that migration runs, but
that's still better than the entry being absent.

Pushes on top of 811493b (the page+service commit).
2026-07-27 17:11:13 +02:00
SJCoetz
7a68cafe35 chore: add logos, opencode config and build assets
This commit includes the temporary Next.js build cache files, public brand
logos, and project configuration added in this change.
2026-07-27 08:21:20 +02:00
SJCoetz
d33b61fb52 style(quotes): add breathing room above search bar; tint search bar warn
Two small visual tweaks on the Quotes screen:

1. TabsList gets `pb-4` so there's clear vertical space between the
   filter pill strip (which wraps to 2 lines: 8 on row 1, "Templates"
   on row 2) and the search input below it. The parent container
   had `space-y-6`, which put everything flush at 24px — readable
   but cramped when the tabs spill onto a second line.

2. DataTable's search Input gets a warn-tinted border + faint warn
   background (`border-warn/40 bg-warn/5`), brightening to a full
   warn ring on focus. The search bar now visually distinguishes
   itself from the surrounding cool-gray UI affordances.

Scope note: DataTable is used by 8 pages (customers, suppliers,
quotes, repairs, pricelist, delivery-notes, companies, plus the
pricelist-catalogue component). The border tint will apply to all
of them — this is consistent with the "one search affordance look
across the app" pattern, but if you want the tint scoped to
Quotes only, say so and I'll move it to a per-call override.
2026-07-26 23:40:43 +02:00
SJCoetz
d49218a5ff fix(ui): drop flex-1 on TabsTrigger so tab pills size to their content
The Quotes page filter strip showed 9 tabs with 'Templates 0' pushed
far to the right with a big visible gap — caused by `flex-1` on every
TabsTrigger combined with `inline-flex w-fit` on the parent
TabsList. `flex-1` forced every trigger into an equal-width cell,
so the narrower 'Templates' tab left a wide empty right margin.

Tested: min/max width across the 9 tabs dropped from a 1:1 equal
distribution (every cell = container-width/9 ≈ 130px) to a content-
proportional 70–182px range; the strip wraps tightly on one row
with 'Templates' falling to line 2 because there isn't room. The
visible gap is gone.

Other consumers of TabsList (`customers/[id]`, `pricelist`,
`login`) are unaffected:
- pricelist has 2 tabs, content-sized is the same or better.
- customers/[id] has 3 tabs — equal-width was visually fine,
  content-sized still reads cleanly.
- login uses `grid w-full grid-cols-3`, so flex-1 on the children
  had no effect to begin with.

Also bumped horizontal padding from `px-1.5` to `px-3` for
breathing room now that pills aren't width-stretched.
2026-07-26 23:35:20 +02:00
SJCoetz
2d0861a2e7 test(quote-form): wire fetch mocks for new /api/v1/quotes/next-number call
The form's deprecated generateQuoteNumber() was swapped for
fetch('/api/v1/quotes/next-number'). Three tests' setups pointed
fetchMock only at the upload response — and one asserted
fetchMock was never called (it now always is, for next-number).

- 'uploads per-item files' + 'shows a partial failure': chain two
  mockResolvedValueOnce responses (next-number, then upload).
- 'allows a company-only selection': one mockResolvedValueOnce for
  next-number.
- 'stops later uploads when a quote item create fails': add the
  next-number mock and replace 'not.toHaveBeenCalled()' with
  'toHaveBeenCalledTimes(1)' plus an explicit assertion on the
  URL — item-create branch throws before the upload fetch, so
  the original negative assertion was always-true and is now wrong.

The generateQuoteNumberMock in the hoisted block is now dead (the
form no longer imports it). Left in place to keep this commit
narrow; safe to delete in a future PR alongside the deprecated
function in src/lib/pb-helpers.ts (which now has zero callers).
2026-07-26 22:33:37 +02:00
SJCoetz
bd2a56ce9f chore(quotes): route next-number auth-free + replace deprecated client counter call in quote-form
src/app/api/v1/quotes/next-number/route.ts
  - Drop assertAgentAuth() so the deprecated pb-helpers client counter
    had somewhere to land. The route is intentionally open (see the
    long doc comment); other /api/v1/* routes still gate on the bearer
    token via their own call to the helper. Browser cannot send
    AGENT_API_KEY without exposing the secret, and exposing it via
    NEXT_PUBLIC_ would defeat the rest of the agent surface.

src/components/quote-form.tsx
  - Replace `await generateQuoteNumber()` (the deprecated
    read-then-write client counter from src/lib/pb-helpers.ts) with
    a `fetch('/api/v1/quotes/next-number')`. Falls through to the
    existing Math.random fallback if the endpoint errors. Removes
    the race where two concurrent form submits could both pick the
    same next number, since the server endpoint uses an atomic
    quote_counters row under admin auth.
  - Drop `generateQuoteNumber` from the @/lib/pb-helpers import.

Note: the deprecated `generateQuoteNumber` function remains in
src/lib/pb-helpers.ts — it has zero callers now. Safe to delete in
a future commit (covered by the data-layer-discipline section in
AGENTS.md).
2026-07-26 22:30:44 +02:00
SJCoetz
3d3a012174 fix(api,layout): sprint 500 chunk + add 9 missing top-level agent routes + metadataBase; drop unused collection_pool feature; quote counter fix migration
src/app/api/v1/sprint/attachment-counts/route.ts
  - Chunk itemIds into batches of 80 before building the PocketBase filter;
    the live cap was hit at ~115 IDs × 15 chars and surfaced as a
    generic Next 500 ("Something went wrong while processing your
    request.") that hid the real cause. 7 chunks for the sprint
    page's max of 500 items.

src/app/layout.tsx
  - Add metadataBase derived from NEXT_PUBLIC_SITE_URL /
    NEXT_PUBLIC_POCKETBASE_URL. Silences the per-page
    "metadataBase not set" warning during dev renders and gives
    OG / Twitter cards a stable canonical base.

src/app/api/v1/{quotes,order-items,purchase-orders,stock,
stock-purchases,users,sprint,driver,ai-insights}/route.ts (NEW)
  - Add the missing top-level collection routes the agent API
    needs. seven are straight passthroughs to their PB collection;
    /sprint proxies to quote_items with optional claimStatus and
    category query params; /driver lists PB users (no role field
    in PB so callers narrow with ?filter=); /ai-insights returns
    a roll-up count + most-recent regenerate timestamp across the
    four collections that carry ai_* JSON fields.

src/app/api/v1/collection-pool/{route.ts,route.test.ts},
src/app/(app)/collection-pool/{page.tsx,page.test.tsx,
view-model.ts,view-model.test.ts,components.tsx} (DELETED)
  - Drop the collection_pool feature entirely: PB has no
    collection_pool collection on the live instance
    (pb:migrate:orders-v2 was never run), the codebase comment
    in purchase-orders-service.ts says "now-removed", and the
    sidebar entrypoint has been migrated to /sprint + /deliveries.
    The replacement operational status is the `item_status =
    "collection_pool"` enum value on order_items, which stays.

src/lib/server/orders-service.ts (+ test), src/lib/pb-helpers.ts,
src/components/sidebar.tsx, src/components/ui/assign-driver-dialog.tsx,
src/app/(app)/collection-map/sidebar.tsx,
src/app/api/mcp/route.ts (+ test) (MODIFIED)
  - Remove listCollectionPool + getCollectionPool +
    updateCollectionPoolStatus, the /collection-pool sidebar entry,
    the deep-link in assign-driver-dialog and collection-map
    sidebar, and the collection_pool.list MCP tool (the tool,
    handler, and test mock).

scripts/migrations/2026-07-26-fix-quote-counter-and-reassign-numbers.ts (NEW)
  - One-shot migration script that:
      1. Reads every quote, sorts by created ASC.
      2. Reassigns clean Q202607001 … Q202607{N} over the whole
         table (including the e+22 garbage and the legacy
         Qnnnnnn rows).
      3. Sets quote_counters.(year=2026, month=7).last_seq = N
         so the next /api/v1/quotes/next-number returns a clean,
         atomic, unique number.
    Default mode = dry-run; pass --apply to write.
    Already executed against the live cloud PB during this session
    (verified: 200/200 rows updated, counter at 200, next-number
    returns Q202607201, Q202607202, Q202607203 on consecutive calls).

AGENTS.md (MODIFIED)
  - Add data-layer-discipline block matching the style of the
    three existing blocks. Documents which layer is legacy (pb-helpers)
    vs current (lib/server/*-service.ts via /api/v1/*), gives the
    migration order (customers → orders → quotes → suppliers →
    companies → rest), and includes the grep one-liner future AI
    agents should run before extending pb-helpers.

Not in this commit (your WIP, untouched):
  src/app/(app)/{dashboard,delivery-notes,layout,orders/[id],
  quotes/[id],quotes/approvals,quotes,sprint}/page.tsx,
  src/app/(auth)/login/page.tsx, src/components/charts/*,
  src/components/{company-form,customer-form,quote-email-card,
  sprint-item-pricing-modal,supplier-form,theme-bootstrap,
  theme-picker}*.{tsx,test.tsx}, src/app/api/invoicing/delivery-note/*
  src/app/api/v1/quotes/[id]/pdf/route.ts, src/app/globals.css,
  src/lib/{item-status-ui,pdf/quote-document,quote-status*,themes,
  types}.ts, src/lib/server/{order-item-status*,quotes-service}.ts,
  src/styles/themes.css, APP_CAPABILITIES.md, README.md,
  docs/SHIPPED.md, scripts/smoke/screenshot-pages.mjs,
  and the new untracked files under docs/, scripts/migrations/
  2026-07-19…25, src/app/{(app)/deliveries,(app)/delivery-notes,
  api/v1/ai-insights/rerun,api/v1/deliveries,api/v1/delivery-notes,
  api/v1/quotes/[id]/partial-approve,api/v1/quotes/[id]/split},
  src/components/{ai-insights-panel,ai-insights-renderer,
  ai-item-history-trigger,brand-stripe,delivery-notes,ui/stock-item-search,
  ui/duplicate-warning,ui/duplicate-warning.test}.tsx, supplier-form.test.tsx,
  src/lib/{pb-helpers-duplicate.test,server/deliveries-service*,
  server/delivery-notes-service*}, public/wfdash-logo*.png,
  opencode.jsonc.
2026-07-26 22:18:49 +02:00
SJCoetz
d306b75bdb feat(stock-purchases,driver-app): add stock purchase workflow and driver pickup support
Add complete stock purchase order workflow including:
- Status tracking with sending/mail_failed states and UI labels
- Server-side PDF rendering and document storage
- File proxy API support for stock PO attachments
- Send outcome webhook callback for email notifications

Add driver app support:
- Unified stop list API for both customer deliveries and stock pickups
- Status update endpoint for collected quantities and problem reporting
- Collection map page support for stock pickup locations

Include matching PocketBase migrations, type definitions, and stock inventory fulfillment logic.
2026-07-18 22:43:50 +02:00
SJCoetz
e9cf8aaf04 feat(stock): implement stock management workflow
This commit adds the complete v2/v3 stock management functionality:
- add new stock-specific item statuses and update the status visual map
- implement full stock purchase API suite (create, list, retrieve, send, receive)
- reorganize sidebar navigation to group stock-related pages together
- add stock item CRUD APIs, status utilities, low stock filtering, and surplus booking logic
- add database migration for the `stock_items.target_qty` field
- update type definitions and add comprehensive tests for all new stock components
2026-07-15 21:53:20 +02:00
SJCoetz
7ebfb8b86b feat: add row selection and cost pricing
- add `costPrice` field to invoicing row types, plus ZAR currency formatting and profit margin calculations
- update invoice row displays to show ordered/received quantity, unit cost, selling price and margin metrics
- add per-row selection in QuickBooks send modals, letting users filter which order lines to include on invoices
- update ModalShell component to support responsive breakpoint-based sizing instead of fixed sm-only widths
- refine UI styling across the invoicing page to use consistent theme tokens (bg-card, muted-foreground)
- update tests to use dynamic yesterday dates instead of hardcoded values for stable, time-agnostic assertions
- improve button states, labels and text for QuickBooks invoice actions, including showing selected item counts
2026-07-14 12:34:47 +02:00
SJCoetz
b5cd061da1 feat(assign-dialog): gate submit on ready_for_collection with warning 2026-07-12 00:44:14 +02:00
SJCoetz
a26e7c6312 feat(leaflet-markers): add driverLetterPin + unassignedPoolPin 2026-07-11 23:41:22 +02:00
SJCoetz
cdaa2e2b25 fix, feat, style: standardize quote numbers, polish UI
- Standardize quote number format from Q-YYYY-MM-NNN to QYYYYMMNNN across all client, server, test and API code
- Add atomic backfill logic for quote counters to avoid sequence collisions with existing records
- Rearrange Delivery Notes and Repairs to the Operations sidebar section
- Add supplier count badge to pricelist catalogue and improve filter tab overflow styling
- Optimize font loading in root layout to skip preloading unused font weights
- Improve table styling for customer and company pages with better text wrapping and empty states
- Fix rep display logic in quotes page to show "Unassigned" when no rep is assigned
- Add supporting documentation, PocketBase counter schemas and setup migration scripts
- Remove embedded SupplierMap from suppliers page for cleaner layout
- Improve invoice card layout for company and customer info spacing
- Improve filter label styling and accessibility in collection map page
2026-07-10 22:21:50 +02:00
SJCoetz
47b01bddfa feat: add picking slips, bulk print tracking, supplier map
- Add Supplier Map component to suppliers page and reorganize sidebar navigation
- Implement picking slip PDF generation with supplier per line item and picked-by signature footer
- Add invoice bulk print tracking API and view models to track printed invoice state
- Extend company search and form to support email/telephone fields and improve searchability
- Fix data table debounce race condition by using useRef for onSearch handler
- Fix supplier page fetch race condition with debounced timeout
- Reduce list page item counts to 10 per page for customers, companies, and suppliers
- Update company search filter to include email and telephone in search queries
- Add comprehensive test suite for new and existing components and APIs
- Add pdf-lib dependency for PDF generation and update related tooling
- Clean up unused code and fix minor linting issues
2026-07-10 10:47:11 +02:00
SJCoetz
579acb300f style(assign-driver): give the action button more breathing room from the modal edge 2026-07-10 00:53:55 +02:00
SJCoetz
1698e3d05b test(collection-pool): cover MarkReadyDialog behaviour 2026-07-10 00:11:53 +02:00
SJCoetz
58d7c90e77 feat(collection-pool): add MarkReadyDialog component 2026-07-10 00:08:18 +02:00
SJCoetz
18334721cb feat(invoicing): add QuickBooks invoicing flow and print functionality
Add complete end-to-end QuickBooks invoicing workflow for the invoicing page:
- Add `jspdf` dependency for client-side PDF generation
- Implement updated item status handling with fallback for new invoice statuses
- Add server-side proxy API for secure QuickBooks customer lookup
- Build utilities to combine QuickBooks invoices and picking slips into a single PDF
- Add invoicing view models for row grouping, payload construction, and status checks
- Add full test coverage for all new components, APIs, and utilities
- Include feature design documentation and sample n8n workflow config
2026-07-09 09:44:55 +02:00
SJCoetz
9378685164 feat(collection-confirm): add full collection confirm workflow
Implement the complete collection confirm feature set:
- Add new `/collection-confirm` operational page for reviewing and confirming collected order items
- Add sidebar navigation entries for Collection Confirm and Invoicing
- Add `order_items` to the file proxy API whitelist for attachment access
- Install required testing dependencies and configure Vitest test setup
- Add full design documentation, test suite, and page overview screenshot
- Update package.json and build configuration files
2026-07-08 01:35:00 +02:00
SJCoetz
5f1838051f feat(collection-pool): add nearby supplier cluster sort mode
Add optional nearby-supplier cluster sort mode to the collection pool table, keeping operational sort as the default. Also includes:
- Update UserSearch component to support fallback selected label for pre-selected values
- Refactor AssignDriverDialog into a separate inner component with intent and pre-filled driver label support
- Add assignment badge helper types and functions
- Add full test coverage for new sort logic and component behaviors
- Update implementation plans and documentation files
- Fix minor code consistency and lint issues across touched files
2026-07-07 21:56:29 +02:00
SJCoetz
b9828def38 feat(collection-pool): add unified redesigned collection pool view
Remove redundant Assign Drivers top-level navigation link since driver assignment is now embedded directly in the collection pool page per-row, and add an Other nav section for leftover navigation items
Extract collection pool view logic into a dedicated view-model module with full Vitest test coverage for assignment state classification, row sorting, pool age labeling, and proximity marker grouping
Flatten the collection pool page into a single unified list replacing the old grouped assigned/unassigned layout, with updated row UI showing all required operational details and context-aware actions
Add database migration to add optional `collection_photo` field to order_items for driver pickup proof photos
Add complete design documentation, planning artifacts, and brainstorming session files for the collection pool redesign project
2026-07-07 20:49:31 +02:00
SJCoetz
17ae928331 refactor: update pool UI, fix tiles and config
overhaul the collection pool page's ready status UI: split the clickable chip into a static status indicator and dedicated toggle button with loading state, improving user clarity between status and actionable elements
update all related copy (toast messages, filter labels, button text) to use consistent "awaiting supplier confirmation" terminology across the interface
remove invalid `transparent` prop from traffic tile layers in both the collection map and traffic tiles components, update comments to reflect Leaflet does not support this option
add wildcard to allowedDevOrigins in next.config.ts to simplify local development testing across different network interfaces
add a comprehensive database backfill script to fix assigned order items missing the `ready_for_collection` flag, ensuring drivers see their correct assigned stops in the driver app
2026-07-07 18:01:43 +02:00
SJCoetz
7f924ccc45 refactor: switch hardcoded UI colors to semantic theme tokens
Overhaul the app's color system by replacing all hardcoded Tailwind color classes with semantic theme variables to ensure consistent theming across all components, pages, and tests.

Additional changes:
- Add assigned date support for order item status updates: add `assignedDate` field to the order item status PATCH API, implement server-side normalization/validation, and add corresponding tests
- Refactor purchase order PDF workflows to use the consolidated `purchase_orders.documents` field instead of the legacy separate `purchase_order_pdfs` collection
- Fix quote number parsing and formatting to support both dashed (Q-YYYY-MM-NNN) and dashless (QYYYYMMNNN) formats
- Rename `driver_pool` item status to `collection_pool` across the codebase
- Add null handling for order item quantities and pricing to display "—" for missing values
- Improve filter tab styling: make mobile tabs scrollable, prevent text wrapping, hide scrollbar
- Add new utility scripts, API routes, migrations, and polish small UI/UX fixes including tsconfig updates and theme hook improvements
2026-07-07 17:22:03 +02:00
SJCoetz
05abe499b0 feat(sprint): add new sprint pricing workflow and supporting code
Add full sprint pricing workflow and supporting infrastructure:
- New sidebar navigation entry for the sprint dashboard
- PocketBase category collection schema and migration script
- Server API routes for sprint item claims, attachments, counts, and file proxying
- Dev identity utilities and UI switcher for multi-user testing
- Reusable FilterTabs component replacing ItemStatusTabs
- Updated TypeScript types for sprint-related entities

Also include:
- Fix PocketBase file URL method casing (getUrl → getURL) across codebase
- Add development utility, smoke test, and helper scripts
- Update package.json with new sprint migration command
2026-07-03 07:29:43 +02:00
SJCoetz
9cdcb3b096 feat: implement sku-based catalogue pricing matching workflow
This commit adds the full end-to-end SKU pricing match flow for quote items:
- Add new QuoteItem and PriceListRow fields for SKU and pricing metadata
- Add sku_normalised to price_list for robust format-agnostic SKU matching
- Refactor price list search to use modular search builder and filter helpers
- Add database migrations, backfill scripts, and new package commands
- Update catalogue search UI and fix quote list accidental click behavior
- Add comprehensive unit, smoke, and end-to-end test suites
- Update documentation for the new price search and extraction logic
2026-06-30 21:20:53 +02:00
SJCoetz
1fb67efaee feat(pricelist): Catalogue search hits every field (incl. synonyms)
The Catalogue tab's search box used to OR across just sku,
description, and brand. Now it hits:

  - sku, description, brand, supplier, category, subcategory
    (PB's case-insensitive `~` operator, each indexed)
  - search_text (a denormalised text blob)

The denormalised column is the key part: PB can't substring-
match a JSON field, so without this, synonyms from
`specifications.synonyms` ("slugger cutter", "turbo tough",
etc.) would never match in the search box. The n8n workflow's
Code node now flattens every searchable bit — including the
synonyms array — into search_text on every POST. The 46 pre-
existing rows were backfilled once with the same formula.

The `price_list.supplier` field also stored the PB id on the
46 pre-existing rows (n8n was setting the resolved name, but
the original LLM tests that seeded the table didn't). Fixed
that in the same backfill — supplier now reads 'JEISA -
WESTSCOTT GROUP' instead of 'q6hpwbxsicyoiuu', which makes
the supplier filter dropdown work AND makes
'supplier ~ "jeisa"' match.

Migration `2026-06-30-add-price-list-search-text`:
  - Adds `search_text` (text) column
  - Adds `idx_pl_search_text` non-unique index
  - Idempotent — re-runs are safe and self-heal the index

Updates:
  - `getPriceLists` helper ORs across all 6 columns + search_text
  - `PriceList` TS type gains `search_text: string`
  - `pricelist-extraction.md` Code node snippet shows how the
    n8n workflow should populate `search_text` on every POST
  - Search input placeholder reads 'Search anything — SKU,
    description, brand, supplier, synonyms…'

Tests: 229/230 (same pre-existing failure, unrelated).
TypeScript clean.
2026-06-30 12:10:27 +02:00
SJCoetz
b75873a51b feat(pricelist): Catalogue tab with search + category/brand filters
Adds a second tab on /pricelist that shows the structured
`price_list` collection (AI-extracted line items) instead of the
raw PDF uploads. Operators can now:

  - Search across SKU, description, and brand
  - Filter by category (twist_drill, hss_cutter, countersink, ...)
  - Filter by brand (JEI, TurboSteel, TurboTough, ...)
  - See cost vs selling price side-by-side in ZAR
  - Click a row's 'Source' link to jump back to the raw PDF

The distinct filter values are pulled from a one-shot getList(1, 500)
on mount, with hardcoded fallbacks (FALLBACK_CATEGORIES /
FALLBACK_BRANDS) so the dropdowns are useful even before any
extractions have run.

Layout: Tabs (Raw Uploads | Catalogue) wrap both views; the
header + page chrome stay shared. The DeleteDialog stays outside
the Tabs since it's a modal.

Also ships an idempotent migration to add `source_file` (text)
to `price_list` — used by the n8n workflow to track which raw
PDF each extracted row came from, and by the verify-after-POST
step the workflow uses to decide whether to flip
`raw_price_list.status` to Completed vs Failed.

Price type is in PriceListRow (shared subset) and PriceList
(full record). Supplier names are resolved client-side via
getAllSuppliers() since the live schema has supplier as a text
field rather than a relation.

Tests: 229/230 (same pre-existing failure, unrelated). TypeScript
clean.
2026-06-30 11:49:48 +02:00
SJCoetz
7f70258037 feat(management): new Pricelist page with drag-drop upload + queue table
Adds /pricelist under the Management section. Operators drag a supplier
price-list file (PDF / CSV / text / image) into the upload zone — or
use the explicit Choose file button — and it lands in PocketBase as an
attachment on the raw_price_list collection. The table below shows the
queue: file, supplier (relation to suppliers), status badge, log
summary preview, upload date, and a delete action per row.

Layout matches the rest of the app: Card for the upload zone, DataTable
for the queue, DeleteDialog for the destructive action. Live updates
via the PB subscribe channel so other tabs / the n8n ingest workflow
show new rows without a manual refresh.

PB schema is sourced from docs/pocketbase/raw_price_list.collection.json.
The migration script is idempotent: it creates the collection from the
schema file on a fresh deploy, and on a pre-existing one (the live PB
already had it, with PB's default null/superuser-only rules) it relaxes
the auth rules to @request.auth.id != '' so any logged-in user can
CRUD — matching every other collection in the app.

Field names match the live PB collection (discovered via admin API):
  - supplier_file (the file)
  - supplier_name (relation to suppliers)
  - status        (select: Pending / Processing / Completed / Failed)
  - log_summary   (free text)
2026-06-30 09:14:36 +02:00
SJCoetz
6c07a91693 refactor: drop order-level status, shift to item-driven workflow
This commit overhauls the order management system to use item-level status tracking instead of a single order-level status:
- Remove deprecated `orders.status` field and add `invoiced` and quote match audit fields
- Add `isOrderComplete` utility with tests to validate fully delivered orders
- Add purchase order PDF rendering support and tooling
- Add geo coordinate fields for companies/customers with map link UI
- Add REST management APIs for customers, companies, and suppliers
- Update customer/company services to use relation fields instead of free text
- Add status pill and tab filter components for item status visualization
- Add quote-to-order matching logic and unit tests
- Update all pages, components, and tests to match the new schema
- Include all required PocketBase migrations and schema updates
2026-06-29 13:06:01 +02:00
SJCoetz
bb4233a9f2 feat(dashboard): add multi-theme system and supporting improvements
Add complete multi-theme dashboard system including:
- 8 selectable themes with persistent per-browser preferences
- Theme bootstrap script to prevent FOUC on page load
- 12+ reusable chart components (area, bar, donut, radial gauge, etc)
- Custom checkbox UI component
- New settings page for appearance customization

Other changes:
- Optimize supplier map API payload with getMappableSuppliers helper
- Add aria-label attributes to back buttons on detail pages for better accessibility
- Update next.config.ts to include new local development IP 192.168.1.128
- Update site manifest with all new favicon and PWA assets
- Fix styling and layout spacing in rewrite quote modal
2026-06-26 17:59:40 +02:00
SJCoetz
588e9762c2 feat: add quote templates, improved email, new statuses
Add complete quote template functionality, enhanced outbound email sending, new quote statuses, and supporting infrastructure:
- Add new quote statuses (template, sending, mail_failed) with tailored UI styling for status pills
- Implement full template workflows: save real quotes as templates, create new quotes from existing templates
- Add support for custom quote dates and extended validity periods on generated PDF documents
- Build out improved email pipeline with persisted compose fields and retry functionality for failed sends
- Add attachment direction tracking to categorize inbound, outbound, and generated quote attachments
- Fix tooltip component to properly handle Radix `asChild` prop to avoid React DOM attribute errors
- Add comprehensive test coverage, schema migrations, and update sample test and production data
2026-06-24 22:57:01 +02:00
SJCoetz
714e910949 feat: add branded quote PDF generation and in-app viewer
This commit adds full quote PDF generation functionality with per-company letterhead branding:
- Server-side PDF rendering using @react-pdf/renderer matching the sample quote layout
- In-app modal PDF viewer with print, download, and new-tab controls
- Letterhead service to fetch and apply company-specific branding (CNL/ISHERWOOD)
- ZAR currency and date formatting utilities for South African quotes
- Updated QuoteAttachment type to handle PocketBase's array-based file tokens
- Fixed PocketBase attachment URL generation in quote email card
- Removed deprecated `included_in_quote` field from QuoteItem and quotes service
- Updated quote item creation test to validate draft item logic
- Added comprehensive unit tests for PDF rendering and formatting helpers
- Added dev scripts for debugging PDF and image rendering issues
- Added sample generated PDF files for visual verification
- Updated tsconfig to exclude development scripts directory
- Added @react-pdf/renderer production dependency
- Removed JEI as a valid letterhead option per requirements
2026-06-23 08:30:38 +02:00
SJCoetz
4c0b6b0163 feat: enhance toasts and add quote item toggle
- completely rewrite the Sonner toaster component with theme-aligned styling, default top-center position, brand-colored toast states, and improved spacing/sizing
- update root layout to remove hardcoded Toaster position prop since the component now handles its own positioning
- add toggle to show/hide completed quote items on the quote process page
- add empty state message when all quote items are fully processed
- memoize displayed quote items for stable sorting and filtering
2026-06-22 23:50:33 +02:00
SJCoetz
86c942a466 feat: add locked pricing and overhaul quote rewrite
- Bump quote rewrite API timeout to 180s for longer AI batch processing
- Extend structured quote item fields to include model and size, remove old type field
- Overhaul n8n quote rewriter workflow with updated prompt and null-safe output handling
- Add locked source-of-truth pricing controls to auto-calculate cost/selling prices
- Remove inline cost/selling inputs from quote items table and refine table layout
- Improve rewrite modal UI with sticky headers, better empty chip handling, and styling
2026-06-22 23:07:53 +02:00