@burdenoff/microfe-ggnomad 2026.731.1 → 2026.825.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/ggnomad/GGNomadRoutes.js +1 -1
  2. package/dist/ggnomad/GGNomadRoutes.js.map +1 -1
  3. package/dist/ggnomad/components/BookingPanel.js +1 -1
  4. package/dist/ggnomad/components/BookingPanel.js.map +1 -1
  5. package/dist/ggnomad/components/EntityCard.js +1 -1
  6. package/dist/ggnomad/components/EntityCard.js.map +1 -1
  7. package/dist/ggnomad/pages/ActivityDetailsPage.js +1 -1
  8. package/dist/ggnomad/pages/ActivityDetailsPage.js.map +1 -1
  9. package/dist/ggnomad/pages/BookingsPage.js +1 -1
  10. package/dist/ggnomad/pages/BookingsPage.js.map +1 -1
  11. package/dist/ggnomad/pages/DashboardPage.js +1 -1
  12. package/dist/ggnomad/pages/DashboardPage.js.map +1 -1
  13. package/dist/ggnomad/pages/EventDetailsPage.js +1 -1
  14. package/dist/ggnomad/pages/EventDetailsPage.js.map +1 -1
  15. package/dist/ggnomad/pages/PropertyDetailsPage.js +1 -1
  16. package/dist/ggnomad/pages/PropertyDetailsPage.js.map +1 -1
  17. package/dist/ggnomad/pages/ProviderPage.js +1 -1
  18. package/dist/ggnomad/pages/ProviderPage.js.map +1 -1
  19. package/dist/ggnomad/pages/StorefrontPage.js +1 -1
  20. package/dist/ggnomad/pages/StorefrontPage.js.map +1 -1
  21. package/dist/ggnomad/pages/StorefrontPublicPage.js +1 -1
  22. package/dist/ggnomad/pages/StorefrontPublicPage.js.map +1 -1
  23. package/dist/ggnomad/pages/TourDetailsPage.js +1 -1
  24. package/dist/ggnomad/pages/TourDetailsPage.js.map +1 -1
  25. package/dist/ggnomad/pages/TripPlanDetailPage.js +1 -1
  26. package/dist/ggnomad/pages/TripPlanDetailPage.js.map +1 -1
  27. package/dist/ggnomad/pages/TripsPage.js +1 -1
  28. package/dist/ggnomad/pages/TripsPage.js.map +1 -1
  29. package/dist/ggnomad/providers/GGNomadProvider.js +1 -1
  30. package/dist/ggnomad/providers/GGNomadProvider.js.map +1 -1
  31. package/package.json +4 -4
@@ -1,7 +1,7 @@
1
1
  import { DashboardPage as e } from "./pages/DashboardPage.js";
2
2
  import { Suspense as t, lazy as n } from "react";
3
3
  import { Loader2 as r } from "lucide-react";
4
- import { Navigate as i, Route as a, Routes as o } from "react-router-dom";
4
+ import { Navigate as i, Route as a, Routes as o } from "react-router";
5
5
  import { jsx as s, jsxs as c } from "react/jsx-runtime";
6
6
  //#region src/ggnomad/GGNomadRoutes.tsx
7
7
  var l = n(() => import("./pages/SearchPage.js").then((e) => ({ default: e.SearchPage }))), u = n(() => import("./pages/TravelPage.js").then((e) => ({ default: e.TravelPage }))), d = n(() => import("./pages/HospitalityPage.js").then((e) => ({ default: e.HospitalityPage }))), f = n(() => import("./pages/PropertyDetailsPage.js").then((e) => ({ default: e.PropertyDetailsPage }))), p = n(() => import("./pages/TourismPage.js").then((e) => ({ default: e.TourismPage }))), m = n(() => import("./pages/TourDetailsPage.js").then((e) => ({ default: e.TourDetailsPage }))), h = n(() => import("./pages/ActivityDetailsPage.js").then((e) => ({ default: e.ActivityDetailsPage }))), g = n(() => import("./pages/EntertainmentPage.js").then((e) => ({ default: e.EntertainmentPage }))), _ = n(() => import("./pages/EventDetailsPage.js").then((e) => ({ default: e.EventDetailsPage }))), v = n(() => import("./pages/BookingsPage.js").then((e) => ({ default: e.BookingsPage }))), y = n(() => import("./pages/FavoritesPage.js").then((e) => ({ default: e.FavoritesPage }))), b = n(() => import("./pages/ProviderPage.js").then((e) => ({ default: e.ProviderPage }))), x = n(() => import("./pages/AnalyticsPage.js").then((e) => ({ default: e.AnalyticsPage }))), S = n(() => import("./pages/ProviderAnalyticsPage.js").then((e) => ({ default: e.ProviderAnalyticsPage }))), C = n(() => import("./pages/TripsPage.js").then((e) => ({ default: e.TripsPage }))), w = n(() => import("./pages/TripPlanDetailPage.js").then((e) => ({ default: e.TripPlanDetailPage }))), T = n(() => import("./pages/TravelPolicyPage.js").then((e) => ({ default: e.TravelPolicyPage }))), E = n(() => import("./pages/OperationsQueuePage.js").then((e) => ({ default: e.OperationsQueuePage }))), D = n(() => import("./pages/StorefrontPage.js").then((e) => ({ default: e.StorefrontPage }))), O = n(() => import("./pages/StorefrontPublicPage.js").then((e) => ({ default: e.StorefrontPublicPage })));
