"use client"; import { useMemo, useState } from "react"; import { AtSign, Globe, Mail, Phone, Search, Share2, Star } from "lucide-react"; import { BarberCard } from "@/components/ui/barber-card"; import { Card } from "@/components/ui/card"; import { ServiceCard } from "@/components/ui/service-card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { BookServiceButton } from "@/features/booking/components/book-service-button"; import { cn } from "@/lib/utils/cn"; import type { AvailableService } from "@/services/scheduling/availability.service"; import type { BusinessReviewSummary } from "@/services/reviews/reviews.service"; import type { GalleryImageSummary } from "@/services/gallery/gallery.service"; import type { StaffSummary } from "@/types/domain"; export interface HomeTabsProps { services: AvailableService[]; categories: { id: string; name: string }[]; portfolioImages: GalleryImageSummary[]; reviews: BusinessReviewSummary; 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; } /** * Sticky Services/Portfolio/Reviews/Details tabs below the Home hero — * Phase 9's Home Screen Redesign. All data is pre-fetched server-side by * the Home page and passed in as plain props; tab switching and the * Services search/category filter are pure client-side state over that * already-fetched data, not new network round trips per interaction. */ export function HomeTabs(props: HomeTabsProps) { return ( Services Portfolio Reviews Details ); } function ServicesTab({ services, categories, }: { services: AvailableService[]; categories: { id: string; name: string }[]; }) { const [query, setQuery] = useState(""); const [activeCategoryId, setActiveCategoryId] = useState(null); const filtered = useMemo(() => { const q = query.trim().toLowerCase(); return services.filter((s) => { if (activeCategoryId && s.categoryId !== activeCategoryId) return false; if (q && !s.name.toLowerCase().includes(q)) return false; return true; }); }, [services, query, activeCategoryId]); const grouped = useMemo(() => { const byCategory = new Map(); for (const service of filtered) { const list = byCategory.get(service.categoryId) ?? []; list.push(service); byCategory.set(service.categoryId, list); } return categories .map((c) => ({ ...c, services: byCategory.get(c.id) ?? [] })) .filter((g) => g.services.length > 0); }, [filtered, categories]); const categoriesInUse = useMemo(() => categories.filter((c) => services.some((s) => s.categoryId === c.id)), [categories, services]); return (
{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 */} {image.caption
))}
); } 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 && (