Files
sent-shop/components/build-box/client/BuildBoxSidebarWithTranslation.tsx
2026-01-19 20:21:14 +01:00

210 lines
8.5 KiB
TypeScript

'use client';
import { Button } from "@/components/ui/Button";
import { Heading, Text } from "@/components/ui/Typography";
import { useTranslation } from "@/lib/hooks/useTranslation";
import { useAppDispatch, useAppSelector } from "@/lib/redux/hooks";
import { BoxItem, removeFromBox, selectBoxItems, selectBoxTotal, updateQuantity } from "@/lib/redux/slices/boxSlice";
import { Minus, Plus, Trash2 } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { AddBoxToCartClient } from "../AddBoxToCartClient";
interface BuildBoxSidebarWithTranslationProps {
isMobile?: boolean;
}
export function BuildBoxSidebarWithTranslation({ isMobile = false }: BuildBoxSidebarWithTranslationProps) {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const items = useAppSelector(selectBoxItems);
const boxTotal = useAppSelector(selectBoxTotal);
const isEmpty = items.length === 0;
const pathname = usePathname();
// Separate box containers from products
const boxContainers = items.filter(item => item.variantId === 'box-container');
const products = items.filter(item => item.variantId !== 'box-container');
// Check if we have products for the first step
const hasProducts = products.length > 0;
// Check if we have a selected box for the second step
const hasBoxSelected = boxContainers.length > 0;
// Determine which page we're on to show appropriate button text
const isCustomizePage = pathname.includes('/customize');
const nextStepUrl = "/build-box/customize";
const nextStepText = t('buildBox.sidebar.nextStep');
// Determine if the next step button should be disabled
const isNextStepDisabled = isEmpty || (!isCustomizePage && !hasProducts);
const handleUpdateQuantity = (id: string, color: string | undefined, newQuantity: number) => {
if (newQuantity < 1) return;
dispatch(updateQuantity({ id, color, quantity: newQuantity }));
};
const handleRemoveItem = (id: string, color: string | undefined) => {
dispatch(removeFromBox({ id, color }));
};
return (
<div className="h-full flex flex-col">
{/* Fixed header - prikazan samo ako nije mobilni prikaz */}
{!isMobile && (
<div className="p-4 pt-8 pb-4 border-b">
<Heading level={3} className="text-center">{t('buildBox.sidebar.title')}</Heading>
</div>
)}
{/* Scrollable products area */}
<div className="flex-1 overflow-y-auto p-4">
{isEmpty ? (
<div className="text-center py-8 text-gray-500">
<Text>{t('buildBox.sidebar.empty')}</Text>
<Text className="text-sm mt-2">{t('buildBox.sidebar.emptySubtext')}</Text>
</div>
) : (
<div className="space-y-6">
{/* Show box container if any */}
{boxContainers.length > 0 && (
<div className="border-b pb-4 mb-4">
<Text className="font-medium mb-2">{t('buildBox.sidebar.boxDesign')}</Text>
{boxContainers.map((box: BoxItem) => (
<div key={box.compositeKey || box.id} className="flex items-start space-x-3">
<div className="w-20 h-24 relative flex-shrink-0">
<Image
src={box.image}
alt={box.name}
fill
className="object-cover rounded-md"
/>
</div>
<div className="flex-grow">
<div className="flex justify-between">
<div>
<div className="text-base font-medium">{box.name}</div>
<div className="text-sm text-gray-500">${box.price}</div>
</div>
<button
onClick={() => handleRemoveItem(box.id, box.color)}
className="text-gray-400 hover:text-red-500"
aria-label="Remove box"
>
<Trash2 size={18} />
</button>
</div>
</div>
</div>
))}
</div>
)}
{/* Product items */}
{products.length > 0 && (
<>
{products.map((item: BoxItem) => (
<div key={item.compositeKey || item.id} className="flex items-start space-x-3">
<div className="w-20 h-24 relative flex-shrink-0">
<Image
src={item.image}
alt={item.name}
fill
className="object-cover rounded-md"
/>
</div>
<div className="flex-grow flex flex-col justify-between h-24">
<div className="flex justify-between">
<div>
<div className="text-base font-medium">${item.price}</div>
{item.color && (
<div className="text-xs text-gray-500 mt-1 flex items-center">
<div
className="w-3 h-3 rounded-full mr-1"
style={{ backgroundColor: item.color }}
/>
<span>Color</span>
</div>
)}
</div>
<button
onClick={() => handleRemoveItem(item.id, item.color)}
className="text-gray-400 hover:text-red-500"
aria-label="Remove item"
>
<Trash2 size={18} />
</button>
</div>
<div className="flex items-center">
<div className="flex border border-gray-300 rounded-md">
<button
onClick={() => handleUpdateQuantity(item.id, item.color, item.quantity - 1)}
className="px-2 py-1 border-r border-gray-300 text-gray-500 hover:bg-gray-100 disabled:opacity-50"
disabled={item.quantity <= 1}
aria-label="Decrease quantity"
>
<Minus size={14} />
</button>
<span className="px-3 py-1 flex items-center justify-center w-8 text-center">
{item.quantity}
</span>
<button
onClick={() => handleUpdateQuantity(item.id, item.color, item.quantity + 1)}
className="px-2 py-1 border-l border-gray-300 text-gray-500 hover:bg-gray-100"
aria-label="Increase quantity"
>
<Plus size={14} />
</button>
</div>
</div>
</div>
</div>
))}
</>
)}
</div>
)}
</div>
{/* Fixed price and button at bottom */}
<div className="border-t p-4">
<div className="flex justify-between mb-4">
<Text className="font-medium">{t('buildBox.sidebar.boxPrice')}</Text>
<Text className="font-medium">${boxTotal.toFixed(2)}</Text>
</div>
{isCustomizePage ? (
<AddBoxToCartClient />
) : (
<Link
href={isNextStepDisabled ? "#" : nextStepUrl}
className={isNextStepDisabled ? "pointer-events-none" : ""}
>
<Button
variant="primary"
className="w-full py-3"
disabled={isNextStepDisabled}
>
{nextStepText}
</Button>
</Link>
)}
{!isCustomizePage && !hasProducts && !isEmpty && (
<Text className="text-xs text-center text-red-500 mt-2">
{t('buildBox.sidebar.addProductWarning')}
</Text>
)}
{isCustomizePage && !hasBoxSelected && !isEmpty && (
<Text className="text-xs text-center text-red-500 mt-2">
{t('buildBox.sidebar.selectBoxWarning')}
</Text>
)}
</div>
</div>
);
}