"use client"; import { create } from "zustand"; import { createJSONStorage, persist } from "zustand/middleware"; import type { CartLine } from "@/lib/headless/providers/medusa-cart"; // ADR 0009: carrito real conectado a un cart_id de Medusa (Store API, // via /api/cart/*) en vez de estado 100% local -- precios y existencia // de linea son los que confirma el servidor, no un calculo optimista // en el cliente. Primer corte a proposito: sin UI de direccion/envio // todavia, el checkout sigue cerrando por WhatsApp (CartDrawer). export type CartItem = CartLine; interface CartState { cartId: string | null; items: CartItem[]; isOpen: boolean; hasHydrated: boolean; openCart: () => void; closeCart: () => void; setHasHydrated: (value: boolean) => void; addItem: (product: { id: string }, variantId?: string) => Promise; removeItem: (productId: string) => Promise; updateQuantity: (productId: string, quantity: number) => Promise; clearCart: () => Promise; syncCart: () => Promise; } async function postJson(url: string, body: unknown) { const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body), }); return response.json(); } export const useCartStore = create()( persist( (set, get) => ({ cartId: null, items: [], isOpen: false, hasHydrated: false, openCart: () => set({ isOpen: true }), closeCart: () => set({ isOpen: false }), setHasHydrated: (value) => set({ hasHydrated: value }), addItem: async (product, variantId) => { set({ isOpen: true }); if (!variantId) return; let cartId = get().cartId; if (!cartId) { const cart = await (await fetch("/api/cart", { method: "POST" })).json(); if (!cart.cartId) return; cartId = cart.cartId; set({ cartId, items: cart.items ?? [] }); } const cart = await postJson("/api/cart/items", { cartId, variantId, quantity: 1 }); if (cart.items) set({ items: cart.items }); }, removeItem: async (productId) => { const { cartId, items } = get(); const line = items.find((item) => item.product.id === productId); if (!cartId || !line) return; const response = await fetch(`/api/cart/items/${line.lineItemId}?cartId=${cartId}`, { method: "DELETE" }); const cart = await response.json(); if (cart.items) set({ items: cart.items }); }, updateQuantity: async (productId, quantity) => { if (quantity <= 0) return get().removeItem(productId); const { cartId, items } = get(); const line = items.find((item) => item.product.id === productId); if (!cartId || !line) return; const cart = await postJson(`/api/cart/items/${line.lineItemId}`, { cartId, quantity }); if (cart.items) set({ items: cart.items }); }, clearCart: async () => { const { cartId, items } = get(); if (!cartId) return; await Promise.all( items.map((item) => fetch(`/api/cart/items/${item.lineItemId}?cartId=${cartId}`, { method: "DELETE" })), ); set({ items: [] }); }, // Al recargar la pagina, el cart_id guardado en localStorage puede // llevar dias -- se refresca contra el servidor en vez de confiar en // el ultimo snapshot local (precios/stock pudieron cambiar). syncCart: async () => { const cartId = get().cartId; if (!cartId) return; try { const response = await fetch(`/api/cart?cartId=${cartId}`); if (!response.ok) return; const cart = await response.json(); if (cart.items) set({ items: cart.items }); } catch { // Sin red: se queda con el ultimo estado local conocido. } }, }), { name: "ari-shopping-cart-v5", storage: createJSONStorage(() => localStorage), partialize: (state) => ({ cartId: state.cartId, items: state.items }), onRehydrateStorage: () => (state) => { state?.setHasHydrated(true); void state?.syncCart(); }, }, ), );