feat: translate bookings page and components to English
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -29,9 +29,9 @@ export default function BookingsPage() {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-primary-800">Reservas</h1>
|
||||
<h1 className="text-2xl font-bold text-primary-800">Bookings</h1>
|
||||
<p className="mt-2 text-primary-600">
|
||||
Gestiona las reservas de canchas. Selecciona un horario para crear o ver una reserva.
|
||||
Manage court bookings. Select a time slot to create or view a booking.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -94,13 +94,13 @@ export function BookingCalendar({ siteId, onSlotClick }: BookingCalendarProps) {
|
||||
const url = siteId ? `/api/courts?siteId=${siteId}` : "/api/courts";
|
||||
const response = await fetch(url);
|
||||
if (!response.ok) {
|
||||
throw new Error("Error al cargar las canchas");
|
||||
throw new Error("Error loading courts");
|
||||
}
|
||||
const data = await response.json();
|
||||
setCourts(data);
|
||||
return data as Court[];
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : "Error desconocido");
|
||||
setError(err instanceof Error ? err.message : "Unknown error");
|
||||
return [];
|
||||
}
|
||||
}, [siteId]);
|
||||
@@ -113,7 +113,7 @@ export function BookingCalendar({ siteId, onSlotClick }: BookingCalendarProps) {
|
||||
`/api/courts/${courtId}/availability?date=${dateStr}`
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Error al cargar disponibilidad`);
|
||||
throw new Error(`Error loading availability`);
|
||||
}
|
||||
return (await response.json()) as CourtAvailability;
|
||||
} catch (err) {
|
||||
@@ -224,7 +224,7 @@ export function BookingCalendar({ siteId, onSlotClick }: BookingCalendarProps) {
|
||||
fetchCourts();
|
||||
}}
|
||||
>
|
||||
Reintentar
|
||||
Retry
|
||||
</Button>
|
||||
</div>
|
||||
</CardContent>
|
||||
@@ -238,7 +238,7 @@ export function BookingCalendar({ siteId, onSlotClick }: BookingCalendarProps) {
|
||||
<Card>
|
||||
<CardHeader className="pb-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle className="text-lg">Calendario</CardTitle>
|
||||
<CardTitle className="text-lg">Calendar</CardTitle>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button variant="outline" size="sm" onClick={goToPrevDay}>
|
||||
<svg
|
||||
@@ -260,7 +260,7 @@ export function BookingCalendar({ siteId, onSlotClick }: BookingCalendarProps) {
|
||||
size="sm"
|
||||
onClick={goToToday}
|
||||
>
|
||||
Hoy
|
||||
Today
|
||||
</Button>
|
||||
<Button variant="outline" size="sm" onClick={goToNextDay}>
|
||||
<svg
|
||||
@@ -286,12 +286,12 @@ export function BookingCalendar({ siteId, onSlotClick }: BookingCalendarProps) {
|
||||
<div className="flex items-center justify-center p-12">
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<div className="h-8 w-8 animate-spin rounded-full border-4 border-primary-200 border-t-primary-600" />
|
||||
<p className="text-sm text-primary-500">Cargando disponibilidad...</p>
|
||||
<p className="text-sm text-primary-500">Loading availability...</p>
|
||||
</div>
|
||||
</div>
|
||||
) : courts.length === 0 ? (
|
||||
<div className="p-6 text-center text-primary-500">
|
||||
<p>No hay canchas disponibles.</p>
|
||||
<p>No courts available.</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="overflow-x-auto">
|
||||
@@ -316,7 +316,7 @@ export function BookingCalendar({ siteId, onSlotClick }: BookingCalendarProps) {
|
||||
{court.name}
|
||||
</h3>
|
||||
<p className="text-xs text-primary-500 mt-1">
|
||||
{court.type === "INDOOR" ? "Interior" : "Exterior"}
|
||||
{court.type === "INDOOR" ? "Indoor" : "Outdoor"}
|
||||
</p>
|
||||
</div>
|
||||
))}
|
||||
@@ -347,7 +347,7 @@ export function BookingCalendar({ siteId, onSlotClick }: BookingCalendarProps) {
|
||||
className="border-r border-primary-200 last:border-r-0 p-2"
|
||||
>
|
||||
<div className="rounded-md border border-primary-200 bg-primary-50 p-3 text-center text-xs text-primary-400">
|
||||
No disponible
|
||||
Not available
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -373,7 +373,7 @@ export function BookingCalendar({ siteId, onSlotClick }: BookingCalendarProps) {
|
||||
|
||||
{timeSlots.length === 0 && (
|
||||
<div className="p-6 text-center text-primary-500">
|
||||
<p>No hay horarios disponibles para este día.</p>
|
||||
<p>No time slots available for this day.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -104,12 +104,12 @@ export function BookingDialog({
|
||||
try {
|
||||
const response = await fetch(`/api/bookings/${slot.bookingId}`);
|
||||
if (!response.ok) {
|
||||
throw new Error("Error al cargar la reserva");
|
||||
throw new Error("Error loading booking");
|
||||
}
|
||||
const data = await response.json();
|
||||
setBooking(data);
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : "Error desconocido");
|
||||
setError(err instanceof Error ? err.message : "Unknown error");
|
||||
} finally {
|
||||
setLoadingBookingInfo(false);
|
||||
}
|
||||
@@ -128,7 +128,7 @@ export function BookingDialog({
|
||||
`/api/clients?search=${encodeURIComponent(search)}&limit=10`
|
||||
);
|
||||
if (!response.ok) {
|
||||
throw new Error("Error al buscar clientes");
|
||||
throw new Error("Error searching players");
|
||||
}
|
||||
const data: ClientsResponse = await response.json();
|
||||
setClients(data.data);
|
||||
@@ -184,13 +184,13 @@ export function BookingDialog({
|
||||
|
||||
if (!response.ok) {
|
||||
const data = await response.json();
|
||||
throw new Error(data.error || "Error al crear la reserva");
|
||||
throw new Error(data.error || "Error creating booking");
|
||||
}
|
||||
|
||||
onBookingCreated?.();
|
||||
onClose();
|
||||
} catch (err) {
|
||||
setError(err instanceof Error ? err.message : "Error al crear la reserva");
|
||||
setError(err instanceof Error ? err.message : "Error creating booking");
|
||||
} finally {
|
||||
setCreatingBooking(false);
|
||||
}
|
||||
@@ -210,20 +210,20 @@ export function BookingDialog({
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify({
|
||||
cancelReason: "Cancelada por el administrador",
|
||||
cancelReason: "Cancelled by administrator",
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
const data = await response.json();
|
||||
throw new Error(data.error || "Error al cancelar la reserva");
|
||||
throw new Error(data.error || "Error cancelling booking");
|
||||
}
|
||||
|
||||
onBookingCancelled?.();
|
||||
onClose();
|
||||
} catch (err) {
|
||||
setError(
|
||||
err instanceof Error ? err.message : "Error al cancelar la reserva"
|
||||
err instanceof Error ? err.message : "Error cancelling booking"
|
||||
);
|
||||
} finally {
|
||||
setCancellingBooking(false);
|
||||
@@ -246,7 +246,7 @@ export function BookingDialog({
|
||||
<CardHeader className="pb-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<CardTitle className="text-lg">
|
||||
{slot.available ? "Nueva Reserva" : "Detalle de Reserva"}
|
||||
{slot.available ? "New Booking" : "Booking Details"}
|
||||
</CardTitle>
|
||||
<button
|
||||
onClick={onClose}
|
||||
@@ -269,16 +269,16 @@ export function BookingDialog({
|
||||
</div>
|
||||
<div className="text-sm text-primary-600 space-y-1 mt-2">
|
||||
<p>
|
||||
<span className="font-medium">Cancha:</span> {slot.courtName}
|
||||
<span className="font-medium">Court:</span> {slot.courtName}
|
||||
</p>
|
||||
<p>
|
||||
<span className="font-medium">Fecha:</span> {formatDate(date)}
|
||||
<span className="font-medium">Date:</span> {formatDate(date)}
|
||||
</p>
|
||||
<p>
|
||||
<span className="font-medium">Hora:</span> {formatTime(slotDate)}
|
||||
<span className="font-medium">Time:</span> {formatTime(slotDate)}
|
||||
</p>
|
||||
<p>
|
||||
<span className="font-medium">Precio:</span>{" "}
|
||||
<span className="font-medium">Price:</span>{" "}
|
||||
{formatCurrency(slot.price)}
|
||||
</p>
|
||||
</div>
|
||||
@@ -296,11 +296,11 @@ export function BookingDialog({
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-primary-700 mb-2">
|
||||
Buscar Cliente
|
||||
Search Player
|
||||
</label>
|
||||
<Input
|
||||
type="text"
|
||||
placeholder="Nombre, email o telefono..."
|
||||
placeholder="Name, email or phone..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
autoFocus
|
||||
@@ -317,7 +317,7 @@ export function BookingDialog({
|
||||
|
||||
{!loadingClients && searchQuery.length >= 2 && clients.length === 0 && (
|
||||
<p className="text-sm text-primary-500 text-center py-4">
|
||||
No se encontraron clientes.
|
||||
No players found.
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -341,7 +341,7 @@ export function BookingDialog({
|
||||
{client.firstName} {client.lastName}
|
||||
</p>
|
||||
<p className="text-xs text-primary-500">
|
||||
{client.email || client.phone || "Sin contacto"}
|
||||
{client.email || client.phone || "No contact"}
|
||||
</p>
|
||||
</div>
|
||||
{client.memberships.length > 0 && (
|
||||
@@ -358,18 +358,18 @@ export function BookingDialog({
|
||||
{selectedClient && (
|
||||
<div className="mt-4 rounded-md border border-accent-200 bg-accent-50 p-3">
|
||||
<p className="text-sm font-medium text-accent-800">
|
||||
Cliente seleccionado:
|
||||
Selected player:
|
||||
</p>
|
||||
<p className="text-sm text-accent-700">
|
||||
{selectedClient.firstName} {selectedClient.lastName}
|
||||
</p>
|
||||
{selectedClient.memberships.length > 0 && (
|
||||
<p className="text-xs text-accent-600 mt-1">
|
||||
Membresia: {selectedClient.memberships[0].plan.name}
|
||||
Membership: {selectedClient.memberships[0].plan.name}
|
||||
{selectedClient.memberships[0].remainingHours !== null &&
|
||||
selectedClient.memberships[0].remainingHours > 0 && (
|
||||
<span className="ml-2">
|
||||
({selectedClient.memberships[0].remainingHours}h restantes)
|
||||
({selectedClient.memberships[0].remainingHours}h remaining)
|
||||
</span>
|
||||
)}
|
||||
</p>
|
||||
@@ -392,7 +392,7 @@ export function BookingDialog({
|
||||
<div className="space-y-4">
|
||||
<div className="rounded-md border border-primary-200 bg-primary-50 p-4 space-y-3">
|
||||
<div>
|
||||
<p className="text-xs text-primary-500">Cliente</p>
|
||||
<p className="text-xs text-primary-500">Player</p>
|
||||
<p className="font-medium text-primary-800">
|
||||
{booking.client.firstName} {booking.client.lastName}
|
||||
</p>
|
||||
@@ -410,7 +410,7 @@ export function BookingDialog({
|
||||
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<p className="text-xs text-primary-500">Estado</p>
|
||||
<p className="text-xs text-primary-500">Status</p>
|
||||
<p
|
||||
className={cn(
|
||||
"text-sm font-medium",
|
||||
@@ -419,21 +419,21 @@ export function BookingDialog({
|
||||
booking.status === "CANCELLED" && "text-red-600"
|
||||
)}
|
||||
>
|
||||
{booking.status === "CONFIRMED" && "Confirmada"}
|
||||
{booking.status === "PENDING" && "Pendiente"}
|
||||
{booking.status === "CANCELLED" && "Cancelada"}
|
||||
{booking.status === "COMPLETED" && "Completada"}
|
||||
{booking.status === "NO_SHOW" && "No asistio"}
|
||||
{booking.status === "CONFIRMED" && "Confirmed"}
|
||||
{booking.status === "PENDING" && "Pending"}
|
||||
{booking.status === "CANCELLED" && "Cancelled"}
|
||||
{booking.status === "COMPLETED" && "Completed"}
|
||||
{booking.status === "NO_SHOW" && "No Show"}
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-xs text-primary-500">Tipo de Pago</p>
|
||||
<p className="text-xs text-primary-500">Payment Type</p>
|
||||
<p className="text-sm text-primary-800">
|
||||
{booking.paymentType === "CASH" && "Efectivo"}
|
||||
{booking.paymentType === "CARD" && "Tarjeta"}
|
||||
{booking.paymentType === "TRANSFER" && "Transferencia"}
|
||||
{booking.paymentType === "MEMBERSHIP" && "Membresia"}
|
||||
{booking.paymentType === "FREE" && "Gratuito"}
|
||||
{booking.paymentType === "CASH" && "Cash"}
|
||||
{booking.paymentType === "CARD" && "Card"}
|
||||
{booking.paymentType === "TRANSFER" && "Transfer"}
|
||||
{booking.paymentType === "MEMBERSHIP" && "Membership"}
|
||||
{booking.paymentType === "FREE" && "Free"}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -450,7 +450,7 @@ export function BookingDialog({
|
||||
|
||||
{!loadingBookingInfo && !booking && (
|
||||
<div className="text-center py-4 text-primary-500">
|
||||
<p>No se pudo cargar la informacion de la reserva.</p>
|
||||
<p>Could not load booking information.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -460,7 +460,7 @@ export function BookingDialog({
|
||||
<CardFooter className="border-t border-primary-200 bg-primary-50 pt-4">
|
||||
<div className="flex w-full gap-3">
|
||||
<Button variant="outline" onClick={onClose} className="flex-1">
|
||||
Cerrar
|
||||
Close
|
||||
</Button>
|
||||
|
||||
{slot.available && (
|
||||
@@ -473,10 +473,10 @@ export function BookingDialog({
|
||||
{creatingBooking ? (
|
||||
<span className="flex items-center gap-2">
|
||||
<div className="h-4 w-4 animate-spin rounded-full border-2 border-white/30 border-t-white" />
|
||||
Creando...
|
||||
Creating...
|
||||
</span>
|
||||
) : (
|
||||
"Crear Reserva"
|
||||
"Create Booking"
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
@@ -491,10 +491,10 @@ export function BookingDialog({
|
||||
{cancellingBooking ? (
|
||||
<span className="flex items-center gap-2">
|
||||
<div className="h-4 w-4 animate-spin rounded-full border-2 border-white/30 border-t-white" />
|
||||
Cancelando...
|
||||
Cancelling...
|
||||
</span>
|
||||
) : (
|
||||
"Cancelar Reserva"
|
||||
"Cancel Booking"
|
||||
)}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user