@@ -1 +1 @@
1
- {"version":3,"file":"GGNomadRoutes.js","names":[],"sources":["../../src/ggnomad/GGNomadRoutes.tsx"],"sourcesContent":["import { lazy, Suspense } from 'react';\nimport { Loader2 } from 'lucide-react';\nimport { Navigate, Route, Routes } from 'react-router-dom';\n\nimport { DashboardPage } from './pages/DashboardPage';\n\nconst SearchPage = lazy(() =>\n import('./pages/SearchPage').then((m) => ({ default: m.SearchPage }))\n);\nconst TravelPage = lazy(() =>\n import('./pages/TravelPage').then((m) => ({ default: m.TravelPage }))\n);\nconst HospitalityPage = lazy(() =>\n import('./pages/HospitalityPage').then((m) => ({ default: m.HospitalityPage }))\n);\nconst PropertyDetailsPage = lazy(() =>\n import('./pages/PropertyDetailsPage').then((m) => ({ default: m.PropertyDetailsPage }))\n);\nconst TourismPage = lazy(() =>\n import('./pages/TourismPage').then((m) => ({ default: m.TourismPage }))\n);\nconst TourDetailsPage = lazy(() =>\n import('./pages/TourDetailsPage').then((m) => ({ default: m.TourDetailsPage }))\n);\nconst ActivityDetailsPage = lazy(() =>\n import('./pages/ActivityDetailsPage').then((m) => ({ default: m.ActivityDetailsPage }))\n);\nconst EntertainmentPage = lazy(() =>\n import('./pages/EntertainmentPage').then((m) => ({ default: m.EntertainmentPage }))\n);\nconst EventDetailsPage = lazy(() =>\n import('./pages/EventDetailsPage').then((m) => ({ default: m.EventDetailsPage }))\n);\nconst BookingsPage = lazy(() =>\n import('./pages/BookingsPage').then((m) => ({ default: m.BookingsPage }))\n);\nconst FavoritesPage = lazy(() =>\n import('./pages/FavoritesPage').then((m) => ({ default: m.FavoritesPage }))\n);\nconst ProviderPage = lazy(() =>\n import('./pages/ProviderPage').then((m) => ({ default: m.ProviderPage }))\n);\nconst AnalyticsPage = lazy(() =>\n import('./pages/AnalyticsPage').then((m) => ({ default: m.AnalyticsPage }))\n);\nconst ProviderAnalyticsPage = lazy(() =>\n import('./pages/ProviderAnalyticsPage').then((m) => ({ default: m.ProviderAnalyticsPage }))\n);\nconst TripsPage = lazy(() => import('./pages/TripsPage').then((m) => ({ default: m.TripsPage })));\nconst TripPlanDetailPage = lazy(() =>\n import('./pages/TripPlanDetailPage').then((m) => ({ default: m.TripPlanDetailPage }))\n);\nconst TravelPolicyPage = lazy(() =>\n import('./pages/TravelPolicyPage').then((m) => ({ default: m.TravelPolicyPage }))\n);\nconst OperationsQueuePage = lazy(() =>\n import('./pages/OperationsQueuePage').then((m) => ({ default: m.OperationsQueuePage }))\n);\nconst StorefrontPage = lazy(() =>\n import('./pages/StorefrontPage').then((m) => ({ default: m.StorefrontPage }))\n);\nconst StorefrontPublicPage = lazy(() =>\n import('./pages/StorefrontPublicPage').then((m) => ({ default: m.StorefrontPublicPage }))\n);\n\nfunction PageLoader() {\n return (\n <div className=\"flex min-h-[320px] items-center justify-center\">\n <Loader2 className=\"h-8 w-8 animate-spin text-primary\" />\n </div>\n );\n}\n\nexport function GGNomadRoutes() {\n return (\n <Suspense fallback={<PageLoader />}>\n <Routes>\n <Route index element={<DashboardPage />} />\n <Route path=\"search\" element={<SearchPage />} />\n <Route path=\"travel\" element={<TravelPage />} />\n <Route path=\"hospitality\" element={<HospitalityPage />} />\n <Route path=\"hospitality/:id\" element={<PropertyDetailsPage />} />\n <Route path=\"tourism\" element={<TourismPage />} />\n <Route path=\"tourism/tours/:id\" element={<TourDetailsPage />} />\n <Route path=\"tourism/activities/:id\" element={<ActivityDetailsPage />} />\n <Route path=\"entertainment\" element={<EntertainmentPage />} />\n <Route path=\"entertainment/:id\" element={<EventDetailsPage />} />\n <Route path=\"bookings\" element={<BookingsPage />} />\n <Route path=\"travel-policy\" element={<TravelPolicyPage />} />\n <Route path=\"operations\" element={<OperationsQueuePage />} />\n <Route path=\"favorites\" element={<FavoritesPage />} />\n <Route path=\"provider\" element={<ProviderPage />} />\n <Route path=\"provider/analytics\" element={<ProviderAnalyticsPage />} />\n <Route path=\"provider/:providerId/storefront\" element={<StorefrontPage />} />\n <Route path=\"store/:slug\" element={<StorefrontPublicPage />} />\n <Route path=\"analytics\" element={<AnalyticsPage />} />\n <Route path=\"trips\" element={<TripsPage />} />\n <Route path=\"trips/:id\" element={<TripPlanDetailPage />} />\n <Route path=\"*\" element={<Navigate to=\"\" replace />} />\n </Routes>\n </Suspense>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"GGNomadRoutes.js","names":[],"sources":["../../src/ggnomad/GGNomadRoutes.tsx"],"sourcesContent":["import { lazy, Suspense } from 'react';\nimport { Loader2 } from 'lucide-react';\nimport { Navigate, Route, Routes } from 'react-router';\n\nimport { DashboardPage } from './pages/DashboardPage';\n\nconst SearchPage = lazy(() =>\n import('./pages/SearchPage').then((m) => ({ default: m.SearchPage }))\n);\nconst TravelPage = lazy(() =>\n import('./pages/TravelPage').then((m) => ({ default: m.TravelPage }))\n);\nconst HospitalityPage = lazy(() =>\n import('./pages/HospitalityPage').then((m) => ({ default: m.HospitalityPage }))\n);\nconst PropertyDetailsPage = lazy(() =>\n import('./pages/PropertyDetailsPage').then((m) => ({ default: m.PropertyDetailsPage }))\n);\nconst TourismPage = lazy(() =>\n import('./pages/TourismPage').then((m) => ({ default: m.TourismPage }))\n);\nconst TourDetailsPage = lazy(() =>\n import('./pages/TourDetailsPage').then((m) => ({ default: m.TourDetailsPage }))\n);\nconst ActivityDetailsPage = lazy(() =>\n import('./pages/ActivityDetailsPage').then((m) => ({ default: m.ActivityDetailsPage }))\n);\nconst EntertainmentPage = lazy(() =>\n import('./pages/EntertainmentPage').then((m) => ({ default: m.EntertainmentPage }))\n);\nconst EventDetailsPage = lazy(() =>\n import('./pages/EventDetailsPage').then((m) => ({ default: m.EventDetailsPage }))\n);\nconst BookingsPage = lazy(() =>\n import('./pages/BookingsPage').then((m) => ({ default: m.BookingsPage }))\n);\nconst FavoritesPage = lazy(() =>\n import('./pages/FavoritesPage').then((m) => ({ default: m.FavoritesPage }))\n);\nconst ProviderPage = lazy(() =>\n import('./pages/ProviderPage').then((m) => ({ default: m.ProviderPage }))\n);\nconst AnalyticsPage = lazy(() =>\n import('./pages/AnalyticsPage').then((m) => ({ default: m.AnalyticsPage }))\n);\nconst ProviderAnalyticsPage = lazy(() =>\n import('./pages/ProviderAnalyticsPage').then((m) => ({ default: m.ProviderAnalyticsPage }))\n);\nconst TripsPage = lazy(() => import('./pages/TripsPage').then((m) => ({ default: m.TripsPage })));\nconst TripPlanDetailPage = lazy(() =>\n import('./pages/TripPlanDetailPage').then((m) => ({ default: m.TripPlanDetailPage }))\n);\nconst TravelPolicyPage = lazy(() =>\n import('./pages/TravelPolicyPage').then((m) => ({ default: m.TravelPolicyPage }))\n);\nconst OperationsQueuePage = lazy(() =>\n import('./pages/OperationsQueuePage').then((m) => ({ default: m.OperationsQueuePage }))\n);\nconst StorefrontPage = lazy(() =>\n import('./pages/StorefrontPage').then((m) => ({ default: m.StorefrontPage }))\n);\nconst StorefrontPublicPage = lazy(() =>\n import('./pages/StorefrontPublicPage').then((m) => ({ default: m.StorefrontPublicPage }))\n);\n\nfunction PageLoader() {\n return (\n <div className=\"flex min-h-[320px] items-center justify-center\">\n <Loader2 className=\"h-8 w-8 animate-spin text-primary\" />\n </div>\n );\n}\n\nexport function GGNomadRoutes() {\n return (\n <Suspense fallback={<PageLoader />}>\n <Routes>\n <Route index element={<DashboardPage />} />\n <Route path=\"search\" element={<SearchPage />} />\n <Route path=\"travel\" element={<TravelPage />} />\n <Route path=\"hospitality\" element={<HospitalityPage />} />\n <Route path=\"hospitality/:id\" element={<PropertyDetailsPage />} />\n <Route path=\"tourism\" element={<TourismPage />} />\n <Route path=\"tourism/tours/:id\" element={<TourDetailsPage />} />\n <Route path=\"tourism/activities/:id\" element={<ActivityDetailsPage />} />\n <Route path=\"entertainment\" element={<EntertainmentPage />} />\n <Route path=\"entertainment/:id\" element={<EventDetailsPage />} />\n <Route path=\"bookings\" element={<BookingsPage />} />\n <Route path=\"travel-policy\" element={<TravelPolicyPage />} />\n <Route path=\"operations\" element={<OperationsQueuePage />} />\n <Route path=\"favorites\" element={<FavoritesPage />} />\n <Route path=\"provider\" element={<ProviderPage />} />\n <Route path=\"provider/analytics\" element={<ProviderAnalyticsPage />} />\n <Route path=\"provider/:providerId/storefront\" element={<StorefrontPage />} />\n <Route path=\"store/:slug\" element={<StorefrontPublicPage />} />\n <Route path=\"analytics\" element={<AnalyticsPage />} />\n <Route path=\"trips\" element={<TripsPage />} />\n <Route path=\"trips/:id\" element={<TripPlanDetailPage />} />\n <Route path=\"*\" element={<Navigate to=\"\" replace />} />\n </Routes>\n </Suspense>\n );\n}\n"],"mappings":"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"}
@@ -6,7 +6,7 @@ import { notify as i } from "../utils/notify.js";
6
6
  import { useState as a } from "react";
7
7
  import { Minus as o, Plus as s } from "lucide-react";
8
8
  import { useI18n as c } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
9
- import { useNavigate as l } from "react-router-dom";
9
+ import { useNavigate as l } from "react-router";
10
10
  import { useMutation as u } from "@apollo/client/react";
11
11
  import { Button as d, GlassCard as f, Input as p } from "@burdenoff/fe-libs/ui";
12
12
  import { Fragment as m, jsx as h, jsxs as g } from "react/jsx-runtime";
@@ -1 +1 @@
1
- {"version":3,"file":"BookingPanel.js","names":[],"sources":["../../../src/ggnomad/components/BookingPanel.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useMutation } from '@apollo/client/react';\nimport type { TypedDocumentNode } from '@apollo/client';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useNavigate } from 'react-router-dom';\nimport { Minus, Plus } from 'lucide-react';\nimport { Button, GlassCard, Input } from '@burdenoff/fe-libs/ui';\n\nimport { CREATE_GGNOMAD_BOOKING_MUTATION } from '../graphql';\nimport { useGGNomad } from '../providers/GGNomadProvider';\nimport { joinPath } from '../utils/navigation';\nimport { notify } from '../utils/notify';\nimport { cn } from '../utils/cn';\n\ntype BookingType = 'PROPERTY' | 'TOUR' | 'EVENT' | 'ACTIVITY';\n\ntype BookingPanelProps = {\n bookingType: BookingType;\n itemId: string;\n itemName: string;\n unitPrice: number;\n currency: string;\n /** For PROPERTY bookings, the number of nights to calculate price */\n nights?: number;\n /** For EVENT bookings, the event date (ISO string) */\n eventDate?: string;\n};\n\n/** Frosted glass booking panel with type-specific inputs and real createGgnomadBooking mutation. */\nexport function BookingPanel({\n bookingType,\n itemId,\n itemName,\n unitPrice,\n currency,\n nights,\n eventDate,\n}: BookingPanelProps) {\n const { t } = useI18n();\n const navigate = useNavigate();\n const { basePath = '/' } = useGGNomad();\n\n // For properties: check-in and check-out dates\n // For events/tours/activities: a single date\n const [selectedDate, setSelectedDate] = useState<string>('');\n const [checkInDate, setCheckInDate] = useState<string>('');\n const [checkOutDate, setCheckOutDate] = useState<string>('');\n\n // Quantity: guests (property), participants (tour/activity), tickets (event)\n const [quantity, setQuantity] = useState(1);\n\n type BookingInput = {\n bookingType: BookingType;\n itemId: string;\n itemName: string;\n guestName: string;\n guestEmail: string;\n guests: number;\n totalPrice: number;\n currency: string;\n paymentMethod: string;\n checkIn?: string;\n checkOut?: string;\n eventDate?: string;\n };\n\n const [createBooking, { loading: creating }] = useMutation(\n CREATE_GGNOMAD_BOOKING_MUTATION as TypedDocumentNode<\n { createGgnomadBooking: { id: string; referenceCode: string; itemName: string } },\n { input: BookingInput }\n >\n );\n\n // Calculate total: for properties, multiply by nights; for others, multiply by quantity\n const multiplier = bookingType === 'PROPERTY' && nights ? nights : quantity;\n const subtotal = unitPrice * multiplier;\n const taxAmount = subtotal * 0.1; // 10% tax\n const total = subtotal + taxAmount;\n\n const handleBooking = async () => {\n // Validate required fields\n if (bookingType === 'PROPERTY') {\n if (!checkInDate || !checkOutDate) {\n notify.warning(\n t('ggnomad.booking.validation.missingDates', 'Missing dates'),\n t(\n 'ggnomad.booking.validation.propertyDatesRequired',\n 'Please select both check-in and check-out dates.'\n )\n );\n return;\n }\n } else if (bookingType === 'EVENT') {\n if (!eventDate) {\n notify.warning(\n t('ggnomad.booking.validation.missingDate', 'Missing date'),\n t('ggnomad.booking.validation.eventDateRequired', 'Event date is required for booking.')\n );\n return;\n }\n } else {\n if (!selectedDate) {\n notify.warning(\n t('ggnomad.booking.validation.missingDate', 'Missing date'),\n t('ggnomad.booking.validation.dateRequired', 'Please select a date for your booking.')\n );\n return;\n }\n }\n\n try {\n const baseInput = {\n bookingType,\n itemId,\n itemName,\n guestName: t('ggnomad.booking.defaultGuestName', 'Demo Guest'),\n guestEmail: 'demo@burdenoff.com',\n guests: quantity,\n totalPrice: Math.round(total * 100) / 100,\n currency,\n paymentMethod: 'card',\n };\n\n const input: BookingInput =\n bookingType === 'PROPERTY'\n ? { ...baseInput, checkIn: checkInDate, checkOut: checkOutDate }\n : bookingType === 'EVENT'\n ? { ...baseInput, eventDate: eventDate }\n : { ...baseInput, eventDate: selectedDate };\n\n const result = await createBooking({\n variables: { input },\n });\n\n if (result.error) {\n notify.error(\n t('ggnomad.booking.toast.createErrorTitle', 'Could not create booking'),\n result.error.message\n );\n return;\n }\n\n const created = result.data?.createGgnomadBooking;\n notify.success(\n t('ggnomad.booking.toast.createSuccessTitle', 'Booking confirmed'),\n created\n ? t('ggnomad.booking.toast.createSuccessBody', '{{ref}} · {{item}}')\n .replace('{{ref}}', created.referenceCode)\n .replace('{{item}}', created.itemName)\n : itemName\n );\n\n // Navigate to bookings list\n navigate(joinPath(basePath, 'bookings'));\n } catch (err) {\n notify.error(\n t('ggnomad.booking.toast.createErrorTitle', 'Could not create booking'),\n err instanceof Error ? err.message : String(err)\n );\n }\n };\n\n const labelClass = 'text-sm font-medium text-text-primary';\n const inputClass = 'w-full';\n\n return (\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam p-5 sticky top-6\">\n <h2 className=\"mb-4 text-lg font-medium tracking-tight text-text-primary\">\n {bookingType === 'PROPERTY'\n ? t('ggnomad.booking.title.property', 'Book this stay')\n : bookingType === 'EVENT'\n ? t('ggnomad.booking.title.event', 'Book tickets')\n : bookingType === 'TOUR'\n ? t('ggnomad.booking.title.tour', 'Book this tour')\n : t('ggnomad.booking.title.activity', 'Book this activity')}\n </h2>\n\n <div className=\"space-y-4 mb-6\">\n {/* Dates section */}\n {bookingType === 'PROPERTY' ? (\n <>\n <div>\n <label htmlFor=\"check-in\" className={labelClass}>\n {t('ggnomad.booking.checkIn', 'Check-in')}\n </label>\n <Input\n id=\"check-in\"\n type=\"date\"\n value={checkInDate}\n onChange={(e) => setCheckInDate(e.target.value)}\n className={cn(inputClass, 'mt-2')}\n aria-label={t('ggnomad.booking.checkIn', 'Check-in')}\n />\n </div>\n <div>\n <label htmlFor=\"check-out\" className={labelClass}>\n {t('ggnomad.booking.checkOut', 'Check-out')}\n </label>\n <Input\n id=\"check-out\"\n type=\"date\"\n value={checkOutDate}\n onChange={(e) => setCheckOutDate(e.target.value)}\n className={cn(inputClass, 'mt-2')}\n aria-label={t('ggnomad.booking.checkOut', 'Check-out')}\n />\n </div>\n </>\n ) : bookingType === 'EVENT' ? (\n <div>\n <label htmlFor=\"event-date\" className={labelClass}>\n {t('ggnomad.booking.date', 'Date')}\n </label>\n <Input\n id=\"event-date\"\n type=\"date\"\n value={eventDate ? eventDate.split('T')[0] : ''}\n disabled\n className={cn(inputClass, 'mt-2')}\n aria-label={t('ggnomad.booking.date', 'Date')}\n />\n </div>\n ) : (\n <div>\n <label htmlFor=\"booking-date\" className={labelClass}>\n {t('ggnomad.booking.date', 'Date')}\n </label>\n <Input\n id=\"booking-date\"\n type=\"date\"\n value={selectedDate}\n onChange={(e) => setSelectedDate(e.target.value)}\n className={cn(inputClass, 'mt-2')}\n aria-label={t('ggnomad.booking.date', 'Date')}\n />\n </div>\n )}\n\n {/* Quantity section */}\n <div>\n <label className={labelClass}>\n {bookingType === 'PROPERTY'\n ? t('ggnomad.booking.guests', 'Guests')\n : bookingType === 'EVENT'\n ? t('ggnomad.booking.tickets', 'Tickets')\n : t('ggnomad.booking.participants', 'Participants')}\n </label>\n <div className=\"flex items-center gap-2 mt-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setQuantity(Math.max(1, quantity - 1))}\n disabled={quantity <= 1 || creating}\n aria-label={t('ggnomad.booking.decreaseQuantity', 'Decrease quantity')}\n className=\"flex-shrink-0\"\n >\n <Minus className=\"size-4\" />\n </Button>\n <input\n type=\"number\"\n min={1}\n value={quantity}\n onChange={(e) => setQuantity(Math.max(1, parseInt(e.target.value) || 1))}\n className=\"flex-1 h-9 rounded-input border border-border-default bg-bg-surface px-3 py-1 text-center text-sm text-text-primary\"\n aria-label={\n bookingType === 'PROPERTY'\n ? t('ggnomad.booking.guests', 'Guests')\n : bookingType === 'EVENT'\n ? t('ggnomad.booking.tickets', 'Tickets')\n : t('ggnomad.booking.participants', 'Participants')\n }\n />\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setQuantity(quantity + 1)}\n disabled={creating}\n aria-label={t('ggnomad.booking.increaseQuantity', 'Increase quantity')}\n className=\"flex-shrink-0\"\n >\n <Plus className=\"size-4\" />\n </Button>\n </div>\n </div>\n </div>\n\n {/* Pricing breakdown */}\n <div className=\"space-y-2 mb-6 p-3 rounded-md bg-bg-sunken\">\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-secondary\">\n {currency} {unitPrice.toFixed(2)} ×{' '}\n {bookingType === 'PROPERTY'\n ? `${nights ?? 1} ${t('ggnomad.booking.nights', 'nights')}`\n : `${quantity} ${t('ggnomad.booking.item', 'item')}`}\n </span>\n <span className=\"text-text-primary font-medium\">\n {currency} {subtotal.toFixed(2)}\n </span>\n </div>\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-secondary\">{t('ggnomad.booking.tax', 'Tax (10%)')}</span>\n <span className=\"text-text-primary\">\n {currency} {taxAmount.toFixed(2)}\n </span>\n </div>\n <div className=\"border-t border-border-seam pt-2 flex justify-between\">\n <span className=\"font-semibold text-text-primary\">\n {t('ggnomad.booking.total', 'Total')}\n </span>\n <span className=\"font-bold text-lg text-text-primary\">\n {currency} {total.toFixed(2)}\n </span>\n </div>\n </div>\n\n {/* Book button */}\n <Button\n onClick={() => void handleBooking()}\n disabled={creating}\n className=\"w-full bg-gradient-to-r from-action-primary-bg to-action-success-bg text-white font-semibold\"\n size=\"lg\"\n >\n {creating\n ? t('ggnomad.booking.button.creating', 'Creating booking…')\n : bookingType === 'PROPERTY'\n ? t('ggnomad.booking.button.property', 'Book this stay')\n : bookingType === 'EVENT'\n ? t('ggnomad.booking.button.event', 'Book tickets')\n : bookingType === 'TOUR'\n ? t('ggnomad.booking.button.tour', 'Book this tour')\n : t('ggnomad.booking.button.activity', 'Book this activity')}\n </Button>\n </GlassCard>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"BookingPanel.js","names":[],"sources":["../../../src/ggnomad/components/BookingPanel.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useMutation } from '@apollo/client/react';\nimport type { TypedDocumentNode } from '@apollo/client';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useNavigate } from 'react-router';\nimport { Minus, Plus } from 'lucide-react';\nimport { Button, GlassCard, Input } from '@burdenoff/fe-libs/ui';\n\nimport { CREATE_GGNOMAD_BOOKING_MUTATION } from '../graphql';\nimport { useGGNomad } from '../providers/GGNomadProvider';\nimport { joinPath } from '../utils/navigation';\nimport { notify } from '../utils/notify';\nimport { cn } from '../utils/cn';\n\ntype BookingType = 'PROPERTY' | 'TOUR' | 'EVENT' | 'ACTIVITY';\n\ntype BookingPanelProps = {\n bookingType: BookingType;\n itemId: string;\n itemName: string;\n unitPrice: number;\n currency: string;\n /** For PROPERTY bookings, the number of nights to calculate price */\n nights?: number;\n /** For EVENT bookings, the event date (ISO string) */\n eventDate?: string;\n};\n\n/** Frosted glass booking panel with type-specific inputs and real createGgnomadBooking mutation. */\nexport function BookingPanel({\n bookingType,\n itemId,\n itemName,\n unitPrice,\n currency,\n nights,\n eventDate,\n}: BookingPanelProps) {\n const { t } = useI18n();\n const navigate = useNavigate();\n const { basePath = '/' } = useGGNomad();\n\n // For properties: check-in and check-out dates\n // For events/tours/activities: a single date\n const [selectedDate, setSelectedDate] = useState<string>('');\n const [checkInDate, setCheckInDate] = useState<string>('');\n const [checkOutDate, setCheckOutDate] = useState<string>('');\n\n // Quantity: guests (property), participants (tour/activity), tickets (event)\n const [quantity, setQuantity] = useState(1);\n\n type BookingInput = {\n bookingType: BookingType;\n itemId: string;\n itemName: string;\n guestName: string;\n guestEmail: string;\n guests: number;\n totalPrice: number;\n currency: string;\n paymentMethod: string;\n checkIn?: string;\n checkOut?: string;\n eventDate?: string;\n };\n\n const [createBooking, { loading: creating }] = useMutation(\n CREATE_GGNOMAD_BOOKING_MUTATION as TypedDocumentNode<\n { createGgnomadBooking: { id: string; referenceCode: string; itemName: string } },\n { input: BookingInput }\n >\n );\n\n // Calculate total: for properties, multiply by nights; for others, multiply by quantity\n const multiplier = bookingType === 'PROPERTY' && nights ? nights : quantity;\n const subtotal = unitPrice * multiplier;\n const taxAmount = subtotal * 0.1; // 10% tax\n const total = subtotal + taxAmount;\n\n const handleBooking = async () => {\n // Validate required fields\n if (bookingType === 'PROPERTY') {\n if (!checkInDate || !checkOutDate) {\n notify.warning(\n t('ggnomad.booking.validation.missingDates', 'Missing dates'),\n t(\n 'ggnomad.booking.validation.propertyDatesRequired',\n 'Please select both check-in and check-out dates.'\n )\n );\n return;\n }\n } else if (bookingType === 'EVENT') {\n if (!eventDate) {\n notify.warning(\n t('ggnomad.booking.validation.missingDate', 'Missing date'),\n t('ggnomad.booking.validation.eventDateRequired', 'Event date is required for booking.')\n );\n return;\n }\n } else {\n if (!selectedDate) {\n notify.warning(\n t('ggnomad.booking.validation.missingDate', 'Missing date'),\n t('ggnomad.booking.validation.dateRequired', 'Please select a date for your booking.')\n );\n return;\n }\n }\n\n try {\n const baseInput = {\n bookingType,\n itemId,\n itemName,\n guestName: t('ggnomad.booking.defaultGuestName', 'Demo Guest'),\n guestEmail: 'demo@burdenoff.com',\n guests: quantity,\n totalPrice: Math.round(total * 100) / 100,\n currency,\n paymentMethod: 'card',\n };\n\n const input: BookingInput =\n bookingType === 'PROPERTY'\n ? { ...baseInput, checkIn: checkInDate, checkOut: checkOutDate }\n : bookingType === 'EVENT'\n ? { ...baseInput, eventDate: eventDate }\n : { ...baseInput, eventDate: selectedDate };\n\n const result = await createBooking({\n variables: { input },\n });\n\n if (result.error) {\n notify.error(\n t('ggnomad.booking.toast.createErrorTitle', 'Could not create booking'),\n result.error.message\n );\n return;\n }\n\n const created = result.data?.createGgnomadBooking;\n notify.success(\n t('ggnomad.booking.toast.createSuccessTitle', 'Booking confirmed'),\n created\n ? t('ggnomad.booking.toast.createSuccessBody', '{{ref}} · {{item}}')\n .replace('{{ref}}', created.referenceCode)\n .replace('{{item}}', created.itemName)\n : itemName\n );\n\n // Navigate to bookings list\n navigate(joinPath(basePath, 'bookings'));\n } catch (err) {\n notify.error(\n t('ggnomad.booking.toast.createErrorTitle', 'Could not create booking'),\n err instanceof Error ? err.message : String(err)\n );\n }\n };\n\n const labelClass = 'text-sm font-medium text-text-primary';\n const inputClass = 'w-full';\n\n return (\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam p-5 sticky top-6\">\n <h2 className=\"mb-4 text-lg font-medium tracking-tight text-text-primary\">\n {bookingType === 'PROPERTY'\n ? t('ggnomad.booking.title.property', 'Book this stay')\n : bookingType === 'EVENT'\n ? t('ggnomad.booking.title.event', 'Book tickets')\n : bookingType === 'TOUR'\n ? t('ggnomad.booking.title.tour', 'Book this tour')\n : t('ggnomad.booking.title.activity', 'Book this activity')}\n </h2>\n\n <div className=\"space-y-4 mb-6\">\n {/* Dates section */}\n {bookingType === 'PROPERTY' ? (\n <>\n <div>\n <label htmlFor=\"check-in\" className={labelClass}>\n {t('ggnomad.booking.checkIn', 'Check-in')}\n </label>\n <Input\n id=\"check-in\"\n type=\"date\"\n value={checkInDate}\n onChange={(e) => setCheckInDate(e.target.value)}\n className={cn(inputClass, 'mt-2')}\n aria-label={t('ggnomad.booking.checkIn', 'Check-in')}\n />\n </div>\n <div>\n <label htmlFor=\"check-out\" className={labelClass}>\n {t('ggnomad.booking.checkOut', 'Check-out')}\n </label>\n <Input\n id=\"check-out\"\n type=\"date\"\n value={checkOutDate}\n onChange={(e) => setCheckOutDate(e.target.value)}\n className={cn(inputClass, 'mt-2')}\n aria-label={t('ggnomad.booking.checkOut', 'Check-out')}\n />\n </div>\n </>\n ) : bookingType === 'EVENT' ? (\n <div>\n <label htmlFor=\"event-date\" className={labelClass}>\n {t('ggnomad.booking.date', 'Date')}\n </label>\n <Input\n id=\"event-date\"\n type=\"date\"\n value={eventDate ? eventDate.split('T')[0] : ''}\n disabled\n className={cn(inputClass, 'mt-2')}\n aria-label={t('ggnomad.booking.date', 'Date')}\n />\n </div>\n ) : (\n <div>\n <label htmlFor=\"booking-date\" className={labelClass}>\n {t('ggnomad.booking.date', 'Date')}\n </label>\n <Input\n id=\"booking-date\"\n type=\"date\"\n value={selectedDate}\n onChange={(e) => setSelectedDate(e.target.value)}\n className={cn(inputClass, 'mt-2')}\n aria-label={t('ggnomad.booking.date', 'Date')}\n />\n </div>\n )}\n\n {/* Quantity section */}\n <div>\n <label className={labelClass}>\n {bookingType === 'PROPERTY'\n ? t('ggnomad.booking.guests', 'Guests')\n : bookingType === 'EVENT'\n ? t('ggnomad.booking.tickets', 'Tickets')\n : t('ggnomad.booking.participants', 'Participants')}\n </label>\n <div className=\"flex items-center gap-2 mt-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setQuantity(Math.max(1, quantity - 1))}\n disabled={quantity <= 1 || creating}\n aria-label={t('ggnomad.booking.decreaseQuantity', 'Decrease quantity')}\n className=\"flex-shrink-0\"\n >\n <Minus className=\"size-4\" />\n </Button>\n <input\n type=\"number\"\n min={1}\n value={quantity}\n onChange={(e) => setQuantity(Math.max(1, parseInt(e.target.value) || 1))}\n className=\"flex-1 h-9 rounded-input border border-border-default bg-bg-surface px-3 py-1 text-center text-sm text-text-primary\"\n aria-label={\n bookingType === 'PROPERTY'\n ? t('ggnomad.booking.guests', 'Guests')\n : bookingType === 'EVENT'\n ? t('ggnomad.booking.tickets', 'Tickets')\n : t('ggnomad.booking.participants', 'Participants')\n }\n />\n <Button\n variant=\"outline\"\n size=\"sm\"\n onClick={() => setQuantity(quantity + 1)}\n disabled={creating}\n aria-label={t('ggnomad.booking.increaseQuantity', 'Increase quantity')}\n className=\"flex-shrink-0\"\n >\n <Plus className=\"size-4\" />\n </Button>\n </div>\n </div>\n </div>\n\n {/* Pricing breakdown */}\n <div className=\"space-y-2 mb-6 p-3 rounded-md bg-bg-sunken\">\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-secondary\">\n {currency} {unitPrice.toFixed(2)} ×{' '}\n {bookingType === 'PROPERTY'\n ? `${nights ?? 1} ${t('ggnomad.booking.nights', 'nights')}`\n : `${quantity} ${t('ggnomad.booking.item', 'item')}`}\n </span>\n <span className=\"text-text-primary font-medium\">\n {currency} {subtotal.toFixed(2)}\n </span>\n </div>\n <div className=\"flex justify-between text-sm\">\n <span className=\"text-text-secondary\">{t('ggnomad.booking.tax', 'Tax (10%)')}</span>\n <span className=\"text-text-primary\">\n {currency} {taxAmount.toFixed(2)}\n </span>\n </div>\n <div className=\"border-t border-border-seam pt-2 flex justify-between\">\n <span className=\"font-semibold text-text-primary\">\n {t('ggnomad.booking.total', 'Total')}\n </span>\n <span className=\"font-bold text-lg text-text-primary\">\n {currency} {total.toFixed(2)}\n </span>\n </div>\n </div>\n\n {/* Book button */}\n <Button\n onClick={() => void handleBooking()}\n disabled={creating}\n className=\"w-full bg-gradient-to-r from-action-primary-bg to-action-success-bg text-white font-semibold\"\n size=\"lg\"\n >\n {creating\n ? t('ggnomad.booking.button.creating', 'Creating booking…')\n : bookingType === 'PROPERTY'\n ? t('ggnomad.booking.button.property', 'Book this stay')\n : bookingType === 'EVENT'\n ? t('ggnomad.booking.button.event', 'Book tickets')\n : bookingType === 'TOUR'\n ? t('ggnomad.booking.button.tour', 'Book this tour')\n : t('ggnomad.booking.button.activity', 'Book this activity')}\n </Button>\n </GlassCard>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AA6BA,SAAgB,EAAa,EAC3B,gBACA,WACA,aACA,cACA,aACA,WACA,gBACoB;CACpB,IAAM,EAAE,SAAM,EAAQ,GAChB,IAAW,EAAY,GACvB,EAAE,cAAW,QAAQ,EAAW,GAIhC,CAAC,GAAc,KAAmB,EAAiB,EAAE,GACrD,CAAC,GAAa,KAAkB,EAAiB,EAAE,GACnD,CAAC,GAAc,KAAmB,EAAiB,EAAE,GAGrD,CAAC,GAAU,KAAe,EAAS,CAAC,GAiBpC,CAAC,GAAe,EAAE,SAAS,OAAc,EAC7C,CAIF,GAIM,IAAW,KADE,MAAgB,cAAc,IAAS,IAAS,IAE7D,IAAY,IAAW,IACvB,IAAQ,IAAW,GAEnB,IAAgB,YAAY;EAEhC,IAAI,MAAgB;OACd,CAAC,KAAe,CAAC,GAAc;IACjC,EAAO,QACL,EAAE,2CAA2C,eAAe,GAC5D,EACE,oDACA,kDACF,CACF;IACA;GACF;SACK,IAAI,MAAgB;OACrB,CAAC,GAAW;IACd,EAAO,QACL,EAAE,0CAA0C,cAAc,GAC1D,EAAE,gDAAgD,qCAAqC,CACzF;IACA;GACF;SAEA,IAAI,CAAC,GAAc;GACjB,EAAO,QACL,EAAE,0CAA0C,cAAc,GAC1D,EAAE,2CAA2C,wCAAwC,CACvF;GACA;EACF;EAGF,IAAI;GACF,IAAM,IAAY;IAChB;IACA;IACA;IACA,WAAW,EAAE,oCAAoC,YAAY;IAC7D,YAAY;IACZ,QAAQ;IACR,YAAY,KAAK,MAAM,IAAQ,GAAG,IAAI;IACtC;IACA,eAAe;GACjB,GASM,IAAS,MAAM,EAAc,EACjC,WAAW,EAAE,OAPb,MAAgB,aACZ;IAAE,GAAG;IAAW,SAAS;IAAa,UAAU;GAAa,IAC7D,MAAgB,UACd;IAAE,GAAG;IAAsB;GAAU,IACrC;IAAE,GAAG;IAAW,WAAW;GAAa,EAG3B,EACrB,CAAC;GAED,IAAI,EAAO,OAAO;IAChB,EAAO,MACL,EAAE,0CAA0C,0BAA0B,GACtE,EAAO,MAAM,OACf;IACA;GACF;GAEA,IAAM,IAAU,EAAO,MAAM;GAW7B,AAVA,EAAO,QACL,EAAE,4CAA4C,mBAAmB,GACjE,IACI,EAAE,2CAA2C,oBAAoB,EAC9D,QAAQ,WAAW,EAAQ,aAAa,EACxC,QAAQ,YAAY,EAAQ,QAAQ,IACvC,CACN,GAGA,EAAS,EAAS,GAAU,UAAU,CAAC;EACzC,SAAS,GAAK;GACZ,EAAO,MACL,EAAE,0CAA0C,0BAA0B,GACtE,aAAe,QAAQ,EAAI,UAAU,OAAO,CAAG,CACjD;EACF;CACF,GAEM,IAAa,yCACb,IAAa;CAEnB,OACE,kBAAC,GAAD;EAAW,WAAU;EAAQ,MAAA;EAAK,WAAU;YAA5C;GACE,kBAAC,MAAD;IAAI,WAAU;cACX,MAAgB,aACb,EAAE,kCAAkC,gBAAgB,IACpD,MAAgB,UACd,EAAE,+BAA+B,cAAc,IAC/C,MAAgB,SACd,EAAE,8BAA8B,gBAAgB,IAChD,EAAE,kCAAkC,oBAAoB;GAC9D,CAAA;GAEJ,kBAAC,OAAD;IAAK,WAAU;cAAf,CAEG,MAAgB,aACf,kBAAA,GAAA,EAAA,UAAA,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;KAAO,SAAQ;KAAW,WAAW;eAClC,EAAE,2BAA2B,UAAU;IACnC,CAAA,GACP,kBAAC,GAAD;KACE,IAAG;KACH,MAAK;KACL,OAAO;KACP,WAAW,MAAM,EAAe,EAAE,OAAO,KAAK;KAC9C,WAAW,EAAG,GAAY,MAAM;KAChC,cAAY,EAAE,2BAA2B,UAAU;IACpD,CAAA,CACE,EAAA,CAAA,GACL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;KAAO,SAAQ;KAAY,WAAW;eACnC,EAAE,4BAA4B,WAAW;IACrC,CAAA,GACP,kBAAC,GAAD;KACE,IAAG;KACH,MAAK;KACL,OAAO;KACP,WAAW,MAAM,EAAgB,EAAE,OAAO,KAAK;KAC/C,WAAW,EAAG,GAAY,MAAM;KAChC,cAAY,EAAE,4BAA4B,WAAW;IACtD,CAAA,CACE,EAAA,CAAA,CACL,EAAA,CAAA,IACA,MAAgB,UAClB,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;KAAO,SAAQ;KAAa,WAAW;eACpC,EAAE,wBAAwB,MAAM;IAC5B,CAAA,GACP,kBAAC,GAAD;KACE,IAAG;KACH,MAAK;KACL,OAAO,IAAY,EAAU,MAAM,GAAG,EAAE,KAAK;KAC7C,UAAA;KACA,WAAW,EAAG,GAAY,MAAM;KAChC,cAAY,EAAE,wBAAwB,MAAM;IAC7C,CAAA,CACE,EAAA,CAAA,IAEL,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;KAAO,SAAQ;KAAe,WAAW;eACtC,EAAE,wBAAwB,MAAM;IAC5B,CAAA,GACP,kBAAC,GAAD;KACE,IAAG;KACH,MAAK;KACL,OAAO;KACP,WAAW,MAAM,EAAgB,EAAE,OAAO,KAAK;KAC/C,WAAW,EAAG,GAAY,MAAM;KAChC,cAAY,EAAE,wBAAwB,MAAM;IAC7C,CAAA,CACE,EAAA,CAAA,GAIP,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;KAAO,WAAW;eACf,MAAgB,aACb,EAAE,0BAA0B,QAAQ,IACpC,MAAgB,UACd,EAAE,2BAA2B,SAAS,IACtC,EAAE,gCAAgC,cAAc;IACjD,CAAA,GACP,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OACE,SAAQ;OACR,MAAK;OACL,eAAe,EAAY,KAAK,IAAI,GAAG,IAAW,CAAC,CAAC;OACpD,UAAU,KAAY,KAAK;OAC3B,cAAY,EAAE,oCAAoC,mBAAmB;OACrE,WAAU;iBAEV,kBAAC,GAAD,EAAO,WAAU,SAAU,CAAA;MACrB,CAAA;MACR,kBAAC,SAAD;OACE,MAAK;OACL,KAAK;OACL,OAAO;OACP,WAAW,MAAM,EAAY,KAAK,IAAI,GAAG,SAAS,EAAE,OAAO,KAAK,KAAK,CAAC,CAAC;OACvE,WAAU;OACV,cACE,MAAgB,aACZ,EAAE,0BAA0B,QAAQ,IACpC,MAAgB,UACd,EAAE,2BAA2B,SAAS,IACtC,EAAE,gCAAgC,cAAc;MAEzD,CAAA;MACD,kBAAC,GAAD;OACE,SAAQ;OACR,MAAK;OACL,eAAe,EAAY,IAAW,CAAC;OACvC,UAAU;OACV,cAAY,EAAE,oCAAoC,mBAAmB;OACrE,WAAU;iBAEV,kBAAC,GAAD,EAAM,WAAU,SAAU,CAAA;MACpB,CAAA;KACL;MACF,EAAA,CAAA,CACF;;GAGL,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,QAAD;OAAM,WAAU;iBAAhB;QACG;QAAS;QAAE,EAAU,QAAQ,CAAC;QAAE;QAAG;QACnC,MAAgB,aACb,GAAG,KAAU,EAAE,GAAG,EAAE,0BAA0B,QAAQ,MACtD,GAAG,EAAS,GAAG,EAAE,wBAAwB,MAAM;OAC/C;UACN,kBAAC,QAAD;OAAM,WAAU;iBAAhB;QACG;QAAS;QAAE,EAAS,QAAQ,CAAC;OAC1B;QACH;;KACL,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,QAAD;OAAM,WAAU;iBAAuB,EAAE,uBAAuB,WAAW;MAAQ,CAAA,GACnF,kBAAC,QAAD;OAAM,WAAU;iBAAhB;QACG;QAAS;QAAE,EAAU,QAAQ,CAAC;OAC3B;QACH;;KACL,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,QAAD;OAAM,WAAU;iBACb,EAAE,yBAAyB,OAAO;MAC/B,CAAA,GACN,kBAAC,QAAD;OAAM,WAAU;iBAAhB;QACG;QAAS;QAAE,EAAM,QAAQ,CAAC;OACvB;QACH;;IACF;;GAGL,kBAAC,GAAD;IACE,eAAe,KAAK,EAAc;IAClC,UAAU;IACV,WAAU;IACV,MAAK;cAEJ,IACG,EAAE,mCAAmC,mBAAmB,IACxD,MAAgB,aACd,EAAE,mCAAmC,gBAAgB,IACrD,MAAgB,UACd,EAAE,gCAAgC,cAAc,IAChD,MAAgB,SACd,EAAE,+BAA+B,gBAAgB,IACjD,EAAE,mCAAmC,oBAAoB;GAC7D,CAAA;EACC;;AAEf"}
@@ -1,5 +1,5 @@
1
1
  import { cn as e } from "../utils/cn.js";
2
- import { Link as t } from "react-router-dom";
2
+ import { Link as t } from "react-router";
3
3
  import { GlassCard as n } from "@burdenoff/fe-libs/ui";
4
4
  import { Fragment as r, jsx as i, jsxs as a } from "react/jsx-runtime";
5
5
  //#region src/ggnomad/components/EntityCard.tsx
@@ -1 +1 @@
1
- {"version":3,"file":"EntityCard.js","names":[],"sources":["../../../src/ggnomad/components/EntityCard.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { Link } from 'react-router-dom';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\n\ntype EntityCardProps = {\n title: ReactNode;\n /** Optional secondary line (category / type / duration). */\n meta?: ReactNode;\n /** Right-aligned price or primary figure. */\n price?: ReactNode;\n badge?: ReactNode;\n footer?: ReactNode;\n /** Optional cover photo URL. Falls back to a branded gradient tile. */\n image?: string | null;\n /** Accessible description for the cover photo. */\n imageAlt?: string;\n /** When provided, the whole card becomes a router Link. */\n to?: string;\n className?: string;\n};\n\n/**\n * An inventory tile for a property / tour / event / activity / provider. When an\n * `image` is supplied it renders a 16:10 cover photo (with a branded gradient\n * fallback otherwise) above the title / meta / price. Hover lifts the shadow and\n * gently zooms the photo. When `to` is set the whole surface is one accessible link.\n */\nexport function EntityCard({\n title,\n meta,\n price,\n badge,\n footer,\n image,\n imageAlt,\n to,\n className,\n}: EntityCardProps) {\n const cover = (\n <div className=\"relative -mx-4 -mt-4 mb-3 aspect-[16/10] overflow-hidden\">\n {image ? (\n <img\n src={image}\n alt={imageAlt ?? ''}\n loading=\"lazy\"\n decoding=\"async\"\n className=\"size-full object-cover transition-transform duration-300 group-hover:scale-[1.04]\"\n />\n ) : (\n <div\n aria-hidden=\"true\"\n className=\"size-full bg-gradient-to-br from-action-primary-bg/25 via-bg-sunken to-bg-sunken\"\n />\n )}\n <div className=\"pointer-events-none absolute inset-0 bg-gradient-to-t from-black/45 via-transparent to-transparent\" />\n {badge ? <div className=\"absolute left-2 top-2\">{badge}</div> : null}\n </div>\n );\n\n const body = (\n <>\n {cover}\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium text-text-primary\">{title}</p>\n {meta ? <p className=\"mt-0.5 text-sm text-text-secondary\">{meta}</p> : null}\n </div>\n {price ? (\n <p className=\"mt-2 text-sm font-medium tabular-nums text-text-primary\">{price}</p>\n ) : null}\n {footer ? <div className=\"mt-2 text-sm text-text-secondary\">{footer}</div> : null}\n </>\n );\n\n if (to) {\n return (\n <Link\n to={to}\n className=\"group block rounded-card focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 focus-visible:ring-offset-bg-canvas\"\n >\n <GlassCard\n treatment=\"plain\"\n className={cn('overflow-hidden border-border-seam p-4', className)}\n >\n {body}\n </GlassCard>\n </Link>\n );\n }\n\n return (\n <GlassCard\n treatment=\"plain\"\n className={cn('group overflow-hidden border-border-seam p-4', className)}\n >\n {body}\n </GlassCard>\n );\n}\n"],"mappings":";;;;;AA4BA,SAAgB,EAAW,EACzB,UACA,SACA,UACA,UACA,WACA,UACA,aACA,OACA,gBACkB;CAsBlB,IAAM,IACJ,kBAAA,GAAA,EAAA,UAAA;EArBA,kBAAC,OAAD;GAAK,WAAU;aAAf;IACG,IACC,kBAAC,OAAD;KACE,KAAK;KACL,KAAK,KAAY;KACjB,SAAQ;KACR,UAAS;KACT,WAAU;IACX,CAAA,IAED,kBAAC,OAAD;KACE,eAAY;KACZ,WAAU;IACX,CAAA;IAEH,kBAAC,OAAD,EAAK,WAAU,qGAAsG,CAAA;IACpH,IAAQ,kBAAC,OAAD;KAAK,WAAU;eAAyB;IAAW,CAAA,IAAI;GAC7D;GAKF;EACD,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,KAAD;IAAG,WAAU;cAA0C;GAAS,CAAA,GAC/D,IAAO,kBAAC,KAAD;IAAG,WAAU;cAAsC;GAAQ,CAAA,IAAI,IACpE;;EACJ,IACC,kBAAC,KAAD;GAAG,WAAU;aAA2D;EAAS,CAAA,IAC/E;EACH,IAAS,kBAAC,OAAD;GAAK,WAAU;aAAoC;EAAY,CAAA,IAAI;CAC7E,EAAA,CAAA;CAmBJ,OAhBI,IAEA,kBAAC,GAAD;EACM;EACJ,WAAU;YAEV,kBAAC,GAAD;GACE,WAAU;GACV,WAAW,EAAG,0CAA0C,CAAS;aAEhE;EACQ,CAAA;CACP,CAAA,IAKR,kBAAC,GAAD;EACE,WAAU;EACV,WAAW,EAAG,gDAAgD,CAAS;YAEtE;CACQ,CAAA;AAEf"}
1
+ {"version":3,"file":"EntityCard.js","names":[],"sources":["../../../src/ggnomad/components/EntityCard.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { Link } from 'react-router';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\n\ntype EntityCardProps = {\n title: ReactNode;\n /** Optional secondary line (category / type / duration). */\n meta?: ReactNode;\n /** Right-aligned price or primary figure. */\n price?: ReactNode;\n badge?: ReactNode;\n footer?: ReactNode;\n /** Optional cover photo URL. Falls back to a branded gradient tile. */\n image?: string | null;\n /** Accessible description for the cover photo. */\n imageAlt?: string;\n /** When provided, the whole card becomes a router Link. */\n to?: string;\n className?: string;\n};\n\n/**\n * An inventory tile for a property / tour / event / activity / provider. When an\n * `image` is supplied it renders a 16:10 cover photo (with a branded gradient\n * fallback otherwise) above the title / meta / price. Hover lifts the shadow and\n * gently zooms the photo. When `to` is set the whole surface is one accessible link.\n */\nexport function EntityCard({\n title,\n meta,\n price,\n badge,\n footer,\n image,\n imageAlt,\n to,\n className,\n}: EntityCardProps) {\n const cover = (\n <div className=\"relative -mx-4 -mt-4 mb-3 aspect-[16/10] overflow-hidden\">\n {image ? (\n <img\n src={image}\n alt={imageAlt ?? ''}\n loading=\"lazy\"\n decoding=\"async\"\n className=\"size-full object-cover transition-transform duration-300 group-hover:scale-[1.04]\"\n />\n ) : (\n <div\n aria-hidden=\"true\"\n className=\"size-full bg-gradient-to-br from-action-primary-bg/25 via-bg-sunken to-bg-sunken\"\n />\n )}\n <div className=\"pointer-events-none absolute inset-0 bg-gradient-to-t from-black/45 via-transparent to-transparent\" />\n {badge ? <div className=\"absolute left-2 top-2\">{badge}</div> : null}\n </div>\n );\n\n const body = (\n <>\n {cover}\n <div className=\"min-w-0\">\n <p className=\"truncate font-medium text-text-primary\">{title}</p>\n {meta ? <p className=\"mt-0.5 text-sm text-text-secondary\">{meta}</p> : null}\n </div>\n {price ? (\n <p className=\"mt-2 text-sm font-medium tabular-nums text-text-primary\">{price}</p>\n ) : null}\n {footer ? <div className=\"mt-2 text-sm text-text-secondary\">{footer}</div> : null}\n </>\n );\n\n if (to) {\n return (\n <Link\n to={to}\n className=\"group block rounded-card focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-border-focus focus-visible:ring-offset-2 focus-visible:ring-offset-bg-canvas\"\n >\n <GlassCard\n treatment=\"plain\"\n className={cn('overflow-hidden border-border-seam p-4', className)}\n >\n {body}\n </GlassCard>\n </Link>\n );\n }\n\n return (\n <GlassCard\n treatment=\"plain\"\n className={cn('group overflow-hidden border-border-seam p-4', className)}\n >\n {body}\n </GlassCard>\n );\n}\n"],"mappings":";;;;;AA4BA,SAAgB,EAAW,EACzB,UACA,SACA,UACA,UACA,WACA,UACA,aACA,OACA,gBACkB;CAsBlB,IAAM,IACJ,kBAAA,GAAA,EAAA,UAAA;EArBA,kBAAC,OAAD;GAAK,WAAU;aAAf;IACG,IACC,kBAAC,OAAD;KACE,KAAK;KACL,KAAK,KAAY;KACjB,SAAQ;KACR,UAAS;KACT,WAAU;IACX,CAAA,IAED,kBAAC,OAAD;KACE,eAAY;KACZ,WAAU;IACX,CAAA;IAEH,kBAAC,OAAD,EAAK,WAAU,qGAAsG,CAAA;IACpH,IAAQ,kBAAC,OAAD;KAAK,WAAU;eAAyB;IAAW,CAAA,IAAI;GAC7D;GAKF;EACD,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,KAAD;IAAG,WAAU;cAA0C;GAAS,CAAA,GAC/D,IAAO,kBAAC,KAAD;IAAG,WAAU;cAAsC;GAAQ,CAAA,IAAI,IACpE;;EACJ,IACC,kBAAC,KAAD;GAAG,WAAU;aAA2D;EAAS,CAAA,IAC/E;EACH,IAAS,kBAAC,OAAD;GAAK,WAAU;aAAoC;EAAY,CAAA,IAAI;CAC7E,EAAA,CAAA;CAmBJ,OAhBI,IAEA,kBAAC,GAAD;EACM;EACJ,WAAU;YAEV,kBAAC,GAAD;GACE,WAAU;GACV,WAAW,EAAG,0CAA0C,CAAS;aAEhE;EACQ,CAAA;CACP,CAAA,IAKR,kBAAC,GAAD;EACE,WAAU;EACV,WAAW,EAAG,gDAAgD,CAAS;YAEtE;CACQ,CAAA;AAEf"}
@@ -12,7 +12,7 @@ import "../components/index.js";
12
12
  import { useState as u } from "react";
13
13
  import { Activity as d, Compass as f, Pencil as p, ShieldCheck as m } from "lucide-react";
14
14
  import { useI18n as h } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
15
- import { useNavigate as g, useParams as _ } from "react-router-dom";
15
+ import { useNavigate as g, useParams as _ } from "react-router";
16
16
  import { useQuery as v } from "@apollo/client/react";
17
17
  import { GlassCard as y, IllustratedEmptyState as b, NextSteps as x, PagePurpose as S } from "@burdenoff/fe-libs/ui";
18
18
  import { Fragment as C, jsx as w, jsxs as T } from "react/jsx-runtime";
@@ -1 +1 @@
1
- {"version":3,"file":"ActivityDetailsPage.js","names":[],"sources":["../../../src/ggnomad/pages/ActivityDetailsPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useQuery } from '@apollo/client/react';\nimport type { TypedDocumentNode } from '@apollo/client';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport { Activity, Compass, Pencil, ShieldCheck } from 'lucide-react';\nimport { GlassCard, IllustratedEmptyState, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';\n\nimport { GGNOMAD_ACTIVITY_QUERY } from '../graphql';\nimport { useGGNomad } from '../providers/GGNomadProvider';\nimport { joinPath } from '../utils/navigation';\nimport { PageShell } from './PageShell';\nimport {\n DetailRow,\n SectionCard,\n DetailHero,\n BookingPanel,\n ReviewsSection,\n ListingEditDialog,\n} from '../components';\n\nexport function ActivityDetailsPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const { basePath = '/' } = useGGNomad();\n const { id = '' } = useParams();\n const [editDialogOpen, setEditDialogOpen] = useState(false);\n const { data, refetch } = useQuery(\n GGNOMAD_ACTIVITY_QUERY as TypedDocumentNode<\n {\n ggnomadActivity: {\n id: string;\n name: string;\n description?: string;\n category: string;\n duration: string;\n maxParticipants: number;\n currency: string;\n price: number;\n status?: string;\n safetyInfo?: string;\n images?: string[];\n } | null;\n },\n { id: string }\n >,\n { variables: { id }, skip: !id }\n );\n const activity = data?.ggnomadActivity;\n\n return (\n <PageShell\n title={activity?.name ?? t('ggnomad.activity.fallbackTitle', 'Activity details')}\n subtitle={t('ggnomad.activity.subtitle', 'Safety, inclusions and participation details')}\n >\n {!activity ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('ggnomad.activity.notFoundTitle', 'No activity found')}\n description={t(\n 'ggnomad.activity.notFoundDescription',\n 'This activity is unavailable or may have been removed.'\n )}\n />\n ) : (\n <>\n <DetailHero image={activity.images?.[0]} alt={activity.name} />\n <PagePurpose icon={<Activity className=\"size-4\" />}>\n {t(\n 'ggnomad.activity.purpose',\n 'Everything you need to know before joining this activity — its category, duration, group size, price and the safety policy that applies — so you can be confident it fits your group before you book.'\n )}\n </PagePurpose>\n\n <NextSteps\n storageKey=\"ggnomad-activity-detail\"\n steps={[\n {\n id: 'browse',\n label: t('ggnomad.activity.next.browse', 'Explore more experiences'),\n description: t(\n 'ggnomad.activity.next.browseDesc',\n 'Return to Tourism to compare tours and activities.'\n ),\n icon: <Compass className=\"size-4\" />,\n onClick: () => navigate(joinPath(basePath, 'tourism')),\n },\n ]}\n />\n\n <div className=\"grid gap-4 lg:grid-cols-3\">\n <div className=\"lg:col-span-2 space-y-4\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam p-5\">\n <div className=\"mb-4 flex items-center justify-between gap-3\">\n <h2 className=\"text-lg font-medium tracking-tight text-text-primary\">\n {t('ggnomad.activity.details', 'Details')}\n </h2>\n <button\n onClick={() => setEditDialogOpen(true)}\n className=\"rounded-md p-2 text-text-secondary hover:bg-action-primary-bg hover:text-action-primary-text\"\n aria-label={t('ggnomad.activity.editListing', 'Edit listing')}\n title={t('ggnomad.activity.editListing', 'Edit listing')}\n >\n <Pencil className=\"size-4\" />\n </button>\n </div>\n <dl className=\"space-y-2 text-sm\">\n <DetailRow\n label={t('ggnomad.activity.category', 'Category')}\n value={activity.category}\n />\n <DetailRow\n label={t('ggnomad.activity.duration', 'Duration')}\n value={activity.duration}\n />\n <DetailRow\n label={t('ggnomad.activity.maxParticipants', 'Max participants')}\n value={String(activity.maxParticipants)}\n />\n <DetailRow\n label={t('ggnomad.activity.price', 'Price')}\n value={`${activity.currency} ${Number(activity.price).toFixed(2)}`}\n />\n </dl>\n </GlassCard>\n <SectionCard title={t('ggnomad.activity.safety', 'Safety information')}>\n <div className=\"flex items-start gap-2 text-sm text-text-primary\">\n <ShieldCheck\n className=\"mt-0.5 size-4 flex-shrink-0 text-status-success-text\"\n aria-hidden=\"true\"\n />\n <p>\n {activity.safetyInfo ??\n t(\n 'ggnomad.activity.defaultSafety',\n 'Standard provider safety policy applies.'\n )}\n </p>\n </div>\n </SectionCard>\n <ReviewsSection targetType=\"ACTIVITY\" targetId={activity.id ?? id} />\n </div>\n <div>\n <BookingPanel\n bookingType=\"ACTIVITY\"\n itemId={activity.id ?? id}\n itemName={activity.name}\n unitPrice={activity.price ?? 0}\n currency={activity.currency ?? 'USD'}\n />\n </div>\n </div>\n\n <ListingEditDialog\n open={editDialogOpen}\n onOpenChange={setEditDialogOpen}\n targetType=\"ACTIVITY\"\n id={activity.id ?? id}\n initial={{\n name: activity.name,\n description: activity.description,\n price: activity.price,\n status: activity.status,\n images: activity.images,\n }}\n onSaved={() => refetch()}\n />\n </>\n )}\n </PageShell>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ActivityDetailsPage.js","names":[],"sources":["../../../src/ggnomad/pages/ActivityDetailsPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useQuery } from '@apollo/client/react';\nimport type { TypedDocumentNode } from '@apollo/client';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useNavigate, useParams } from 'react-router';\nimport { Activity, Compass, Pencil, ShieldCheck } from 'lucide-react';\nimport { GlassCard, IllustratedEmptyState, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';\n\nimport { GGNOMAD_ACTIVITY_QUERY } from '../graphql';\nimport { useGGNomad } from '../providers/GGNomadProvider';\nimport { joinPath } from '../utils/navigation';\nimport { PageShell } from './PageShell';\nimport {\n DetailRow,\n SectionCard,\n DetailHero,\n BookingPanel,\n ReviewsSection,\n ListingEditDialog,\n} from '../components';\n\nexport function ActivityDetailsPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const { basePath = '/' } = useGGNomad();\n const { id = '' } = useParams();\n const [editDialogOpen, setEditDialogOpen] = useState(false);\n const { data, refetch } = useQuery(\n GGNOMAD_ACTIVITY_QUERY as TypedDocumentNode<\n {\n ggnomadActivity: {\n id: string;\n name: string;\n description?: string;\n category: string;\n duration: string;\n maxParticipants: number;\n currency: string;\n price: number;\n status?: string;\n safetyInfo?: string;\n images?: string[];\n } | null;\n },\n { id: string }\n >,\n { variables: { id }, skip: !id }\n );\n const activity = data?.ggnomadActivity;\n\n return (\n <PageShell\n title={activity?.name ?? t('ggnomad.activity.fallbackTitle', 'Activity details')}\n subtitle={t('ggnomad.activity.subtitle', 'Safety, inclusions and participation details')}\n >\n {!activity ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('ggnomad.activity.notFoundTitle', 'No activity found')}\n description={t(\n 'ggnomad.activity.notFoundDescription',\n 'This activity is unavailable or may have been removed.'\n )}\n />\n ) : (\n <>\n <DetailHero image={activity.images?.[0]} alt={activity.name} />\n <PagePurpose icon={<Activity className=\"size-4\" />}>\n {t(\n 'ggnomad.activity.purpose',\n 'Everything you need to know before joining this activity — its category, duration, group size, price and the safety policy that applies — so you can be confident it fits your group before you book.'\n )}\n </PagePurpose>\n\n <NextSteps\n storageKey=\"ggnomad-activity-detail\"\n steps={[\n {\n id: 'browse',\n label: t('ggnomad.activity.next.browse', 'Explore more experiences'),\n description: t(\n 'ggnomad.activity.next.browseDesc',\n 'Return to Tourism to compare tours and activities.'\n ),\n icon: <Compass className=\"size-4\" />,\n onClick: () => navigate(joinPath(basePath, 'tourism')),\n },\n ]}\n />\n\n <div className=\"grid gap-4 lg:grid-cols-3\">\n <div className=\"lg:col-span-2 space-y-4\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam p-5\">\n <div className=\"mb-4 flex items-center justify-between gap-3\">\n <h2 className=\"text-lg font-medium tracking-tight text-text-primary\">\n {t('ggnomad.activity.details', 'Details')}\n </h2>\n <button\n onClick={() => setEditDialogOpen(true)}\n className=\"rounded-md p-2 text-text-secondary hover:bg-action-primary-bg hover:text-action-primary-text\"\n aria-label={t('ggnomad.activity.editListing', 'Edit listing')}\n title={t('ggnomad.activity.editListing', 'Edit listing')}\n >\n <Pencil className=\"size-4\" />\n </button>\n </div>\n <dl className=\"space-y-2 text-sm\">\n <DetailRow\n label={t('ggnomad.activity.category', 'Category')}\n value={activity.category}\n />\n <DetailRow\n label={t('ggnomad.activity.duration', 'Duration')}\n value={activity.duration}\n />\n <DetailRow\n label={t('ggnomad.activity.maxParticipants', 'Max participants')}\n value={String(activity.maxParticipants)}\n />\n <DetailRow\n label={t('ggnomad.activity.price', 'Price')}\n value={`${activity.currency} ${Number(activity.price).toFixed(2)}`}\n />\n </dl>\n </GlassCard>\n <SectionCard title={t('ggnomad.activity.safety', 'Safety information')}>\n <div className=\"flex items-start gap-2 text-sm text-text-primary\">\n <ShieldCheck\n className=\"mt-0.5 size-4 flex-shrink-0 text-status-success-text\"\n aria-hidden=\"true\"\n />\n <p>\n {activity.safetyInfo ??\n t(\n 'ggnomad.activity.defaultSafety',\n 'Standard provider safety policy applies.'\n )}\n </p>\n </div>\n </SectionCard>\n <ReviewsSection targetType=\"ACTIVITY\" targetId={activity.id ?? id} />\n </div>\n <div>\n <BookingPanel\n bookingType=\"ACTIVITY\"\n itemId={activity.id ?? id}\n itemName={activity.name}\n unitPrice={activity.price ?? 0}\n currency={activity.currency ?? 'USD'}\n />\n </div>\n </div>\n\n <ListingEditDialog\n open={editDialogOpen}\n onOpenChange={setEditDialogOpen}\n targetType=\"ACTIVITY\"\n id={activity.id ?? id}\n initial={{\n name: activity.name,\n description: activity.description,\n price: activity.price,\n status: activity.status,\n images: activity.images,\n }}\n onSaved={() => refetch()}\n />\n </>\n )}\n </PageShell>\n );\n}\n"],"mappings":"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"}
@@ -9,7 +9,7 @@ import "../components/index.js";
9
9
  import { useMemo as u, useState as d } from "react";
10
10
  import { CalendarCheck as f, Compass as p, Hotel as m, Plus as h, QrCode as g, Ticket as _, XCircle as v } from "lucide-react";
11
11
  import { useI18n as y } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
12
- import { useNavigate as b } from "react-router-dom";
12
+ import { useNavigate as b } from "react-router";
13
13
  import { useMutation as x, useQuery as S } from "@apollo/client/react";
14
14
  import { AlertDialog as C, AlertDialogAction as w, AlertDialogCancel as T, AlertDialogContent as E, AlertDialogDescription as D, AlertDialogFooter as O, AlertDialogHeader as k, AlertDialogTitle as A, CTAOverflowMenu as j, GlassCard as M, IllustratedEmptyState as N, NextSteps as P, PagePurpose as F } from "@burdenoff/fe-libs/ui";
15
15
  import { jsx as I, jsxs as L } from "react/jsx-runtime";
@@ -1 +1 @@
1
- {"version":3,"file":"BookingsPage.js","names":[],"sources":["../../../src/ggnomad/pages/BookingsPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport type { TypedDocumentNode } from '@apollo/client';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { CalendarCheck, Compass, Hotel, Plus, QrCode, Ticket, XCircle } from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n CTAOverflowMenu,\n GlassCard,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n CANCEL_GGNOMAD_BOOKING_MUTATION,\n CREATE_GGNOMAD_BOOKING_MUTATION,\n GGNOMAD_BOOKABLE_SAMPLE_QUERY,\n GGNOMAD_BOOKINGS_QUERY,\n} from '../graphql';\nimport { PageShell } from './PageShell';\nimport { EntityCard, RedeemTicketPanel, StatusBadge, TicketDialog } from '../components';\nimport { notify } from '../utils/notify';\n\ntype BookingType = 'PROPERTY' | 'TOUR' | 'EVENT' | 'ACTIVITY';\n\ntype PricedListing = { id: string; name: string; price: number; currency: string };\n\ntype CancelTarget = { id: string; referenceCode: string };\n\ntype TicketTarget = { id: string; referenceCode: string; status: string };\n\nexport function BookingsPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const [cancelTarget, setCancelTarget] = useState<CancelTarget | null>(null);\n const [ticketTarget, setTicketTarget] = useState<TicketTarget | null>(null);\n\n const { data, refetch } = useQuery(\n GGNOMAD_BOOKINGS_QUERY as TypedDocumentNode<\n {\n ggnomadBookings: Array<{\n id: string;\n referenceCode: string;\n itemName: string;\n status: string;\n totalPrice: number;\n currency: string;\n }>;\n },\n { limit: number; offset: number }\n >,\n { variables: { limit: 20, offset: 0 } }\n );\n\n // One real listing of each type so the demo booking targets an id that\n // actually exists in the workspace (the backend rejects synthetic ids).\n const { data: sampleData } = useQuery(\n GGNOMAD_BOOKABLE_SAMPLE_QUERY as TypedDocumentNode<\n {\n ggnomadProperties: Array<{\n id: string;\n name: string;\n pricePerNight: number;\n currency: string;\n }>;\n ggnomadTours: Array<{ id: string; name: string; price: number; currency: string }>;\n ggnomadEvents: Array<{ id: string; name: string; price: number; currency: string }>;\n ggnomadActivities: Array<{ id: string; name: string; price: number; currency: string }>;\n },\n Record<string, never>\n >\n );\n\n const [createBooking, { loading: creating }] = useMutation(\n CREATE_GGNOMAD_BOOKING_MUTATION as TypedDocumentNode<\n { createGgnomadBooking: { id: string; referenceCode: string; itemName: string } },\n {\n input: {\n bookingType: BookingType;\n itemId: string;\n itemName: string;\n guestName: string;\n guestEmail: string;\n guests: number;\n totalPrice: number;\n currency: string;\n paymentMethod: string;\n };\n }\n >\n );\n\n const [cancelBooking, { loading: cancelling }] = useMutation(\n CANCEL_GGNOMAD_BOOKING_MUTATION as TypedDocumentNode<\n { cancelGgnomadBooking: { id: string; status: string } },\n { id: string; reason: string }\n >\n );\n\n const bookings = useMemo(() => data?.ggnomadBookings ?? [], [data?.ggnomadBookings]);\n\n const statusCounts = useMemo(() => {\n const counts: Record<string, number> = {\n CONFIRMED: 0,\n CHECKED_IN: 0,\n COMPLETED: 0,\n CANCELLED: 0,\n };\n bookings.forEach((booking) => {\n const status = booking.status?.toUpperCase();\n if (status && status in counts) {\n counts[status] += 1;\n }\n });\n return counts;\n }, [bookings]);\n\n /** First available real listing, in priority order, mapped to a booking type. */\n const resolveDemoTarget = (): { type: BookingType; listing: PricedListing } | null => {\n const property = sampleData?.ggnomadProperties?.[0];\n if (property) {\n return {\n type: 'PROPERTY',\n listing: {\n id: property.id,\n name: property.name,\n price: property.pricePerNight,\n currency: property.currency,\n },\n };\n }\n const tour = sampleData?.ggnomadTours?.[0];\n if (tour) return { type: 'TOUR', listing: tour };\n const event = sampleData?.ggnomadEvents?.[0];\n if (event) return { type: 'EVENT', listing: event };\n const activity = sampleData?.ggnomadActivities?.[0];\n if (activity) return { type: 'ACTIVITY', listing: activity };\n return null;\n };\n\n const handleCreateDemoBooking = async () => {\n const target = resolveDemoTarget();\n if (!target) {\n notify.warning(\n t('ggnomad.bookings.toast.noListingsTitle', 'No listings available to book yet'),\n t(\n 'ggnomad.bookings.toast.noListingsBody',\n 'Add a property, tour, event or activity first, then create a booking against it.'\n )\n );\n return;\n }\n const { type, listing } = target;\n try {\n const result = await createBooking({\n variables: {\n input: {\n bookingType: type,\n itemId: listing.id,\n itemName: listing.name,\n guestName: t('ggnomad.bookings.demo.guestName', 'Demo Guest'),\n guestEmail: 'demo@burdenoff.com',\n guests: 2,\n totalPrice: Number(listing.price ?? 0),\n currency: listing.currency ?? 'USD',\n paymentMethod: 'card',\n },\n },\n });\n if (result.error) {\n notify.error(\n t('ggnomad.bookings.toast.createErrorTitle', 'Could not create booking'),\n result.error.message\n );\n return;\n }\n const created = result.data?.createGgnomadBooking;\n notify.success(\n t('ggnomad.bookings.toast.createSuccessTitle', 'Booking confirmed'),\n created\n ? t('ggnomad.bookings.toast.createSuccessBody', '{{ref}} · {{item}}')\n .replace('{{ref}}', created.referenceCode)\n .replace('{{item}}', created.itemName)\n : listing.name\n );\n await refetch();\n } catch (err) {\n notify.error(\n t('ggnomad.bookings.toast.createErrorTitle', 'Could not create booking'),\n err instanceof Error ? err.message : String(err)\n );\n }\n };\n\n const confirmCancel = async () => {\n if (!cancelTarget) return;\n try {\n const result = await cancelBooking({\n variables: {\n id: cancelTarget.id,\n reason: t('ggnomad.bookings.actions.cancelReason', 'Cancelled via UI'),\n },\n });\n if (result.error) {\n notify.error(\n t('ggnomad.bookings.toast.cancelErrorTitle', 'Could not cancel booking'),\n result.error.message\n );\n return;\n }\n notify.success(\n t('ggnomad.bookings.toast.cancelSuccessTitle', 'Booking cancelled'),\n cancelTarget.referenceCode\n );\n await refetch();\n } catch (err) {\n notify.error(\n t('ggnomad.bookings.toast.cancelErrorTitle', 'Could not cancel booking'),\n err instanceof Error ? err.message : String(err)\n );\n } finally {\n setCancelTarget(null);\n }\n };\n\n return (\n <PageShell\n title={t('ggnomad.bookings.title', 'Bookings')}\n titleAccent={t('ggnomad.bookings.title.accent', 'Bookings')}\n subtitle={t(\n 'ggnomad.bookings.subtitle',\n 'End-to-end booking operations with real API mutations'\n )}\n actions={\n <CTAOverflowMenu\n primary={{\n label: t('ggnomad.bookings.actions.createDemoBooking', 'Create demo booking'),\n onSelect: () => void handleCreateDemoBooking(),\n disabled: creating,\n icon: <Plus />,\n }}\n actions={[]}\n />\n }\n >\n <PagePurpose icon={<CalendarCheck className=\"size-4\" />}>\n {t(\n 'ggnomad.bookings.purpose',\n 'Your single place to manage every reservation across travel, stays, tours and events. Track each booking’s status and reference code in real time, and cancel a reservation when plans change — all backed by live API mutations.'\n )}\n </PagePurpose>\n\n <NextSteps\n storageKey=\"ggnomad-bookings\"\n steps={[\n {\n id: 'book-stay',\n label: t('ggnomad.bookings.next.stay', 'Book a place to stay'),\n description: t(\n 'ggnomad.bookings.next.stayDesc',\n 'Browse hospitality listings and reserve a property.'\n ),\n icon: <Hotel className=\"size-4\" />,\n onClick: () => navigate('../hospitality'),\n },\n {\n id: 'book-tour',\n label: t('ggnomad.bookings.next.tour', 'Add a tour or activity'),\n description: t(\n 'ggnomad.bookings.next.tourDesc',\n 'Pick a curated experience from Tourism.'\n ),\n icon: <Compass className=\"size-4\" />,\n onClick: () => navigate('../tourism'),\n },\n {\n id: 'book-event',\n label: t('ggnomad.bookings.next.event', 'Grab event tickets'),\n description: t(\n 'ggnomad.bookings.next.eventDesc',\n 'Find live events to round out the itinerary.'\n ),\n icon: <Ticket className=\"size-4\" />,\n onClick: () => navigate('../entertainment'),\n },\n ]}\n />\n\n <RedeemTicketPanel onRedeemed={() => void refetch()} />\n\n {bookings.length > 0 && (\n <div className=\"grid gap-3 sm:grid-cols-4\">\n {[\n {\n status: 'CONFIRMED',\n label: t('ggnomad.bookings.pipeline.confirmed', 'Confirmed'),\n icon: CalendarCheck,\n },\n {\n status: 'CHECKED_IN',\n label: t('ggnomad.bookings.pipeline.checkedIn', 'Checked-in'),\n icon: Hotel,\n },\n {\n status: 'COMPLETED',\n label: t('ggnomad.bookings.pipeline.completed', 'Completed'),\n icon: Ticket,\n },\n {\n status: 'CANCELLED',\n label: t('ggnomad.bookings.pipeline.cancelled', 'Cancelled'),\n icon: XCircle,\n },\n ].map((stage) => {\n const Icon = stage.icon;\n const count = statusCounts[stage.status];\n return (\n <div\n key={stage.status}\n className=\"flex flex-col items-center gap-2 rounded-lg border border-border-seam bg-bg-surface/40 p-3 text-center\"\n >\n <div className=\"flex size-8 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <Icon className=\"size-4\" />\n </div>\n <p className=\"text-xs font-medium text-text-secondary\">{stage.label}</p>\n <p className=\"text-lg font-semibold tabular-nums text-text-primary\">{count}</p>\n </div>\n );\n })}\n </div>\n )}\n\n {bookings.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('ggnomad.bookings.emptyTitle', 'No bookings yet')}\n description={t(\n 'ggnomad.bookings.emptyDescription',\n 'Create your first booking to see it tracked here in real time.'\n )}\n />\n ) : (\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam p-5\">\n <h2 className=\"mb-4 text-lg font-medium tracking-tight text-text-primary\">\n {t('ggnomad.bookings.activeBookings', 'Your bookings')}\n </h2>\n <div className=\"grid gap-3 sm:grid-cols-2\">\n {bookings.map((booking) => (\n <EntityCard\n key={booking.id}\n title={booking.referenceCode}\n meta={booking.itemName}\n price={`${booking.currency} ${Number(booking.totalPrice).toFixed(2)}`}\n badge={\n <StatusBadge status={booking.status} live={booking.status !== 'CANCELLED'} />\n }\n footer={\n booking.status !== 'CANCELLED' ? (\n <CTAOverflowMenu\n align=\"end\"\n actions={[\n {\n label: t('ggnomad.bookings.actions.viewTicket', 'View mobile ticket'),\n onSelect: () =>\n setTicketTarget({\n id: booking.id,\n referenceCode: booking.referenceCode,\n status: booking.status,\n }),\n icon: <QrCode />,\n },\n {\n label: t('ggnomad.bookings.actions.cancel', 'Cancel booking'),\n onSelect: () =>\n setCancelTarget({\n id: booking.id,\n referenceCode: booking.referenceCode,\n }),\n intent: 'destructive',\n icon: <XCircle />,\n },\n ]}\n />\n ) : null\n }\n />\n ))}\n </div>\n </GlassCard>\n )}\n\n <AlertDialog\n open={cancelTarget !== null}\n onOpenChange={(open) => {\n if (!open) setCancelTarget(null);\n }}\n >\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>\n {t('ggnomad.bookings.cancelDialog.title', 'Cancel this booking?')}\n </AlertDialogTitle>\n <AlertDialogDescription>\n {t(\n 'ggnomad.bookings.cancelDialog.body',\n 'This releases reservation {{ref}} and cannot be undone.'\n ).replace('{{ref}}', cancelTarget?.referenceCode ?? '')}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel disabled={cancelling}>\n {t('ggnomad.bookings.cancelDialog.keep', 'Keep booking')}\n </AlertDialogCancel>\n <AlertDialogAction\n className=\"bg-action-danger-bg text-action-danger-text hover:bg-action-danger-bgHover\"\n disabled={cancelling}\n onClick={(event) => {\n event.preventDefault();\n void confirmCancel();\n }}\n >\n {cancelling\n ? t('ggnomad.bookings.cancelDialog.cancelling', 'Cancelling…')\n : t('ggnomad.bookings.cancelDialog.confirm', 'Cancel booking')}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n\n {ticketTarget ? (\n <TicketDialog\n open={ticketTarget !== null}\n onOpenChange={(open) => {\n if (!open) setTicketTarget(null);\n }}\n bookingId={ticketTarget.id}\n referenceCode={ticketTarget.referenceCode}\n bookingStatus={ticketTarget.status}\n />\n ) : null}\n </PageShell>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"BookingsPage.js","names":[],"sources":["../../../src/ggnomad/pages/BookingsPage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport type { TypedDocumentNode } from '@apollo/client';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { CalendarCheck, Compass, Hotel, Plus, QrCode, Ticket, XCircle } from 'lucide-react';\nimport { useNavigate } from 'react-router';\nimport {\n AlertDialog,\n AlertDialogAction,\n AlertDialogCancel,\n AlertDialogContent,\n AlertDialogDescription,\n AlertDialogFooter,\n AlertDialogHeader,\n AlertDialogTitle,\n CTAOverflowMenu,\n GlassCard,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n CANCEL_GGNOMAD_BOOKING_MUTATION,\n CREATE_GGNOMAD_BOOKING_MUTATION,\n GGNOMAD_BOOKABLE_SAMPLE_QUERY,\n GGNOMAD_BOOKINGS_QUERY,\n} from '../graphql';\nimport { PageShell } from './PageShell';\nimport { EntityCard, RedeemTicketPanel, StatusBadge, TicketDialog } from '../components';\nimport { notify } from '../utils/notify';\n\ntype BookingType = 'PROPERTY' | 'TOUR' | 'EVENT' | 'ACTIVITY';\n\ntype PricedListing = { id: string; name: string; price: number; currency: string };\n\ntype CancelTarget = { id: string; referenceCode: string };\n\ntype TicketTarget = { id: string; referenceCode: string; status: string };\n\nexport function BookingsPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const [cancelTarget, setCancelTarget] = useState<CancelTarget | null>(null);\n const [ticketTarget, setTicketTarget] = useState<TicketTarget | null>(null);\n\n const { data, refetch } = useQuery(\n GGNOMAD_BOOKINGS_QUERY as TypedDocumentNode<\n {\n ggnomadBookings: Array<{\n id: string;\n referenceCode: string;\n itemName: string;\n status: string;\n totalPrice: number;\n currency: string;\n }>;\n },\n { limit: number; offset: number }\n >,\n { variables: { limit: 20, offset: 0 } }\n );\n\n // One real listing of each type so the demo booking targets an id that\n // actually exists in the workspace (the backend rejects synthetic ids).\n const { data: sampleData } = useQuery(\n GGNOMAD_BOOKABLE_SAMPLE_QUERY as TypedDocumentNode<\n {\n ggnomadProperties: Array<{\n id: string;\n name: string;\n pricePerNight: number;\n currency: string;\n }>;\n ggnomadTours: Array<{ id: string; name: string; price: number; currency: string }>;\n ggnomadEvents: Array<{ id: string; name: string; price: number; currency: string }>;\n ggnomadActivities: Array<{ id: string; name: string; price: number; currency: string }>;\n },\n Record<string, never>\n >\n );\n\n const [createBooking, { loading: creating }] = useMutation(\n CREATE_GGNOMAD_BOOKING_MUTATION as TypedDocumentNode<\n { createGgnomadBooking: { id: string; referenceCode: string; itemName: string } },\n {\n input: {\n bookingType: BookingType;\n itemId: string;\n itemName: string;\n guestName: string;\n guestEmail: string;\n guests: number;\n totalPrice: number;\n currency: string;\n paymentMethod: string;\n };\n }\n >\n );\n\n const [cancelBooking, { loading: cancelling }] = useMutation(\n CANCEL_GGNOMAD_BOOKING_MUTATION as TypedDocumentNode<\n { cancelGgnomadBooking: { id: string; status: string } },\n { id: string; reason: string }\n >\n );\n\n const bookings = useMemo(() => data?.ggnomadBookings ?? [], [data?.ggnomadBookings]);\n\n const statusCounts = useMemo(() => {\n const counts: Record<string, number> = {\n CONFIRMED: 0,\n CHECKED_IN: 0,\n COMPLETED: 0,\n CANCELLED: 0,\n };\n bookings.forEach((booking) => {\n const status = booking.status?.toUpperCase();\n if (status && status in counts) {\n counts[status] += 1;\n }\n });\n return counts;\n }, [bookings]);\n\n /** First available real listing, in priority order, mapped to a booking type. */\n const resolveDemoTarget = (): { type: BookingType; listing: PricedListing } | null => {\n const property = sampleData?.ggnomadProperties?.[0];\n if (property) {\n return {\n type: 'PROPERTY',\n listing: {\n id: property.id,\n name: property.name,\n price: property.pricePerNight,\n currency: property.currency,\n },\n };\n }\n const tour = sampleData?.ggnomadTours?.[0];\n if (tour) return { type: 'TOUR', listing: tour };\n const event = sampleData?.ggnomadEvents?.[0];\n if (event) return { type: 'EVENT', listing: event };\n const activity = sampleData?.ggnomadActivities?.[0];\n if (activity) return { type: 'ACTIVITY', listing: activity };\n return null;\n };\n\n const handleCreateDemoBooking = async () => {\n const target = resolveDemoTarget();\n if (!target) {\n notify.warning(\n t('ggnomad.bookings.toast.noListingsTitle', 'No listings available to book yet'),\n t(\n 'ggnomad.bookings.toast.noListingsBody',\n 'Add a property, tour, event or activity first, then create a booking against it.'\n )\n );\n return;\n }\n const { type, listing } = target;\n try {\n const result = await createBooking({\n variables: {\n input: {\n bookingType: type,\n itemId: listing.id,\n itemName: listing.name,\n guestName: t('ggnomad.bookings.demo.guestName', 'Demo Guest'),\n guestEmail: 'demo@burdenoff.com',\n guests: 2,\n totalPrice: Number(listing.price ?? 0),\n currency: listing.currency ?? 'USD',\n paymentMethod: 'card',\n },\n },\n });\n if (result.error) {\n notify.error(\n t('ggnomad.bookings.toast.createErrorTitle', 'Could not create booking'),\n result.error.message\n );\n return;\n }\n const created = result.data?.createGgnomadBooking;\n notify.success(\n t('ggnomad.bookings.toast.createSuccessTitle', 'Booking confirmed'),\n created\n ? t('ggnomad.bookings.toast.createSuccessBody', '{{ref}} · {{item}}')\n .replace('{{ref}}', created.referenceCode)\n .replace('{{item}}', created.itemName)\n : listing.name\n );\n await refetch();\n } catch (err) {\n notify.error(\n t('ggnomad.bookings.toast.createErrorTitle', 'Could not create booking'),\n err instanceof Error ? err.message : String(err)\n );\n }\n };\n\n const confirmCancel = async () => {\n if (!cancelTarget) return;\n try {\n const result = await cancelBooking({\n variables: {\n id: cancelTarget.id,\n reason: t('ggnomad.bookings.actions.cancelReason', 'Cancelled via UI'),\n },\n });\n if (result.error) {\n notify.error(\n t('ggnomad.bookings.toast.cancelErrorTitle', 'Could not cancel booking'),\n result.error.message\n );\n return;\n }\n notify.success(\n t('ggnomad.bookings.toast.cancelSuccessTitle', 'Booking cancelled'),\n cancelTarget.referenceCode\n );\n await refetch();\n } catch (err) {\n notify.error(\n t('ggnomad.bookings.toast.cancelErrorTitle', 'Could not cancel booking'),\n err instanceof Error ? err.message : String(err)\n );\n } finally {\n setCancelTarget(null);\n }\n };\n\n return (\n <PageShell\n title={t('ggnomad.bookings.title', 'Bookings')}\n titleAccent={t('ggnomad.bookings.title.accent', 'Bookings')}\n subtitle={t(\n 'ggnomad.bookings.subtitle',\n 'End-to-end booking operations with real API mutations'\n )}\n actions={\n <CTAOverflowMenu\n primary={{\n label: t('ggnomad.bookings.actions.createDemoBooking', 'Create demo booking'),\n onSelect: () => void handleCreateDemoBooking(),\n disabled: creating,\n icon: <Plus />,\n }}\n actions={[]}\n />\n }\n >\n <PagePurpose icon={<CalendarCheck className=\"size-4\" />}>\n {t(\n 'ggnomad.bookings.purpose',\n 'Your single place to manage every reservation across travel, stays, tours and events. Track each booking’s status and reference code in real time, and cancel a reservation when plans change — all backed by live API mutations.'\n )}\n </PagePurpose>\n\n <NextSteps\n storageKey=\"ggnomad-bookings\"\n steps={[\n {\n id: 'book-stay',\n label: t('ggnomad.bookings.next.stay', 'Book a place to stay'),\n description: t(\n 'ggnomad.bookings.next.stayDesc',\n 'Browse hospitality listings and reserve a property.'\n ),\n icon: <Hotel className=\"size-4\" />,\n onClick: () => navigate('../hospitality'),\n },\n {\n id: 'book-tour',\n label: t('ggnomad.bookings.next.tour', 'Add a tour or activity'),\n description: t(\n 'ggnomad.bookings.next.tourDesc',\n 'Pick a curated experience from Tourism.'\n ),\n icon: <Compass className=\"size-4\" />,\n onClick: () => navigate('../tourism'),\n },\n {\n id: 'book-event',\n label: t('ggnomad.bookings.next.event', 'Grab event tickets'),\n description: t(\n 'ggnomad.bookings.next.eventDesc',\n 'Find live events to round out the itinerary.'\n ),\n icon: <Ticket className=\"size-4\" />,\n onClick: () => navigate('../entertainment'),\n },\n ]}\n />\n\n <RedeemTicketPanel onRedeemed={() => void refetch()} />\n\n {bookings.length > 0 && (\n <div className=\"grid gap-3 sm:grid-cols-4\">\n {[\n {\n status: 'CONFIRMED',\n label: t('ggnomad.bookings.pipeline.confirmed', 'Confirmed'),\n icon: CalendarCheck,\n },\n {\n status: 'CHECKED_IN',\n label: t('ggnomad.bookings.pipeline.checkedIn', 'Checked-in'),\n icon: Hotel,\n },\n {\n status: 'COMPLETED',\n label: t('ggnomad.bookings.pipeline.completed', 'Completed'),\n icon: Ticket,\n },\n {\n status: 'CANCELLED',\n label: t('ggnomad.bookings.pipeline.cancelled', 'Cancelled'),\n icon: XCircle,\n },\n ].map((stage) => {\n const Icon = stage.icon;\n const count = statusCounts[stage.status];\n return (\n <div\n key={stage.status}\n className=\"flex flex-col items-center gap-2 rounded-lg border border-border-seam bg-bg-surface/40 p-3 text-center\"\n >\n <div className=\"flex size-8 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <Icon className=\"size-4\" />\n </div>\n <p className=\"text-xs font-medium text-text-secondary\">{stage.label}</p>\n <p className=\"text-lg font-semibold tabular-nums text-text-primary\">{count}</p>\n </div>\n );\n })}\n </div>\n )}\n\n {bookings.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('ggnomad.bookings.emptyTitle', 'No bookings yet')}\n description={t(\n 'ggnomad.bookings.emptyDescription',\n 'Create your first booking to see it tracked here in real time.'\n )}\n />\n ) : (\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam p-5\">\n <h2 className=\"mb-4 text-lg font-medium tracking-tight text-text-primary\">\n {t('ggnomad.bookings.activeBookings', 'Your bookings')}\n </h2>\n <div className=\"grid gap-3 sm:grid-cols-2\">\n {bookings.map((booking) => (\n <EntityCard\n key={booking.id}\n title={booking.referenceCode}\n meta={booking.itemName}\n price={`${booking.currency} ${Number(booking.totalPrice).toFixed(2)}`}\n badge={\n <StatusBadge status={booking.status} live={booking.status !== 'CANCELLED'} />\n }\n footer={\n booking.status !== 'CANCELLED' ? (\n <CTAOverflowMenu\n align=\"end\"\n actions={[\n {\n label: t('ggnomad.bookings.actions.viewTicket', 'View mobile ticket'),\n onSelect: () =>\n setTicketTarget({\n id: booking.id,\n referenceCode: booking.referenceCode,\n status: booking.status,\n }),\n icon: <QrCode />,\n },\n {\n label: t('ggnomad.bookings.actions.cancel', 'Cancel booking'),\n onSelect: () =>\n setCancelTarget({\n id: booking.id,\n referenceCode: booking.referenceCode,\n }),\n intent: 'destructive',\n icon: <XCircle />,\n },\n ]}\n />\n ) : null\n }\n />\n ))}\n </div>\n </GlassCard>\n )}\n\n <AlertDialog\n open={cancelTarget !== null}\n onOpenChange={(open) => {\n if (!open) setCancelTarget(null);\n }}\n >\n <AlertDialogContent>\n <AlertDialogHeader>\n <AlertDialogTitle>\n {t('ggnomad.bookings.cancelDialog.title', 'Cancel this booking?')}\n </AlertDialogTitle>\n <AlertDialogDescription>\n {t(\n 'ggnomad.bookings.cancelDialog.body',\n 'This releases reservation {{ref}} and cannot be undone.'\n ).replace('{{ref}}', cancelTarget?.referenceCode ?? '')}\n </AlertDialogDescription>\n </AlertDialogHeader>\n <AlertDialogFooter>\n <AlertDialogCancel disabled={cancelling}>\n {t('ggnomad.bookings.cancelDialog.keep', 'Keep booking')}\n </AlertDialogCancel>\n <AlertDialogAction\n className=\"bg-action-danger-bg text-action-danger-text hover:bg-action-danger-bgHover\"\n disabled={cancelling}\n onClick={(event) => {\n event.preventDefault();\n void confirmCancel();\n }}\n >\n {cancelling\n ? t('ggnomad.bookings.cancelDialog.cancelling', 'Cancelling…')\n : t('ggnomad.bookings.cancelDialog.confirm', 'Cancel booking')}\n </AlertDialogAction>\n </AlertDialogFooter>\n </AlertDialogContent>\n </AlertDialog>\n\n {ticketTarget ? (\n <TicketDialog\n open={ticketTarget !== null}\n onOpenChange={(open) => {\n if (!open) setTicketTarget(null);\n }}\n bookingId={ticketTarget.id}\n referenceCode={ticketTarget.referenceCode}\n bookingStatus={ticketTarget.status}\n />\n ) : null}\n </PageShell>\n );\n}\n"],"mappings":"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"}
@@ -7,7 +7,7 @@ import "../components/index.js";
7
7
  import { useMemo as l } from "react";
8
8
  import { Building as u, CalendarCheck as d, Compass as f, DollarSign as p, Hotel as m, Search as h, Users as g } from "lucide-react";
9
9
  import { useI18n as _ } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
10
- import { useNavigate as v } from "react-router-dom";
10
+ import { useNavigate as v } from "react-router";
11
11
  import { useQuery as y } from "@apollo/client/react";
12
12
  import { EmphasisPanel as b, IllustratedEmptyState as x, NextSteps as S, PagePurpose as C } from "@burdenoff/fe-libs/ui";
13
13
  import { jsx as w, jsxs as T } from "react/jsx-runtime";
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../src/ggnomad/pages/DashboardPage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { useQuery } from '@apollo/client/react';\nimport type { TypedDocumentNode } from '@apollo/client';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Building, CalendarCheck, Compass, DollarSign, Hotel, Search, Users } from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport {\n EmphasisPanel,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n GGNOMAD_OVERVIEW_QUERY,\n GGNOMAD_BOOKINGS_QUERY,\n GGNOMAD_PROPERTIES_QUERY,\n GGNOMAD_TOURS_QUERY,\n GGNOMAD_EVENTS_QUERY,\n} from '../graphql';\nimport { PageShell } from './PageShell';\nimport { SectionCard, StatCard, StatusBadge } from '../components';\n\nconst CHART_COLORS = [\n 'var(--color-chart-1)',\n 'var(--color-chart-2)',\n 'var(--color-chart-3)',\n 'var(--color-chart-4)',\n 'var(--color-chart-5)',\n];\n\nconst humanizeCategory = (key: string): string =>\n key\n .replace(/[_-]+/g, ' ')\n .trim()\n .replace(/\\b\\w/g, (char) => char.toUpperCase()) || key;\n\nexport function DashboardPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const overview = useQuery(\n GGNOMAD_OVERVIEW_QUERY as TypedDocumentNode<\n {\n ggnomadOverviewStats: {\n users: number;\n listings: number;\n bookings: number;\n monthlyRevenue: number;\n };\n ggnomadAnalytics: {\n totalRevenue: number;\n totalBookings: number;\n totalUsers: number;\n conversionRate: number;\n revenueByCategory: Record<string, unknown>;\n };\n },\n Record<string, never>\n >\n );\n const bookings = useQuery(\n GGNOMAD_BOOKINGS_QUERY as TypedDocumentNode<\n {\n ggnomadBookings: Array<{\n id: string;\n referenceCode: string;\n itemName: string;\n status: string;\n }>;\n },\n { limit: number; offset: number }\n >,\n {\n variables: { limit: 5, offset: 0 },\n }\n );\n const properties = useQuery(\n GGNOMAD_PROPERTIES_QUERY as TypedDocumentNode<\n { ggnomadProperties: Array<{ id: string }> },\n { limit: number; offset: number }\n >,\n { variables: { limit: 4, offset: 0 } }\n );\n const tours = useQuery(\n GGNOMAD_TOURS_QUERY as TypedDocumentNode<\n { ggnomadTours: Array<{ id: string }> },\n { limit: number; offset: number }\n >,\n {\n variables: { limit: 4, offset: 0 },\n }\n );\n const events = useQuery(\n GGNOMAD_EVENTS_QUERY as TypedDocumentNode<\n { ggnomadEvents: Array<{ id: string }> },\n { limit: number; offset: number }\n >,\n {\n variables: { limit: 4, offset: 0 },\n }\n );\n\n const stats = overview.data?.ggnomadOverviewStats;\n const analytics = overview.data?.ggnomadAnalytics;\n const recentBookings = bookings.data?.ggnomadBookings ?? [];\n\n const categories = useMemo(() => {\n const raw = analytics?.revenueByCategory ?? {};\n return Object.entries(raw)\n .map(([key, value]) => ({ label: humanizeCategory(key), value: Number(value) }))\n .filter((entry) => Number.isFinite(entry.value) && entry.value > 0)\n .sort((a, b) => b.value - a.value);\n }, [analytics?.revenueByCategory]);\n\n const maxCategory = categories.length > 0 ? categories[0].value : 0;\n\n return (\n <PageShell\n hero\n title={t('ggnomad.dashboard.title', 'GGNomad Dashboard')}\n titleAccent={t('ggnomad.dashboard.title.accent', 'GGNomad')}\n subtitle={t(\n 'ggnomad.dashboard.subtitle',\n 'Unified travel, hospitality, tourism and entertainment operations'\n )}\n >\n <PagePurpose icon={<Compass className=\"size-4\" />}>\n {t(\n 'ggnomad.dashboard.purpose',\n 'Your command centre for the whole GGNomad marketplace — see live revenue, users, listings and bookings across travel, hospitality, tourism and entertainment in one place, and jump straight into the area you want to work on.'\n )}\n </PagePurpose>\n\n <NextSteps\n storageKey=\"ggnomad-dashboard\"\n steps={[\n {\n id: 'search',\n label: t('ggnomad.dashboard.next.search', 'Search the catalogue'),\n description: t(\n 'ggnomad.dashboard.next.searchDesc',\n 'Find inventory across every domain at once.'\n ),\n icon: <Search className=\"size-4\" />,\n onClick: () => navigate('search'),\n },\n {\n id: 'stays',\n label: t('ggnomad.dashboard.next.stays', 'Browse places to stay'),\n description: t(\n 'ggnomad.dashboard.next.staysDesc',\n 'Explore hospitality listings and open a property.'\n ),\n icon: <Hotel className=\"size-4\" />,\n onClick: () => navigate('hospitality'),\n },\n {\n id: 'tourism',\n label: t('ggnomad.dashboard.next.tourism', 'Discover tours & activities'),\n description: t(\n 'ggnomad.dashboard.next.tourismDesc',\n 'Plan what to do at each destination.'\n ),\n icon: <Compass className=\"size-4\" />,\n onClick: () => navigate('tourism'),\n },\n {\n id: 'bookings',\n label: t('ggnomad.dashboard.next.bookings', 'Manage bookings'),\n description: t(\n 'ggnomad.dashboard.next.bookingsDesc',\n 'Track and cancel reservations in real time.'\n ),\n icon: <CalendarCheck className=\"size-4\" />,\n onClick: () => navigate('bookings'),\n },\n ]}\n />\n\n <section className=\"grid gap-4 sm:grid-cols-2 xl:grid-cols-4\">\n <StatCard\n hero\n label={t('ggnomad.dashboard.stats.monthlyRevenue', 'Monthly Revenue')}\n value={`$${Number(stats?.monthlyRevenue ?? 0).toLocaleString()}`}\n icon={<DollarSign />}\n />\n <StatCard\n label={t('ggnomad.dashboard.stats.users', 'Users')}\n value={Number(stats?.users ?? 0).toLocaleString()}\n icon={<Users />}\n />\n <StatCard\n label={t('ggnomad.dashboard.stats.listings', 'Listings')}\n value={Number(stats?.listings ?? 0).toLocaleString()}\n icon={<Building />}\n />\n <StatCard\n label={t('ggnomad.dashboard.stats.bookings', 'Bookings')}\n value={Number(stats?.bookings ?? 0).toLocaleString()}\n icon={<CalendarCheck />}\n />\n </section>\n\n <EmphasisPanel>\n <div className=\"grid gap-4 sm:grid-cols-3\">\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('ggnomad.dashboard.inventory.properties', 'Properties')}\n </p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums text-text-primary\">\n {properties.data?.ggnomadProperties?.length ?? 0}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('ggnomad.dashboard.inventory.tours', 'Tours')}\n </p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums text-text-primary\">\n {tours.data?.ggnomadTours?.length ?? 0}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('ggnomad.dashboard.inventory.events', 'Events')}\n </p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums text-text-primary\">\n {events.data?.ggnomadEvents?.length ?? 0}\n </p>\n </div>\n </div>\n <p className=\"mt-4 text-sm text-text-secondary\">\n {t('ggnomad.dashboard.inventory.liveDataNotice', 'Data is live from the GraphQL API.')}\n </p>\n </EmphasisPanel>\n\n <SectionCard title={t('ggnomad.dashboard.sections.recentBookings', 'Recent bookings')}>\n {recentBookings.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('ggnomad.dashboard.recentBookings.emptyTitle', 'No recent bookings')}\n description={t(\n 'ggnomad.dashboard.recentBookings.emptyDescription',\n 'New bookings across travel, hospitality and tourism will appear here.'\n )}\n />\n ) : (\n <ul className=\"space-y-2 text-sm\">\n {recentBookings.map((booking) => (\n <li\n key={booking.id}\n className=\"flex items-center justify-between gap-3 rounded-lg border border-border-seam bg-bg-surface/40 p-3\"\n >\n <span className=\"min-w-0 truncate text-text-primary\">\n {booking.referenceCode} — {booking.itemName}\n </span>\n <StatusBadge status={booking.status} />\n </li>\n ))}\n </ul>\n )}\n </SectionCard>\n\n <SectionCard title={t('ggnomad.dashboard.sections.revenueByCategory', 'Revenue by category')}>\n {categories.length === 0 ? (\n <div className=\"flex flex-col items-center gap-2 py-8 text-center\">\n <span className=\"flex size-10 items-center justify-center rounded-full bg-[var(--color-accent-soft)] text-text-secondary\">\n <DollarSign className=\"size-5\" />\n </span>\n <p className=\"text-sm font-medium text-text-primary\">\n {t('ggnomad.dashboard.empty.title', 'Revenue data coming soon')}\n </p>\n <p className=\"max-w-sm text-sm text-text-secondary\">\n {t(\n 'ggnomad.dashboard.empty.description',\n 'Revenue by category appears here as bookings come in across stays, tours and events.'\n )}\n </p>\n </div>\n ) : (\n <ul className=\"flex flex-col gap-4\">\n {categories.map((category, index) => {\n const pct = maxCategory > 0 ? Math.max((category.value / maxCategory) * 100, 2) : 0;\n return (\n <li key={category.label}>\n <div className=\"flex items-baseline justify-between gap-3\">\n <span className=\"truncate text-sm font-medium text-text-primary\">\n {category.label}\n </span>\n <span className=\"flex-shrink-0 text-sm tabular-nums text-text-secondary\">\n ${category.value.toLocaleString()}\n </span>\n </div>\n <div className=\"mt-1.5 h-2.5 w-full overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-full rounded-full transition-[width] duration-500 ease-out\"\n style={{\n width: `${pct}%`,\n backgroundColor: CHART_COLORS[index % CHART_COLORS.length],\n }}\n />\n </div>\n </li>\n );\n })}\n </ul>\n )}\n </SectionCard>\n </PageShell>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../src/ggnomad/pages/DashboardPage.tsx"],"sourcesContent":["import { useMemo } from 'react';\nimport { useQuery } from '@apollo/client/react';\nimport type { TypedDocumentNode } from '@apollo/client';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Building, CalendarCheck, Compass, DollarSign, Hotel, Search, Users } from 'lucide-react';\nimport { useNavigate } from 'react-router';\nimport {\n EmphasisPanel,\n IllustratedEmptyState,\n NextSteps,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\n\nimport {\n GGNOMAD_OVERVIEW_QUERY,\n GGNOMAD_BOOKINGS_QUERY,\n GGNOMAD_PROPERTIES_QUERY,\n GGNOMAD_TOURS_QUERY,\n GGNOMAD_EVENTS_QUERY,\n} from '../graphql';\nimport { PageShell } from './PageShell';\nimport { SectionCard, StatCard, StatusBadge } from '../components';\n\nconst CHART_COLORS = [\n 'var(--color-chart-1)',\n 'var(--color-chart-2)',\n 'var(--color-chart-3)',\n 'var(--color-chart-4)',\n 'var(--color-chart-5)',\n];\n\nconst humanizeCategory = (key: string): string =>\n key\n .replace(/[_-]+/g, ' ')\n .trim()\n .replace(/\\b\\w/g, (char) => char.toUpperCase()) || key;\n\nexport function DashboardPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const overview = useQuery(\n GGNOMAD_OVERVIEW_QUERY as TypedDocumentNode<\n {\n ggnomadOverviewStats: {\n users: number;\n listings: number;\n bookings: number;\n monthlyRevenue: number;\n };\n ggnomadAnalytics: {\n totalRevenue: number;\n totalBookings: number;\n totalUsers: number;\n conversionRate: number;\n revenueByCategory: Record<string, unknown>;\n };\n },\n Record<string, never>\n >\n );\n const bookings = useQuery(\n GGNOMAD_BOOKINGS_QUERY as TypedDocumentNode<\n {\n ggnomadBookings: Array<{\n id: string;\n referenceCode: string;\n itemName: string;\n status: string;\n }>;\n },\n { limit: number; offset: number }\n >,\n {\n variables: { limit: 5, offset: 0 },\n }\n );\n const properties = useQuery(\n GGNOMAD_PROPERTIES_QUERY as TypedDocumentNode<\n { ggnomadProperties: Array<{ id: string }> },\n { limit: number; offset: number }\n >,\n { variables: { limit: 4, offset: 0 } }\n );\n const tours = useQuery(\n GGNOMAD_TOURS_QUERY as TypedDocumentNode<\n { ggnomadTours: Array<{ id: string }> },\n { limit: number; offset: number }\n >,\n {\n variables: { limit: 4, offset: 0 },\n }\n );\n const events = useQuery(\n GGNOMAD_EVENTS_QUERY as TypedDocumentNode<\n { ggnomadEvents: Array<{ id: string }> },\n { limit: number; offset: number }\n >,\n {\n variables: { limit: 4, offset: 0 },\n }\n );\n\n const stats = overview.data?.ggnomadOverviewStats;\n const analytics = overview.data?.ggnomadAnalytics;\n const recentBookings = bookings.data?.ggnomadBookings ?? [];\n\n const categories = useMemo(() => {\n const raw = analytics?.revenueByCategory ?? {};\n return Object.entries(raw)\n .map(([key, value]) => ({ label: humanizeCategory(key), value: Number(value) }))\n .filter((entry) => Number.isFinite(entry.value) && entry.value > 0)\n .sort((a, b) => b.value - a.value);\n }, [analytics?.revenueByCategory]);\n\n const maxCategory = categories.length > 0 ? categories[0].value : 0;\n\n return (\n <PageShell\n hero\n title={t('ggnomad.dashboard.title', 'GGNomad Dashboard')}\n titleAccent={t('ggnomad.dashboard.title.accent', 'GGNomad')}\n subtitle={t(\n 'ggnomad.dashboard.subtitle',\n 'Unified travel, hospitality, tourism and entertainment operations'\n )}\n >\n <PagePurpose icon={<Compass className=\"size-4\" />}>\n {t(\n 'ggnomad.dashboard.purpose',\n 'Your command centre for the whole GGNomad marketplace — see live revenue, users, listings and bookings across travel, hospitality, tourism and entertainment in one place, and jump straight into the area you want to work on.'\n )}\n </PagePurpose>\n\n <NextSteps\n storageKey=\"ggnomad-dashboard\"\n steps={[\n {\n id: 'search',\n label: t('ggnomad.dashboard.next.search', 'Search the catalogue'),\n description: t(\n 'ggnomad.dashboard.next.searchDesc',\n 'Find inventory across every domain at once.'\n ),\n icon: <Search className=\"size-4\" />,\n onClick: () => navigate('search'),\n },\n {\n id: 'stays',\n label: t('ggnomad.dashboard.next.stays', 'Browse places to stay'),\n description: t(\n 'ggnomad.dashboard.next.staysDesc',\n 'Explore hospitality listings and open a property.'\n ),\n icon: <Hotel className=\"size-4\" />,\n onClick: () => navigate('hospitality'),\n },\n {\n id: 'tourism',\n label: t('ggnomad.dashboard.next.tourism', 'Discover tours & activities'),\n description: t(\n 'ggnomad.dashboard.next.tourismDesc',\n 'Plan what to do at each destination.'\n ),\n icon: <Compass className=\"size-4\" />,\n onClick: () => navigate('tourism'),\n },\n {\n id: 'bookings',\n label: t('ggnomad.dashboard.next.bookings', 'Manage bookings'),\n description: t(\n 'ggnomad.dashboard.next.bookingsDesc',\n 'Track and cancel reservations in real time.'\n ),\n icon: <CalendarCheck className=\"size-4\" />,\n onClick: () => navigate('bookings'),\n },\n ]}\n />\n\n <section className=\"grid gap-4 sm:grid-cols-2 xl:grid-cols-4\">\n <StatCard\n hero\n label={t('ggnomad.dashboard.stats.monthlyRevenue', 'Monthly Revenue')}\n value={`$${Number(stats?.monthlyRevenue ?? 0).toLocaleString()}`}\n icon={<DollarSign />}\n />\n <StatCard\n label={t('ggnomad.dashboard.stats.users', 'Users')}\n value={Number(stats?.users ?? 0).toLocaleString()}\n icon={<Users />}\n />\n <StatCard\n label={t('ggnomad.dashboard.stats.listings', 'Listings')}\n value={Number(stats?.listings ?? 0).toLocaleString()}\n icon={<Building />}\n />\n <StatCard\n label={t('ggnomad.dashboard.stats.bookings', 'Bookings')}\n value={Number(stats?.bookings ?? 0).toLocaleString()}\n icon={<CalendarCheck />}\n />\n </section>\n\n <EmphasisPanel>\n <div className=\"grid gap-4 sm:grid-cols-3\">\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('ggnomad.dashboard.inventory.properties', 'Properties')}\n </p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums text-text-primary\">\n {properties.data?.ggnomadProperties?.length ?? 0}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('ggnomad.dashboard.inventory.tours', 'Tours')}\n </p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums text-text-primary\">\n {tours.data?.ggnomadTours?.length ?? 0}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('ggnomad.dashboard.inventory.events', 'Events')}\n </p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums text-text-primary\">\n {events.data?.ggnomadEvents?.length ?? 0}\n </p>\n </div>\n </div>\n <p className=\"mt-4 text-sm text-text-secondary\">\n {t('ggnomad.dashboard.inventory.liveDataNotice', 'Data is live from the GraphQL API.')}\n </p>\n </EmphasisPanel>\n\n <SectionCard title={t('ggnomad.dashboard.sections.recentBookings', 'Recent bookings')}>\n {recentBookings.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('ggnomad.dashboard.recentBookings.emptyTitle', 'No recent bookings')}\n description={t(\n 'ggnomad.dashboard.recentBookings.emptyDescription',\n 'New bookings across travel, hospitality and tourism will appear here.'\n )}\n />\n ) : (\n <ul className=\"space-y-2 text-sm\">\n {recentBookings.map((booking) => (\n <li\n key={booking.id}\n className=\"flex items-center justify-between gap-3 rounded-lg border border-border-seam bg-bg-surface/40 p-3\"\n >\n <span className=\"min-w-0 truncate text-text-primary\">\n {booking.referenceCode} — {booking.itemName}\n </span>\n <StatusBadge status={booking.status} />\n </li>\n ))}\n </ul>\n )}\n </SectionCard>\n\n <SectionCard title={t('ggnomad.dashboard.sections.revenueByCategory', 'Revenue by category')}>\n {categories.length === 0 ? (\n <div className=\"flex flex-col items-center gap-2 py-8 text-center\">\n <span className=\"flex size-10 items-center justify-center rounded-full bg-[var(--color-accent-soft)] text-text-secondary\">\n <DollarSign className=\"size-5\" />\n </span>\n <p className=\"text-sm font-medium text-text-primary\">\n {t('ggnomad.dashboard.empty.title', 'Revenue data coming soon')}\n </p>\n <p className=\"max-w-sm text-sm text-text-secondary\">\n {t(\n 'ggnomad.dashboard.empty.description',\n 'Revenue by category appears here as bookings come in across stays, tours and events.'\n )}\n </p>\n </div>\n ) : (\n <ul className=\"flex flex-col gap-4\">\n {categories.map((category, index) => {\n const pct = maxCategory > 0 ? Math.max((category.value / maxCategory) * 100, 2) : 0;\n return (\n <li key={category.label}>\n <div className=\"flex items-baseline justify-between gap-3\">\n <span className=\"truncate text-sm font-medium text-text-primary\">\n {category.label}\n </span>\n <span className=\"flex-shrink-0 text-sm tabular-nums text-text-secondary\">\n ${category.value.toLocaleString()}\n </span>\n </div>\n <div className=\"mt-1.5 h-2.5 w-full overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-full rounded-full transition-[width] duration-500 ease-out\"\n style={{\n width: `${pct}%`,\n backgroundColor: CHART_COLORS[index % CHART_COLORS.length],\n }}\n />\n </div>\n </li>\n );\n })}\n </ul>\n )}\n </SectionCard>\n </PageShell>\n );\n}\n"],"mappings":"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"}
@@ -12,7 +12,7 @@ import "../components/index.js";
12
12
  import { useState as u } from "react";
13
13
  import { Mic2 as d, Pencil as f, Ticket as p } from "lucide-react";
14
14
  import { useI18n as m } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
15
- import { useNavigate as h, useParams as g } from "react-router-dom";
15
+ import { useNavigate as h, useParams as g } from "react-router";
16
16
  import { useQuery as _ } from "@apollo/client/react";
17
17
  import { GlassCard as v, IllustratedEmptyState as y, NextSteps as b, PagePurpose as x } from "@burdenoff/fe-libs/ui";
18
18
  import { Fragment as S, jsx as C, jsxs as w } from "react/jsx-runtime";
@@ -1 +1 @@
1
- {"version":3,"file":"EventDetailsPage.js","names":[],"sources":["../../../src/ggnomad/pages/EventDetailsPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useQuery } from '@apollo/client/react';\nimport type { TypedDocumentNode } from '@apollo/client';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useNavigate, useParams } from 'react-router-dom';\nimport { Mic2, Pencil, Ticket } from 'lucide-react';\nimport { GlassCard, IllustratedEmptyState, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';\n\nimport { GGNOMAD_EVENT_QUERY } from '../graphql';\nimport { useGGNomad } from '../providers/GGNomadProvider';\nimport { joinPath } from '../utils/navigation';\nimport { PageShell } from './PageShell';\nimport {\n DetailRow,\n SectionCard,\n DetailHero,\n BookingPanel,\n ReviewsSection,\n ListingEditDialog,\n} from '../components';\n\nexport function EventDetailsPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const { basePath = '/' } = useGGNomad();\n const { id = '' } = useParams();\n const [editDialogOpen, setEditDialogOpen] = useState(false);\n const { data, refetch } = useQuery(\n GGNOMAD_EVENT_QUERY as TypedDocumentNode<\n {\n ggnomadEvent: {\n id: string;\n name: string;\n description?: string;\n category: string;\n startsAt: string;\n endsAt?: string;\n currency: string;\n price: number;\n status?: string;\n performers: string[];\n images?: string[];\n } | null;\n },\n { id: string }\n >,\n { variables: { id }, skip: !id }\n );\n const event = data?.ggnomadEvent;\n\n return (\n <PageShell\n title={event?.name ?? t('ggnomad.event.fallbackTitle', 'Event details')}\n subtitle={t('ggnomad.event.subtitle', 'Live event details, venue and lineup')}\n >\n {!event ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('ggnomad.event.notFoundTitle', 'No event found')}\n description={t(\n 'ggnomad.event.notFoundDescription',\n 'This event is unavailable or may have been removed.'\n )}\n />\n ) : (\n <>\n <DetailHero image={event.images?.[0]} alt={event.name} />\n <PagePurpose icon={<Ticket className=\"size-4\" />}>\n {t(\n 'ggnomad.event.purpose',\n 'All the details for this live event in one place — category, start and end times, ticket price and the full lineup — so you can decide and book tickets before they sell out.'\n )}\n </PagePurpose>\n\n <NextSteps\n storageKey=\"ggnomad-event-detail\"\n steps={[\n {\n id: 'browse',\n label: t('ggnomad.event.next.browse', 'See more events'),\n description: t(\n 'ggnomad.event.next.browseDesc',\n 'Return to Entertainment for other live experiences.'\n ),\n icon: <Ticket className=\"size-4\" />,\n onClick: () => navigate(joinPath(basePath, 'entertainment')),\n },\n ]}\n />\n\n <div className=\"grid gap-4 lg:grid-cols-3\">\n <div className=\"lg:col-span-2 space-y-4\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam p-5\">\n <div className=\"mb-4 flex items-center justify-between gap-3\">\n <h2 className=\"text-lg font-medium tracking-tight text-text-primary\">\n {t('ggnomad.event.details', 'Details')}\n </h2>\n <button\n onClick={() => setEditDialogOpen(true)}\n className=\"rounded-md p-2 text-text-secondary hover:bg-action-primary-bg hover:text-action-primary-text\"\n aria-label={t('ggnomad.event.editListing', 'Edit listing')}\n title={t('ggnomad.event.editListing', 'Edit listing')}\n >\n <Pencil className=\"size-4\" />\n </button>\n </div>\n <dl className=\"space-y-2 text-sm\">\n <DetailRow\n label={t('ggnomad.event.category', 'Category')}\n value={event.category}\n />\n <DetailRow\n label={t('ggnomad.event.starts', 'Starts')}\n value={new Date(event.startsAt).toLocaleString()}\n />\n <DetailRow\n label={t('ggnomad.event.ends', 'Ends')}\n value={\n event.endsAt\n ? new Date(event.endsAt).toLocaleString()\n : t('ggnomad.event.na', 'N/A')\n }\n />\n <DetailRow\n label={t('ggnomad.event.price', 'Price')}\n value={`${event.currency} ${Number(event.price).toFixed(2)}`}\n />\n </dl>\n </GlassCard>\n <SectionCard title={t('ggnomad.event.performers', 'Performers')}>\n <ul className=\"space-y-2 text-sm\">\n {(event.performers ?? []).map((performer) => (\n <li key={performer} className=\"flex items-start gap-2 text-text-primary\">\n <Mic2\n className=\"mt-0.5 size-4 flex-shrink-0 text-action-primary-bg\"\n aria-hidden=\"true\"\n />\n {performer}\n </li>\n ))}\n </ul>\n </SectionCard>\n <ReviewsSection targetType=\"EVENT\" targetId={event.id ?? id} />\n </div>\n <div>\n <BookingPanel\n bookingType=\"EVENT\"\n itemId={event.id ?? id}\n itemName={event.name}\n unitPrice={event.price ?? 0}\n currency={event.currency ?? 'USD'}\n eventDate={event.startsAt}\n />\n </div>\n </div>\n\n <ListingEditDialog\n open={editDialogOpen}\n onOpenChange={setEditDialogOpen}\n targetType=\"EVENT\"\n id={event.id ?? id}\n initial={{\n name: event.name,\n description: event.description,\n price: event.price,\n status: event.status,\n images: event.images,\n }}\n onSaved={() => refetch()}\n />\n </>\n )}\n </PageShell>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"EventDetailsPage.js","names":[],"sources":["../../../src/ggnomad/pages/EventDetailsPage.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useQuery } from '@apollo/client/react';\nimport type { TypedDocumentNode } from '@apollo/client';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useNavigate, useParams } from 'react-router';\nimport { Mic2, Pencil, Ticket } from 'lucide-react';\nimport { GlassCard, IllustratedEmptyState, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';\n\nimport { GGNOMAD_EVENT_QUERY } from '../graphql';\nimport { useGGNomad } from '../providers/GGNomadProvider';\nimport { joinPath } from '../utils/navigation';\nimport { PageShell } from './PageShell';\nimport {\n DetailRow,\n SectionCard,\n DetailHero,\n BookingPanel,\n ReviewsSection,\n ListingEditDialog,\n} from '../components';\n\nexport function EventDetailsPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const { basePath = '/' } = useGGNomad();\n const { id = '' } = useParams();\n const [editDialogOpen, setEditDialogOpen] = useState(false);\n const { data, refetch } = useQuery(\n GGNOMAD_EVENT_QUERY as TypedDocumentNode<\n {\n ggnomadEvent: {\n id: string;\n name: string;\n description?: string;\n category: string;\n startsAt: string;\n endsAt?: string;\n currency: string;\n price: number;\n status?: string;\n performers: string[];\n images?: string[];\n } | null;\n },\n { id: string }\n >,\n { variables: { id }, skip: !id }\n );\n const event = data?.ggnomadEvent;\n\n return (\n <PageShell\n title={event?.name ?? t('ggnomad.event.fallbackTitle', 'Event details')}\n subtitle={t('ggnomad.event.subtitle', 'Live event details, venue and lineup')}\n >\n {!event ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('ggnomad.event.notFoundTitle', 'No event found')}\n description={t(\n 'ggnomad.event.notFoundDescription',\n 'This event is unavailable or may have been removed.'\n )}\n />\n ) : (\n <>\n <DetailHero image={event.images?.[0]} alt={event.name} />\n <PagePurpose icon={<Ticket className=\"size-4\" />}>\n {t(\n 'ggnomad.event.purpose',\n 'All the details for this live event in one place — category, start and end times, ticket price and the full lineup — so you can decide and book tickets before they sell out.'\n )}\n </PagePurpose>\n\n <NextSteps\n storageKey=\"ggnomad-event-detail\"\n steps={[\n {\n id: 'browse',\n label: t('ggnomad.event.next.browse', 'See more events'),\n description: t(\n 'ggnomad.event.next.browseDesc',\n 'Return to Entertainment for other live experiences.'\n ),\n icon: <Ticket className=\"size-4\" />,\n onClick: () => navigate(joinPath(basePath, 'entertainment')),\n },\n ]}\n />\n\n <div className=\"grid gap-4 lg:grid-cols-3\">\n <div className=\"lg:col-span-2 space-y-4\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam p-5\">\n <div className=\"mb-4 flex items-center justify-between gap-3\">\n <h2 className=\"text-lg font-medium tracking-tight text-text-primary\">\n {t('ggnomad.event.details', 'Details')}\n </h2>\n <button\n onClick={() => setEditDialogOpen(true)}\n className=\"rounded-md p-2 text-text-secondary hover:bg-action-primary-bg hover:text-action-primary-text\"\n aria-label={t('ggnomad.event.editListing', 'Edit listing')}\n title={t('ggnomad.event.editListing', 'Edit listing')}\n >\n <Pencil className=\"size-4\" />\n </button>\n </div>\n <dl className=\"space-y-2 text-sm\">\n <DetailRow\n label={t('ggnomad.event.category', 'Category')}\n value={event.category}\n />\n <DetailRow\n label={t('ggnomad.event.starts', 'Starts')}\n value={new Date(event.startsAt).toLocaleString()}\n />\n <DetailRow\n label={t('ggnomad.event.ends', 'Ends')}\n value={\n event.endsAt\n ? new Date(event.endsAt).toLocaleString()\n : t('ggnomad.event.na', 'N/A')\n }\n />\n <DetailRow\n label={t('ggnomad.event.price', 'Price')}\n value={`${event.currency} ${Number(event.price).toFixed(2)}`}\n />\n </dl>\n </GlassCard>\n <SectionCard title={t('ggnomad.event.performers', 'Performers')}>\n <ul className=\"space-y-2 text-sm\">\n {(event.performers ?? []).map((performer) => (\n <li key={performer} className=\"flex items-start gap-2 text-text-primary\">\n <Mic2\n className=\"mt-0.5 size-4 flex-shrink-0 text-action-primary-bg\"\n aria-hidden=\"true\"\n />\n {performer}\n </li>\n ))}\n </ul>\n </SectionCard>\n <ReviewsSection targetType=\"EVENT\" targetId={event.id ?? id} />\n </div>\n <div>\n <BookingPanel\n bookingType=\"EVENT\"\n itemId={event.id ?? id}\n itemName={event.name}\n unitPrice={event.price ?? 0}\n currency={event.currency ?? 'USD'}\n eventDate={event.startsAt}\n />\n </div>\n </div>\n\n <ListingEditDialog\n open={editDialogOpen}\n onOpenChange={setEditDialogOpen}\n targetType=\"EVENT\"\n id={event.id ?? id}\n initial={{\n name: event.name,\n description: event.description,\n price: event.price,\n status: event.status,\n images: event.images,\n }}\n onSaved={() => refetch()}\n />\n </>\n )}\n </PageShell>\n );\n}\n"],"mappings":"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"}
@@ -12,7 +12,7 @@ import "../components/index.js";
12
12
  import { useState as u } from "react";
13
13
  import { Hotel as d, Pencil as f } from "lucide-react";
14
14
  import { useI18n as p } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
15
- import { useNavigate as m, useParams as h } from "react-router-dom";
15
+ import { useNavigate as m, useParams as h } from "react-router";
16
16
  import { useQuery as g } from "@apollo/client/react";
17
17
  import { GlassCard as _, IllustratedEmptyState as v, NextSteps as y, PagePurpose as b } from "@burdenoff/fe-libs/ui";
18
18
  import { Fragment as x, jsx as S, jsxs as C } from "react/jsx-runtime";