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:
74
apps/web/components/bookings/time-slot.tsx
Normal file
74
apps/web/components/bookings/time-slot.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user