import { create } from "zustand"; import type { Customer, MenuItem, Order } from "@/lib/api/types"; import { iranMobileForApi } from "@/lib/phone"; export interface CartItem { menuItem: MenuItem; quantity: number; notes?: string; orderItemId?: string; isVoided?: boolean; } export interface AppliedCoupon { id: string; code: string; discountAmount: number; } interface CartState { items: CartItem[]; syncedQtyByMenuId: Record; couponCode: string; appliedCoupon: AppliedCoupon | null; tableId: string | null; activeOrderId: string | null; customerId: string | null; guestName: string; guestPhone: string; getPendingLines: () => { menuItemId: string; quantity: number; notes?: string }[]; addItem: (item: MenuItem) => void; removeItem: (menuItemId: string) => void; updateQty: (menuItemId: string, quantity: number) => void; setCouponCode: (code: string) => void; setAppliedCoupon: (coupon: AppliedCoupon | null) => void; clearCoupon: () => void; setTableId: (tableId: string | null) => void; setActiveOrderId: (orderId: string | null) => void; setGuestName: (name: string) => void; setGuestPhone: (phone: string) => void; setCustomer: (customer: Customer | null) => void; clearCustomer: () => void; hydrateFromOrder: (order: Order, menuById: Map) => void; clearCart: () => void; clearSession: () => void; subtotal: () => number; } const clearCouponState = { couponCode: "", appliedCoupon: null as AppliedCoupon | null, }; function orderLineToMenuItem( line: Order["items"][number], menuById: Map ): MenuItem { const existing = menuById.get(line.menuItemId); if (existing) return existing; return { id: line.menuItemId, categoryId: "", name: line.menuItemName, price: line.unitPrice, isAvailable: true, }; } export const useCartStore = create((set, get) => ({ items: [], syncedQtyByMenuId: {}, couponCode: "", appliedCoupon: null, tableId: null, activeOrderId: null, customerId: null, guestName: "", guestPhone: "", getPendingLines: () => { const { items, syncedQtyByMenuId } = get(); const pending: { menuItemId: string; quantity: number; notes?: string }[] = []; for (const line of items) { const synced = syncedQtyByMenuId[line.menuItem.id] ?? 0; const delta = line.quantity - synced; if (delta > 0) { pending.push({ menuItemId: line.menuItem.id, quantity: delta, notes: line.notes, }); } } return pending; }, addItem: (menuItem) => { const existing = get().items.find((i) => i.menuItem.id === menuItem.id); if (existing) { set({ items: get().items.map((i) => i.menuItem.id === menuItem.id ? { ...i, quantity: i.quantity + 1 } : i ), ...clearCouponState, }); } else { set({ items: [...get().items, { menuItem, quantity: 1 }], ...clearCouponState }); } }, removeItem: (menuItemId) => set({ items: get().items.filter((i) => i.menuItem.id !== menuItemId), ...clearCouponState, }), updateQty: (menuItemId, quantity) => { if (quantity <= 0) { get().removeItem(menuItemId); return; } set({ items: get().items.map((i) => i.menuItem.id === menuItemId ? { ...i, quantity } : i ), ...clearCouponState, }); }, setCouponCode: (code) => set({ couponCode: code }), setAppliedCoupon: (coupon) => set({ appliedCoupon: coupon }), clearCoupon: () => set(clearCouponState), setTableId: (tableId) => set({ tableId }), setActiveOrderId: (activeOrderId) => set({ activeOrderId }), setGuestName: (guestName) => set((s) => ({ guestName, customerId: s.customerId && guestName !== s.guestName ? null : s.customerId, })), setGuestPhone: (guestPhone) => set((s) => ({ guestPhone, customerId: s.customerId && guestPhone !== s.guestPhone ? null : s.customerId, })), setCustomer: (customer) => set({ customerId: customer?.id ?? null, guestName: customer?.name ?? "", guestPhone: customer?.phone ? (iranMobileForApi(customer.phone) ?? customer.phone) : "", }), clearCustomer: () => set({ customerId: null }), hydrateFromOrder: (order, menuById) => { const syncedQtyByMenuId: Record = {}; for (const line of order.items) { syncedQtyByMenuId[line.menuItemId] = line.quantity; } set({ activeOrderId: order.id, tableId: order.tableId ?? null, customerId: order.customerId ?? null, guestName: order.guestName ?? order.customerName ?? "", guestPhone: order.guestPhone ?? order.customerPhone ?? "", syncedQtyByMenuId, items: order.items.map((line) => ({ menuItem: orderLineToMenuItem(line, menuById), quantity: line.quantity, notes: line.notes, orderItemId: line.id, isVoided: line.isVoided ?? false, })), ...clearCouponState, }); }, clearCart: () => set({ items: [], ...clearCouponState, }), clearSession: () => set({ items: [], syncedQtyByMenuId: {}, tableId: null, activeOrderId: null, customerId: null, guestName: "", guestPhone: "", ...clearCouponState, }), subtotal: () => get().items.reduce( (sum, i) => i.isVoided ? sum : sum + i.menuItem.price * i.quantity, 0 ), }));