chore: transfer repo
This commit is contained in:
210
components/build-box/client/BuildBoxSidebarWithTranslation.tsx
Normal file
210
components/build-box/client/BuildBoxSidebarWithTranslation.tsx
Normal file
@@ -0,0 +1,210 @@
|
||||
'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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user