// Ported from legacy henza_pwa/src/store/cartStore.ts, adapted to our string // ids and flat (non-paginated) product list. Persisted the same way (a single // localStorage key), just via Pinia's own state instead of @vueuse's // useStorage wrapping a hand-rolled class. import { defineStore } from 'pinia' import { api, type ProductSummary } from '../api/client' export interface CartItem { pid: string vid: string product: ProductSummary qty: number note?: string | null } const STORAGE_KEY = 'appCart' function loadFromStorage(): CartItem[] { try { const raw = localStorage.getItem(STORAGE_KEY) return raw ? JSON.parse(raw) : [] } catch { return [] } } function persist(items: CartItem[]) { localStorage.setItem(STORAGE_KEY, JSON.stringify(items)) } export const useCartStore = defineStore('cart', { state: () => ({ items: loadFromStorage() as CartItem[], }), actions: { // Re-syncs cart entries against fresh product data (e.g. price/weight // changes since the item was added), dropping entries whose // product/variant no longer exists — same intent as legacy's init(). async init() { try { const products = await api.listProducts() const refreshed: CartItem[] = [] for (const item of this.items) { const product = products.find((p) => p.id === item.pid) const variant = product?.variants.find((v) => v.id === item.vid) if (product && variant) { refreshed.push({ ...item, product }) } } this.items = refreshed persist(this.items) } catch (e) { console.log(e) } }, addToCart(data: CartItem) { // Refuse a variant that cannot be ordered — out of stock and not flagged // pre-order. The same guard the admin cart applies (stores/cart.ts), kept // here so it holds even if a caller forgets to disable its button. const target = data.product?.variants?.find((v) => v.id === data.vid) if (!target) return false if (!((target.qty ?? 0) > 0 || target.isPreOrder)) return false const index = this.find_index(data) if (index === -1) this.items.push({ ...data, qty: this.capQty(data.qty, target) }) else { this.items[index].product = data.product this.items[index].qty = this.capQty(data.qty, target) } persist(this.items) return true }, /** * Stock ceiling for a cart line. A pre-order variant is orderable beyond * what is on hand (that is the point of pre-ordering), so it is uncapped; * anything else stops at its on-hand quantity. */ capQty(want: number, variant: { qty?: number; isPreOrder?: boolean }) { const n = Math.max(1, Math.floor(want) || 1) if (variant.isPreOrder) return n const max = variant.qty ?? 0 return max > 0 ? Math.min(n, max) : n }, /** The ceiling for an item already in the cart; null when uncapped. */ maxQty(item: Pick) { const i = this.find_index(item) if (i === -1) return null const v = this.variant(this.items[i]) if (!v || v.isPreOrder) return null return (v.qty ?? 0) > 0 ? (v.qty as number) : null }, remove(item: CartItem) { const index = this.find_index(item) if (index > -1) this.items.splice(index, 1) persist(this.items) }, // Legacy's exact name (typo'd "Feild"), kept for consistency with the rest // of this faithfully-ported store's method names. updateFeild(value: unknown, index: number | null, item: Pick, field: keyof CartItem) { if (!this.isInCart(item)) return false const i = index ?? this.find_index(item) // A quantity change is clamped to what is actually in stock (unless the // variant is pre-orderable), so the ceiling holds no matter which screen // drives the stepper. if (field === 'qty') { const v = this.variant(this.items[i]) value = v ? this.capQty(Number(value), v) : Math.max(1, Number(value) || 1) } ;(this.items[i] as any)[field] = value persist(this.items) return true }, find_index(item: Pick) { return this.items.findIndex((cartItem) => cartItem.pid === item.pid && cartItem.vid === item.vid) }, isInCart(item: Pick) { return this.find_index(item) > -1 }, variant(item: CartItem) { return item.product.variants.find((v) => v.id === item.vid) }, total() { return this.items .reduce((sum, item) => { const v = this.variant(item) return sum + (v ? parseFloat(v.rashWeightGrams) * item.qty : 0) }, 0) .toFixed(2) }, clear() { this.items = [] persist(this.items) }, }, })