store: cart with WhatsApp checkout #38

Closed
opened 2026-08-24 08:27:41 +00:00 by yuris · 0 comments
Owner

Add a localStorage cart that checks out through a single WhatsApp deep link — no payment gateway, no accounts, no server, no new dependencies. lib/cart.ts exposes a useCart hook (lines, add, setQty, remove, clear, total, hasUnpricedLine) over storage key apja-cart-v1, reading on mount inside useEffect so a static export cannot hydration-mismatch, syncing across components via a module-level listener set plus a storage listener, and discarding malformed stored values as an empty cart. Add a header CartButton with a count badge, and an add-to-cart control with a quantity stepper and a fulfilment choice between shipping and installing at the Situbondo workshop — each option shown only when actually possible, as static text when only one is, and replaced by a "tanyakan ketersediaan" WhatsApp link when out of stock. /cart handles the empty cart, unpriced lines, a mix of shippable and pickup-only lines, and a required destination gating checkout. lib/checkout-message.ts is a pure tested function producing the WhatsApp text, capping at 15 itemised lines plus dan N barang lainnya because wa.me truncates long messages unpredictably. Checkout does not clear the cart; an explicit "Kosongkan keranjang" button does.

Acceptance: pnpm --filter garage-store test passes; adding two items, choosing install on one, and pressing checkout opens a wa.me URL whose decoded text matches the specified format.

Add a `localStorage` cart that checks out through a single WhatsApp deep link — no payment gateway, no accounts, no server, no new dependencies. `lib/cart.ts` exposes a `useCart` hook (`lines`, `add`, `setQty`, `remove`, `clear`, `total`, `hasUnpricedLine`) over storage key `apja-cart-v1`, reading on mount inside `useEffect` so a static export cannot hydration-mismatch, syncing across components via a module-level listener set plus a `storage` listener, and discarding malformed stored values as an empty cart. Add a header `CartButton` with a count badge, and an add-to-cart control with a quantity stepper and a fulfilment choice between shipping and installing at the Situbondo workshop — each option shown only when actually possible, as static text when only one is, and replaced by a "tanyakan ketersediaan" WhatsApp link when out of stock. `/cart` handles the empty cart, unpriced lines, a mix of shippable and pickup-only lines, and a required destination gating checkout. `lib/checkout-message.ts` is a pure tested function producing the WhatsApp text, capping at 15 itemised lines plus `dan N barang lainnya` because `wa.me` truncates long messages unpredictably. Checkout does not clear the cart; an explicit "Kosongkan keranjang" button does. Acceptance: `pnpm --filter garage-store test` passes; adding two items, choosing install on one, and pressing checkout opens a `wa.me` URL whose decoded text matches the specified format.
yuris closed this issue 2026-08-24 10:32:46 +00:00
Sign in to join this conversation.
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
yuris/garage#38
No description provided.