From 3b41b9052b6adb192491e52ae7310480b833b433 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Ckirukib=E2=80=9D?= <“kirubeljkl679@gmail.com”> Date: Mon, 27 Apr 2026 20:08:05 +0300 Subject: [PATCH] feat(guest): refine portal navigation and auth routing Unify guest login/profile paths, add booking-ref CTA entry points, and introduce responsive logged-in navigation with desktop dropdowns and mobile hamburger menu. Made-with: Cursor --- src/app/confirmation/page.tsx | 22 +- src/app/guest/GuestSpaGymBookingClient.tsx | 174 +++++++++ src/app/guest/gym/page.tsx | 16 +- src/app/guest/laundry/LaundryClient.tsx | 4 +- src/app/guest/layout.tsx | 39 +++ src/app/{ => guest}/login/LoginPageClient.tsx | 45 ++- src/app/guest/login/page.tsx | 23 ++ src/app/guest/page.tsx | 4 +- src/app/guest/profile/ProfilePageClient.tsx | 251 +++++++++++++ src/app/guest/profile/page.tsx | 10 + .../guest/room-service/RoomServiceClient.tsx | 4 +- src/app/guest/spa/page.tsx | 15 +- src/app/login/page.tsx | 31 +- src/app/profile/ProfilePageClient.tsx | 247 ------------- src/app/profile/page.tsx | 12 +- src/app/providers.tsx | 6 +- src/app/reserve-held/page.tsx | 7 + src/components/Footer.tsx | 4 +- src/components/Header.tsx | 35 +- src/components/HeaderAccount.tsx | 12 +- src/components/HeaderNav.tsx | 304 ++++++++++++++++ src/components/RequireAuth.tsx | 7 +- src/context/AuthContext.tsx | 330 +----------------- src/context/GuestSessionContext.tsx | 269 ++++++++++++++ src/lib/guest/repository.ts | 77 ++++ src/lib/guest/types.ts | 34 ++ src/lib/mocks/guestBookings.ts | 49 +++ 27 files changed, 1367 insertions(+), 664 deletions(-) create mode 100644 src/app/guest/GuestSpaGymBookingClient.tsx create mode 100644 src/app/guest/layout.tsx rename src/app/{ => guest}/login/LoginPageClient.tsx (85%) create mode 100644 src/app/guest/login/page.tsx create mode 100644 src/app/guest/profile/ProfilePageClient.tsx create mode 100644 src/app/guest/profile/page.tsx delete mode 100644 src/app/profile/ProfilePageClient.tsx create mode 100644 src/components/HeaderNav.tsx create mode 100644 src/context/GuestSessionContext.tsx create mode 100644 src/lib/guest/repository.ts create mode 100644 src/lib/guest/types.ts create mode 100644 src/lib/mocks/guestBookings.ts diff --git a/src/app/confirmation/page.tsx b/src/app/confirmation/page.tsx index 6e33908..d9bd9a8 100644 --- a/src/app/confirmation/page.tsx +++ b/src/app/confirmation/page.tsx @@ -89,13 +89,21 @@ export default function ConfirmationPage() { - resetBooking()} - className="btn-mustard mt-10 inline-flex px-10 py-3.5 text-sm" - > - Back to home - +
+ + Open guest portal + + resetBooking()} + className="btn-mustard inline-flex px-10 py-3.5 text-sm" + > + Back to home + +
); } diff --git a/src/app/guest/GuestSpaGymBookingClient.tsx b/src/app/guest/GuestSpaGymBookingClient.tsx new file mode 100644 index 0000000..f986825 --- /dev/null +++ b/src/app/guest/GuestSpaGymBookingClient.tsx @@ -0,0 +1,174 @@ +"use client"; + +import Image from "next/image"; +import Link from "next/link"; +import { useMemo, useState } from "react"; +import { RequireAuth } from "@/components/RequireAuth"; +import { useAuth } from "@/context/AuthContext"; +import { spaGymServices, type SpaGymKind, type SpaGymService } from "@/lib/mocks/services"; + +const MOCK_SLOTS = ["09:00", "11:00", "14:00", "16:00", "18:00"]; + +type Props = { kind: SpaGymKind; title: string; description: string }; + +export function GuestSpaGymBookingClient({ kind, title, description }: Props) { + return ( + + + + ); +} + +function Inner({ kind, title, description }: Props) { + const { addOrder } = useAuth(); + const [serviceId, setServiceId] = useState(null); + const [slot, setSlot] = useState(MOCK_SLOTS[0]!); + const [note, setNote] = useState(""); + const [done, setDone] = useState(false); + + const items = useMemo(() => spaGymServices.filter((s) => s.kind === kind), [kind]); + const selected = useMemo( + () => items.find((s) => s.id === serviceId) ?? null, + [items, serviceId], + ); + + function book() { + if (!selected) return; + addOrder({ + category: kind === "spa" ? "spa" : "gym", + title: `${kind === "spa" ? "Spa" : "Gym"} · ${selected.title}`, + detail: `${selected.duration} · ${slot}${note ? ` · ${note}` : ""} · $${selected.priceUsd}`, + totalUsd: selected.priceUsd, + status: "confirmed", + }); + setDone(true); + } + + return ( +
+
+ + +
+
+

+ {title} +

+

{description}

+
+ + Full spa & gym menu → + +
+ + {done ? ( +
+ Request recorded (demo). See My stay for orders. +
+ ) : null} + +
+
+ {items.map((s: SpaGymService) => ( + + ))} +
+ +