freshyo/apps/web-ui/src/hooks/cart-query-hooks.ts
2026-09-06 18:47:21 +05:30

146 lines
4.1 KiB
TypeScript

import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import type { CartData as SharedCartData } from '@packages/shared'
type CartType = 'regular' | 'flash'
interface CartItem {
id: number
skuId: number
quantity: number
storeId: number | null
addedAt: number
slotId?: number | null
deliveryDate?: string | null
}
type CartData = SharedCartData<CartItem>
function getCartKey(cartType: CartType): string {
return `local-cart-${cartType}`
}
function readCart(cartType: CartType): CartItem[] {
try {
const raw = localStorage.getItem(getCartKey(cartType))
return raw ? JSON.parse(raw) : []
} catch {
return []
}
}
function writeCart(cartType: CartType, items: CartItem[]) {
localStorage.setItem(getCartKey(cartType), JSON.stringify(items))
}
// Build the exact shape useGetCart returns, so setQueryData matches the cache.
function toCartData(items: CartItem[]): CartData {
const totalItems = items.reduce((sum, item) => sum + item.quantity, 0)
return { items, totalItems, totalAmount: 0 }
}
export function useGetCart(cartType: CartType = 'regular') {
return useQuery({
queryKey: [getCartKey(cartType)],
queryFn: () => toCartData(readCart(cartType)),
})
}
export function useAddToCart(cartType: CartType = 'regular') {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async ({
skuId,
quantity,
storeId,
slotId,
deliveryDate,
}: {
skuId: number
quantity: number
storeId?: number | null
slotId?: number | null
deliveryDate?: string | null
}) => {
const items = readCart(cartType)
const existing = items.find((i) => i.skuId === skuId)
if (existing) {
existing.quantity += quantity
if (slotId) existing.slotId = slotId
if (deliveryDate) existing.deliveryDate = deliveryDate
} else {
items.push({
id: Date.now(),
skuId,
quantity,
storeId: storeId ?? null,
addedAt: Date.now(),
slotId: slotId ?? null,
deliveryDate: deliveryDate ?? null,
})
}
writeCart(cartType, items)
// Optimistically update the cache so the UI reflects the cart instantly,
// before any refetch completes.
queryClient.setQueryData([getCartKey(cartType)], toCartData(items))
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [getCartKey(cartType)] })
},
})
}
export function useUpdateCartItem(cartType: CartType = 'regular') {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async ({
skuId,
quantity,
slotId,
deliveryDate,
}: {
skuId: number
quantity: number
slotId?: number | null
deliveryDate?: string | null
}) => {
let items = readCart(cartType)
const existing = items.find((i) => i.skuId === skuId)
if (!existing) return
if (quantity <= 0) {
// Quantity hit 0 — remove the item from the cart entirely
items = items.filter((i) => i.skuId !== skuId)
} else {
existing.quantity = quantity
if (slotId !== undefined) existing.slotId = slotId
if (deliveryDate !== undefined) existing.deliveryDate = deliveryDate
}
writeCart(cartType, items)
queryClient.setQueryData([getCartKey(cartType)], toCartData(items))
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [getCartKey(cartType)] })
},
})
}
export function useRemoveFromCart(cartType: CartType = 'regular') {
const queryClient = useQueryClient()
return useMutation({
mutationFn: async (skuId: number) => {
let items = readCart(cartType)
items = items.filter((i) => i.skuId !== skuId)
writeCart(cartType, items)
queryClient.setQueryData([getCartKey(cartType)], toCartData(items))
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [getCartKey(cartType)] })
},
})
}
export function clearLocalCart(cartType: CartType = 'regular') {
localStorage.removeItem(getCartKey(cartType))
}