You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CHANGELOG.md
+20Lines changed: 20 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -13,6 +13,26 @@ All notable changes are documented here. The project uses [Semantic Versioning](
13
13
14
14
---
15
15
16
+
## [0.4.5] - 2026-03-18
17
+
18
+
### Added
19
+
-**Per-item quote pricing controls** — Quote lines now support discount metadata (`discount_type`, `discount_amount`) alongside drag-to-reorder for cleaner quote composition.
20
+
-**Reusable quote policy content** — New payment policy and rental terms templates can be managed centrally and attached to individual quotes.
21
+
-**Item accessory relationships** — Inventory items can now store accessory links for future quote-builder automation.
22
+
-**Repo metadata polish** — Added a real `LICENSE` file and refreshed the GitHub-facing README for clearer discovery and evaluation.
23
+
24
+
### Changed
25
+
-**Public quote experience** — Quote expiration is now surfaced consistently, discounted pricing is reflected in public totals, and public approvals/signatures respect expiration state.
26
+
-**Operator UX** — Added UI scale controls, direct quote navigation from Messages, clearer quote status borders, and more polished quote-builder pricing affordances.
27
+
-**Quickstart and discoverability** — README now leads with product value, deployment options, badges, keywords, and a cleaner getting-started flow.
28
+
29
+
### Fixed
30
+
-**Legacy public quote API parity** — `/api/quotes/public/:token` now returns expiration status, payment policy data, rental terms, and discount fields so the public React page matches the new backend feature set.
31
+
-**Accessory messaging accuracy** — InventoryPage no longer claims permanent accessories auto-add to quotes before that behavior exists.
32
+
-**Local runtime noise** — `.gitignore` now correctly excludes local lockfiles, runtime uploads, and editor-specific config so release diffs stay focused.
Copy file name to clipboardExpand all lines: ai/API_DEVELOPMENT.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,7 +2,7 @@
2
2
3
3
**All development going forward must consider the API.**
4
4
5
-
This document is part of the AI coordination layer. When implementing features, Cursor and Claude must treat the API as a first-class surface.
5
+
This document is part of the AI coordination layer. When implementing features, all editor/agent workflows must treat the API as a first-class surface.
Copy file name to clipboardExpand all lines: ai/CURSOR_BRIEFING.md
+11-5Lines changed: 11 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -4,14 +4,20 @@
4
4
5
5
BadShuffle is a self-hosted inventory and quoting tool for event rental businesses. It runs as two local Windows executables (server + client), or via Docker. Stack: Node/Express + sql.js SQLite on the back end, React + Vite on the front end.
6
6
7
-
## Current state: v0.4.4 (latest release)
7
+
## Current state: v0.4.5 (latest release)
8
8
9
9
Latest release:
10
10
11
11
```
12
-
release: v0.4.4 — quote filtering + 2-step creation wizard, public quote live messaging, picker-level availability, theme/map settings, extension extraction hardening, contract_description support
-**Quote pricing + layout polish** — Drag-to-reorder line items, per-item discounts, status-colored quote cards, and stronger conflict visibility.
17
+
-**Public quote parity** — Public quote route and page now respect quote expiration, show rental/payment policy content, and calculate totals using discounted pricing.
18
+
-**Inventory + operator UX** — Item accessories can be linked in Inventory, Messages can jump straight to the related quote, and UI scale can be adjusted globally from Settings.
19
+
-**Repo hygiene** — README, quickstart, license, gitignore, and AI coordination docs were refreshed for a cleaner release surface.
20
+
15
21
### What shipped in v0.4.4
16
22
-**Quote list + create flow** — Quotes page now supports search/status/date/venue/balance filters, and a 2-step quote creation wizard with optional Google Places address autocomplete.
17
23
-**Public quote messaging** — New no-auth thread routes (`GET/POST /api/quotes/public/:token/messages`) and live message UI on the public quote page.
Copy file name to clipboardExpand all lines: ai/FEATURES.md
+29-3Lines changed: 29 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -23,8 +23,9 @@ All features below are implemented unless marked as stub or partial.
23
23
## Price Overrides
24
24
25
25
-**Per-line price override:**`quote_items.unit_price_override` (REAL, nullable). NULL = use inventory unit_price. Set inline in QuoteBuilder (click price → input → Enter/Esc to confirm/cancel). Override shown in purple with ✕ reset button.
26
-
-**Effective price:**`effectivePrice(it) = it.unit_price_override ?? it.unit_price` — used in all total computations (QuoteDetailPage, public quote, QuoteExport).
-**Per-item discount:**`quote_items.discount_type` (`none` | `percent` | `fixed`) + `quote_items.discount_amount` (REAL). Inline edit in QuoteBuilder (discount badge click → popover). Applied in `effectivePrice()` in QuoteDetailPage and PublicQuotePage.
27
+
-**Effective price:**`effectivePrice(it)` — applies `unit_price_override` then discount. Used in all total computations (QuoteDetailPage, PublicQuotePage, QuoteExport).
-**Fields:**`quotes.expires_at` (TEXT date, YYYY-MM-DD) + `quotes.expiration_message` (TEXT). Set in QuoteDetailPage edit form.
59
+
-**Public quote behavior:** If `expires_at < today`, `is_expired = true` is returned by the public quote API. PublicQuotePage shows a customizable expiration banner and hides the contract signature block (replaced with an "expired" placeholder). If the contract was already signed, it remains visible.
-**Quote linkage:**`quotes.payment_policy_id` (nullable FK). Selectable in QuoteDetailPage edit form. Shown as a "Payment Policy" section on the public quote page when set.
67
+
-**Logic location:**`server/routes/templates.js`, `server/api/v1.js` (fetches linked policy for public quote), `client/src/pages/TemplatesPage.jsx` (CRUD section), `client/src/pages/QuoteDetailPage.jsx` (selector), `client/src/pages/PublicQuotePage.jsx` (display).
-**Quote linkage:**`quotes.rental_terms_id` (nullable FK). Selectable in QuoteDetailPage edit form. Shown as a "Rental Terms" section on the public quote page when set.
74
+
-**Logic location:**`server/routes/templates.js`, `server/api/v1.js` (fetches linked terms for public quote), `client/src/pages/TemplatesPage.jsx` (CRUD section), `client/src/pages/QuoteDetailPage.jsx` (selector), `client/src/pages/PublicQuotePage.jsx` (display).
75
+
55
76
## Billing
56
77
57
78
-**Payments:** Add/remove payments on a quote (amount, method, reference, note, paid_at). Stored in quote_payments; billing_history records payment_received, payment_removed, refunded.
@@ -144,7 +165,12 @@ All features below are implemented unless marked as stub or partial.
144
165
-**Extension:** Download extension ZIP (public); extension tokens for API (admin). ExtensionPage.
145
166
-**Import:** Inventory from CSV/XLSX/Sheets (sheets.js); leads from CSV/XLSX/Sheets with column mapping (leads preview/import). ImportPage.
-**Settings:** Company, tax, currency, SMTP/IMAP; `count_oos_oversold`; AI provider keys (Claude, OpenAI, Gemini) and per-feature enable/model settings; `ui_theme`, `google_places_api_key`, `map_default_style`; `ui_scale` (75–150%, applied as root font-size). SettingsPage (operator).
169
+
-**UI Scale:** Range slider 75–150% (step 5) in SettingsPage. Applies immediately via `document.documentElement.style.fontSize = (scale/100)*14 + 'px'`. Persisted to `localStorage` (`bs_ui_scale`) and loaded by `main.jsx` before first render.
170
+
-**Quote tile borders:**`QuoteCard.jsx` shows colored left border by status — draft=yellow, sent=blue, approved/confirmed/closed=green, conflict or unsigned changes=red.
171
+
-**Conflict stop sign:** SVG uses `<line>` + `<circle>` for a visible white ! on the red octagon. Present in QuoteCard, QuoteBuilder, QuotePage.
172
+
-**Drag-to-reorder quote items:** HTML5 drag handles (⠿) per line item in QuoteBuilder. On drop calls `PUT /api/quotes/:id/items/reorder` with ordered IDs; server updates `sort_order` in a single DB transaction.
173
+
-**Item accessories (permanent):**`item_accessories` table (item_id → accessory_id, UNIQUE, ON DELETE CASCADE). CRUD at `GET/POST/DELETE /api/items/:id/accessories`. InventoryPage edit form shows current accessories list and search-to-add. These permanent accessories are data-only — the quote builder does not yet auto-add them when the parent item is added (future enhancement).
Copy file name to clipboardExpand all lines: ai/KNOWN_GAPS.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -59,6 +59,7 @@ Areas that are incomplete, stubbed, or represent technical debt. Use this to kno
59
59
-**Local/trusted use:** CORS allows localhost and extension; JWT_SECRET must be set in production. Public quote and approve-by-token are unauthenticated by design (token is secret link).
60
60
-**File storage:** Files on disk in `uploads/`; no S3 or external storage. photo_url can be external URL (proxied) or file id (served via /api/files/:id/serve with signed URL for public).
61
61
-**Bundles:** item_associations represent parent/child; UI can show components but current quote builder does not auto-expand bundles into line items (add parent or children manually).
62
+
-**Permanent accessories:**`item_accessories` table exists and is managed in InventoryPage. When a product is added to a quote, its permanent accessories are NOT automatically added — that auto-add behavior is not yet implemented. The data is stored and surfaced on item edit; the quote builder must be updated to query and auto-insert accessories when the parent item is added.
Copy file name to clipboardExpand all lines: ai/README.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -17,6 +17,6 @@ This folder holds **consolidated project documentation** so future AI sessions c
17
17
18
18
## Relation to `ai/` (lowercase)
19
19
20
-
The repo also has an **`ai/`** folder (lowercase) with coordination docs: HANDOFF.md, STATUS.md, CURSOR_BRIEFING.md, DECISIONS.md, TODO.md, README.md. Those are for Claude/Cursor workflow and task handoff. This **`AI/`** folder is for **system context**: what the app does, how it’s built, and what’s left to do — so any AI (or human) can onboard quickly.
20
+
The repo also has an **`ai/`** folder (lowercase) with coordination docs: HANDOFF.md, STATUS.md, CURSOR_BRIEFING.md, DECISIONS.md, TODO.md, README.md. Those are for agent workflow and task handoff. This **`AI/`** folder is for **system context**: what the app does, how it’s built, and what’s left to do — so any AI (or human) can onboard quickly.
21
21
22
22
When you change the system (e.g. add pull sheets, new routes, new tables), update the relevant file here (e.g. FEATURES.md, DATA_MODELS.md, ARCHITECTURE.md) so the next session stays in sync.
0 commit comments