chore: transfer repo
This commit is contained in:
142
components/cart/CartProductItem.tsx
Normal file
142
components/cart/CartProductItem.tsx
Normal file
@@ -0,0 +1,142 @@
|
||||
'use client';
|
||||
|
||||
import { colorHexMap } from '@/components/products/utils/colorUtils';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { Text } from '@/components/ui/Typography';
|
||||
import { useTranslation } from '@/lib/hooks/useTranslation';
|
||||
import { useCart } from 'components/cart/cart-context';
|
||||
import Price from 'components/price';
|
||||
import { CartItem } from 'lib/shopify/types';
|
||||
import { Trash2 } from 'lucide-react';
|
||||
import Image from 'next/image';
|
||||
import { QuantityControls } from './QuantityControls';
|
||||
|
||||
interface CartProductItemProps {
|
||||
item: CartItem;
|
||||
onUpdate: (merchandiseId: string, updateType: 'plus' | 'minus' | 'delete', itemId?: string) => void;
|
||||
isPending: boolean;
|
||||
isInBox?: boolean;
|
||||
boxGroupId?: string;
|
||||
}
|
||||
|
||||
export function CartProductItem({ item, onUpdate, isPending, isInBox = false, boxGroupId }: CartProductItemProps) {
|
||||
const { t } = useTranslation();
|
||||
const { cart } = useCart();
|
||||
|
||||
// Check if this item has a color option
|
||||
const colorOption = item.merchandise.selectedOptions.find(option =>
|
||||
option.name.toLowerCase() === 'color' ||
|
||||
option.name.toLowerCase() === 'colour' ||
|
||||
option.name.toLowerCase() === 'boja'
|
||||
);
|
||||
|
||||
// Get the color hex code from the color name if it exists
|
||||
const colorHex = colorOption
|
||||
? colorHexMap[colorOption.value.toLowerCase()] || colorOption.value
|
||||
: null;
|
||||
|
||||
// Get the unique ID of this cart item
|
||||
const itemId = item.id;
|
||||
|
||||
// Handle quantity changes
|
||||
const handleIncrease = () => onUpdate(item.merchandise.id, 'plus', itemId);
|
||||
const handleDecrease = () => onUpdate(item.merchandise.id, 'minus', itemId);
|
||||
|
||||
// Enhanced delete handler
|
||||
const handleDelete = () => {
|
||||
// Basic delete operation for this item
|
||||
onUpdate(item.merchandise.id, 'delete', itemId);
|
||||
|
||||
// Special handling for box items
|
||||
if (isInBox && boxGroupId && cart) {
|
||||
// Find all items in this box group
|
||||
const boxItems = cart.lines.filter(line => {
|
||||
const attrs = line.attributes || [];
|
||||
const itemBoxGroupId = attrs.find(attr => attr.key === '_box_group_id')?.value;
|
||||
const itemBoxType = attrs.find(attr => attr.key === '_box_type')?.value;
|
||||
return itemBoxGroupId === boxGroupId && itemBoxType === 'item' && line.id !== itemId;
|
||||
});
|
||||
|
||||
// If this is the last item (only 1 left - the one we're deleting), also delete the box container
|
||||
if (boxItems.length === 0) {
|
||||
// Find the box container
|
||||
const boxContainer = cart.lines.find(line => {
|
||||
const attrs = line.attributes || [];
|
||||
const containerBoxGroupId = attrs.find(attr => attr.key === '_box_group_id')?.value;
|
||||
const containerBoxType = attrs.find(attr => attr.key === '_box_type')?.value;
|
||||
return containerBoxGroupId === boxGroupId && containerBoxType === 'container';
|
||||
});
|
||||
|
||||
// Delete the box container too
|
||||
if (boxContainer && boxContainer.id) {
|
||||
onUpdate(boxContainer.merchandise.id, 'delete', boxContainer.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`flex flex-wrap md:flex-nowrap items-start ${!isInBox ? 'border-b pb-6' : 'pb-4'}`}>
|
||||
{/* Product Image */}
|
||||
<div className="relative h-20 w-20 flex-shrink-0 overflow-hidden">
|
||||
<Image
|
||||
src={item.merchandise.product.featuredImage?.url || ''}
|
||||
alt={item.merchandise.product.title}
|
||||
fill
|
||||
className="object-cover"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Product Details */}
|
||||
<div className="flex-1 pl-4 min-w-0">
|
||||
<div className="flex flex-col">
|
||||
<Text weight={isInBox ? 'regular' : 'semibold'} className="pr-2 break-words">
|
||||
{item.merchandise.product.title}
|
||||
</Text>
|
||||
|
||||
{/* Show color indicator if color is available */}
|
||||
{colorHex && (
|
||||
<div className="flex items-center mt-1">
|
||||
<div
|
||||
className="w-4 h-4 rounded-full"
|
||||
style={{ backgroundColor: colorHex }}
|
||||
aria-label={`${colorOption?.value || 'Unknown'}`}
|
||||
/>
|
||||
<Text size="xs" color="muted" as="span" className="ml-2">
|
||||
{colorOption?.value || ''}
|
||||
</Text>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Price
|
||||
amount={item.cost.totalAmount.amount}
|
||||
currencyCode={item.cost.totalAmount.currencyCode}
|
||||
className="text-sm mt-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Quantity Controls */}
|
||||
<div className="flex items-center ml-auto mt-2 md:mt-0">
|
||||
<div className="mr-4">
|
||||
<QuantityControls
|
||||
quantity={item.quantity}
|
||||
onIncrease={handleIncrease}
|
||||
onDecrease={handleDecrease}
|
||||
isDisabled={isPending}
|
||||
minQuantity={1}
|
||||
/>
|
||||
</div>
|
||||
<Button
|
||||
onClick={handleDelete}
|
||||
variant="default"
|
||||
className="p-0 h-auto border-0 bg-transparent hover:bg-transparent text-gray-500 hover:text-red-500"
|
||||
disabled={isPending}
|
||||
aria-label={t('cart.remove')}
|
||||
>
|
||||
<Trash2 size={20} />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user