"use client"; import Link from "next/link"; import { useEffect, useMemo, useState } from "react"; import { RequireAuth } from "@/components/RequireAuth"; import { useAuth } from "@/context/AuthContext"; import type { OrderCategory, OrderRecord } from "@/context/AuthContext"; import { guestMe, guestOrders, guestPointsHistory, guestSpaBookings, guestShuttles, type PointLedgerRow, type SpaBookingRow, type ShuttleRow, } from "@/lib/guest-hotel-api"; const orderTabs: { id: OrderCategory | "all"; label: string }[] = [ { id: "all", label: "All" }, { id: "room-service", label: "Room service" }, { id: "laundry", label: "Laundry" }, { id: "gym", label: "Gym" }, { id: "spa", label: "Spa" }, ]; function formatWhen(iso: string) { try { return new Date(iso).toLocaleString(undefined, { dateStyle: "medium", timeStyle: "short", }); } catch { return iso; } } function OrderRow({ o }: { o: OrderRecord }) { return (
  • {o.title}

    {o.detail}

    {formatWhen(o.placedAt)}

    {o.status}

    ${o.totalUsd.toFixed(0)}

  • ); } export function ProfilePageClient() { return ( ); } function ProfileContent() { const { session, logout, accessToken } = useAuth(); const [orderFilter, setOrderFilter] = useState("all"); const [apiBalance, setApiBalance] = useState(null); const [apiLedger, setApiLedger] = useState([]); const [apiOrders, setApiOrders] = useState([]); const [appointments, setAppointments] = useState([]); const [apiShuttles, setApiShuttles] = useState([]); useEffect(() => { if (!accessToken || !session) return; const pid = session.propertyId; if (!pid) return; let cancelled = false; (async () => { try { const me = await guestMe(pid, accessToken); 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); setApiLedger(ph.data ?? []); setApiOrders( (ord.data ?? []).map((o) => ({ id: o.id, category: o.type, title: o.type === "room-service" ? "Room Service Order" : o.type === "laundry" ? "Laundry Request" : o.type === "gym" ? "Gym Booking" : "Spa Booking", detail: o.detail, totalUsd: Number(o.total ?? 0), placedAt: o.createdAt, status: (["pending", "confirmed", "completed"].includes(o.status.toLowerCase()) ? o.status.toLowerCase() : "pending") as OrderRecord["status"], })), ); setAppointments(spa.data ?? []); setApiShuttles(shuttles); } } catch { if (!cancelled) { setApiOrders([]); setAppointments([]); setApiShuttles([]); } } })(); return () => { cancelled = true; }; }, [accessToken, session]); const filteredOrders = useMemo(() => { if (orderFilter === "all") return apiOrders; return apiOrders.filter((o) => o.category === orderFilter); }, [apiOrders, orderFilter]); if (!session) { return null; } return (

    Hello, {session.displayName}

    {session.email} {session.bookingCode ? ( <> {" ยท "} Booking code{" "} {session.bookingCode} ) : null}

    Guest hub

    Rewards points

    <>

    {(apiBalance ?? session.points).toLocaleString()}

    {apiBalance != null ? "Balance" : "Balance unavailable"}

    Booked appointments

    {appointments.length === 0 ? (

    No gym/spa bookings found.

    ) : (
      {appointments.map((a) => (
    • {a.status}

      {a.offering?.name ?? "Spa/Gym booking"}

      {formatWhen(a.scheduledAt ?? a.createdAt)}

    • ))}
    )}
    {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

    Room service, laundry, gym, and spa

    {orderTabs.map((t) => ( ))}
    {filteredOrders.length === 0 ? (

    No orders in this category yet.

    ) : (
      {filteredOrders.map((o) => ( ))}
    )}

    Rewards history

      {apiLedger.length > 0 ? apiLedger.map((r) => (
    • {r.reason.replace(/_/g, " ")}

      {formatWhen(r.createdAt)}

      = 0 ? "badge-mustard" : "rounded-full bg-red-100 px-3 py-1 text-xs font-semibold text-red-800" } > {r.delta >= 0 ? "+" : ""} {r.delta} pts
    • )) : null}
    {apiLedger.length === 0 ? (

    No rewards history returned yet.

    ) : null}
    ); }