Files
app-padel/apps/web/components/dashboard/recent-bookings.tsx
2026-03-01 21:22:53 +00:00

202 lines
6.2 KiB
TypeScript

"use client";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Button } from "@/components/ui/button";
import { cn, formatTime } from "@/lib/utils";
import Link from "next/link";
interface Booking {
id: string;
startTime: string;
endTime: string;
status: string;
court: {
id: string;
name: string;
};
client: {
id: string;
name: string;
} | null;
}
interface RecentBookingsProps {
bookings: Booking[];
isLoading?: boolean;
}
const statusConfig: Record<string, { label: string; className: string }> = {
PENDING: {
label: "Pending",
className: "bg-yellow-100 text-yellow-700",
},
CONFIRMED: {
label: "Confirmed",
className: "bg-blue-100 text-blue-700",
},
COMPLETED: {
label: "Completed",
className: "bg-green-100 text-green-700",
},
CANCELLED: {
label: "Cancelled",
className: "bg-red-100 text-red-700",
},
NO_SHOW: {
label: "No Show",
className: "bg-gray-100 text-gray-700",
},
};
export function RecentBookings({ bookings, isLoading = false }: RecentBookingsProps) {
if (isLoading) {
return <RecentBookingsSkeleton />;
}
return (
<Card>
<CardHeader className="pb-2">
<div className="flex items-center justify-between">
<CardTitle className="text-lg flex items-center gap-2">
<svg
className="w-5 h-5 text-primary-500"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
/>
</svg>
Today's Bookings
</CardTitle>
<Link href="/bookings">
<Button variant="ghost" size="sm" className="text-sm">
View all
<svg
className="w-4 h-4 ml-1"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M9 5l7 7-7 7"
/>
</svg>
</Button>
</Link>
</div>
</CardHeader>
<CardContent>
{bookings.length === 0 ? (
<div className="flex flex-col items-center justify-center py-8 text-primary-500">
<svg
className="w-12 h-12 mb-2 opacity-50"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
/>
</svg>
<p className="text-sm">No bookings for today</p>
</div>
) : (
<div className="space-y-3">
{bookings.map((booking) => {
const status = statusConfig[booking.status] || statusConfig.PENDING;
const startTime = new Date(booking.startTime);
const endTime = new Date(booking.endTime);
return (
<div
key={booking.id}
className="flex items-center gap-4 p-3 rounded-lg bg-primary-50 hover:bg-primary-100 transition-colors"
>
{/* Time */}
<div className="flex-shrink-0 text-center min-w-[70px]">
<p className="text-sm font-semibold text-primary-800">
{formatTime(startTime)}
</p>
<p className="text-xs text-primary-500">
- {formatTime(endTime)}
</p>
</div>
{/* Divider */}
<div className="w-px h-10 bg-primary-200"></div>
{/* Details */}
<div className="flex-1 min-w-0">
<p className="text-sm font-medium text-primary-800 truncate">
{booking.client?.name || "Walk-in"}
</p>
<p className="text-xs text-primary-500 truncate">
{booking.court.name}
</p>
</div>
{/* Status badge */}
<span
className={cn(
"flex-shrink-0 px-2 py-1 text-xs font-medium rounded-full",
status.className
)}
>
{status.label}
</span>
</div>
);
})}
</div>
)}
</CardContent>
</Card>
);
}
// Loading skeleton
export function RecentBookingsSkeleton() {
return (
<Card>
<CardHeader className="pb-2">
<div className="flex items-center justify-between">
<div className="h-6 bg-primary-100 rounded w-36 animate-pulse"></div>
<div className="h-8 bg-primary-100 rounded w-20 animate-pulse"></div>
</div>
</CardHeader>
<CardContent>
<div className="space-y-3">
{[1, 2, 3, 4, 5].map((i) => (
<div
key={i}
className="flex items-center gap-4 p-3 rounded-lg bg-primary-50 animate-pulse"
>
<div className="flex-shrink-0 text-center min-w-[70px]">
<div className="h-4 bg-primary-100 rounded w-16 mb-1"></div>
<div className="h-3 bg-primary-100 rounded w-12 mx-auto"></div>
</div>
<div className="w-px h-10 bg-primary-200"></div>
<div className="flex-1">
<div className="h-4 bg-primary-100 rounded w-32 mb-1"></div>
<div className="h-3 bg-primary-100 rounded w-20"></div>
</div>
<div className="h-6 bg-primary-100 rounded-full w-20"></div>
</div>
))}
</div>
</CardContent>
</Card>
);
}