import { Avatar, AvatarImage } from '@/components/ui/avatar';
import { Button } from '@/components/ui/button';
import { Calendar } from '@/components/ui/calendar';
import { Card, CardContent } from '@/components/ui/card';
import { Label } from '@/components/ui/label';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group';
import { cn } from '@/lib/utils';
import { addDays, format, startOfWeek } from 'date-fns';
import { ChevronDown, ChevronLeft, ChevronRight } from 'lucide-react';
import { useState } from 'react';
export interface Service {
id: string;
name: string;
price: number;
duration: number;
}
export interface Staff {
id: string;
name: string;
title: string;
avatar: string;
}
export interface TimeSlot {
date: string;
slots: string[];
}
const services: Service[] = [
{ id: 'male-cut', name: 'Male Haircut', price: 45, duration: 60 },
{ id: 'female-cut', name: 'Female Haircut', price: 45, duration: 60 },
{ id: 'massage', name: 'Back Massage', price: 700, duration: 60 },
];
const staffList: Staff[] = [
{ id: 'random', name: 'No Preference', title: 'Best Availability', avatar: '' },
{ id: 'alice', name: 'Alice Johnson', title: 'Senior Stylist', avatar: 'https://blookie.io/stock/person1.webp' },
{ id: 'john', name: 'John Smith', title: 'Barber', avatar: 'https://blookie.io/stock/person3.webp' },
];
const today = new Date();
const weekStart = startOfWeek(today, { weekStartsOn: 1 });
const dummySlots: TimeSlot[] = [];
for (let week = 0; week < 2; week++) {
for (let day = 0; day < 7; day++) {
const date = addDays(weekStart, week * 7 + day);
const iso = format(date, 'yyyy-MM-dd');
const allSlots = ['09:00', '10:00', '11:00', '12:00', '13:00'];
const slots = allSlots.filter(() => Math.random() > 0.3);
dummySlots.push({ date: iso, slots });
}
}
function ServiceSidebar({
selectedService,
selectedStaff,
onSelectStaff,
}: {
selectedService: Service;
selectedStaff: string;
onSelectStaff: (id: string) => void;
}) {
return (
<div className="space-y-6">
<div>
<p className="text-base font-medium">{selectedService.name}</p>
<p className="text-muted-foreground mt-2 flex items-center gap-2 text-sm">
<span>{selectedService.duration} min</span>
<span className="text-foreground">${selectedService.price}</span>
<a href="#" className="text-primary ml-auto underline">
Change
</a>
</p>
</div>
<div>
<h3 className="font-medium">Choose Your Provider</h3>
<RadioGroup value={selectedStaff} onValueChange={onSelectStaff} className="mt-4 grid gap-3">
{staffList.map((staff) => {
const isSelected = selectedStaff === staff.id;
return (
<Label
key={staff.id}
htmlFor={staff.id}
className={cn(`relative flex items-center gap-4 rounded-lg border p-4`, isSelected && 'border-primary')}
>
<RadioGroupItem value={staff.id} id={staff.id} className="absolute top-2 right-2 h-4 w-4" />
<Avatar className="bg-muted">
<AvatarImage src={staff.avatar} alt={staff.name} />
</Avatar>
<div className="grid gap-1">
<div className="text-sm/none font-medium">{staff.name}</div>
<div className="text-muted-foreground text-xs">{staff.title}</div>
</div>
</Label>
);
})}
</RadioGroup>
</div>
</div>
);
}
export default function BookingSection() {
const [currentWeekStartDate, setCurrentWeekStartDate] = useState<Date>(weekStart);
const [selectedService] = useState<Service>(services[0]);
const [selectedStaff, setSelectedStaff] = useState<string>(staffList[0].id);
const [popoverOpen, setPopoverOpen] = useState(false);
const days = Array.from({ length: 7 }).map((_, i) => addDays(currentWeekStartDate, i));
function handleDateSelect(date: Date) {
const newStart = startOfWeek(date, { weekStartsOn: 1 });
setCurrentWeekStartDate(newStart);
setPopoverOpen(false);
}
return (
<section className="py-16 lg:py-32">
<div className="mx-auto w-full max-w-2xl px-6 lg:max-w-7xl">
<div className="relative flex flex-col gap-8 lg:flex-row">
<div className="flex flex-1 flex-col">
<div className="flex items-center justify-between">
<Popover open={popoverOpen} onOpenChange={setPopoverOpen}>
<PopoverTrigger asChild>
<Button size="sm" variant="ghost">
{format(days[0], 'MMM d')}–{format(days[6], 'd, yyyy')} <ChevronDown />
</Button>
</PopoverTrigger>
<PopoverContent align="start">
<Calendar
mode="single"
required
selected={currentWeekStartDate}
onSelect={(date) => date && handleDateSelect(date)}
/>
</PopoverContent>
</Popover>
<div className="flex items-center gap-2">
<Button variant="ghost" size="sm" onClick={() => setCurrentWeekStartDate((prev) => addDays(prev, -7))}>
<ChevronLeft />
</Button>
<Button variant="ghost" size="sm" onClick={() => setCurrentWeekStartDate((prev) => addDays(prev, 7))}>
<ChevronRight />
</Button>
<Popover>
<PopoverTrigger asChild>
<Button size="sm" variant="outline" className="lg:hidden">
Details
</Button>
</PopoverTrigger>
<PopoverContent align="end">
<ServiceSidebar
selectedService={selectedService}
selectedStaff={selectedStaff}
onSelectStaff={setSelectedStaff}
/>
</PopoverContent>
</Popover>
</div>
</div>
<div className="mt-4 grid flex-1 grid-cols-7 overflow-hidden rounded-xl border">
{days.map((date) => {
const dayStr = format(date, 'EEE');
const dateIso = format(date, 'yyyy-MM-dd');
const times = dummySlots.find((s) => s.date === dateIso)?.slots || [];
return (
<div key={dateIso} className="bg-muted/50 [&:not(:last-child)]:border-r">
<div className="flex flex-col items-center gap-1 p-4 text-sm">
<div>{dayStr}</div>
<div>{format(date, 'd')}</div>
</div>
<div className="grid gap-2 p-0.5 sm:p-2 [&:not(:last-child)]:border-r">
{times.map((time) => (
<Button key={time} variant="outline" className="px-0 text-xs sm:text-sm">
{time}
</Button>
))}
</div>
</div>
);
})}
</div>
</div>
<Card className="hidden w-84 shadow-none lg:block">
<CardContent>
<ServiceSidebar selectedService={selectedService} selectedStaff={selectedStaff} onSelectStaff={setSelectedStaff} />
</CardContent>
</Card>
</div>
</div>
</section>
);
}