feat(bookings): add calendar UI with booking creation

- Add TimeSlot component with available/booked visual states
- Add BookingCalendar component with date navigation and court columns
- Add BookingDialog for creating bookings and viewing/cancelling existing ones
- Add bookings page with calendar integration
- Fix sidebar navigation paths for route group structure

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Ivan
2026-02-01 06:50:22 +00:00
parent ef21748191
commit cdf6e8ebe6
6 changed files with 1029 additions and 8 deletions

View File

@@ -0,0 +1,74 @@
"use client";
import { cn, formatTime, formatCurrency } from "@/lib/utils";
interface TimeSlotProps {
time: string; // HH:MM format
available: boolean;
price: number;
clientName?: string;
onClick?: () => void;
}
export function TimeSlot({
time,
available,
price,
clientName,
onClick,
}: TimeSlotProps) {
// Convert HH:MM string to a Date object for formatting
const [hours, minutes] = time.split(":").map(Number);
const timeDate = new Date();
timeDate.setHours(hours, minutes, 0, 0);
return (
<button
type="button"
onClick={onClick}
disabled={!available && !clientName}
className={cn(
"w-full rounded-md border p-3 text-left transition-all",
"focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2",
available && [
"border-accent bg-accent-50 hover:bg-accent-100",
"cursor-pointer",
],
!available && clientName && [
"border-primary-200 bg-primary-100",
"cursor-pointer hover:bg-primary-150",
],
!available && !clientName && [
"border-primary-200 bg-primary-100 opacity-50",
"cursor-not-allowed",
]
)}
>
<div className="flex items-center justify-between">
<span className="text-sm font-medium text-primary-800">
{formatTime(timeDate)}
</span>
<span
className={cn(
"text-xs font-semibold",
available ? "text-accent-700" : "text-primary-500"
)}
>
{formatCurrency(price)}
</span>
</div>
{clientName && (
<div className="mt-1">
<span className="text-xs text-primary-600 truncate block">
{clientName}
</span>
</div>
)}
{available && (
<div className="mt-1">
<span className="text-xs text-accent-600">Disponible</span>
</div>
)}
</button>
);
}