From f3e7896169abd22391e785bd3ce841916600a871 Mon Sep 17 00:00:00 2001
From: brooktewabe
Date: Wed, 15 Apr 2026 14:52:44 +0300
Subject: [PATCH] shuttle and image render updates
---
src/app/profile/ProfilePageClient.tsx | 51 ++++++++++++++++++++++++++-
src/components/RoomSelectBooking.tsx | 6 ++--
src/lib/guest-hotel-api.ts | 23 ++++++++++++
src/types/room.ts | 1 +
4 files changed, 77 insertions(+), 4 deletions(-)
diff --git a/src/app/profile/ProfilePageClient.tsx b/src/app/profile/ProfilePageClient.tsx
index c1a9fc7..7e75302 100644
--- a/src/app/profile/ProfilePageClient.tsx
+++ b/src/app/profile/ProfilePageClient.tsx
@@ -10,8 +10,10 @@ import {
guestOrders,
guestPointsHistory,
guestSpaBookings,
+ guestShuttles,
type PointLedgerRow,
type SpaBookingRow,
+ type ShuttleRow,
} from "@/lib/guest-hotel-api";
const orderTabs: { id: OrderCategory | "all"; label: string }[] = [
@@ -68,6 +70,7 @@ function ProfileContent() {
const [apiLedger, setApiLedger] = useState([]);
const [apiOrders, setApiOrders] = useState([]);
const [appointments, setAppointments] = useState([]);
+ const [apiShuttles, setApiShuttles] = useState([]);
useEffect(() => {
if (!accessToken || !session) return;
@@ -80,6 +83,17 @@ function ProfileContent() {
const ph = await guestPointsHistory(pid, accessToken);
const ord = await guestOrders(pid, accessToken);
const spa = await guestSpaBookings(pid, accessToken);
+ let shuttles: ShuttleRow[] = [];
+ if (session.bookingId) {
+ try {
+ const sh = await guestShuttles(pid, session.bookingId, accessToken);
+ shuttles = (sh.data ?? []).sort(
+ (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime()
+ );
+ } catch {
+ // Ignore if shuttles fail (e.g. no access or 404)
+ }
+ }
if (!cancelled) {
setApiBalance(me.balance);
@@ -105,11 +119,13 @@ function ProfileContent() {
})),
);
setAppointments(spa.data ?? []);
+ setApiShuttles(shuttles);
}
} catch {
if (!cancelled) {
setApiOrders([]);
setAppointments([]);
+ setApiShuttles([]);
}
}
})();
@@ -180,7 +196,7 @@ function ProfileContent() {
{(apiBalance ?? session.points).toLocaleString()}
- {apiBalance != null ? "Live balance" : "Balance unavailable"}
+ {apiBalance != null ? "Balance" : "Balance unavailable"}
>
@@ -214,6 +230,39 @@ function ProfileContent() {
)}
+ {session.bookingId && (
+
+ Airport shuttle
+ {apiShuttles.length === 0 ? (
+
+ No airport shuttles requested.
+
+ ) : (
+
+ {apiShuttles.map((s) => (
+ -
+
+ {s.status}
+
+
+ {s.direction === "AIRPORT_TO_HOTEL" ? "Airport pickup (to hotel)" : s.direction === "HOTEL_TO_AIRPORT" ? "Hotel drop-off (to airport)" : s.direction.replace(/_/g, " ")}
+
+
+ Requested for: {formatWhen(s.requestedAt)}
+
+ {s.flightRef && (
+ Flight: {s.flightRef}
+ )}
+
+ ))}
+
+ )}
+
+ )}
+
Orders
diff --git a/src/components/RoomSelectBooking.tsx b/src/components/RoomSelectBooking.tsx
index 83d8368..bded96f 100644
--- a/src/components/RoomSelectBooking.tsx
+++ b/src/components/RoomSelectBooking.tsx
@@ -41,7 +41,7 @@ export function RoomSelectBooking({ selected, onSelect }: Props) {
<>
>
) : (
- Choose a room category…
+ Choose a room
)}
{open ? "▴" : "▾"}
@@ -80,7 +80,7 @@ export function RoomSelectBooking({ selected, onSelect }: Props) {
>
+new Date(b.createdAt) - +new Date(a.createdAt)) };
}
}
+
+export type ShuttleRow = {
+ id: string;
+ bookingId: string;
+ direction: string;
+ requestedAt: string;
+ flightRef: string | null;
+ notes: string | null;
+ status: string;
+ createdAt: string;
+ updatedAt: string;
+};
+
+export async function guestShuttles(
+ propertyId: string,
+ bookingId: string,
+ accessToken: string,
+): Promise<{ data: ShuttleRow[] }> {
+ return apiFetch(`/properties/${propertyId}/hotel/guest/bookings/${bookingId}/shuttles`, {
+ method: "GET",
+ accessToken,
+ });
+}
diff --git a/src/types/room.ts b/src/types/room.ts
index a6e0f95..f479023 100644
--- a/src/types/room.ts
+++ b/src/types/room.ts
@@ -1,6 +1,7 @@
export type Room = {
id: string;
slug: string;
+ imageKeys?: string[];
name: string;
shortDescription: string;
longDescription: string;