From 6632b7e9153ae508a9384f62bbf172c17f75fbb8 Mon Sep 17 00:00:00 2001 From: Vula Builder Date: Tue, 14 Jul 2026 18:35:17 +0000 Subject: [PATCH] Deploy --- src/app/services/page.tsx | 138 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 138 insertions(+) create mode 100644 src/app/services/page.tsx diff --git a/src/app/services/page.tsx b/src/app/services/page.tsx new file mode 100644 index 0000000..53cc268 --- /dev/null +++ b/src/app/services/page.tsx @@ -0,0 +1,138 @@ +'use client' +import { useState, useMemo } from 'react' +import { useMedusaProducts } from '@/hooks/useMedusaProducts' +import Image from 'next/image' +import Link from 'next/link' +import { ArrowLeft, Sparkles } from 'lucide-react' + +const CURRENCY_SYMBOLS: Record = { zar: 'R', usd: '$', eur: '€', gbp: '£', kes: 'KSh', ngn: '₦', ghs: '₵' } +const formatPrice = (amount: number, code?: string) => { + const sym = CURRENCY_SYMBOLS[(code ?? '').toLowerCase()] ?? (code?.toUpperCase() ?? '') + return `${sym}${(amount / 100).toFixed(0)}` +} + +export default function ServicesPage() { + const { products: services, loading, error } = useMedusaProducts(100) + const [activeCategory, setActiveCategory] = useState('All') + + // Derive unique categories from the products themselves — no extra API call needed + const categories = useMemo(() => { + const seen = new Map() + services.forEach(s => { + s.categories?.forEach(c => { if (!seen.has(c.id)) seen.set(c.id, c.name) }) + }) + return Array.from(seen.entries()).map(([id, name]) => ({ id, name })) + }, [services]) + + const filtered = useMemo(() => { + if (activeCategory === 'All') return services + return services.filter(s => s.categories?.some(c => c.name === activeCategory)) + }, [services, activeCategory]) + + if (loading) { + return ( +
+
+
+ ) + } + + if (error) { + return ( +
+

Failed to load services. Please try again.

+
+ ) + } + + return ( +
+ {/* Page header */} +
+
+ + + Back to home + +

Our Services

+ {services.length > 0 && ( +

{services.length} service{services.length !== 1 ? 's' : ''} available

+ )} +
+
+ +
+ {/* Category filter tabs */} + {categories.length > 0 && ( +
+ {['All', ...categories.map(c => c.name)].map(cat => ( + + ))} +
+ )} + + {/* Services grid */} + {filtered.length === 0 ? ( +
+ +

+ {activeCategory !== 'All' ? `No services in ${activeCategory}.` : 'Services coming soon.'} +

+ {activeCategory !== 'All' && ( + + )} +
+ ) : ( +
+ {filtered.map((service) => { + const variant = service.variants?.[0] + const calcPrice = variant?.calculated_price + const rawPrice = variant?.prices?.[0] + const amount = calcPrice?.calculated_amount ?? rawPrice?.amount + const currency = calcPrice?.currency_code ?? rawPrice?.currency_code + return ( +
+ {service.thumbnail && ( +
+ {service.title} +
+ )} +
+ {service.categories?.[0] && ( + {service.categories[0].name} + )} +

{service.title}

+ {service.description && ( +

{service.description}

+ )} +
+ {amount !== undefined ? ( +

{formatPrice(amount, currency)}

+ ) : ( +

Price on request

+ )} + + Book Now + +
+
+
+ ) + })} +
+ )} +
+
+ ) +}