Zastosowanie wszystkich poprawek UI i logiki kalkulatora
This commit is contained in:
@@ -0,0 +1,203 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import Image from 'next/image';
|
||||
import { ShoppingCart, GitCompare, Phone } from 'lucide-react';
|
||||
import { Product } from '@/content/products';
|
||||
import { useCartStore } from '@/lib/cart-store';
|
||||
import { useCompareStore } from '@/lib/compare-store';
|
||||
import { useState } from 'react';
|
||||
|
||||
const brandLogos: Record<string, string> = {
|
||||
'Gree': '/brands/gree.webp',
|
||||
'Rotenso': '/brands/rotenso.webp',
|
||||
'Toshiba': '/brands/toshiba.webp',
|
||||
'Daikin': '/brands/daikin.webp',
|
||||
'CHV': '/brands/chv.webp',
|
||||
'Haier': '/brands/haier.webp',
|
||||
'LG': '/brands/lg.webp',
|
||||
'Samsung': '/brands/samsung.webp',
|
||||
'Mitsubishi Electric': '/brands/mitsubishi.webp',
|
||||
'Fuji Electric': '/brands/fuji.webp',
|
||||
'Aux': '/brands/aux-brand.webp',
|
||||
'AUX': '/brands/aux-brand.webp',
|
||||
};
|
||||
|
||||
interface ProductCardProps {
|
||||
product: Product;
|
||||
}
|
||||
|
||||
export function ProductCard({ product }: ProductCardProps) {
|
||||
const addItem = useCartStore((s) => s.addItem);
|
||||
const { addItem: addCompare, removeItem: removeCompare, isAdded, items: compareItems } = useCompareStore();
|
||||
const [added, setAdded] = useState(false);
|
||||
|
||||
const handleAddToCart = (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
if (!product.price) return;
|
||||
addItem({
|
||||
slug: product.slug,
|
||||
name: product.name,
|
||||
brand: product.brand,
|
||||
image: product.image,
|
||||
price: product.price,
|
||||
});
|
||||
setAdded(true);
|
||||
};
|
||||
|
||||
const handleCompare = (e: React.MouseEvent) => {
|
||||
e.preventDefault();
|
||||
if (isAdded(product.slug)) {
|
||||
removeCompare(product.slug);
|
||||
} else {
|
||||
addCompare({
|
||||
slug: product.slug,
|
||||
name: product.name,
|
||||
brand: product.brand,
|
||||
price: product.price,
|
||||
image: product.image,
|
||||
specs: product.specs,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const inCompare = isAdded(product.slug);
|
||||
const compareDisabled = compareItems.length >= 3 && !inCompare;
|
||||
|
||||
return (
|
||||
<div className="group relative bg-white rounded-xl border border-border hover:border-accent hover:shadow-md transition-all duration-200 overflow-hidden flex flex-col">
|
||||
<Link href={`/sklep/${product.slug}`} className="block relative aspect-[4/3] bg-slate-50 overflow-hidden">
|
||||
<Image
|
||||
src={product.image}
|
||||
alt={product.name}
|
||||
fill
|
||||
sizes="(max-width: 640px) 100vw, (max-width: 1024px) 50vw, 33vw"
|
||||
className="object-contain p-6 group-hover:scale-105 transition-transform duration-300"
|
||||
unoptimized={product.image.startsWith('http')}
|
||||
/>
|
||||
|
||||
<div className="absolute top-3 left-3 z-10 flex flex-col gap-1.5">
|
||||
{brandLogos[product.brand] ? (
|
||||
<div className="bg-white rounded px-1.5 py-1 inline-block shadow-sm">
|
||||
<Image
|
||||
src={brandLogos[product.brand]}
|
||||
alt={product.brand}
|
||||
width={40}
|
||||
height={14}
|
||||
className="object-contain"
|
||||
unoptimized
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<span className="text-xs font-semibold text-primary bg-white/95 px-2 py-1 rounded-md border border-border shadow-sm">
|
||||
{product.brand}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{product.category === 'multisplit' && (
|
||||
<span className="absolute top-2 right-2 px-2 py-1 bg-accent/10 rounded-md text-xs font-semibold text-accent border border-accent/20">
|
||||
Multisplit
|
||||
</span>
|
||||
)}
|
||||
|
||||
{/* Porównywarka - pojawia się na hover */}
|
||||
<button
|
||||
onClick={handleCompare}
|
||||
disabled={compareDisabled}
|
||||
title={inCompare ? 'Usuń z porównywarki' : compareDisabled ? 'Max 3 produkty' : 'Dodaj do porównania'}
|
||||
className={`absolute bottom-2 right-2 p-1.5 rounded-lg border transition-all ${
|
||||
inCompare
|
||||
? 'bg-accent text-white border-accent opacity-100'
|
||||
: 'bg-white/90 text-text-muted border-border opacity-0 group-hover:opacity-100 hover:border-accent hover:text-accent'
|
||||
} ${compareDisabled ? 'opacity-30 cursor-not-allowed' : ''}`}
|
||||
>
|
||||
<GitCompare className="size-3.5" />
|
||||
</button>
|
||||
</Link>
|
||||
|
||||
<div className="p-4 flex flex-col flex-1 gap-2">
|
||||
<Link href={`/sklep/${product.slug}`}>
|
||||
<h3 className="text-sm font-semibold text-primary font-heading leading-snug line-clamp-2 hover:text-accent transition-colors">
|
||||
{product.name}
|
||||
</h3>
|
||||
</Link>
|
||||
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{product.specs['Moc chłodnicza'] && (
|
||||
<span className="px-2 py-0.5 bg-slate-50 rounded text-xs text-text-muted border border-border">
|
||||
{product.specs['Moc chłodnicza']} kW
|
||||
</span>
|
||||
)}
|
||||
{product.specs['Klasa energetyczna'] && (
|
||||
<span className="px-2 py-0.5 bg-green-50 rounded text-xs text-green-700 border border-green-100 font-medium">
|
||||
{product.specs['Klasa energetyczna']}
|
||||
</span>
|
||||
)}
|
||||
{product.specs['Czynnik chłodniczy'] && (
|
||||
<span className="px-2 py-0.5 bg-slate-50 rounded text-xs text-text-muted border border-border">
|
||||
{product.specs['Czynnik chłodniczy']}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Cena + CTA */}
|
||||
<div className="mt-auto pt-2 flex items-end justify-between gap-2">
|
||||
{product.category === 'multisplit' ? (
|
||||
<>
|
||||
<div className="flex items-center gap-1 text-accent">
|
||||
<Phone className="size-4" />
|
||||
<span className="text-sm font-semibold">Wycena indywidualna</span>
|
||||
</div>
|
||||
<Link
|
||||
href={`/zapytanie-multisplit/${product.slug}`}
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
<button className="flex items-center gap-1.5 px-3 py-2 rounded-lg text-xs font-bold bg-accent text-white hover:bg-accent/90 transition-colors shrink-0">
|
||||
Zapytaj o wycenę
|
||||
</button>
|
||||
</Link>
|
||||
</>
|
||||
) : product.price !== null ? (
|
||||
<>
|
||||
<div>
|
||||
<p className="text-lg font-bold text-primary font-heading leading-none">
|
||||
{product.price.toLocaleString('pl-PL')} zł
|
||||
</p>
|
||||
<p className="text-xs text-text-muted mt-0.5">brutto</p>
|
||||
</div>
|
||||
{added ? (
|
||||
<Link
|
||||
href="/koszyk"
|
||||
className="flex items-center gap-1.5 px-3 py-2 rounded-lg text-xs font-bold bg-green-500 text-white hover:bg-green-600 transition-colors shrink-0"
|
||||
onClick={e => e.stopPropagation()}
|
||||
>
|
||||
<ShoppingCart className="size-3.5" /> Zobacz koszyk
|
||||
</Link>
|
||||
) : (
|
||||
<button
|
||||
onClick={handleAddToCart}
|
||||
className="flex items-center gap-1.5 px-3 py-2 rounded-lg text-xs font-bold bg-accent text-white hover:bg-accent/90 transition-colors shrink-0"
|
||||
>
|
||||
<ShoppingCart className="size-3.5" /> Do koszyka
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<div className="flex items-center gap-1 text-accent">
|
||||
<Phone className="size-3.5" />
|
||||
<span className="text-sm font-semibold">Zapytaj o cenę</span>
|
||||
</div>
|
||||
<Link href="/kontakt" onClick={e => e.stopPropagation()}>
|
||||
<button className="flex items-center gap-1.5 px-3 py-2 rounded-lg text-xs font-bold bg-primary text-white hover:bg-primary/90 transition-colors shrink-0">
|
||||
Zapytaj
|
||||
</button>
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user