// composables/useCart.ts import type { CartSummary } from '~/types/cart' /** * Shopping Cart composable * * Manages cart state and provides methods for cart operations * * Features: * - Reactive cart state (cart, items, total, itemCount) * - Auto-fetch cart on mount * - Add, update, and remove items * - Loading states for async operations * - Error handling with notifications for removed items * * Usage: * const { cart, items, total, itemCount, loading, addItem, updateItem, removeItem, fetchCart } = useCart() */ // Global cart state (shared across all components) const cartState = ref(null) const loading = ref(false) const initialized = ref(false) export function useCart() { // Computed reactive properties const cart = computed(() => cartState.value?.cart ?? null) const items = computed(() => cartState.value?.items ?? []) const total = computed(() => cartState.value?.total ?? 0) const itemCount = computed(() => cartState.value?.itemCount ?? 0) /** * Fetch cart from server * Auto-cleans unavailable products and returns removed items */ async function fetchCart() { loading.value = true try { const data = await $fetch('/api/cart', { method: 'GET', }) cartState.value = data // Show notification if products were removed if (data.removedItems && data.removedItems.length > 0) { // TODO: Show toast notification when toast composable is implemented // For now, log to console console.warn('Products removed from cart:', data.removedItems) } return data } catch (error) { console.error('Failed to fetch cart:', error) // Set to null on error cartState.value = null throw error } finally { loading.value = false } } /** * Add item to cart * * @param productId - Product UUID * @param quantity - Quantity to add (default: 1) */ async function addItem(productId: string, quantity: number = 1) { loading.value = true try { await $fetch('/api/cart/items', { method: 'POST', body: { productId, quantity, }, }) // Refresh cart to get updated state await fetchCart() } catch (error) { console.error('Failed to add item to cart:', error) throw error } finally { loading.value = false } } /** * Update cart item quantity * * @param itemId - Cart item UUID * @param quantity - New quantity (must be >= 1) */ async function updateItem(itemId: string, quantity: number) { if (quantity < 1) { throw new Error('Quantity must be at least 1') } loading.value = true try { await $fetch(`/api/cart/items/${itemId}`, { method: 'PATCH', body: { quantity, }, }) // Refresh cart to get updated state await fetchCart() } catch (error) { console.error('Failed to update cart item:', error) throw error } finally { loading.value = false } } /** * Remove item from cart * * @param itemId - Cart item UUID */ async function removeItem(itemId: string) { loading.value = true try { await $fetch(`/api/cart/items/${itemId}`, { method: 'DELETE', }) // Refresh cart to get updated state await fetchCart() } catch (error) { console.error('Failed to remove cart item:', error) throw error } finally { loading.value = false } } /** * Clear all items from cart * For future use (not implemented in API yet) */ async function clearCart() { // TODO: Implement when API endpoint is ready console.warn('clearCart() not yet implemented') } /** * Initialize cart on mount * Only fetches once per session */ onMounted(async () => { if (!initialized.value) { await fetchCart() initialized.value = true } }) return { // State cart, items, total, itemCount, loading, // Methods fetchCart, addItem, updateItem, removeItem, clearCart, } }