1 Commits

View File

@@ -23,6 +23,7 @@ type ProductCard = Product & {
onQuantityChange?: (quantity: number) => void; onQuantityChange?: (quantity: number) => void;
initialQuantity?: number; initialQuantity?: number;
priceButtonProps?: Partial<ButtonPropsForVariant<CTAButtonVariant>>; priceButtonProps?: Partial<ButtonPropsForVariant<CTAButtonVariant>>;
ingredients?: string[]; // Added for flip card back
}; };
interface ProductCardThreeProps { interface ProductCardThreeProps {
@@ -62,6 +63,30 @@ interface ProductCardThreeProps {
textBoxButtonTextClassName?: string; textBoxButtonTextClassName?: string;
} }
// Helper function to generate ingredients based on product name
const generateIngredients = (productName: string): string[] => {
productName = productName.toLowerCase();
if (productName.includes("croissant")) {
return ["Wheat Flour", "Butter", "Water", "Sugar", "Yeast", "Salt", "Milk"];
}
if (productName.includes("cupcake")) {
return ["All-Purpose Flour", "Sugar", "Eggs", "Milk", "Butter", "Baking Powder", "Vanilla Extract", "Salt", "Icing Sugar"];
}
if (productName.includes("sourdough")) {
return ["Bread Flour", "Water", "Sourdough Starter", "Salt"];
}
if (productName.includes("cake")) {
return ["All-Purpose Flour", "Sugar", "Eggs", "Butter", "Milk", "Cocoa Powder", "Baking Soda", "Vanilla Extract", "Cream Cheese Frosting"];
}
if (productName.includes("bread")) {
return ["Wheat Flour", "Water", "Yeast", "Salt", "Sugar"];
}
if (productName.includes("pastry")) {
return ["Flour", "Butter", "Sugar", "Eggs", "Milk", "Fruit Filling"];
}
// Default ingredients for other products
return ["Flour", "Sugar", "Eggs", "Butter", "Milk", "Baking Powder", "Vanilla Extract"];
};
interface ProductCardItemProps { interface ProductCardItemProps {
product: ProductCard; product: ProductCard;
@@ -86,6 +111,7 @@ const ProductCardItem = memo(({
}: ProductCardItemProps) => { }: ProductCardItemProps) => {
const theme = useTheme(); const theme = useTheme();
const [quantity, setQuantity] = useState(product.initialQuantity || 1); const [quantity, setQuantity] = useState(product.initialQuantity || 1);
const ingredients = product.ingredients || generateIngredients(product.name);
const handleIncrement = useCallback((e: React.MouseEvent) => { const handleIncrement = useCallback((e: React.MouseEvent) => {
e.stopPropagation(); e.stopPropagation();
@@ -103,60 +129,95 @@ const ProductCardItem = memo(({
} }
}, [quantity, product]); }, [quantity, product]);
const handleClick = useCallback(() => { const handleViewDetailsClick = useCallback((e: React.MouseEvent) => {
e.stopPropagation(); // Prevent any parent click handlers
product.onProductClick?.(); // This will navigate to /shop/:id or trigger static product action
}, [product]);
const handleBuyClick = useCallback((e: React.MouseEvent) => {
e.stopPropagation(); // Prevent any parent click handlers
if (isFromApi && onBuyClick) { if (isFromApi && onBuyClick) {
onBuyClick(product.id, quantity); onBuyClick(product.id, quantity);
} else { } else {
product.onProductClick?.(); console.log(`Buying ${quantity} of ${product.name}`);
} }
}, [isFromApi, onBuyClick, product, quantity]); }, [isFromApi, onBuyClick, product, quantity]);
return ( return (
<article <article
className={cls("card group relative h-full flex flex-col gap-4 cursor-pointer p-4 rounded-theme-capped", cardClassName)} className={cls("relative h-full [perspective:1000px]", cardClassName)}
onClick={handleClick}
role="article" role="article"
aria-label={`${product.name} - ${product.price}`} aria-label={`${product.name} - ${product.price}`}
> >
<ProductImage <div className="relative w-full h-full [transform-style:preserve-3d] transition-transform duration-500 group-hover:[transform:rotateY(180deg)]">
imageSrc={product.imageSrc} {/* Front of the card */}
imageAlt={product.imageAlt || product.name} <div className="card absolute inset-0 [backface-visibility:hidden] flex flex-col gap-4 p-4 rounded-theme-capped">
isFavorited={product.isFavorited} <ProductImage
onFavoriteToggle={product.onFavorite} imageSrc={product.imageSrc}
imageClassName={imageClassName} imageAlt={product.imageAlt || product.name}
/> isFavorited={product.isFavorited}
onFavoriteToggle={product.onFavorite}
imageClassName={imageClassName}
/>
<div className="relative z-1 flex flex-col gap-3"> <div className="relative z-1 flex flex-col gap-3">
<h3 className={cls("text-xl font-medium leading-[1.15] truncate", shouldUseLightText ? "text-background" : "text-foreground", cardNameClassName)}> <h3 className={cls("text-xl font-medium leading-[1.15] truncate", shouldUseLightText ? "text-background" : "text-foreground", cardNameClassName)}>
{product.name} {product.name}
</h3> </h3>
<div className="flex items-center justify-between gap-4"> <div className="flex items-center justify-between gap-4">
<div className={cls("flex items-center gap-2", quantityControlsClassName)}> <div className={cls("flex items-center gap-2", quantityControlsClassName)}>
<QuantityButton <QuantityButton
onClick={handleDecrement} onClick={handleDecrement}
ariaLabel="Decrease quantity" ariaLabel="Decrease quantity"
Icon={Minus} Icon={Minus}
/> />
<span className={cls("text-base font-medium min-w-[2ch] text-center leading-[1]", shouldUseLightText ? "text-background" : "text-foreground")}> <span className={cls("text-base font-medium min-w-[2ch] text-center leading-[1]", shouldUseLightText ? "text-background" : "text-foreground")}>
{quantity} {quantity}
</span> </span>
<QuantityButton <QuantityButton
onClick={handleIncrement} onClick={handleIncrement}
ariaLabel="Increase quantity" ariaLabel="Increase quantity"
Icon={Plus} Icon={Plus}
/> />
</div>
<Button
{...getButtonProps(
{
text: product.price,
props: product.priceButtonProps,
},
0,
theme.defaultButtonVariant
)}
onClick={handleBuyClick}
/>
</div>
</div> </div>
</div>
{/* Back of the card */}
<div className={cls("card absolute inset-0 [backface-visibility:hidden] [transform:rotateY(180deg)] flex flex-col gap-2 p-4 rounded-theme-capped justify-between", shouldUseLightText ? "text-background" : "text-foreground")}>
<div>
<h3 className="text-xl font-medium leading-[1.15] mb-2">Ingredients:</h3>
<ul className="list-disc list-inside text-base max-h-30 overflow-y-auto">
{ingredients.map((ingredient, i) => (
<li key={i} className="mb-1">{ingredient}</li>
))}
</ul>
</div>
<Button <Button
{...getButtonProps( {...getButtonProps(
{ {
text: product.price, text: "View Details",
props: product.priceButtonProps, href: isFromApi ? `/shop/${product.id}` : "#",
}, },
0, 0,
theme.defaultButtonVariant theme.defaultButtonVariant
)} )}
onClick={handleViewDetailsClick}
className="mt-4 w-full"
/> />
</div> </div>
</div> </div>