253 lines
11 KiB
TypeScript
253 lines
11 KiB
TypeScript
import { ShoppingCart, Trash } from 'iconsax-react'
|
||
import {
|
||
Popover,
|
||
PopoverContent,
|
||
PopoverTrigger,
|
||
} from "@/components/ui/popover"
|
||
import { useGetCart, useUpdateCart } from '@/app/cart/hooks/useCartData'
|
||
import { useRemoveFromCart } from '@/app/cart/hooks/useCartData'
|
||
import { CartItemType } from '@/app/cart/types/Types'
|
||
import { LocalCartItem } from '@/app/product/store/LocalCartStore'
|
||
|
||
// Union type for all possible cart items
|
||
type CartItem = CartItemType | EnrichedOfflineCartItem
|
||
import { NumberFormat } from '@/config/func'
|
||
import Image from 'next/image'
|
||
import Link from 'next/link'
|
||
import { Button } from '@/components/ui/button'
|
||
import { useSharedStore } from '@/share/store/sharedStore'
|
||
import { useLocalCart } from '@/app/product/hooks/useLocalCart'
|
||
import { useOfflineCartProducts, EnrichedOfflineCartItem } from '@/share/hooks/useOfflineCartProducts'
|
||
import { useEffect, useRef } from 'react'
|
||
|
||
const Cart = () => {
|
||
const { isLogin } = useSharedStore()
|
||
const { data: cartData, isLoading } = useGetCart()
|
||
const removeFromCartMutation = useRemoveFromCart()
|
||
const updateCartMutation = useUpdateCart()
|
||
|
||
// Local cart hooks for offline users
|
||
const {
|
||
localCartItems,
|
||
removeFromLocalCart,
|
||
getLocalCartItemsCount,
|
||
clearLocalCart
|
||
} = useLocalCart()
|
||
|
||
// Track previous login state to detect login events
|
||
const prevIsLoginRef = useRef(isLogin)
|
||
|
||
// Get enriched product data for offline cart items
|
||
const {
|
||
enrichedCartItems,
|
||
isLoading: isLoadingOfflineProducts
|
||
} = useOfflineCartProducts(localCartItems)
|
||
|
||
// Sync local cart to server when user logs in
|
||
useEffect(() => {
|
||
const prevIsLogin = prevIsLoginRef.current
|
||
prevIsLoginRef.current = isLogin
|
||
|
||
// Check if user just logged in and has local cart items
|
||
if (!prevIsLogin && isLogin && localCartItems.length > 0) {
|
||
// Add each local cart item to server cart
|
||
const syncPromises = localCartItems.map(async (item) => {
|
||
try {
|
||
await updateCartMutation.mutateAsync({
|
||
productId: item.productId,
|
||
variantId: item.variantId,
|
||
quantity: item.quantity
|
||
})
|
||
} catch (error) {
|
||
console.error('Failed to sync cart item:', item, error)
|
||
}
|
||
})
|
||
|
||
// Clear local cart after sync attempt
|
||
Promise.all(syncPromises).finally(() => {
|
||
clearLocalCart()
|
||
})
|
||
}
|
||
}, [isLogin, localCartItems, updateCartMutation, clearLocalCart])
|
||
|
||
const handleRemoveItem = async (productId: number, variantId: string) => {
|
||
if (isLogin) {
|
||
await removeFromCartMutation.mutateAsync({
|
||
productId,
|
||
variantId
|
||
})
|
||
} else {
|
||
removeFromLocalCart(productId, variantId)
|
||
}
|
||
}
|
||
|
||
// Get cart data based on login status
|
||
const cart = cartData?.results?.cart
|
||
const onlineItems = cart?.items || []
|
||
const onlineItemsCount = cart?.items_count || 0
|
||
|
||
// Use appropriate data based on login status
|
||
const items = isLogin ? onlineItems : enrichedCartItems
|
||
const itemsCount = isLogin ? onlineItemsCount : getLocalCartItemsCount()
|
||
const isItemsLoading = isLogin ? isLoading : isLoadingOfflineProducts
|
||
|
||
return (
|
||
<Popover>
|
||
<PopoverTrigger asChild>
|
||
<div className='h-10 p-3 rounded-[12px] border border-border flex items-center gap-2.5 relative'>
|
||
<ShoppingCart size={20} color='#333333' />
|
||
{itemsCount > 0 && (
|
||
<div className='absolute -top-2 -right-2 bg-primary text-white text-xs rounded-full w-5 h-5 flex items-center justify-center'>
|
||
{itemsCount}
|
||
</div>
|
||
)}
|
||
</div>
|
||
</PopoverTrigger>
|
||
<PopoverContent className="w-[350px] ml-2 z-[9999] p-0 max-h-[600px] overflow-y-auto" style={{ left: 4 }}>
|
||
<div className='p-4'>
|
||
{/* Header */}
|
||
{
|
||
itemsCount > 0 && (
|
||
<div className='text-primary text-sm font-medium mb-4'>
|
||
{itemsCount} کالا در سبد خرید شما ثبت شده
|
||
</div>
|
||
)
|
||
}
|
||
|
||
{/* Cart Items */}
|
||
<div className='space-y-3 mb-4'>
|
||
{isItemsLoading ? (
|
||
<div className='text-center py-4 text-gray-500'>در حال بارگذاری...</div>
|
||
) : items.length > 0 ? (
|
||
items.slice(0, 3).map((item: CartItem, index: number) => (
|
||
<CartItemCard
|
||
key={isLogin ? `${(item as CartItemType).product._id}-${(item as CartItemType).variant._id}` : `${(item as LocalCartItem).productId}-${(item as LocalCartItem).variantId}-${index}`}
|
||
item={item}
|
||
isOnline={isLogin}
|
||
onRemove={() => handleRemoveItem(
|
||
isLogin ? (item as CartItemType).product._id : (item as LocalCartItem).productId,
|
||
isLogin ? (item as CartItemType).variant._id : (item as LocalCartItem).variantId
|
||
)}
|
||
/>
|
||
))
|
||
) : (
|
||
<div className='text-center py-4'>
|
||
<div className='text-gray-500 mb-2'>
|
||
{!isLogin ? 'سبد خرید شما خالی است' : 'سبد خرید شما خالی است'}
|
||
</div>
|
||
{!isLogin && (
|
||
<div className='text-xs text-gray-400'>
|
||
آیتمهای اضافه شده به سبد خرید در مرورگر شما ذخیره میشوند
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
|
||
{/* View Cart Button */}
|
||
{itemsCount > 0 && isLogin && (
|
||
<Link href="/cart" className='block'>
|
||
<Button className='w-full bg-primary hover:bg-primary/90 text-white rounded-lg py-3'>
|
||
مشاهده سبد خرید
|
||
</Button>
|
||
</Link>
|
||
)}
|
||
|
||
{/* Login encouragement message for offline users */}
|
||
{!isLogin && (
|
||
<div className='mt-4 p-3 bg-blue-50 border border-blue-200 rounded-lg text-center'>
|
||
<div className='text-xs text-blue-700 mb-2'>
|
||
برای ذخیره سبد خرید و همگامسازی در همه دستگاهها، وارد حساب کاربری شوید
|
||
</div>
|
||
<Link href="/auth" className='inline-block'>
|
||
<Button size="sm" variant="outline" className='text-blue-600 border-blue-300 hover:bg-blue-100'>
|
||
ورود به حساب
|
||
</Button>
|
||
</Link>
|
||
</div>
|
||
)}
|
||
</div>
|
||
</PopoverContent>
|
||
</Popover>
|
||
)
|
||
}
|
||
|
||
// Cart Item Card Component
|
||
const CartItemCard = ({
|
||
item,
|
||
isOnline,
|
||
onRemove
|
||
}: {
|
||
item: CartItem,
|
||
isOnline: boolean,
|
||
onRemove: () => void
|
||
}) => {
|
||
// Handle both online and offline items with enriched data
|
||
const productTitle = isOnline
|
||
? (item as CartItemType).product.title_fa
|
||
: (item as EnrichedOfflineCartItem).product?.title_fa || 'در حال بارگذاری محصول...'
|
||
|
||
const productImage = isOnline
|
||
? (item as CartItemType).product.imagesUrl.cover
|
||
: (item as EnrichedOfflineCartItem).product?.imagesUrl?.cover || '/images/logo.png'
|
||
|
||
const productCategory = isOnline
|
||
? ((item as CartItemType).product.category?._id || 'دستهبندی')
|
||
: ((item as EnrichedOfflineCartItem).product?.category?._id || 'دستهبندی')
|
||
|
||
const productPrice = isOnline
|
||
? (item as CartItemType).variant.price.selling_price
|
||
: (item as EnrichedOfflineCartItem).variant?.price?.selling_price || 0
|
||
|
||
const quantity = isOnline
|
||
? (item as CartItemType).quantity
|
||
: (item as LocalCartItem).quantity
|
||
|
||
// Check if product data is still loading for offline items
|
||
const isProductLoading = !isOnline && !(item as EnrichedOfflineCartItem).product
|
||
const hasError = !isOnline && !isProductLoading && !(item as EnrichedOfflineCartItem).variant
|
||
|
||
return (
|
||
<div className='bg-white rounded-lg p-3 flex gap-3 shadow-sm border border-gray-100'>
|
||
{/* Product Image */}
|
||
<div className='flex-shrink-0'>
|
||
<Image
|
||
src={productImage}
|
||
alt={productTitle}
|
||
width={60}
|
||
height={60}
|
||
className='w-15 h-15 object-contain rounded'
|
||
/>
|
||
</div>
|
||
|
||
{/* Product Details */}
|
||
<div className='flex-1 min-w-0'>
|
||
<div className='text-xs text-gray-500 mb-1'>
|
||
{productCategory}
|
||
</div>
|
||
<div className='text-sm text-gray-800 mb-2 line-clamp-2'>
|
||
{hasError ? 'خطا در بارگذاری محصول' : isProductLoading ? 'در حال بارگذاری محصول...' : productTitle}
|
||
</div>
|
||
<div className='flex items-center justify-between'>
|
||
<div className='text-sm font-semibold text-gray-900'>
|
||
{hasError ? (
|
||
`${quantity} عدد`
|
||
) : isProductLoading ? (
|
||
'در حال بارگذاری...'
|
||
) : (
|
||
`${NumberFormat(productPrice)} تومان`
|
||
)}
|
||
</div>
|
||
<button
|
||
onClick={onRemove}
|
||
className='p-1 hover:bg-red-100 rounded'
|
||
>
|
||
<Trash size={16} color='#AD3434' />
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
)
|
||
}
|
||
|
||
export default Cart |