11import { useLanguage } from "@/contexts/LanguageContext" ;
22import type { LifecyclePhase } from "@/pages/MomBaby" ;
33import { motion , AnimatePresence } from "framer-motion" ;
4- import { Heart , Sparkles , Baby , ShoppingBag , ArrowRight } from "lucide-react" ;
4+ import { Heart , Sparkles , Baby , ShoppingBag , ArrowRight , Loader2 } from "lucide-react" ;
55import { cn } from "@/lib/utils" ;
6+ import { useQuery } from "@tanstack/react-query" ;
7+ import { fetchProducts , normalizePrice , type ShopifyProduct } from "@/lib/shopify" ;
8+ import { Link } from "react-router-dom" ;
69
7- interface PhaseData {
10+ interface PhaseConfig {
811 id : LifecyclePhase ;
912 en : string ;
1013 ar : string ;
1114 icon : typeof Heart ;
1215 color : string ;
13- categories : { en : string ; ar : string ; count : number } [ ] ;
14- featured : { en : string ; ar : string ; brand : string ; price : string } [ ] ;
16+ /** Shopify search query to fetch products for this phase */
17+ shopifyQuery : string ;
18+ categories : { en : string ; ar : string ; shopifyQuery : string } [ ] ;
1519}
1620
17- const phasesData : PhaseData [ ] = [
21+ const phasesConfig : PhaseConfig [ ] = [
1822 {
1923 id : "before-birth" ,
2024 en : "Before Birth" ,
2125 ar : "قبل الولادة" ,
2226 icon : Heart ,
2327 color : "text-rose-clay" ,
28+ shopifyQuery : "product_type:Stretch Mark OR product_type:Supplements OR tag:pregnancy OR tag:prenatal OR (tag:baby AND tag:skincare)" ,
2429 categories : [
25- { en : "Supplements & Fertility" , ar : "المكملات والخصوبة" , count : 24 } ,
26- { en : "Stretch Mark Prevention" , ar : "الوقاية من التمدد" , count : 18 } ,
27- { en : "Pregnancy-Safe Skincare" , ar : "عناية آمنة للحمل" , count : 32 } ,
28- { en : "Hair & Scalp Care" , ar : "العناية بالشعر" , count : 12 } ,
29- ] ,
30- featured : [
31- { en : "Natalben Supra Pregnancy" , ar : "ناتالبن سوبرا للحمل" , brand : "Natalben" , price : "21.73" } ,
32- { en : "Mustela Stretch Marks Cream" , ar : "كريم علامات التمدد" , brand : "Mustela" , price : "18.50" } ,
33- { en : "A-Derma Exomega Control" , ar : "إكزوميغا كونترول" , brand : "A-Derma" , price : "15.20" } ,
30+ { en : "Stretch Mark Prevention" , ar : "الوقاية من التمدد" , shopifyQuery : "product_type:Stretch Mark" } ,
31+ { en : "Pregnancy-Safe Skincare" , ar : "عناية آمنة للحمل" , shopifyQuery : "tag:pregnancy AND tag:skincare" } ,
32+ { en : "Hair & Scalp Care" , ar : "العناية بالشعر" , shopifyQuery : "tag:pregnancy AND product_type:Shampoo" } ,
33+ { en : "Supplements & Fertility" , ar : "المكملات والخصوبة" , shopifyQuery : "product_type:Supplements OR tag:prenatal" } ,
3434 ] ,
3535 } ,
3636 {
@@ -39,16 +39,12 @@ const phasesData: PhaseData[] = [
3939 ar : "بعد الولادة" ,
4040 icon : Sparkles ,
4141 color : "text-accent" ,
42+ shopifyQuery : "product_type:Breast Pump OR tag:breastfeeding OR tag:nursing OR product_type:Nursing" ,
4243 categories : [
43- { en : "Breast Pumps & Accessories" , ar : "مضخات الثدي والإكسسوارات" , count : 28 } ,
44- { en : "Milk Storage" , ar : "تخزين الحليب" , count : 15 } ,
45- { en : "Nipple Care" , ar : "العناية بالحلمات" , count : 9 } ,
46- { en : "Body Recovery" , ar : "استعادة الجسم" , count : 22 } ,
47- ] ,
48- featured : [
49- { en : "Medela Swing Maxi Double" , ar : "مضخة مديلا سوينج ماكسي" , brand : "Medela" , price : "189.00" } ,
50- { en : "Barral MotherProtect Oil" , ar : "زيت باريل للأم" , brand : "Barral" , price : "12.40" } ,
51- { en : "Lierac Body Sculpt Gel" , ar : "جل شد الجسم ليراك" , brand : "Lierac" , price : "28.90" } ,
44+ { en : "Breast Pumps & Accessories" , ar : "مضخات الثدي والإكسسوارات" , shopifyQuery : "product_type:Breast Pump" } ,
45+ { en : "Nursing Accessories" , ar : "ملحقات الرضاعة" , shopifyQuery : "tag:nursing" } ,
46+ { en : "Nipple Care" , ar : "العناية بالحلمات" , shopifyQuery : "tag:nipple" } ,
47+ { en : "Body Recovery" , ar : "استعادة الجسم" , shopifyQuery : "tag:postpartum" } ,
5248 ] ,
5349 } ,
5450 {
@@ -57,16 +53,12 @@ const phasesData: PhaseData[] = [
5753 ar : "السنوات الأولى" ,
5854 icon : Baby ,
5955 color : "text-primary" ,
56+ shopifyQuery : "product_type:Baby Powder OR product_type:Baby Oil OR product_type:Baby Shampoo OR product_type:Baby Cream OR product_type:Baby Lotion OR product_type:Baby Wash OR product_type:Baby Towel OR product_type:Baby Clothes" ,
6057 categories : [
61- { en : "Feeding & Accessories" , ar : "الإطعام والإكسسوارات" , count : 190 } ,
62- { en : "Diaper Changing" , ar : "تغيير الحفاض" , count : 59 } ,
63- { en : "Bath & Hygiene" , ar : "الاستحمام والنظافة" , count : 34 } ,
64- { en : "Oral Health" , ar : "الصحة الفموية" , count : 6 } ,
65- ] ,
66- featured : [
67- { en : "Mustela Stelatopia+ Cream" , ar : "كريم ستيلاتوبيا+" , brand : "Mustela" , price : "22.80" } ,
68- { en : "Bioderma ABCDerm Gel" , ar : "جل أبيسيدرم بيوديرما" , brand : "Bioderma" , price : "14.50" } ,
69- { en : "Isdin Nutratopic Pro-AMP" , ar : "نيوتراتوبيك برو" , brand : "Isdin" , price : "19.30" } ,
58+ { en : "Bath & Hygiene" , ar : "الاستحمام والنظافة" , shopifyQuery : "product_type:Baby Shampoo OR product_type:Baby Wash OR product_type:Baby Towel" } ,
59+ { en : "Skin Care" , ar : "العناية بالبشرة" , shopifyQuery : "product_type:Baby Cream OR product_type:Baby Lotion OR product_type:Baby Oil" } ,
60+ { en : "Diaper Changing" , ar : "تغيير الحفاض" , shopifyQuery : "product_type:Baby Powder" } ,
61+ { en : "Clothing" , ar : "الملابس" , shopifyQuery : "product_type:Baby Clothes" } ,
7062 ] ,
7163 } ,
7264 {
@@ -75,30 +67,127 @@ const phasesData: PhaseData[] = [
7567 ar : "أساسيات الأمومة" ,
7668 icon : ShoppingBag ,
7769 color : "text-burgundy" ,
70+ shopifyQuery : "product_type:Baby Carrier OR product_type:Baby Stroller OR product_type:Baby Gift Set OR product_type:Baby Bag OR tag:maternity" ,
7871 categories : [
79- { en : "Hospital Bags & Baskets" , ar : "حقائب المستشفى" , count : 12 } ,
80- { en : "Thermometers & Monitors" , ar : "موازين الحرارة والمراقبة" , count : 13 } ,
81- { en : "Gift Sets & Bundles" , ar : "أطقم الهدايا" , count : 18 } ,
82- { en : "Nebulizers & Respiratory" , ar : "أجهزة الاستنشاق" , count : 5 } ,
83- ] ,
84- featured : [
85- { en : "Mustela Essential Basket" , ar : "سلة مستيلا الأساسية" , brand : "Mustela" , price : "45.00" } ,
86- { en : "Owlet Dream Sock Monitor" , ar : "جوارب أولت الذكية" , brand : "Owlet" , price : "299.00" } ,
87- { en : "Braun No Touch Thermometer" , ar : "ميزان حرارة براون" , brand : "Braun" , price : "52.00" } ,
72+ { en : "Carriers & Strollers" , ar : "الحاملات والعربات" , shopifyQuery : "product_type:Baby Carrier OR product_type:Baby Stroller" } ,
73+ { en : "Gift Sets & Bundles" , ar : "أطقم الهدايا" , shopifyQuery : "product_type:Baby Gift Set" } ,
74+ { en : "Bags & Travel" , ar : "حقائب السفر" , shopifyQuery : "product_type:Baby Bag" } ,
75+ { en : "Thermometers & Monitors" , ar : "موازين الحرارة والمراقبة" , shopifyQuery : "tag:thermometer OR tag:monitor" } ,
8876 ] ,
8977 } ,
9078] ;
9179
80+ function usePhaseProducts ( phase : PhaseConfig , enabled : boolean ) {
81+ return useQuery ( {
82+ queryKey : [ "mom-baby-phase" , phase . id ] ,
83+ queryFn : ( ) => fetchProducts ( 6 , phase . shopifyQuery ) ,
84+ enabled,
85+ staleTime : 5 * 60 * 1000 ,
86+ } ) ;
87+ }
88+
9289interface Props {
9390 activePhase : LifecyclePhase ;
9491 activeConcern : string | null ;
9592}
9693
94+ function PhaseSection ( { phase, isAr } : { phase : PhaseConfig ; isAr : boolean } ) {
95+ const { data, isLoading } = usePhaseProducts ( phase , true ) ;
96+ const products = data ?. products || [ ] ;
97+
98+ return (
99+ < div >
100+ { /* Phase header */ }
101+ < div className = "flex items-center gap-3 mb-6" >
102+ < phase . icon className = { cn ( "w-6 h-6" , phase . color ) } />
103+ < h2 className = "font-heading text-2xl md:text-3xl text-foreground" >
104+ { isAr ? phase . ar : phase . en }
105+ </ h2 >
106+ </ div >
107+
108+ { /* Categories grid */ }
109+ < div className = "grid grid-cols-2 md:grid-cols-4 gap-3 mb-8" >
110+ { phase . categories . map ( ( cat ) => (
111+ < Link
112+ key = { cat . en }
113+ to = { `/products?q=${ encodeURIComponent ( cat . shopifyQuery ) } ` }
114+ className = "group rounded-xl border border-border bg-card p-4 text-start hover:border-accent/50 hover:shadow-warm transition-all duration-300"
115+ >
116+ < span className = "block text-sm font-body font-medium text-foreground group-hover:text-primary transition-colors" >
117+ { isAr ? cat . ar : cat . en }
118+ </ span >
119+ </ Link >
120+ ) ) }
121+ </ div >
122+
123+ { /* Featured products — real Shopify data */ }
124+ { isLoading ? (
125+ < div className = "flex items-center justify-center py-12" >
126+ < Loader2 className = "w-6 h-6 animate-spin text-muted-foreground" />
127+ </ div >
128+ ) : products . length === 0 ? (
129+ < p className = "text-sm text-muted-foreground text-center py-8" >
130+ { isAr ? "لا توجد منتجات حالياً" : "No products available yet" }
131+ </ p >
132+ ) : (
133+ < div className = "grid grid-cols-1 sm:grid-cols-3 gap-4" >
134+ { products . slice ( 0 , 3 ) . map ( ( p : ShopifyProduct ) => {
135+ const product = p . node ;
136+ const imageUrl = product . images . edges [ 0 ] ?. node . url ;
137+ const price = normalizePrice ( product . priceRange . minVariantPrice . amount ) ;
138+ const currency = product . priceRange . minVariantPrice . currencyCode ;
139+
140+ return (
141+ < Link
142+ key = { product . id }
143+ to = { `/product/${ product . handle } ` }
144+ className = "product-card-hover group rounded-xl border border-border bg-card p-5 cursor-pointer"
145+ >
146+ { /* Product image */ }
147+ < div className = "w-full aspect-square rounded-lg bg-muted/50 mb-4 overflow-hidden flex items-center justify-center" >
148+ { imageUrl ? (
149+ < img
150+ src = { imageUrl }
151+ alt = { product . title }
152+ className = "w-full h-full object-contain"
153+ loading = "lazy"
154+ />
155+ ) : (
156+ < phase . icon className = "w-10 h-10 text-muted-foreground/30" />
157+ ) }
158+ </ div >
159+ < p className = "text-[10px] font-body uppercase tracking-widest text-accent mb-1" >
160+ { product . vendor }
161+ </ p >
162+ < h3 className = "text-sm font-body font-medium text-foreground mb-2 line-clamp-2" >
163+ { product . title }
164+ </ h3 >
165+ < div className = "flex items-center justify-between" >
166+ < span className = "text-base font-heading font-bold text-primary" >
167+ { price . toFixed ( 2 ) } { currency === "JOD" ? "JD" : currency }
168+ </ span >
169+ < span className = "inline-flex items-center gap-1 text-xs text-accent opacity-0 group-hover:opacity-100 transition-opacity" >
170+ { isAr ? "عرض" : "View" }
171+ < ArrowRight className = "w-3 h-3" />
172+ </ span >
173+ </ div >
174+ </ Link >
175+ ) ;
176+ } ) }
177+ </ div >
178+ ) }
179+ </ div >
180+ ) ;
181+ }
182+
97183export default function LifecycleSection ( { activePhase, activeConcern } : Props ) {
98184 const { locale } = useLanguage ( ) ;
99185 const isAr = locale === "ar" ;
100186
101- const visible = activePhase === "all" ? phasesData : phasesData . filter ( ( p ) => p . id === activePhase ) ;
187+ const visible =
188+ activePhase === "all"
189+ ? phasesConfig
190+ : phasesConfig . filter ( ( p ) => p . id === activePhase ) ;
102191
103192 return (
104193 < section className = "py-12" >
@@ -113,62 +202,7 @@ export default function LifecycleSection({ activePhase, activeConcern }: Props)
113202 className = "space-y-16"
114203 >
115204 { visible . map ( ( phase ) => (
116- < div key = { phase . id } >
117- { /* Phase header */ }
118- < div className = "flex items-center gap-3 mb-6" >
119- < phase . icon className = { cn ( "w-6 h-6" , phase . color ) } />
120- < h2 className = "font-heading text-2xl md:text-3xl text-foreground" >
121- { isAr ? phase . ar : phase . en }
122- </ h2 >
123- </ div >
124-
125- { /* Categories grid */ }
126- < div className = "grid grid-cols-2 md:grid-cols-4 gap-3 mb-8" >
127- { phase . categories . map ( ( cat ) => (
128- < button
129- key = { cat . en }
130- className = "group rounded-xl border border-border bg-card p-4 text-start hover:border-accent/50 hover:shadow-warm transition-all duration-300"
131- >
132- < span className = "block text-sm font-body font-medium text-foreground group-hover:text-primary transition-colors" >
133- { isAr ? cat . ar : cat . en }
134- </ span >
135- < span className = "text-xs text-muted-foreground mt-1 block" >
136- { cat . count } { isAr ? "منتج" : "products" }
137- </ span >
138- </ button >
139- ) ) }
140- </ div >
141-
142- { /* Featured products */ }
143- < div className = "grid grid-cols-1 sm:grid-cols-3 gap-4" >
144- { phase . featured . map ( ( product ) => (
145- < div
146- key = { product . en }
147- className = "product-card-hover group rounded-xl border border-border bg-card p-5 cursor-pointer"
148- >
149- { /* Placeholder image area */ }
150- < div className = "w-full aspect-square rounded-lg bg-muted/50 mb-4 flex items-center justify-center" >
151- < phase . icon className = "w-10 h-10 text-muted-foreground/30" />
152- </ div >
153- < p className = "text-[10px] font-body uppercase tracking-widest text-accent mb-1" >
154- { product . brand }
155- </ p >
156- < h3 className = "text-sm font-body font-medium text-foreground mb-2 line-clamp-2" >
157- { isAr ? product . ar : product . en }
158- </ h3 >
159- < div className = "flex items-center justify-between" >
160- < span className = "text-base font-heading font-bold text-primary" >
161- ${ product . price }
162- </ span >
163- < span className = "inline-flex items-center gap-1 text-xs text-accent opacity-0 group-hover:opacity-100 transition-opacity" >
164- { isAr ? "عرض" : "View" }
165- < ArrowRight className = "w-3 h-3" />
166- </ span >
167- </ div >
168- </ div >
169- ) ) }
170- </ div >
171- </ div >
205+ < PhaseSection key = { phase . id } phase = { phase } isAr = { isAr } />
172206 ) ) }
173207 </ motion . div >
174208 </ AnimatePresence >
0 commit comments