Yaltopia-Hotels/src/pages/BookingDetailPage.tsx

149 lines
5.2 KiB
TypeScript

import { useEffect, useState } from "react";
import { Link, useParams } from "react-router-dom";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Textarea } from "@/components/ui/textarea";
import { useAuth } from "@/context/AuthContext";
import { apiGet, apiPatch } from "@/lib/api";
import { isLikelyApiHotelBooking, mapApiBookingToBooking } from "@/lib/hotel-adapters";
import type { Booking } from "@/lib/types";
import { useAuthStore } from "@/store/authStore";
import { formatDate, formatDateTime, formatMoney } from "@/lib/format";
import { roomDisplayName } from "@/lib/room-utils";
export function BookingDetailPage() {
const { id } = useParams<{ id: string }>();
const selectedPropertyId = useAuthStore((s) => s.selectedPropertyId);
const [b, setB] = useState<Booking | null>(null);
const [note, setNote] = useState("");
const { canEditBookings } = useAuth();
useEffect(() => {
if (!id) return;
apiGet<unknown>(`/bookings/${id}`)
.then((raw) => {
if (isLikelyApiHotelBooking(raw)) setB(mapApiBookingToBooking(raw));
else setB(raw as Booking);
})
.catch(console.error);
}, [id, selectedPropertyId]);
if (!b) return <p className="text-muted-foreground">Loading</p>;
async function addNote() {
if (!b || !note.trim() || !canEditBookings) return;
const next = await apiPatch<unknown>(`/bookings/${b.id}`, {
internalNotes: [note.trim()],
});
setB(
isLikelyApiHotelBooking(next)
? mapApiBookingToBooking(next)
: (next as Booking)
);
setNote("");
}
return (
<div className="space-y-6">
<div className="flex flex-wrap items-center justify-between gap-3">
<div>
<Button variant="ghost" size="sm" asChild>
<Link to="/bookings"> Back</Link>
</Button>
<h1 className="mt-2 text-2xl font-bold">
{b.guest.firstName} {b.guest.lastName}
</h1>
<p className="text-muted-foreground text-sm">{b.id}</p>
</div>
<Badge>{b.status}</Badge>
</div>
<div className="grid gap-4 lg:grid-cols-2">
<Card className="rounded-2xl">
<CardHeader>
<CardTitle className="text-base">Guest</CardTitle>
</CardHeader>
<CardContent className="space-y-1 text-sm">
<p>
<span className="text-muted-foreground">Email:</span>{" "}
{b.guest.email}
</p>
<p>
<span className="text-muted-foreground">Phone:</span>{" "}
{b.guest.phone}
</p>
<p>
<span className="text-muted-foreground">PNR:</span>{" "}
{b.guest.flightBookingNumber}
</p>
<p>
<span className="text-muted-foreground">Arrival:</span>{" "}
{b.guest.arrivalTime}
</p>
</CardContent>
</Card>
<Card className="rounded-2xl">
<CardHeader>
<CardTitle className="text-base">Stay</CardTitle>
</CardHeader>
<CardContent className="space-y-1 text-sm">
<p>
{formatDate(b.checkIn)} {formatDate(b.checkOut)} ({b.nights}{" "}
nights)
</p>
<p>Guests: {b.guests}</p>
<p>Room: {b.roomDisplayLabel ?? roomDisplayName(b.roomId)}</p>
{b.holdReference && <p>Hold: {b.holdReference}</p>}
{b.confirmationId && <p>Payment ref: {b.confirmationId}</p>}
{b.paidAt && <p>Paid: {formatDateTime(b.paidAt)}</p>}
</CardContent>
</Card>
<Card className="rounded-2xl lg:col-span-2">
<CardHeader>
<CardTitle className="text-base">Pricing</CardTitle>
</CardHeader>
<CardContent className="grid gap-2 text-sm sm:grid-cols-2">
<p>Nightly subtotal: {formatMoney(b.pricing.nightlySubtotal)}</p>
<p>Coupon: {b.pricing.couponCode ?? "—"}</p>
<p>Discount: {b.pricing.discountPercent}%</p>
<p>Tax: {formatMoney(b.pricing.taxAmount)}</p>
<p className="font-semibold sm:col-span-2">
Total: {formatMoney(b.pricing.total)}
</p>
<p>Referral: {b.referralCode ?? "—"}</p>
</CardContent>
</Card>
<Card className="rounded-2xl lg:col-span-2">
<CardHeader>
<CardTitle className="text-base">Internal notes</CardTitle>
</CardHeader>
<CardContent className="space-y-3">
<ul className="list-inside list-disc text-sm text-muted-foreground">
{(b.internalNotes ?? []).map((n, i) => (
<li key={i}>{n}</li>
))}
</ul>
{canEditBookings && (
<>
<Textarea
placeholder="Add note…"
value={note}
onChange={(e) => setNote(e.target.value)}
/>
<Button size="sm" onClick={addNote}>
Save note
</Button>
</>
)}
</CardContent>
</Card>
</div>
</div>
);
}