@@ -25,9 +25,9 @@ const stepLabel = (step: string) => {
2525} ;
2626
2727const 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
3333export 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