This commit is contained in:
Vula Builder
2026-08-03 11:42:12 +00:00
parent 5de3a6238f
commit 0a87a5db00
@@ -0,0 +1,85 @@
'use client'
import Image from 'next/image'
import Link from 'next/link'
import { ArrowRight, Sparkles } from 'lucide-react'
import { Card, CardContent } from '@/components/ui/Card'
import Badge from '@/components/ui/Badge'
import { useProducts } from '@/hooks/useProducts'
const CURRENCY_SYMBOLS: Record<string, string> = { zar: 'R', usd: '$', eur: '€', gbp: '£', kes: 'KSh', ngn: '₦', ghs: '₵' }
const formatPrice = (amount = 0, code = 'zar') => {
const symbol = CURRENCY_SYMBOLS[(code ?? '').toLowerCase()] ?? (code?.toUpperCase() ?? '')
return `${symbol}${Number(amount || 0).toFixed(0)}`
}
function ServiceCard({ service }: { service: any }) {
const firstVariant = service.variants?.[0]
const price = firstVariant?.price_amount ?? service.price_amount ?? 0
const currency = firstVariant?.currency_code ?? service.currency_code ?? 'zar'
const image = service.thumbnail
return (
<Card data-vula-section="signatureservices" className="flex flex-col overflow-hidden rounded-xl bg-white shadow-md md:flex-row">
<div className="relative h-44 w-full flex-shrink-0 md:min-h-full md:w-1/3">
{image ? (
<Image src={image} alt={service.title} fill sizes="(max-width: 768px) 100vw, 33vw" className="object-cover" />
) : (
<div className="flex h-full w-full items-center justify-center bg-[#E8F4FD]">
<Sparkles className="h-8 w-8 text-[#0077BE]" />
</div>
)}
</div>
<CardContent className="flex flex-1 flex-col justify-between gap-3 p-5">
<div>
<div className="flex items-start justify-between gap-2">
<h3 className="text-lg font-semibold text-[#1A2B3C]">{service.title}</h3>
<Badge variant="secondary" className="whitespace-nowrap bg-[#FFD700]/20 text-[#1A2B3C]">
{price > 0 ? formatPrice(price, currency) : 'On request'}
</Badge>
</div>
<p className="mt-2 line-clamp-2 text-sm text-[#1A2B3C]/70">{service.description ?? ''}</p>
</div>
<div className="flex items-center justify-between">
<span className="text-xs text-[#1A2B3C]/50">{price > 0 ? 'From ' + formatPrice(price, currency) : 'Price on request'}</span>
<Link href={`/booking/${service.id}`} className="inline-flex items-center gap-1 text-sm font-semibold text-[#0077BE] transition-colors duration-200 hover:text-[#00A8CC]">
Book this service <ArrowRight className="h-4 w-4" />
</Link>
</div>
</CardContent>
</Card>
)
}
export default function SignatureServicesSection() {
const { products = [], isLoading } = useProducts(8)
return (
<section id="signature-services" className="bg-[#F0F8FF] py-20 lg:py-24">
<div className="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
<div className="mb-12 max-w-2xl">
<p className="mb-2 text-sm font-semibold uppercase tracking-widest text-[#00A8CC]">Signature services</p>
<h2 className="text-3xl font-bold text-[#1A2B3C] lg:text-4xl">Find your exact service, see the price, book in minutes.</h2>
<p className="mt-4 max-w-[44ch] text-[#1A2B3C]/70">Cuts, colour, keratin, perms, braids and nails each service priced transparently, each bookable in under a minute.</p>
</div>
{isLoading ? (
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
{[1, 2, 3, 4].map((i) => (
<div key={i} className="h-48 animate-pulse rounded-xl bg-white shadow-sm" />
))}
</div>
) : products.length === 0 ? (
<p className="rounded-xl bg-white p-8 text-center text-[#1A2B3C]/60 shadow-sm">Our full service menu is being updated. Please check back soon.</p>
) : (
<div className="grid grid-cols-1 gap-6 md:grid-cols-2">
{products.slice(0, 8).map((service: any) => (
<ServiceCard key={service.id} service={service} />
))}
</div>
)}
</div>
</section>
)
}