setQuery(e.target.value)}
placeholder="Search services"
className="border-line focus:ring-ink w-full rounded-xl border bg-white py-2.5 pr-4 pl-11 text-sm outline-none focus:ring-2"
/>
{categoriesInUse.length > 1 && (
{categoriesInUse.map((c) => (
))}
)}
{grouped.length === 0 ? (
No services match.
) : (
grouped.map((group) => (
{group.name}
{group.services.map((service) => (
}
/>
))}
))
)}
);
}
function PortfolioTab({ images }: { images: GalleryImageSummary[] }) {
const categories = useMemo(
() => Array.from(new Set(images.map((i) => i.category).filter((c): c is string => !!c))),
[images],
);
const [activeCategory, setActiveCategory] = useState(null);
const filtered = activeCategory ? images.filter((i) => i.category === activeCategory) : images;
if (images.length === 0) {
return No portfolio images yet.
;
}
return (
{categories.length > 0 && (
{categories.map((c) => (
))}
)}
{filtered.map((image) => (
{/* eslint-disable-next-line @next/next/no-img-element -- local/user-supplied URL, same pattern used app-wide */}
))}
);
}
function ReviewsTab({ reviews }: { reviews: BusinessReviewSummary }) {
if (reviews.reviews.length === 0) {
return No reviews yet.
;
}
return (
{reviews.isDemoData && (
Demo data
)}
{reviews.reviews.map((review) => (
{review.authorName}
{review.relativeDate}
{Array.from({ length: review.rating }, (_, i) => (
))}
{review.text}
))}
);
}
function DetailsTab({
description,
phone,
email,
website,
instagramUrl,
facebookUrl,
hoursRows,
staff,
mapEmbedSrc,
}: {
description: string | null;
phone: string | null;
email: string | null;
website: string | null;
instagramUrl: string | null;
facebookUrl: string | null;
hoursRows: { label: string; hoursLabel: string }[];
staff: (StaffSummary & { isPreferred: boolean })[];
mapEmbedSrc: string | null;
}) {
const hasSocial = website || instagramUrl || facebookUrl;
return (
{mapEmbedSrc && (
)}
{description && (
)}
{staff.length > 0 && (
Staff
{staff.map((s) => (
))}
)}
{hoursRows.length > 0 && (
Opening Hours
{hoursRows.map((row) => (
{row.label}
{row.hoursLabel}
))}
)}
{(hasSocial || phone || email) && (
)}
);
}