This commit includes the temporary Next.js build cache files, public brand logos, and project configuration added in this change.
17 KiB
WFDASH — Westcott Fasteners Dashboard
Overview
WFDASH is a Next.js (App Router) + PocketBase application for Westcott Fasteners, a fastener supply business. It manages the complete quote-to-cash lifecycle for industrial fastener orders — from inbound RFQ emails through pricing, supplier procurement, collection, delivery, and invoicing.
The system is operator-centric, built for the office team who process dozens of quotes and orders daily. It replaces manual spreadsheet/email workflows with structured pipelines, real-time dashboards, and automation via n8n workflows.
Core Domains
1. Quotes Pipeline (/quotes, /sprint)
Inbound RFQ → Processing → Approval → Send → Follow-up
| Feature | Description |
|---|---|
| Email Intake | n8n webhook receives inbound RFQ emails, extracts items via AI, creates quotes in PocketBase. Idempotent on Message-ID. |
| Sprint (/sprint) | Item-level work queue. Operators claim individual line items (30-min soft TTL), price them, and save — the item then drops off the list. Replaces "claim whole quote" bottleneck. |
| Categories | Admin-managed categories (JEI, TOOLS, FASTENERS, OIL, HUSQVARNA, UNKNOWN) with brand/supplier/keyword match rules. Items auto-bucket or land in UNKNOWN for review. |
| AI Pricing | n8n AI agent reads supplier price lists, matches SKUs, writes cost_price + selling_price with pricing_source="ai" or "catalogue". Operator reviews before saving. |
| Quote Statuses | new → claimed → processed → approval_pending → approved → emailed → complete (also rejected, problem, cancelled, mail_failed, follow_up) |
| Approvals | Dedicated /quotes/approvals page for quotes needing sign-off. |
| PDF Generation | Branded quote PDFs (CNL / ISHERWOOD letterhead) with supplier authorization letters. |
| Email Send | Modal with CC/BCC, custom subject/body, attachment picker. Status flips to sending → n8n sends via SMTP → callback records emailed or mail_failed. Retry supported. |
| Follow-ups | Per-quote follow_up_due date; dashboard widget flags overdue. |
| Customer Replies | Inbound replies tracked via quote_replies collection; shows "Replied" chip on quote row. |
| Templates | Reusable quote scaffolding; "Create from template" spawns a real quote. |
2. Orders (/orders, /orders/new, /orders/[id])
Quote → Order → Line Items → Delivery → Invoice
| Feature | Description |
|---|---|
| Create from Quote | One-click converts a quote to an order, copying all line items. Atomic order number generation (ORD-YYYY-MM-NNN) via server-side counter — continues sequence across month boundaries, concurrency-safe. |
| Order List | Collapsible cards grouped by item status tabs: New, Processing, Collection Pool, Collected, Delivered, Cancelled. Search spans order #, company, customer, item description, or price. |
| Item Status Lifecycle | pending → processed → po_generated → collection_pool → assigned → collected → stock_verified → picking → ready_delivery → delivered (terminal). Also ready_invoice, invoiced, stock_booked, cancelled. |
| Quote Matching | Auto-links order to quote via exact match on quote_number / reference / RFQ / external_number. Suggests a "potential quote" when fuzzy. |
| Repair Orders | is_repair_order flag for non-standard jobs. |
3. Operations / Collection (/collection-pool, /collection-map, /collection-confirm)
Supplier Collection → Driver Assignment → Confirmation
| Page | Purpose |
|---|---|
| Collection Pool | Supplier-grouped view of items in collection_pool or assigned status. Per-row "Assign driver" + per-supplier bulk assign. Shows PO #, supplier, age, ready-for-collection flag. |
| Collection Map | Leaflet map with supplier markers clustered by proximity. Click a cluster → see items → assign driver inline. |
| Collection Confirm | Driver-facing (mobile) — scan/confirm collection, marks items collected. |
4. Invoicing (/invoicing)
Delivery Note → QuickBooks Invoice → Print
| Feature | Description |
|---|---|
| To Invoice Tab | Orders grouped by order number where at least one item is ready_invoice. Per-line qty/price overrides for the QB payload. |
| QuickBooks Integration | Modal picks QB company (CNL / ISHERWOOD), searches QB customers via n8n webhook, builds invoice payload, POSTs to n8n → QB. Returns PDF URL stored on order_items.qb_invoice_pdf. |
| Delivery Notes | Parallel flow: mark lines delivery_note status, auto-generates DN{YYMMDD}NN number. Same line editor (include/exclude, qty/price override). |
| Invoiced Tab | Two sections: Today's Unprinted (grouped by invoice #, print badges) + All Invoiced (searchable history). Print tracking (printedAt, printedBy, printCount) stored in order_item_details.print JSON. |
| PDF Print | Server-rendered React-PDF → streamed back. One-click print for single or bulk (today's batch). |
5. Stock Management (/stock, /stock/[id], /stock-orders)
Inventory → Surplus Booking → Stock Orders → Ledger
| Feature | Description |
|---|---|
| Stock Items | SKU, name, unit, location, current_qty, min_qty, supplier. Status badge: OK / Low / Critical / Inactive. |
| Stock Ledger | Immutable append-only ledger (stock_ledger) with signed change_qty and reason (purchase_surplus, stock_order, customer_fulfillment, adjustment, return). current_qty denormalized for fast reads. |
| Surplus Booking | When a supplier delivers more than ordered, operator splits the line: customer qty → order, surplus qty → stock. Creates stock_ledger entry (reason=purchase_surplus) + links order_item.surplus_qty + stock_booked_from. |
| Stock Orders (v2) | Pure stock replenishment: SP-YYYYMMDDNN number, multi-line, status draft → sent → received → cancelled. Receive flow books ledger + updates current_qty per line. |
| Fulfilment from Stock | /api/v1/stock/fulfill — books out qty from stock for a customer order, creates ledger entry (reason=customer_fulfillment). |
6. Pricelist / Catalogue (/pricelist)
Raw Uploads → AI Extraction → Structured Catalogue
| Tab | Purpose |
|---|---|
| Raw Uploads | Drag-drop PDF/CSV/TXT/images (≤50 MB). Optional supplier link, status, free-text notes for AI (e.g. "Brand is JEI. Apply 30% markup. ZAR excl. VAT."). Upload creates raw_price_list row; n8n workflow picks up, extracts, writes to price_list. |
| Catalogue | Searchable price_list table: SKU, normalised SKU, description, category, brand, supplier, cost/selling/markup, stock_qty, rich specifications JSON (diameter, length, material, synonyms). search_text column enables fuzzy/synonym search. |
7. Purchase Orders (embedded in Orders flow)
PO Generation → Email → Supplier Reply Tracking
- Created from order items grouped by supplier.
- Versioned PDFs stored on
purchase_orders.documentsarray +documents_history. - Email modal (to/cc/bcc, HTML+text body, attachment picker) → n8n sends → callback records
email_message_id,last_reply_at,supplier_reply_preview. - Stale PO detection (
purchase-orders/staleendpoint).
8. Delivery Notes (/delivery-notes)
Generated from delivered order items
- Auto-numbered
DN{YYMMDD}NN. - Status:
draft → ready → delivered → cancelled. - Linked to order + company + customer.
- Printable PDF (shares invoice print pipeline).
9. Repairs (/repairs)
Lightweight repair job tracker
- Repair number auto-generated.
- Status:
new → assessing → quoted → approved → in_progress → completed → cancelled. - Links to customer / company / order (optional).
- Item description + fault description + notes.
10. Companies / Customers / Suppliers (/companies, /customers, /suppliers)
Master data management
- Companies: name, address, email, tel, VAT, reg number, geo-coordinates (for map).
- Customers: name, emails (primary/alt/buyer), company relation, rep (user), delivery coordinates, description.
- Suppliers: name, email, number, address, supply_tags (for category matching), coordinates, closing time.
11. Dashboard (/dashboard)
Real-time KPIs & Charts
| Widget | Data |
|---|---|
| KPI Cards | Customers, Active Quotes, Orders This Month, Revenue (sparkline per card) |
| Concentric Rings | Quote pipeline depth (New / Claimed / Approved / Processed) |
| Orders Trend | New vs Fulfilled (8 weeks, stacked bars) |
| Status Mix | Quote distribution (progress bars) |
| Growth Trends | Customers vs Orders (12 weeks, area chart) |
| Approval Rate | Radial gauge (Approved / Active) |
| Activity Pulse | Multi-line (Customers / Quotes / Orders velocity) |
| Quote Pipeline | Donut chart (all non-template quotes) |
| Top Customers | Horizontal bars by quote volume |
| Follow-ups Due | Clickable cards → quote detail |
Real-time: PocketBase subscriptions on customers, quotes, orders → auto-refresh.
12. Settings (/settings)
- Theme picker (light/dark/system) with CSS variables.
- Developer identity switcher (for local multi-user testing).
Automation & Integrations (n8n)
| Workflow | Trigger | Action |
|---|---|---|
| Email Intake | Inbound email (IMAP/webhook) | Parse → AI extract items → Create quote + items + attachments |
| Quote Outbound | Quote status = sending |
Render PDF → SMTP send → Callback recordSendOutcome |
| Supplier RFQ | Sprint bulk "Email suppliers" | Group by supplier → Send RFQ → Track outbound_message_id |
| Supplier Reply Monitor | Inbound email with In-Reply-To matching PO/quote |
Update supplier_replied_at, supplier_reply_count |
| Price List Ingest | New raw_price_list row |
AI extract → Write price_list rows → Update log_summary |
| QuickBooks Invoice | Invoicing modal submit | Build QB payload → QB API → Return PDF URL |
| PO Stale Alert | Scheduled | Find POs without reply > N days → Notify |
Technical Architecture
| Layer | Technology |
|---|---|
| Frontend | Next.js 15 (App Router), React 19, TypeScript, Tailwind CSS |
| Backend | PocketBase (Go) — auth, real-time, file storage, admin UI |
| Automation | n8n (self-hosted) — workflows, AI agents, webhooks |
| Charts | Custom SVG (no Chart.js/Recharts) — sparklines, bars, donuts, radial, area, concentric |
@react-pdf/renderer (server-side in API routes) |
|
| Maps | Leaflet + OpenStreetMap |
| Auth | PocketBase auth (email/password), cookie-based session via middleware |
| Real-time | PocketBase subscribe("*", callback) on collections |
| API | Next.js Route Handlers (/api/v1/...) + PocketBase SDK (server) |
Key Design Patterns
- Item-Level Workflows — Not quote/order level. Sprint, Collection Pool, Invoicing all operate on
order_items/quote_itemsrows. - Optimistic UI + Real-time — Local state updates immediately; PB subscriptions reconcile.
- Server-Side Number Generation — Atomic counters (
quote_counters,order_counters) prevent collisions. - Idempotency — Email intake dedups on
Message-ID; quote/PO sends record outboundMessage-IDfor bounce matching. - Human-in-the-Loop AI — AI writes
pricing_source="ai"+ zero prices; operator must claim, review, save. - JSON Audit Fields —
order_item_details,quote_item.pricing_notes,printmetadata stored as JSON on the row — no extra collections for append-only logs. - Per-User Color Coding —
claimed_by/rep→ deterministic hue (OKLCH) for row tinting + avatar chips. - Working-Day SLA —
isOlderThanWorkingDays()excludes weekends; dashboard ages quotes in working days.
Data Model Highlights (PocketBase Collections)
| Collection | Purpose |
|---|---|
quotes |
Quote header (number, customer, company, status, email thread, PDF, follow-up) |
quote_items |
Line items (description, qty, cost/sell/markup, supplier, claim_status, pricing_source, category) |
quote_attachments |
Inbound RFQ files + generated PDFs + outbound attachments |
quote_replies |
Customer email replies (thread) |
orders |
Order header (number, company, customer, quote link, rep, invoiced flag) |
order_items |
Line items (status lifecycle, supplier, PO link, in_stock, surplus_qty, pricing audit) |
purchase_orders |
Supplier POs (versioned PDFs, email payload, supplier replies) |
collection_pool |
Driver assignment queue (links order_item + supplier + driver) |
stock_items |
Inventory master (SKU, qty, min, location, supplier) |
stock_ledger |
Immutable movements (signed qty, reason, reference) |
stock_purchases |
Stock replenishment orders |
price_list |
Structured catalogue (AI-extracted) |
raw_price_list |
Uploaded supplier files awaiting extraction |
categories |
Admin-managed buckets for sprint (match_brands, match_supplier_tags, match_keywords) |
delivery_notes |
Delivery note header |
delivery_note_items |
Line items on delivery note |
repairs |
Repair jobs |
companies / customers / suppliers |
Master data |
users (auth) |
Operators + drivers |
Pages / Routes Summary
| Route | Purpose |
|---|---|
/dashboard |
KPI dashboard |
/quotes |
Quote list (tabs: New, Claimed, Pending Approval, Completed, Email Failed, Problem, All, Templates) |
/quotes/[id] |
Quote detail (items, pricing, PDF, email, approvals) |
/quotes/[id]/process |
Sprint-style item processing for a single quote |
/quotes/approvals |
Approval queue |
/sprint |
Global item-level pricing queue (by category) |
/orders |
Order list (item-status tabs) |
/orders/new |
Create order (from quote or blank) |
/orders/[id] |
Order detail (items, status transitions, PO, delivery, invoice) |
/orders/assign |
Legacy driver assign (hidden from nav) |
/invoicing |
To Invoice / Invoiced (QB + delivery notes + print) |
/collection-pool |
Supplier collection queue + bulk assign |
/collection-map |
Map view of suppliers |
/collection-confirm |
Mobile driver confirmation |
/stock |
Stock list + low-stock filter |
/stock/[id] |
Stock detail + ledger + adjustments |
/stock-orders |
Stock replenishment orders |
/pricelist |
Raw uploads + Catalogue tabs |
/suppliers |
Supplier CRUD |
/companies |
Company CRUD |
/customers |
Customer CRUD |
/repairs |
Repair job tracker |
/delivery-notes |
Delivery note list |
/settings |
Theme, dev identity |
API Endpoints (Key)
| Endpoint | Method | Purpose |
|---|---|---|
/api/v1/quotes/[id]/pdf |
GET | Render quote PDF |
/api/v1/quotes/[id]/send-email |
POST | Validate + queue outbound send |
/api/v1/quotes/[id]/retry-email |
POST | Retry failed send |
/api/v1/quotes/[id]/create-from-template |
POST | Spawn quote from template |
/api/v1/orders/from-quote |
POST | Convert quote → order |
/api/v1/order-items/[id]/status |
PATCH | Transition item status |
/api/v1/invoicing/customers |
GET/POST | QB customer search/create |
/api/v1/stock/items |
GET/POST | Stock CRUD |
/api/v1/stock/items/[id]/adjust |
POST | Manual qty adjustment + ledger |
/api/v1/stock/fulfill |
POST | Book out stock for customer order |
/api/v1/stock/surplus-book |
POST | Book surplus from collection |
/api/v1/stock/purchases |
GET/POST | Stock orders CRUD |
/api/v1/stock/purchases/[id]/receive |
POST | Receive stock order lines |
/api/v1/sprint/items/[id]/claim |
POST | Race-safe 30-min claim |
/api/v1/sprint/items/[id]/attachments/upload |
POST | Upload pricing proof |
/api/v1/purchase-orders/[id]/send-email |
POST | Send PO to supplier |
/api/v1/purchase-orders/[id]/pdf |
GET | Render PO PDF |
/api/v1/files/[collection]/[recordId]/[filename] |
GET | Stream PocketBase file |
Deployment Notes
- PocketBase runs separately (Docker/binary). App connects via
NEXT_PUBLIC_POCKETBASE_URL. - n8n runs separately; webhooks configured in n8n point to
/api/v1/...routes. - Files served via PocketBase
/api/files/...— proxied through Next.js route for auth if needed. - Environment:
NEXT_PUBLIC_POCKETBASE_URL,POCKETBASE_ADMIN_EMAIL,POCKETBASE_ADMIN_PASSWORD(for server SDK).
Summary
WFDASH is a complete operational ERP for a fastener distributor, covering:
- Quote intake (email → AI → structured quote)
- Item-level pricing (Sprint claim/price/save)
- Order management (quote → order → multi-status item lifecycle)
- Supplier procurement (PO generation, email, reply tracking)
- Collection logistics (pool → map → driver assign → confirm)
- Inventory (stock items, ledger, surplus booking, stock orders)
- Invoicing (delivery notes + QuickBooks + PDF print)
- Catalogue (raw uploads → AI extraction → searchable price list)
- Repairs (lightweight job tracker)
- Real-time dashboard (KPIs, charts, follow-ups)
Built for speed, auditability, and operator ergonomics — every screen is a work queue, not a CRUD form.