Skip to content

Commit bc88c49

Browse files
Make Digital Tray clickable
Linked Digital Tray cards to product detail pages using product handle, wrapping cards in Link when handle exists and passing proper routes to /product/{handle} to enable navigation from the homepage tray. X-Lovable-Edit-ID: edt-983ecb29-1f22-447c-a609-0d2578f6c2ea
2 parents 2ae0771 + 637f8d1 commit bc88c49

1 file changed

Lines changed: 40 additions & 34 deletions

File tree

src/components/home/Hero.tsx

Lines changed: 40 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,9 @@ const stepLabel = (step: string) => {
2525
};
2626

2727
const fallbackProducts = [
28-
{ title: "Retinol Night Treatment", brand: "Asper Beauty", price: 68, step: "Treatment", icon: FlaskConical },
29-
{ title: "Vitamin C Brightening Cream", brand: "Asper Beauty", price: 52, step: "Protection", icon: Sparkles },
30-
{ title: "Nourishing Hair Oil", brand: "Kérastase", price: 32, step: "Nourish", icon: Droplets },
28+
{ title: "Retinol Night Treatment", brand: "Asper Beauty", price: 68, step: "Treatment", icon: FlaskConical, handle: "" },
29+
{ title: "Vitamin C Brightening Cream", brand: "Asper Beauty", price: 52, step: "Protection", icon: Sparkles, handle: "" },
30+
{ title: "Nourishing Hair Oil", brand: "Kérastase", price: 32, step: "Nourish", icon: Droplets, handle: "" },
3131
];
3232

3333
export default function Hero() {
@@ -41,7 +41,7 @@ export default function Hero() {
4141
queryFn: async () => {
4242
const { data } = await supabase
4343
.from("products")
44-
.select("title, brand, price, regimen_step, image_url")
44+
.select("title, brand, price, regimen_step, image_url, handle")
4545
.eq("is_hero", true)
4646
.order("bestseller_rank", { ascending: true, nullsFirst: false })
4747
.limit(3);
@@ -57,6 +57,7 @@ export default function Hero() {
5757
price: p.price ?? 0,
5858
step: stepLabel(p.regimen_step),
5959
icon: stepIcon(p.regimen_step),
60+
handle: p.handle,
6061
}))
6162
: fallbackProducts;
6263

@@ -161,36 +162,41 @@ export default function Hero() {
161162

162163
{/* Product tray cards */}
163164
<div className="relative space-y-4 py-8">
164-
{trayProducts.map((product, i) => (
165-
<motion.div
166-
key={product.title}
167-
className={cn(
168-
"relative bg-card rounded-xl border border-border p-5 shadow-sm hover:shadow-lg hover:border-accent/40 transition-all duration-300 group",
169-
i === 0 && "lg:ml-4",
170-
i === 1 && "lg:ml-12",
171-
i === 2 && "lg:ml-6",
172-
)}
173-
initial={{ opacity: 0, y: 20 }}
174-
animate={{ opacity: 1, y: 0 }}
175-
transition={{ duration: 0.6, delay: 0.4 + i * 0.15, ease: [0.19, 1, 0.22, 1] }}
176-
>
177-
<div className="flex items-center gap-4">
178-
<div className="w-12 h-12 rounded-lg bg-secondary flex items-center justify-center flex-shrink-0 group-hover:bg-accent/10 transition-colors">
179-
<product.icon className="h-5 w-5 text-primary" />
180-
</div>
181-
<div className="flex-1 min-w-0">
182-
<p className="text-[10px] font-body uppercase tracking-[0.2em] text-muted-foreground">{product.brand}</p>
183-
<p className="text-sm font-heading font-semibold text-foreground truncate">{product.title}</p>
184-
</div>
185-
<div className="text-right flex-shrink-0">
186-
<p className="text-sm font-semibold text-foreground">{Number(product.price).toFixed(2)} <span className="text-xs text-muted-foreground">JOD</span></p>
187-
<span className="text-[10px] font-body uppercase tracking-wider text-accent">{product.step}</span>
188-
</div>
189-
</div>
190-
{/* Gold stitch on hover */}
191-
<div className="absolute inset-0 rounded-xl border border-accent/0 group-hover:border-accent/60 transition-colors duration-300 pointer-events-none" />
192-
</motion.div>
193-
))}
165+
{trayProducts.map((product, i) => {
166+
const Wrapper = product.handle ? Link : "div";
167+
const wrapperProps = product.handle ? { to: `/product/${product.handle}` } : {};
168+
return (
169+
<Wrapper key={product.title} {...(wrapperProps as any)} className="block">
170+
<motion.div
171+
className={cn(
172+
"relative bg-card rounded-xl border border-border p-5 shadow-sm hover:shadow-lg hover:border-accent/40 transition-all duration-300 group cursor-pointer",
173+
i === 0 && "lg:ml-4",
174+
i === 1 && "lg:ml-12",
175+
i === 2 && "lg:ml-6",
176+
)}
177+
initial={{ opacity: 0, y: 20 }}
178+
animate={{ opacity: 1, y: 0 }}
179+
transition={{ duration: 0.6, delay: 0.4 + i * 0.15, ease: [0.19, 1, 0.22, 1] }}
180+
>
181+
<div className="flex items-center gap-4">
182+
<div className="w-12 h-12 rounded-lg bg-secondary flex items-center justify-center flex-shrink-0 group-hover:bg-accent/10 transition-colors">
183+
<product.icon className="h-5 w-5 text-primary" />
184+
</div>
185+
<div className="flex-1 min-w-0">
186+
<p className="text-[10px] font-body uppercase tracking-[0.2em] text-muted-foreground">{product.brand}</p>
187+
<p className="text-sm font-heading font-semibold text-foreground truncate">{product.title}</p>
188+
</div>
189+
<div className="text-right flex-shrink-0">
190+
<p className="text-sm font-semibold text-foreground">{Number(product.price).toFixed(2)} <span className="text-xs text-muted-foreground">JOD</span></p>
191+
<span className="text-[10px] font-body uppercase tracking-wider text-accent">{product.step}</span>
192+
</div>
193+
</div>
194+
{/* Gold stitch on hover */}
195+
<div className="absolute inset-0 rounded-xl border border-accent/0 group-hover:border-accent/60 transition-colors duration-300 pointer-events-none" />
196+
</motion.div>
197+
</Wrapper>
198+
);
199+
})}
194200
</div>
195201

196202
{/* AI Concierge chat bubble */}

0 commit comments

Comments
 (0)