@burdenoff/microfe-ggnomad 2026.1029.1 → 2026.1029.2
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.
- package/dist/ggnomad/components/BookingPanel.d.ts.map +1 -1
- package/dist/ggnomad/components/BookingPanel.js +47 -64
- package/dist/ggnomad/components/BookingPanel.js.map +1 -1
- package/dist/ggnomad/pages/BookingsPage.d.ts.map +1 -1
- package/dist/ggnomad/pages/BookingsPage.js +54 -52
- package/dist/ggnomad/pages/BookingsPage.js.map +1 -1
- package/dist/ggnomad/pages/TripPlanDetailPage.d.ts.map +1 -1
- package/dist/ggnomad/pages/TripPlanDetailPage.js +278 -273
- package/dist/ggnomad/pages/TripPlanDetailPage.js.map +1 -1
- package/dist/ggnomad/utils/bookingDates.d.ts +21 -0
- package/dist/ggnomad/utils/bookingDates.d.ts.map +1 -0
- package/dist/ggnomad/utils/bookingDates.js +22 -0
- package/dist/ggnomad/utils/bookingDates.js.map +1 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BookingPanel.d.ts","sourceRoot":"","sources":["../../../src/ggnomad/components/BookingPanel.tsx"],"names":[],"mappings":"AAeA,KAAK,WAAW,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,CAAC;AAE9D,KAAK,iBAAiB,GAAG;IACvB,WAAW,EAAE,WAAW,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,oGAAoG;AACpG,wBAAgB,YAAY,CAAC,EAC3B,WAAW,EACX,MAAM,EACN,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,SAAS,GACV,EAAE,iBAAiB,+
|
|
1
|
+
{"version":3,"file":"BookingPanel.d.ts","sourceRoot":"","sources":["../../../src/ggnomad/components/BookingPanel.tsx"],"names":[],"mappings":"AAeA,KAAK,WAAW,GAAG,UAAU,GAAG,MAAM,GAAG,OAAO,GAAG,UAAU,CAAC;AAE9D,KAAK,iBAAiB,GAAG;IACvB,WAAW,EAAE,WAAW,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,sDAAsD;IACtD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,oGAAoG;AACpG,wBAAgB,YAAY,CAAC,EAC3B,WAAW,EACX,MAAM,EACN,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,SAAS,GACV,EAAE,iBAAiB,+BAgUnB"}
|
|
@@ -13,7 +13,7 @@ import { Button as f, GlassCard as p, Input as m } from "@burdenoff/fe-libs/ui";
|
|
|
13
13
|
import { Fragment as h, jsx as g, jsxs as _ } from "react/jsx-runtime";
|
|
14
14
|
//#region src/ggnomad/components/BookingPanel.tsx
|
|
15
15
|
function v({ bookingType: v, itemId: y, itemName: b, unitPrice: x, currency: S, eventDate: C }) {
|
|
16
|
-
let { t: w } = l(), T = u(), { basePath: E = "/" } = r(), [D, O] = o(""), [k, A] = o(""), [j, M] = o(""), [N, P] = o(1), [F, { loading: I }] = d(e), L = v === "PROPERTY" ? t(k, j) : 0, R = x * (v === "PROPERTY" ? L : N), z = R
|
|
16
|
+
let { t: w } = l(), T = u(), { basePath: E = "/" } = r(), [D, O] = o(""), [k, A] = o(""), [j, M] = o(""), [N, P] = o(1), [F, { loading: I }] = d(e), L = v === "PROPERTY" ? t(k, j) : 0, R = x * (v === "PROPERTY" ? L : N), z = R, B = async () => {
|
|
17
17
|
if (v === "PROPERTY") {
|
|
18
18
|
if (!k || !j) {
|
|
19
19
|
a.warning(w("ggnomad.booking.validation.missingDates", "Missing dates"), w("ggnomad.booking.validation.propertyDatesRequired", "Please select both check-in and check-out dates."));
|
|
@@ -40,7 +40,7 @@ function v({ bookingType: v, itemId: y, itemName: b, unitPrice: x, currency: S,
|
|
|
40
40
|
guestName: w("ggnomad.booking.defaultGuestName", "Demo Guest"),
|
|
41
41
|
guestEmail: "demo@burdenoff.com",
|
|
42
42
|
guests: N,
|
|
43
|
-
totalPrice: Math.round(
|
|
43
|
+
totalPrice: Math.round(z * 100) / 100,
|
|
44
44
|
currency: S,
|
|
45
45
|
paymentMethod: "card"
|
|
46
46
|
}, t = v === "PROPERTY" ? {
|
|
@@ -63,7 +63,7 @@ function v({ bookingType: v, itemId: y, itemName: b, unitPrice: x, currency: S,
|
|
|
63
63
|
} catch (e) {
|
|
64
64
|
a.error(w("ggnomad.booking.toast.createErrorTitle", "Could not create booking"), e instanceof Error ? e.message : String(e));
|
|
65
65
|
}
|
|
66
|
-
},
|
|
66
|
+
}, V = "text-sm font-medium text-text-primary", H = "w-full";
|
|
67
67
|
return /* @__PURE__ */ _(p, {
|
|
68
68
|
treatment: "glass",
|
|
69
69
|
glow: !0,
|
|
@@ -77,18 +77,18 @@ function v({ bookingType: v, itemId: y, itemName: b, unitPrice: x, currency: S,
|
|
|
77
77
|
className: "space-y-4 mb-6",
|
|
78
78
|
children: [v === "PROPERTY" ? /* @__PURE__ */ _(h, { children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
79
79
|
htmlFor: "check-in",
|
|
80
|
-
className:
|
|
80
|
+
className: V,
|
|
81
81
|
children: w("ggnomad.booking.checkIn", "Check-in")
|
|
82
82
|
}), /* @__PURE__ */ g(m, {
|
|
83
83
|
id: "check-in",
|
|
84
84
|
type: "date",
|
|
85
85
|
value: k,
|
|
86
86
|
onChange: (e) => A(e.target.value),
|
|
87
|
-
className: n(
|
|
87
|
+
className: n(H, "mt-2"),
|
|
88
88
|
"aria-label": w("ggnomad.booking.checkIn", "Check-in")
|
|
89
89
|
})] }), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
90
90
|
htmlFor: "check-out",
|
|
91
|
-
className:
|
|
91
|
+
className: V,
|
|
92
92
|
children: w("ggnomad.booking.checkOut", "Check-out")
|
|
93
93
|
}), /* @__PURE__ */ g(m, {
|
|
94
94
|
id: "check-out",
|
|
@@ -96,32 +96,32 @@ function v({ bookingType: v, itemId: y, itemName: b, unitPrice: x, currency: S,
|
|
|
96
96
|
min: k || void 0,
|
|
97
97
|
value: j,
|
|
98
98
|
onChange: (e) => M(e.target.value),
|
|
99
|
-
className: n(
|
|
99
|
+
className: n(H, "mt-2"),
|
|
100
100
|
"aria-label": w("ggnomad.booking.checkOut", "Check-out")
|
|
101
101
|
})] })] }) : v === "EVENT" ? /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
102
102
|
htmlFor: "event-date",
|
|
103
|
-
className:
|
|
103
|
+
className: V,
|
|
104
104
|
children: w("ggnomad.booking.date", "Date")
|
|
105
105
|
}), /* @__PURE__ */ g(m, {
|
|
106
106
|
id: "event-date",
|
|
107
107
|
type: "date",
|
|
108
108
|
value: C ? C.split("T")[0] : "",
|
|
109
109
|
disabled: !0,
|
|
110
|
-
className: n(
|
|
110
|
+
className: n(H, "mt-2"),
|
|
111
111
|
"aria-label": w("ggnomad.booking.date", "Date")
|
|
112
112
|
})] }) : /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
113
113
|
htmlFor: "booking-date",
|
|
114
|
-
className:
|
|
114
|
+
className: V,
|
|
115
115
|
children: w("ggnomad.booking.date", "Date")
|
|
116
116
|
}), /* @__PURE__ */ g(m, {
|
|
117
117
|
id: "booking-date",
|
|
118
118
|
type: "date",
|
|
119
119
|
value: D,
|
|
120
120
|
onChange: (e) => O(e.target.value),
|
|
121
|
-
className: n(
|
|
121
|
+
className: n(H, "mt-2"),
|
|
122
122
|
"aria-label": w("ggnomad.booking.date", "Date")
|
|
123
123
|
})] }), /* @__PURE__ */ _("div", { children: [/* @__PURE__ */ g("label", {
|
|
124
|
-
className:
|
|
124
|
+
className: V,
|
|
125
125
|
children: v === "PROPERTY" ? w("ggnomad.booking.guests", "Guests") : v === "EVENT" ? w("ggnomad.booking.tickets", "Tickets") : w("ggnomad.booking.participants", "Participants")
|
|
126
126
|
}), /* @__PURE__ */ _("div", {
|
|
127
127
|
className: "flex items-center gap-2 mt-2",
|
|
@@ -160,60 +160,43 @@ function v({ bookingType: v, itemId: y, itemName: b, unitPrice: x, currency: S,
|
|
|
160
160
|
children: w("ggnomad.booking.selectDatesForTotal", "Select check-in and check-out dates to see the total.")
|
|
161
161
|
}) : /* @__PURE__ */ _("div", {
|
|
162
162
|
className: "space-y-2 mb-6 p-3 rounded-md bg-bg-sunken",
|
|
163
|
-
children: [
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
/* @__PURE__ */
|
|
186
|
-
className: "
|
|
187
|
-
children:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
})]
|
|
198
|
-
}),
|
|
199
|
-
/* @__PURE__ */ _("div", {
|
|
200
|
-
className: "border-t border-border-seam pt-2 flex justify-between",
|
|
201
|
-
children: [/* @__PURE__ */ g("span", {
|
|
202
|
-
className: "font-semibold text-text-primary",
|
|
203
|
-
children: w("ggnomad.booking.total", "Total")
|
|
204
|
-
}), /* @__PURE__ */ _("span", {
|
|
205
|
-
className: "font-bold text-lg text-text-primary",
|
|
206
|
-
children: [
|
|
207
|
-
S,
|
|
208
|
-
" ",
|
|
209
|
-
B.toFixed(2)
|
|
210
|
-
]
|
|
211
|
-
})]
|
|
212
|
-
})
|
|
213
|
-
]
|
|
163
|
+
children: [/* @__PURE__ */ _("div", {
|
|
164
|
+
className: "flex justify-between text-sm",
|
|
165
|
+
children: [/* @__PURE__ */ _("span", {
|
|
166
|
+
className: "text-text-secondary",
|
|
167
|
+
children: [
|
|
168
|
+
S,
|
|
169
|
+
" ",
|
|
170
|
+
x.toFixed(2),
|
|
171
|
+
" ×",
|
|
172
|
+
" ",
|
|
173
|
+
v === "PROPERTY" ? L === 1 ? `1 ${w("ggnomad.booking.night", "night")}` : `${L} ${w("ggnomad.booking.nights", "nights")}` : `${N} ${w("ggnomad.booking.item", "item")}`
|
|
174
|
+
]
|
|
175
|
+
}), /* @__PURE__ */ _("span", {
|
|
176
|
+
className: "text-text-primary font-medium",
|
|
177
|
+
children: [
|
|
178
|
+
S,
|
|
179
|
+
" ",
|
|
180
|
+
R.toFixed(2)
|
|
181
|
+
]
|
|
182
|
+
})]
|
|
183
|
+
}), /* @__PURE__ */ _("div", {
|
|
184
|
+
className: "border-t border-border-seam pt-2 flex justify-between",
|
|
185
|
+
children: [/* @__PURE__ */ g("span", {
|
|
186
|
+
className: "font-semibold text-text-primary",
|
|
187
|
+
children: w("ggnomad.booking.total", "Total")
|
|
188
|
+
}), /* @__PURE__ */ _("span", {
|
|
189
|
+
className: "font-bold text-lg text-text-primary",
|
|
190
|
+
children: [
|
|
191
|
+
S,
|
|
192
|
+
" ",
|
|
193
|
+
z.toFixed(2)
|
|
194
|
+
]
|
|
195
|
+
})]
|
|
196
|
+
})]
|
|
214
197
|
}),
|
|
215
198
|
/* @__PURE__ */ g(f, {
|
|
216
|
-
onClick: () => void
|
|
199
|
+
onClick: () => void B(),
|
|
217
200
|
disabled: I,
|
|
218
201
|
className: "w-full bg-gradient-to-r from-action-primary-bg to-action-success-bg text-white font-semibold",
|
|
219
202
|
size: "lg",
|
|
@@ -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';\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 { nightsBetween } from '../utils/format';\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 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 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 // A property is priced per night, so the multiplier is the length of the selected\n // stay — derived from the two date inputs above rather than assumed. Every other\n // listing type is priced per ticket / seat / participant.\n const nights = bookingType === 'PROPERTY' ? nightsBetween(checkInDate, checkOutDate) : 0;\n const multiplier = bookingType === 'PROPERTY' ? 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 if (nights < 1) {\n notify.warning(\n t('ggnomad.booking.validation.invalidStayTitle', 'Check-out must be after check-in'),\n t(\n 'ggnomad.booking.validation.invalidStayBody',\n 'Pick a check-out date at least one night after check-in.'\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 min={checkInDate || undefined}\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 — a per-night total is only meaningful once the stay\n length is known, so it is withheld until both dates are chosen. */}\n {bookingType === 'PROPERTY' && nights < 1 ? (\n <div className=\"mb-6 rounded-md bg-bg-sunken p-3 text-sm text-text-secondary\">\n {t(\n 'ggnomad.booking.selectDatesForTotal',\n 'Select check-in and check-out dates to see the total.'\n )}\n </div>\n ) : (\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\n ? `1 ${t('ggnomad.booking.night', 'night')}`\n : `${nights} ${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\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 { nightsBetween } from '../utils/format';\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 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 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 // A property is priced per night, so the multiplier is the length of the selected\n // stay — derived from the two date inputs above rather than assumed. Every other\n // listing type is priced per ticket / seat / participant.\n const nights = bookingType === 'PROPERTY' ? nightsBetween(checkInDate, checkOutDate) : 0;\n const multiplier = bookingType === 'PROPERTY' ? nights : quantity;\n const subtotal = unitPrice * multiplier;\n // BOFF-10193 — the service prices the booking from the listing (nightly or\n // per-person rate x the quantity, plus any demand surcharge on busy dates)\n // and does not add tax, so the panel shows that same amount. The 10% \"tax\"\n // line this panel used to add had no counterpart in the booking record.\n const total = subtotal;\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 if (nights < 1) {\n notify.warning(\n t('ggnomad.booking.validation.invalidStayTitle', 'Check-out must be after check-in'),\n t(\n 'ggnomad.booking.validation.invalidStayBody',\n 'Pick a check-out date at least one night after check-in.'\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 min={checkInDate || undefined}\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 — a per-night total is only meaningful once the stay\n length is known, so it is withheld until both dates are chosen. */}\n {bookingType === 'PROPERTY' && nights < 1 ? (\n <div className=\"mb-6 rounded-md bg-bg-sunken p-3 text-sm text-text-secondary\">\n {t(\n 'ggnomad.booking.selectDatesForTotal',\n 'Select check-in and check-out dates to see the total.'\n )}\n </div>\n ) : (\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\n ? `1 ${t('ggnomad.booking.night', 'night')}`\n : `${nights} ${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=\"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\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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BookingsPage.d.ts","sourceRoot":"","sources":["../../../src/ggnomad/pages/BookingsPage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"BookingsPage.d.ts","sourceRoot":"","sources":["../../../src/ggnomad/pages/BookingsPage.tsx"],"names":[],"mappings":"AA4EA,wBAAgB,YAAY,gCAif3B"}
|
|
@@ -2,26 +2,27 @@ import { CANCEL_GGNOMAD_BOOKING_MUTATION as e, CREATE_GGNOMAD_BOOKING_MUTATION a
|
|
|
2
2
|
import { formatDate as i, humanizeEnum as a } from "../utils/format.js";
|
|
3
3
|
import { PageShell as o } from "./PageShell.js";
|
|
4
4
|
import { cn as s } from "../utils/cn.js";
|
|
5
|
-
import { StatusBadge as
|
|
6
|
-
import { EntityCard as
|
|
7
|
-
import { notify as
|
|
8
|
-
import { TicketDialog as
|
|
9
|
-
import { RedeemTicketPanel as
|
|
5
|
+
import { StatusBadge as ee } from "../components/StatusBadge.js";
|
|
6
|
+
import { EntityCard as te } from "../components/EntityCard.js";
|
|
7
|
+
import { notify as c } from "../utils/notify.js";
|
|
8
|
+
import { TicketDialog as l } from "../components/TicketDialog.js";
|
|
9
|
+
import { RedeemTicketPanel as u } from "../components/RedeemTicketPanel.js";
|
|
10
10
|
import "../components/index.js";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
11
|
+
import { stayDates as d } from "../utils/bookingDates.js";
|
|
12
|
+
import { useMemo as f, useState as p } from "react";
|
|
13
|
+
import { CalendarCheck as m, CalendarRange as h, Compass as g, Hotel as _, Leaf as v, Plus as y, QrCode as b, Ticket as x, Users as S, XCircle as C } from "lucide-react";
|
|
14
|
+
import { useI18n as w } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
15
|
+
import { useNavigate as T } from "react-router";
|
|
16
|
+
import { useMutation as E, useQuery as D } from "@apollo/client/react";
|
|
17
|
+
import { AlertDialog as ne, AlertDialogAction as re, AlertDialogCancel as ie, AlertDialogContent as O, AlertDialogDescription as k, AlertDialogFooter as A, AlertDialogHeader as j, AlertDialogTitle as M, CTAOverflowMenu as N, GlassCard as P, IllustratedEmptyState as F, NextSteps as I, PagePurpose as L } from "@burdenoff/fe-libs/ui";
|
|
17
18
|
import { jsx as R, jsxs as z } from "react/jsx-runtime";
|
|
18
19
|
//#region src/ggnomad/pages/BookingsPage.tsx
|
|
19
20
|
function B() {
|
|
20
|
-
let { t: B } =
|
|
21
|
+
let { t: B } = w(), V = T(), [H, U] = p(null), [W, G] = p(null), [K, q] = p(null), { data: J, refetch: Y } = D(r, { variables: {
|
|
21
22
|
limit: 20,
|
|
22
23
|
offset: 0,
|
|
23
24
|
status: K ?? void 0
|
|
24
|
-
} }), { data: X } =
|
|
25
|
+
} }), { data: X } = D(n), [ae, { loading: oe }] = E(t), [se, { loading: Z }] = E(e), Q = f(() => J?.ggnomadBookings ?? [], [J?.ggnomadBookings]), ce = f(() => {
|
|
25
26
|
let e = {
|
|
26
27
|
CONFIRMED: 0,
|
|
27
28
|
CHECKED_IN: 0,
|
|
@@ -32,7 +33,7 @@ function B() {
|
|
|
32
33
|
let n = t.status?.toUpperCase();
|
|
33
34
|
n && n in e && (e[n] += 1);
|
|
34
35
|
}), e;
|
|
35
|
-
}, [Q]),
|
|
36
|
+
}, [Q]), le = () => {
|
|
36
37
|
let e = X?.ggnomadProperties?.[0];
|
|
37
38
|
if (e) return {
|
|
38
39
|
type: "PROPERTY",
|
|
@@ -58,15 +59,15 @@ function B() {
|
|
|
58
59
|
type: "ACTIVITY",
|
|
59
60
|
listing: r
|
|
60
61
|
} : null;
|
|
61
|
-
},
|
|
62
|
-
let e =
|
|
62
|
+
}, $ = async () => {
|
|
63
|
+
let e = le();
|
|
63
64
|
if (!e) {
|
|
64
|
-
|
|
65
|
+
c.warning(B("ggnomad.bookings.toast.noListingsTitle", "No listings available to book yet"), B("ggnomad.bookings.toast.noListingsBody", "Add a property, tour, event or activity first, then create a booking against it."));
|
|
65
66
|
return;
|
|
66
67
|
}
|
|
67
68
|
let { type: t, listing: n } = e;
|
|
68
69
|
try {
|
|
69
|
-
let e = await
|
|
70
|
+
let e = await ae({ variables: { input: {
|
|
70
71
|
bookingType: t,
|
|
71
72
|
itemId: n.id,
|
|
72
73
|
itemName: n.name,
|
|
@@ -75,30 +76,31 @@ function B() {
|
|
|
75
76
|
guests: 2,
|
|
76
77
|
totalPrice: Number(n.price ?? 0),
|
|
77
78
|
currency: n.currency ?? "USD",
|
|
78
|
-
paymentMethod: "card"
|
|
79
|
+
paymentMethod: "card",
|
|
80
|
+
...t === "PROPERTY" ? d(null, null) : {}
|
|
79
81
|
} } });
|
|
80
82
|
if (e.error) {
|
|
81
|
-
|
|
83
|
+
c.error(B("ggnomad.bookings.toast.createErrorTitle", "Could not create booking"), e.error.message);
|
|
82
84
|
return;
|
|
83
85
|
}
|
|
84
86
|
let r = e.data?.createGgnomadBooking;
|
|
85
|
-
|
|
87
|
+
c.success(B("ggnomad.bookings.toast.createSuccessTitle", "Booking confirmed"), r ? B("ggnomad.bookings.toast.createSuccessBody", "{{ref}} · {{item}}").replace("{{ref}}", r.referenceCode).replace("{{item}}", r.itemName) : n.name), await Y();
|
|
86
88
|
} catch (e) {
|
|
87
|
-
|
|
89
|
+
c.error(B("ggnomad.bookings.toast.createErrorTitle", "Could not create booking"), e instanceof Error ? e.message : String(e));
|
|
88
90
|
}
|
|
89
|
-
},
|
|
91
|
+
}, ue = async () => {
|
|
90
92
|
if (H) try {
|
|
91
|
-
let e = await
|
|
93
|
+
let e = await se({ variables: {
|
|
92
94
|
id: H.id,
|
|
93
95
|
reason: B("ggnomad.bookings.actions.cancelReason", "Cancelled via UI")
|
|
94
96
|
} });
|
|
95
97
|
if (e.error) {
|
|
96
|
-
|
|
98
|
+
c.error(B("ggnomad.bookings.toast.cancelErrorTitle", "Could not cancel booking"), e.error.message);
|
|
97
99
|
return;
|
|
98
100
|
}
|
|
99
|
-
|
|
101
|
+
c.success(B("ggnomad.bookings.toast.cancelSuccessTitle", "Booking cancelled"), H.referenceCode), await Y();
|
|
100
102
|
} catch (e) {
|
|
101
|
-
|
|
103
|
+
c.error(B("ggnomad.bookings.toast.cancelErrorTitle", "Could not cancel booking"), e instanceof Error ? e.message : String(e));
|
|
102
104
|
} finally {
|
|
103
105
|
U(null);
|
|
104
106
|
}
|
|
@@ -110,15 +112,15 @@ function B() {
|
|
|
110
112
|
actions: /* @__PURE__ */ R(N, {
|
|
111
113
|
primary: {
|
|
112
114
|
label: B("ggnomad.bookings.actions.createDemoBooking", "Create demo booking"),
|
|
113
|
-
onSelect: () => void
|
|
114
|
-
disabled:
|
|
115
|
-
icon: /* @__PURE__ */ R(
|
|
115
|
+
onSelect: () => void $(),
|
|
116
|
+
disabled: oe,
|
|
117
|
+
icon: /* @__PURE__ */ R(y, {})
|
|
116
118
|
},
|
|
117
119
|
actions: []
|
|
118
120
|
}),
|
|
119
121
|
children: [
|
|
120
122
|
/* @__PURE__ */ R(L, {
|
|
121
|
-
icon: /* @__PURE__ */ R(
|
|
123
|
+
icon: /* @__PURE__ */ R(m, { className: "size-4" }),
|
|
122
124
|
children: B("ggnomad.bookings.purpose", "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.")
|
|
123
125
|
}),
|
|
124
126
|
/* @__PURE__ */ R(I, {
|
|
@@ -128,51 +130,51 @@ function B() {
|
|
|
128
130
|
id: "book-stay",
|
|
129
131
|
label: B("ggnomad.bookings.next.stay", "Book a place to stay"),
|
|
130
132
|
description: B("ggnomad.bookings.next.stayDesc", "Browse hospitality listings and reserve a property."),
|
|
131
|
-
icon: /* @__PURE__ */ R(
|
|
133
|
+
icon: /* @__PURE__ */ R(_, { className: "size-4" }),
|
|
132
134
|
onClick: () => V("../hospitality")
|
|
133
135
|
},
|
|
134
136
|
{
|
|
135
137
|
id: "book-tour",
|
|
136
138
|
label: B("ggnomad.bookings.next.tour", "Add a tour or activity"),
|
|
137
139
|
description: B("ggnomad.bookings.next.tourDesc", "Pick a curated experience from Tourism."),
|
|
138
|
-
icon: /* @__PURE__ */ R(
|
|
140
|
+
icon: /* @__PURE__ */ R(g, { className: "size-4" }),
|
|
139
141
|
onClick: () => V("../tourism")
|
|
140
142
|
},
|
|
141
143
|
{
|
|
142
144
|
id: "book-event",
|
|
143
145
|
label: B("ggnomad.bookings.next.event", "Grab event tickets"),
|
|
144
146
|
description: B("ggnomad.bookings.next.eventDesc", "Find live events to round out the itinerary."),
|
|
145
|
-
icon: /* @__PURE__ */ R(
|
|
147
|
+
icon: /* @__PURE__ */ R(x, { className: "size-4" }),
|
|
146
148
|
onClick: () => V("../entertainment")
|
|
147
149
|
}
|
|
148
150
|
]
|
|
149
151
|
}),
|
|
150
|
-
/* @__PURE__ */ R(
|
|
152
|
+
/* @__PURE__ */ R(u, { onRedeemed: () => void Y() }),
|
|
151
153
|
Q.length > 0 || K ? /* @__PURE__ */ R("div", {
|
|
152
154
|
className: "grid gap-3 sm:grid-cols-4",
|
|
153
155
|
children: [
|
|
154
156
|
{
|
|
155
157
|
status: "CONFIRMED",
|
|
156
158
|
label: B("ggnomad.bookings.pipeline.confirmed", "Confirmed"),
|
|
157
|
-
icon:
|
|
159
|
+
icon: m
|
|
158
160
|
},
|
|
159
161
|
{
|
|
160
162
|
status: "CHECKED_IN",
|
|
161
163
|
label: B("ggnomad.bookings.pipeline.checkedIn", "Checked-in"),
|
|
162
|
-
icon:
|
|
164
|
+
icon: _
|
|
163
165
|
},
|
|
164
166
|
{
|
|
165
167
|
status: "COMPLETED",
|
|
166
168
|
label: B("ggnomad.bookings.pipeline.completed", "Completed"),
|
|
167
|
-
icon:
|
|
169
|
+
icon: x
|
|
168
170
|
},
|
|
169
171
|
{
|
|
170
172
|
status: "CANCELLED",
|
|
171
173
|
label: B("ggnomad.bookings.pipeline.cancelled", "Cancelled"),
|
|
172
|
-
icon:
|
|
174
|
+
icon: C
|
|
173
175
|
}
|
|
174
176
|
].map((e) => {
|
|
175
|
-
let t = e.icon, n = K === e.status, r =
|
|
177
|
+
let t = e.icon, n = K === e.status, r = ce[e.status];
|
|
176
178
|
return /* @__PURE__ */ z("button", {
|
|
177
179
|
type: "button",
|
|
178
180
|
"aria-pressed": n,
|
|
@@ -222,11 +224,11 @@ function B() {
|
|
|
222
224
|
className: "grid gap-3 sm:grid-cols-2",
|
|
223
225
|
children: Q.map((e) => {
|
|
224
226
|
let t = i(e.checkIn), n = i(e.checkOut), r = i(e.eventDate), o = t ? n ? `${t} – ${n}` : t : r;
|
|
225
|
-
return /* @__PURE__ */ R(
|
|
227
|
+
return /* @__PURE__ */ R(te, {
|
|
226
228
|
title: e.referenceCode,
|
|
227
229
|
meta: e.itemName,
|
|
228
230
|
price: `${e.currency} ${Number(e.totalPrice).toFixed(2)}`,
|
|
229
|
-
badge: /* @__PURE__ */ R(
|
|
231
|
+
badge: /* @__PURE__ */ R(ee, {
|
|
230
232
|
status: e.status,
|
|
231
233
|
live: e.status !== "CANCELLED"
|
|
232
234
|
}),
|
|
@@ -238,14 +240,14 @@ function B() {
|
|
|
238
240
|
/* @__PURE__ */ R("span", { children: a(e.bookingType) }),
|
|
239
241
|
o ? /* @__PURE__ */ z("span", {
|
|
240
242
|
className: "inline-flex items-center gap-1",
|
|
241
|
-
children: [/* @__PURE__ */ R(
|
|
243
|
+
children: [/* @__PURE__ */ R(h, {
|
|
242
244
|
className: "size-3.5",
|
|
243
245
|
"aria-hidden": "true"
|
|
244
246
|
}), o]
|
|
245
247
|
}) : null,
|
|
246
248
|
/* @__PURE__ */ z("span", {
|
|
247
249
|
className: "inline-flex items-center gap-1 tabular-nums",
|
|
248
|
-
children: [/* @__PURE__ */ R(
|
|
250
|
+
children: [/* @__PURE__ */ R(S, {
|
|
249
251
|
className: "size-3.5",
|
|
250
252
|
"aria-hidden": "true"
|
|
251
253
|
}), e.guests === 1 ? B("ggnomad.bookings.oneGuest", "1 guest") : B("ggnomad.bookings.guests", "{{n}} guests").replace("{{n}}", String(e.guests))]
|
|
@@ -253,7 +255,7 @@ function B() {
|
|
|
253
255
|
/* @__PURE__ */ R("span", { children: B("ggnomad.bookings.payment", "Payment: {{status}}").replace("{{status}}", a(e.paymentStatus).toLowerCase()) }),
|
|
254
256
|
e.carbonEstimateKg !== null && e.carbonEstimateKg !== void 0 ? /* @__PURE__ */ z("span", {
|
|
255
257
|
className: "inline-flex items-center gap-1 tabular-nums text-status-success-text",
|
|
256
|
-
children: [/* @__PURE__ */ R(
|
|
258
|
+
children: [/* @__PURE__ */ R(v, {
|
|
257
259
|
className: "size-3.5",
|
|
258
260
|
"aria-hidden": "true"
|
|
259
261
|
}), B("ggnomad.bookings.carbonEstimate", "≈{{kg}} kg CO₂e est.").replace("{{kg}}", e.carbonEstimateKg.toLocaleString())]
|
|
@@ -268,7 +270,7 @@ function B() {
|
|
|
268
270
|
referenceCode: e.referenceCode,
|
|
269
271
|
status: e.status
|
|
270
272
|
}),
|
|
271
|
-
icon: /* @__PURE__ */ R(
|
|
273
|
+
icon: /* @__PURE__ */ R(b, {})
|
|
272
274
|
}, {
|
|
273
275
|
label: B("ggnomad.bookings.actions.cancel", "Cancel booking"),
|
|
274
276
|
onSelect: () => U({
|
|
@@ -276,7 +278,7 @@ function B() {
|
|
|
276
278
|
referenceCode: e.referenceCode
|
|
277
279
|
}),
|
|
278
280
|
intent: "destructive",
|
|
279
|
-
icon: /* @__PURE__ */ R(
|
|
281
|
+
icon: /* @__PURE__ */ R(C, {})
|
|
280
282
|
}]
|
|
281
283
|
})]
|
|
282
284
|
})
|
|
@@ -284,24 +286,24 @@ function B() {
|
|
|
284
286
|
})
|
|
285
287
|
})]
|
|
286
288
|
}),
|
|
287
|
-
/* @__PURE__ */ R(
|
|
289
|
+
/* @__PURE__ */ R(ne, {
|
|
288
290
|
open: H !== null,
|
|
289
291
|
onOpenChange: (e) => {
|
|
290
292
|
e || U(null);
|
|
291
293
|
},
|
|
292
|
-
children: /* @__PURE__ */ z(
|
|
294
|
+
children: /* @__PURE__ */ z(O, { children: [/* @__PURE__ */ z(j, { children: [/* @__PURE__ */ R(M, { children: B("ggnomad.bookings.cancelDialog.title", "Cancel this booking?") }), /* @__PURE__ */ R(k, { children: B("ggnomad.bookings.cancelDialog.body", "This releases reservation {{ref}} and cannot be undone.").replace("{{ref}}", H?.referenceCode ?? "") })] }), /* @__PURE__ */ z(A, { children: [/* @__PURE__ */ R(ie, {
|
|
293
295
|
disabled: Z,
|
|
294
296
|
children: B("ggnomad.bookings.cancelDialog.keep", "Keep booking")
|
|
295
|
-
}), /* @__PURE__ */ R(
|
|
297
|
+
}), /* @__PURE__ */ R(re, {
|
|
296
298
|
className: "bg-action-danger-bg text-action-danger-text hover:bg-action-danger-bgHover",
|
|
297
299
|
disabled: Z,
|
|
298
300
|
onClick: (e) => {
|
|
299
|
-
e.preventDefault(),
|
|
301
|
+
e.preventDefault(), ue();
|
|
300
302
|
},
|
|
301
303
|
children: Z ? B("ggnomad.bookings.cancelDialog.cancelling", "Cancelling…") : B("ggnomad.bookings.cancelDialog.confirm", "Cancel booking")
|
|
302
304
|
})] })] })
|
|
303
305
|
}),
|
|
304
|
-
W ? /* @__PURE__ */ R(
|
|
306
|
+
W ? /* @__PURE__ */ R(l, {
|
|
305
307
|
open: W !== null,
|
|
306
308
|
onOpenChange: (e) => {
|
|
307
309
|
e || G(null);
|