You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
178 lines
4.1 KiB
178 lines
4.1 KiB
// 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<CartSummary | null>(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<CartSummary>('/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,
|
|
}
|
|
}
|
|
|