@burdenoff/microfe-ggnomad 2026.626.1 → 2026.705.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.
- package/dist/ggnomad/components/LoadMoreButton.js +19 -0
- package/dist/ggnomad/components/LoadMoreButton.js.map +1 -0
- package/dist/ggnomad/components/index.js +1 -0
- package/dist/ggnomad/graphql.js +61 -17
- package/dist/ggnomad/graphql.js.map +1 -1
- package/dist/ggnomad/pages/ActivityDetailsPage.js +55 -53
- package/dist/ggnomad/pages/ActivityDetailsPage.js.map +1 -1
- package/dist/ggnomad/pages/AnalyticsPage.js +82 -35
- package/dist/ggnomad/pages/AnalyticsPage.js.map +1 -1
- package/dist/ggnomad/pages/BookingsPage.js +143 -69
- package/dist/ggnomad/pages/BookingsPage.js.map +1 -1
- package/dist/ggnomad/pages/EntertainmentPage.js +43 -34
- package/dist/ggnomad/pages/EntertainmentPage.js.map +1 -1
- package/dist/ggnomad/pages/EventDetailsPage.js +55 -53
- package/dist/ggnomad/pages/EventDetailsPage.js.map +1 -1
- package/dist/ggnomad/pages/FavoritesPage.js +98 -38
- package/dist/ggnomad/pages/FavoritesPage.js.map +1 -1
- package/dist/ggnomad/pages/HospitalityPage.js +49 -40
- package/dist/ggnomad/pages/HospitalityPage.js.map +1 -1
- package/dist/ggnomad/pages/PropertyDetailsPage.js +60 -58
- package/dist/ggnomad/pages/PropertyDetailsPage.js.map +1 -1
- package/dist/ggnomad/pages/ProviderPage.js +70 -61
- package/dist/ggnomad/pages/ProviderPage.js.map +1 -1
- package/dist/ggnomad/pages/TourDetailsPage.js +55 -53
- package/dist/ggnomad/pages/TourDetailsPage.js.map +1 -1
- package/dist/ggnomad/pages/TourismPage.js +48 -40
- package/dist/ggnomad/pages/TourismPage.js.map +1 -1
- package/dist/ggnomad/utils/notify.js +36 -0
- package/dist/ggnomad/utils/notify.js.map +1 -0
- package/package.json +2 -1
|
@@ -1,119 +1,193 @@
|
|
|
1
|
-
import { CANCEL_GGNOMAD_BOOKING_MUTATION as e, CREATE_GGNOMAD_BOOKING_MUTATION as t,
|
|
2
|
-
import { PageShell as
|
|
3
|
-
import { StatusBadge as
|
|
4
|
-
import { EntityCard as
|
|
1
|
+
import { CANCEL_GGNOMAD_BOOKING_MUTATION as e, CREATE_GGNOMAD_BOOKING_MUTATION as t, GGNOMAD_BOOKABLE_SAMPLE_QUERY as n, GGNOMAD_BOOKINGS_QUERY as r } from "../graphql.js";
|
|
2
|
+
import { PageShell as i } from "./PageShell.js";
|
|
3
|
+
import { StatusBadge as a } from "../components/StatusBadge.js";
|
|
4
|
+
import { EntityCard as o } from "../components/EntityCard.js";
|
|
5
5
|
import "../components/index.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
6
|
+
import { notify as s } from "../utils/notify.js";
|
|
7
|
+
import { useState as c } from "react";
|
|
8
|
+
import { CalendarCheck as l, Compass as u, Hotel as d, Plus as f, Ticket as p, XCircle as m } from "lucide-react";
|
|
9
|
+
import { useI18n as h } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
10
|
+
import { useNavigate as g } from "react-router-dom";
|
|
11
|
+
import { useMutation as _, useQuery as v } from "@apollo/client/react";
|
|
12
|
+
import { AlertDialog as y, AlertDialogAction as b, AlertDialogCancel as x, AlertDialogContent as S, AlertDialogDescription as C, AlertDialogFooter as w, AlertDialogHeader as T, AlertDialogTitle as E, CTAOverflowMenu as D, GlassCard as O, IllustratedEmptyState as k, NextSteps as A, PagePurpose as j } from "@burdenoff/fe-libs/ui";
|
|
13
|
+
import { jsx as M, jsxs as N } from "react/jsx-runtime";
|
|
12
14
|
//#region src/ggnomad/pages/BookingsPage.tsx
|
|
13
|
-
function
|
|
14
|
-
let { t:
|
|
15
|
+
function P() {
|
|
16
|
+
let { t: P } = h(), F = g(), [I, L] = c(null), { data: R, refetch: z } = v(r, { variables: {
|
|
15
17
|
first: 20,
|
|
16
18
|
skip: 0
|
|
17
|
-
} }),
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
19
|
+
} }), { data: B } = v(n), [V, { loading: H }] = _(t), [U, { loading: W }] = _(e), G = R?.ggnomadBookings ?? [], K = () => {
|
|
20
|
+
let e = B?.ggnomadProperties?.[0];
|
|
21
|
+
if (e) return {
|
|
22
|
+
type: "PROPERTY",
|
|
23
|
+
listing: {
|
|
24
|
+
id: e.id,
|
|
25
|
+
name: e.name,
|
|
26
|
+
price: e.pricePerNight,
|
|
27
|
+
currency: e.currency
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
let t = B?.ggnomadTours?.[0];
|
|
31
|
+
if (t) return {
|
|
32
|
+
type: "TOUR",
|
|
33
|
+
listing: t
|
|
34
|
+
};
|
|
35
|
+
let n = B?.ggnomadEvents?.[0];
|
|
36
|
+
if (n) return {
|
|
37
|
+
type: "EVENT",
|
|
38
|
+
listing: n
|
|
39
|
+
};
|
|
40
|
+
let r = B?.ggnomadActivities?.[0];
|
|
41
|
+
return r ? {
|
|
42
|
+
type: "ACTIVITY",
|
|
43
|
+
listing: r
|
|
44
|
+
} : null;
|
|
45
|
+
}, q = async () => {
|
|
46
|
+
let e = K();
|
|
47
|
+
if (!e) {
|
|
48
|
+
s.warning(P("ggnomad.bookings.toast.noListingsTitle", "No listings available to book yet"), P("ggnomad.bookings.toast.noListingsBody", "Add a property, tour, event or activity first, then create a booking against it."));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
let { type: t, listing: n } = e;
|
|
52
|
+
try {
|
|
53
|
+
let e = await V({ variables: { input: {
|
|
54
|
+
bookingType: t,
|
|
55
|
+
itemId: n.id,
|
|
56
|
+
itemName: n.name,
|
|
57
|
+
guestName: P("ggnomad.bookings.demo.guestName", "Demo Guest"),
|
|
58
|
+
guestEmail: "demo@burdenoff.com",
|
|
59
|
+
guests: 2,
|
|
60
|
+
totalPrice: Number(n.price ?? 0),
|
|
61
|
+
currency: n.currency ?? "USD",
|
|
62
|
+
paymentMethod: "card"
|
|
63
|
+
} } });
|
|
64
|
+
if (e.error) {
|
|
65
|
+
s.error(P("ggnomad.bookings.toast.createErrorTitle", "Could not create booking"), e.error.message);
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
let r = e.data?.createGgnomadBooking;
|
|
69
|
+
s.success(P("ggnomad.bookings.toast.createSuccessTitle", "Booking confirmed"), r ? P("ggnomad.bookings.toast.createSuccessBody", "{{ref}} · {{item}}").replace("{{ref}}", r.referenceCode).replace("{{item}}", r.itemName) : n.name), await z();
|
|
70
|
+
} catch (e) {
|
|
71
|
+
s.error(P("ggnomad.bookings.toast.createErrorTitle", "Could not create booking"), e instanceof Error ? e.message : String(e));
|
|
72
|
+
}
|
|
73
|
+
}, J = async () => {
|
|
74
|
+
if (I) try {
|
|
75
|
+
let e = await U({ variables: {
|
|
76
|
+
id: I.id,
|
|
77
|
+
reason: P("ggnomad.bookings.actions.cancelReason", "Cancelled via UI")
|
|
78
|
+
} });
|
|
79
|
+
if (e.error) {
|
|
80
|
+
s.error(P("ggnomad.bookings.toast.cancelErrorTitle", "Could not cancel booking"), e.error.message);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
s.success(P("ggnomad.bookings.toast.cancelSuccessTitle", "Booking cancelled"), I.referenceCode), await z();
|
|
84
|
+
} catch (e) {
|
|
85
|
+
s.error(P("ggnomad.bookings.toast.cancelErrorTitle", "Could not cancel booking"), e instanceof Error ? e.message : String(e));
|
|
86
|
+
} finally {
|
|
87
|
+
L(null);
|
|
88
|
+
}
|
|
35
89
|
};
|
|
36
|
-
return /* @__PURE__ */
|
|
37
|
-
title:
|
|
38
|
-
titleAccent:
|
|
39
|
-
subtitle:
|
|
40
|
-
actions: /* @__PURE__ */
|
|
90
|
+
return /* @__PURE__ */ N(i, {
|
|
91
|
+
title: P("ggnomad.bookings.title", "Bookings"),
|
|
92
|
+
titleAccent: P("ggnomad.bookings.title.accent", "Bookings"),
|
|
93
|
+
subtitle: P("ggnomad.bookings.subtitle", "End-to-end booking operations with real API mutations"),
|
|
94
|
+
actions: /* @__PURE__ */ M(D, {
|
|
41
95
|
primary: {
|
|
42
|
-
label:
|
|
43
|
-
onSelect:
|
|
44
|
-
disabled:
|
|
45
|
-
icon: /* @__PURE__ */
|
|
96
|
+
label: P("ggnomad.bookings.actions.createDemoBooking", "Create demo booking"),
|
|
97
|
+
onSelect: () => void q(),
|
|
98
|
+
disabled: H,
|
|
99
|
+
icon: /* @__PURE__ */ M(f, {})
|
|
46
100
|
},
|
|
47
101
|
actions: []
|
|
48
102
|
}),
|
|
49
103
|
children: [
|
|
50
|
-
/* @__PURE__ */
|
|
51
|
-
icon: /* @__PURE__ */
|
|
52
|
-
children:
|
|
104
|
+
/* @__PURE__ */ M(j, {
|
|
105
|
+
icon: /* @__PURE__ */ M(l, { className: "size-4" }),
|
|
106
|
+
children: P("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.")
|
|
53
107
|
}),
|
|
54
|
-
/* @__PURE__ */
|
|
108
|
+
/* @__PURE__ */ M(A, {
|
|
55
109
|
storageKey: "ggnomad-bookings",
|
|
56
110
|
steps: [
|
|
57
111
|
{
|
|
58
112
|
id: "book-stay",
|
|
59
|
-
label:
|
|
60
|
-
description:
|
|
61
|
-
icon: /* @__PURE__ */
|
|
62
|
-
onClick: () =>
|
|
113
|
+
label: P("ggnomad.bookings.next.stay", "Book a place to stay"),
|
|
114
|
+
description: P("ggnomad.bookings.next.stayDesc", "Browse hospitality listings and reserve a property."),
|
|
115
|
+
icon: /* @__PURE__ */ M(d, { className: "size-4" }),
|
|
116
|
+
onClick: () => F("../hospitality")
|
|
63
117
|
},
|
|
64
118
|
{
|
|
65
119
|
id: "book-tour",
|
|
66
|
-
label:
|
|
67
|
-
description:
|
|
68
|
-
icon: /* @__PURE__ */
|
|
69
|
-
onClick: () =>
|
|
120
|
+
label: P("ggnomad.bookings.next.tour", "Add a tour or activity"),
|
|
121
|
+
description: P("ggnomad.bookings.next.tourDesc", "Pick a curated experience from Tourism."),
|
|
122
|
+
icon: /* @__PURE__ */ M(u, { className: "size-4" }),
|
|
123
|
+
onClick: () => F("../tourism")
|
|
70
124
|
},
|
|
71
125
|
{
|
|
72
126
|
id: "book-event",
|
|
73
|
-
label:
|
|
74
|
-
description:
|
|
75
|
-
icon: /* @__PURE__ */
|
|
76
|
-
onClick: () =>
|
|
127
|
+
label: P("ggnomad.bookings.next.event", "Grab event tickets"),
|
|
128
|
+
description: P("ggnomad.bookings.next.eventDesc", "Find live events to round out the itinerary."),
|
|
129
|
+
icon: /* @__PURE__ */ M(p, { className: "size-4" }),
|
|
130
|
+
onClick: () => F("../entertainment")
|
|
77
131
|
}
|
|
78
132
|
]
|
|
79
133
|
}),
|
|
80
|
-
|
|
134
|
+
G.length === 0 ? /* @__PURE__ */ M(k, {
|
|
81
135
|
illustration: "empty-data",
|
|
82
|
-
title:
|
|
83
|
-
description:
|
|
84
|
-
}) : /* @__PURE__ */
|
|
136
|
+
title: P("ggnomad.bookings.emptyTitle", "No bookings yet"),
|
|
137
|
+
description: P("ggnomad.bookings.emptyDescription", "Create your first booking to see it tracked here in real time.")
|
|
138
|
+
}) : /* @__PURE__ */ N(O, {
|
|
85
139
|
treatment: "glass",
|
|
86
140
|
glow: !0,
|
|
87
141
|
className: "border-border-seam p-5",
|
|
88
|
-
children: [/* @__PURE__ */
|
|
142
|
+
children: [/* @__PURE__ */ M("h2", {
|
|
89
143
|
className: "mb-4 text-lg font-medium tracking-tight text-text-primary",
|
|
90
|
-
children:
|
|
91
|
-
}), /* @__PURE__ */
|
|
144
|
+
children: P("ggnomad.bookings.activeBookings", "Your bookings")
|
|
145
|
+
}), /* @__PURE__ */ M("div", {
|
|
92
146
|
className: "grid gap-3 sm:grid-cols-2",
|
|
93
|
-
children:
|
|
147
|
+
children: G.map((e) => /* @__PURE__ */ M(o, {
|
|
94
148
|
title: e.referenceCode,
|
|
95
149
|
meta: e.itemName,
|
|
96
150
|
price: `${e.currency} ${Number(e.totalPrice).toFixed(2)}`,
|
|
97
|
-
badge: /* @__PURE__ */
|
|
151
|
+
badge: /* @__PURE__ */ M(a, {
|
|
98
152
|
status: e.status,
|
|
99
153
|
live: e.status !== "CANCELLED"
|
|
100
154
|
}),
|
|
101
|
-
footer: e.status === "CANCELLED" ? null : /* @__PURE__ */
|
|
155
|
+
footer: e.status === "CANCELLED" ? null : /* @__PURE__ */ M(D, {
|
|
102
156
|
align: "end",
|
|
103
157
|
actions: [{
|
|
104
|
-
label:
|
|
105
|
-
onSelect: () =>
|
|
158
|
+
label: P("ggnomad.bookings.actions.cancel", "Cancel booking"),
|
|
159
|
+
onSelect: () => L({
|
|
160
|
+
id: e.id,
|
|
161
|
+
referenceCode: e.referenceCode
|
|
162
|
+
}),
|
|
106
163
|
intent: "destructive",
|
|
107
|
-
icon: /* @__PURE__ */
|
|
164
|
+
icon: /* @__PURE__ */ M(m, {})
|
|
108
165
|
}]
|
|
109
166
|
})
|
|
110
167
|
}, e.id))
|
|
111
168
|
})]
|
|
169
|
+
}),
|
|
170
|
+
/* @__PURE__ */ M(y, {
|
|
171
|
+
open: I !== null,
|
|
172
|
+
onOpenChange: (e) => {
|
|
173
|
+
e || L(null);
|
|
174
|
+
},
|
|
175
|
+
children: /* @__PURE__ */ N(S, { children: [/* @__PURE__ */ N(T, { children: [/* @__PURE__ */ M(E, { children: P("ggnomad.bookings.cancelDialog.title", "Cancel this booking?") }), /* @__PURE__ */ M(C, { children: P("ggnomad.bookings.cancelDialog.body", "This releases reservation {{ref}} and cannot be undone.").replace("{{ref}}", I?.referenceCode ?? "") })] }), /* @__PURE__ */ N(w, { children: [/* @__PURE__ */ M(x, {
|
|
176
|
+
disabled: W,
|
|
177
|
+
children: P("ggnomad.bookings.cancelDialog.keep", "Keep booking")
|
|
178
|
+
}), /* @__PURE__ */ M(b, {
|
|
179
|
+
className: "bg-action-danger-bg text-action-danger-text hover:bg-action-danger-bgHover",
|
|
180
|
+
disabled: W,
|
|
181
|
+
onClick: (e) => {
|
|
182
|
+
e.preventDefault(), J();
|
|
183
|
+
},
|
|
184
|
+
children: W ? P("ggnomad.bookings.cancelDialog.cancelling", "Cancelling…") : P("ggnomad.bookings.cancelDialog.confirm", "Cancel booking")
|
|
185
|
+
})] })] })
|
|
112
186
|
})
|
|
113
187
|
]
|
|
114
188
|
});
|
|
115
189
|
}
|
|
116
190
|
//#endregion
|
|
117
|
-
export {
|
|
191
|
+
export { P as BookingsPage };
|
|
118
192
|
|
|
119
193
|
//# sourceMappingURL=BookingsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BookingsPage.js","names":[],"sources":["../../../src/ggnomad/pages/BookingsPage.tsx"],"sourcesContent":["import { 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, Ticket, XCircle } from 'lucide-react';\nimport { useNavigate } from 'react-router-dom';\nimport {\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_BOOKINGS_QUERY,\n} from '../graphql';\nimport { PageShell } from './PageShell';\nimport { EntityCard, StatusBadge } from '../components';\n\nexport function BookingsPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\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 { first: number; skip: number }\n >,\n { variables: { first: 20, skip: 0 } }\n );\n const [createBooking, { loading: creating }] = useMutation(CREATE_GGNOMAD_BOOKING_MUTATION, {\n onCompleted: () => void refetch(),\n });\n const [cancelBooking] = useMutation(CANCEL_GGNOMAD_BOOKING_MUTATION, {\n onCompleted: () => void refetch(),\n });\n\n const bookings = data?.ggnomadBookings ?? [];\n\n const handleCreateDemoBooking = () => {\n void createBooking({\n variables: {\n input: {\n bookingType: 'PROPERTY',\n itemId: 'demo-property',\n itemName: 'Demo Property',\n guestName: 'Demo Guest',\n guestEmail: 'demo@burdenoff.com',\n guestPhone: '+910000000000',\n guests: 2,\n totalPrice: 299,\n currency: 'USD',\n paymentMethod: 'card',\n },\n },\n });\n };\n\n const handleCancel = (id: string) => {\n if (\n !window.confirm(\n t('ggnomad.bookings.actions.cancelConfirm', 'Cancel this booking? This cannot be undone.')\n )\n ) {\n return;\n }\n void cancelBooking({\n variables: {\n id,\n reason: t('ggnomad.bookings.actions.cancelReason', 'Cancelled via UI'),\n },\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: 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 {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.cancel', 'Cancel booking'),\n onSelect: () => handleCancel(booking.id),\n intent: 'destructive',\n icon: <XCircle />,\n },\n ]}\n />\n ) : null\n }\n />\n ))}\n </div>\n </GlassCard>\n )}\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 { 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, 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, StatusBadge } 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\nexport function BookingsPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const [cancelTarget, setCancelTarget] = useState<CancelTarget | 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 { first: number; skip: number }\n >,\n { variables: { first: 20, skip: 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 = data?.ggnomadBookings ?? [];\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 {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.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 </PageShell>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1,50 +1,59 @@
|
|
|
1
1
|
import { GGNOMAD_EVENTS_QUERY as e } from "../graphql.js";
|
|
2
2
|
import { PageShell as t } from "./PageShell.js";
|
|
3
3
|
import { EntityCard as n } from "../components/EntityCard.js";
|
|
4
|
+
import { LoadMoreButton as r } from "../components/LoadMoreButton.js";
|
|
4
5
|
import "../components/index.js";
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
6
|
+
import { useState as i } from "react";
|
|
7
|
+
import { Ticket as a } from "lucide-react";
|
|
8
|
+
import { useI18n as o } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
9
|
+
import { useQuery as s } from "@apollo/client/react";
|
|
10
|
+
import { GlassCard as c, IllustratedEmptyState as l, PagePurpose as u } from "@burdenoff/fe-libs/ui";
|
|
11
|
+
import { jsx as d, jsxs as f } from "react/jsx-runtime";
|
|
10
12
|
//#region src/ggnomad/pages/EntertainmentPage.tsx
|
|
11
|
-
function
|
|
12
|
-
let { t:
|
|
13
|
-
first:
|
|
13
|
+
function p() {
|
|
14
|
+
let { t: p } = o(), [m, h] = i(12), { data: g } = s(e, { variables: {
|
|
15
|
+
first: m,
|
|
14
16
|
skip: 0
|
|
15
|
-
} }),
|
|
16
|
-
return /* @__PURE__ */
|
|
17
|
-
title:
|
|
18
|
-
titleAccent:
|
|
19
|
-
subtitle:
|
|
20
|
-
children: [/* @__PURE__ */
|
|
21
|
-
icon: /* @__PURE__ */
|
|
22
|
-
children:
|
|
23
|
-
}),
|
|
17
|
+
} }), _ = g?.ggnomadEvents ?? [];
|
|
18
|
+
return /* @__PURE__ */ f(t, {
|
|
19
|
+
title: p("ggnomad.entertainment.title", "Entertainment"),
|
|
20
|
+
titleAccent: p("ggnomad.entertainment.title.accent", "Entertainment"),
|
|
21
|
+
subtitle: p("ggnomad.entertainment.subtitle", "Events and live experiences"),
|
|
22
|
+
children: [/* @__PURE__ */ d(u, {
|
|
23
|
+
icon: /* @__PURE__ */ d(a, { className: "size-4" }),
|
|
24
|
+
children: p("ggnomad.entertainment.purpose", "Find live events and experiences — concerts, shows and local happenings. Open an event to see its lineup, venue and timing, then book tickets for your trip dates.")
|
|
25
|
+
}), _.length === 0 ? /* @__PURE__ */ d(l, {
|
|
24
26
|
illustration: "empty-data",
|
|
25
|
-
title:
|
|
26
|
-
description:
|
|
27
|
-
}) : /* @__PURE__ */
|
|
27
|
+
title: p("ggnomad.entertainment.emptyTitle", "No events scheduled"),
|
|
28
|
+
description: p("ggnomad.entertainment.emptyDescription", "Upcoming events and live experiences will be listed here.")
|
|
29
|
+
}) : /* @__PURE__ */ f(c, {
|
|
28
30
|
treatment: "glass",
|
|
29
31
|
glow: !0,
|
|
30
32
|
className: "border-border-seam p-5",
|
|
31
|
-
children: [
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
33
|
+
children: [
|
|
34
|
+
/* @__PURE__ */ d("h2", {
|
|
35
|
+
className: "mb-4 text-lg font-medium tracking-tight text-text-primary",
|
|
36
|
+
children: p("ggnomad.entertainment.upcomingEvents", "Upcoming events")
|
|
37
|
+
}),
|
|
38
|
+
/* @__PURE__ */ d("div", {
|
|
39
|
+
className: "grid gap-3 sm:grid-cols-2 xl:grid-cols-3",
|
|
40
|
+
children: _.map((e) => /* @__PURE__ */ d(n, {
|
|
41
|
+
to: e.id,
|
|
42
|
+
title: e.name,
|
|
43
|
+
meta: e.category,
|
|
44
|
+
price: `${e.currency} ${Number(e.price).toFixed(2)}`,
|
|
45
|
+
footer: new Date(e.startsAt).toLocaleString()
|
|
46
|
+
}, e.id))
|
|
47
|
+
}),
|
|
48
|
+
/* @__PURE__ */ d(r, {
|
|
49
|
+
visible: _.length >= m,
|
|
50
|
+
onLoadMore: () => h((e) => e + 12)
|
|
51
|
+
})
|
|
52
|
+
]
|
|
44
53
|
})]
|
|
45
54
|
});
|
|
46
55
|
}
|
|
47
56
|
//#endregion
|
|
48
|
-
export {
|
|
57
|
+
export { p as EntertainmentPage };
|
|
49
58
|
|
|
50
59
|
//# sourceMappingURL=EntertainmentPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EntertainmentPage.js","names":[],"sources":["../../../src/ggnomad/pages/EntertainmentPage.tsx"],"sourcesContent":["import { useQuery } from '@apollo/client/react';\nimport type { TypedDocumentNode } from '@apollo/client';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { Ticket } from 'lucide-react';\nimport { GlassCard, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\n\nimport { GGNOMAD_EVENTS_QUERY } from '../graphql';\nimport { PageShell } from './PageShell';\nimport { EntityCard } from '../components';\n\nexport function EntertainmentPage() {\n const { t } = useI18n();\n const { data } = useQuery(\n GGNOMAD_EVENTS_QUERY as TypedDocumentNode<\n {\n ggnomadEvents: Array<{\n id: string;\n name: string;\n category: string;\n startsAt: string;\n price: number;\n currency: string;\n }>;\n },\n { first: number; skip: number }\n >,\n { variables: { first:
|
|
1
|
+
{"version":3,"file":"EntertainmentPage.js","names":[],"sources":["../../../src/ggnomad/pages/EntertainmentPage.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 { Ticket } from 'lucide-react';\nimport { GlassCard, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\n\nimport { GGNOMAD_EVENTS_QUERY } from '../graphql';\nimport { PageShell } from './PageShell';\nimport { EntityCard, LoadMoreButton } from '../components';\n\nexport function EntertainmentPage() {\n const { t } = useI18n();\n const [limit, setLimit] = useState(12);\n const { data } = useQuery(\n GGNOMAD_EVENTS_QUERY as TypedDocumentNode<\n {\n ggnomadEvents: Array<{\n id: string;\n name: string;\n category: string;\n startsAt: string;\n price: number;\n currency: string;\n }>;\n },\n { first: number; skip: number }\n >,\n { variables: { first: limit, skip: 0 } }\n );\n const events = data?.ggnomadEvents ?? [];\n\n return (\n <PageShell\n title={t('ggnomad.entertainment.title', 'Entertainment')}\n titleAccent={t('ggnomad.entertainment.title.accent', 'Entertainment')}\n subtitle={t('ggnomad.entertainment.subtitle', 'Events and live experiences')}\n >\n <PagePurpose icon={<Ticket className=\"size-4\" />}>\n {t(\n 'ggnomad.entertainment.purpose',\n 'Find live events and experiences — concerts, shows and local happenings. Open an event to see its lineup, venue and timing, then book tickets for your trip dates.'\n )}\n </PagePurpose>\n\n {events.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('ggnomad.entertainment.emptyTitle', 'No events scheduled')}\n description={t(\n 'ggnomad.entertainment.emptyDescription',\n 'Upcoming events and live experiences will be listed here.'\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.entertainment.upcomingEvents', 'Upcoming events')}\n </h2>\n <div className=\"grid gap-3 sm:grid-cols-2 xl:grid-cols-3\">\n {events.map((event) => (\n <EntityCard\n key={event.id}\n to={event.id}\n title={event.name}\n meta={event.category}\n price={`${event.currency} ${Number(event.price).toFixed(2)}`}\n footer={new Date(event.startsAt).toLocaleString()}\n />\n ))}\n </div>\n <LoadMoreButton\n visible={events.length >= limit}\n onLoadMore={() => setLimit((l) => l + 12)}\n />\n </GlassCard>\n )}\n </PageShell>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAWA,SAAgB,IAAoB;CAClC,IAAM,EAAE,SAAM,GAAS,EACjB,CAAC,GAAO,KAAY,EAAS,GAAG,EAChC,EAAE,YAAS,EACf,GAaA,EAAE,WAAW;EAAE,OAAO;EAAO,MAAM;EAAG,EAAE,CACzC,EACK,IAAS,GAAM,iBAAiB,EAAE;AAExC,QACE,kBAAC,GAAD;EACE,OAAO,EAAE,+BAA+B,gBAAgB;EACxD,aAAa,EAAE,sCAAsC,gBAAgB;EACrE,UAAU,EAAE,kCAAkC,8BAA8B;YAH9E,CAKE,kBAAC,GAAD;GAAa,MAAM,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA;aAC7C,EACC,iCACA,qKACD;GACW,CAAA,EAEb,EAAO,WAAW,IACjB,kBAAC,GAAD;GACE,cAAa;GACb,OAAO,EAAE,oCAAoC,sBAAsB;GACnE,aAAa,EACX,0CACA,4DACD;GACD,CAAA,GAEF,kBAAC,GAAD;GAAW,WAAU;GAAQ,MAAA;GAAK,WAAU;aAA5C;IACE,kBAAC,MAAD;KAAI,WAAU;eACX,EAAE,wCAAwC,kBAAkB;KAC1D,CAAA;IACL,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAO,KAAK,MACX,kBAAC,GAAD;MAEE,IAAI,EAAM;MACV,OAAO,EAAM;MACb,MAAM,EAAM;MACZ,OAAO,GAAG,EAAM,SAAS,GAAG,OAAO,EAAM,MAAM,CAAC,QAAQ,EAAE;MAC1D,QAAQ,IAAI,KAAK,EAAM,SAAS,CAAC,gBAAgB;MACjD,EANK,EAAM,GAMX,CACF;KACE,CAAA;IACN,kBAAC,GAAD;KACE,SAAS,EAAO,UAAU;KAC1B,kBAAkB,GAAU,MAAM,IAAI,GAAG;KACzC,CAAA;IACQ;KAEJ"}
|
|
@@ -3,79 +3,81 @@ import { PageShell as t } from "./PageShell.js";
|
|
|
3
3
|
import { SectionCard as n } from "../components/SectionCard.js";
|
|
4
4
|
import { DetailRow as r } from "../components/DetailRow.js";
|
|
5
5
|
import "../components/index.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
6
|
+
import { useGGNomad as i } from "../providers/GGNomadProvider.js";
|
|
7
|
+
import { joinPath as a } from "../utils/navigation.js";
|
|
8
|
+
import { CalendarCheck as o, Mic2 as s, Ticket as c } from "lucide-react";
|
|
9
|
+
import { useI18n as l } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
10
|
+
import { useNavigate as u, useParams as d } from "react-router-dom";
|
|
11
|
+
import { useQuery as f } from "@apollo/client/react";
|
|
12
|
+
import { GlassCard as p, IllustratedEmptyState as m, NextSteps as h, PagePurpose as g } from "@burdenoff/fe-libs/ui";
|
|
13
|
+
import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
12
14
|
//#region src/ggnomad/pages/EventDetailsPage.tsx
|
|
13
|
-
function
|
|
14
|
-
let { t:
|
|
15
|
-
variables: { id:
|
|
16
|
-
skip: !
|
|
17
|
-
}),
|
|
18
|
-
return /* @__PURE__ */
|
|
19
|
-
title:
|
|
20
|
-
subtitle:
|
|
21
|
-
children:
|
|
22
|
-
/* @__PURE__ */ g
|
|
23
|
-
icon: /* @__PURE__ */
|
|
24
|
-
children:
|
|
15
|
+
function b() {
|
|
16
|
+
let { t: b } = l(), x = u(), { basePath: S = "/" } = i(), { id: C = "" } = d(), { data: w } = f(e, {
|
|
17
|
+
variables: { id: C },
|
|
18
|
+
skip: !C
|
|
19
|
+
}), T = w?.ggnomadEvent;
|
|
20
|
+
return /* @__PURE__ */ v(t, {
|
|
21
|
+
title: T?.name ?? b("ggnomad.event.fallbackTitle", "Event details"),
|
|
22
|
+
subtitle: b("ggnomad.event.subtitle", "Live event details, venue and lineup"),
|
|
23
|
+
children: T ? /* @__PURE__ */ y(_, { children: [
|
|
24
|
+
/* @__PURE__ */ v(g, {
|
|
25
|
+
icon: /* @__PURE__ */ v(c, { className: "size-4" }),
|
|
26
|
+
children: b("ggnomad.event.purpose", "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.")
|
|
25
27
|
}),
|
|
26
|
-
/* @__PURE__ */
|
|
28
|
+
/* @__PURE__ */ v(h, {
|
|
27
29
|
storageKey: "ggnomad-event-detail",
|
|
28
30
|
steps: [{
|
|
29
31
|
id: "book",
|
|
30
|
-
label:
|
|
31
|
-
description:
|
|
32
|
-
icon: /* @__PURE__ */
|
|
33
|
-
onClick: () =>
|
|
32
|
+
label: b("ggnomad.event.next.book", "Book tickets"),
|
|
33
|
+
description: b("ggnomad.event.next.bookDesc", "Reserve seats and track them under Bookings."),
|
|
34
|
+
icon: /* @__PURE__ */ v(o, { className: "size-4" }),
|
|
35
|
+
onClick: () => x(a(S, "bookings"))
|
|
34
36
|
}, {
|
|
35
37
|
id: "browse",
|
|
36
|
-
label:
|
|
37
|
-
description:
|
|
38
|
-
icon: /* @__PURE__ */
|
|
39
|
-
onClick: () =>
|
|
38
|
+
label: b("ggnomad.event.next.browse", "See more events"),
|
|
39
|
+
description: b("ggnomad.event.next.browseDesc", "Return to Entertainment for other live experiences."),
|
|
40
|
+
icon: /* @__PURE__ */ v(c, { className: "size-4" }),
|
|
41
|
+
onClick: () => x(a(S, "entertainment"))
|
|
40
42
|
}]
|
|
41
43
|
}),
|
|
42
|
-
/* @__PURE__ */
|
|
44
|
+
/* @__PURE__ */ y("div", {
|
|
43
45
|
className: "grid gap-4 lg:grid-cols-2",
|
|
44
|
-
children: [/* @__PURE__ */
|
|
46
|
+
children: [/* @__PURE__ */ y(p, {
|
|
45
47
|
treatment: "glass",
|
|
46
48
|
glow: !0,
|
|
47
49
|
className: "border-border-seam p-5",
|
|
48
|
-
children: [/* @__PURE__ */
|
|
50
|
+
children: [/* @__PURE__ */ v("h2", {
|
|
49
51
|
className: "mb-4 text-lg font-medium tracking-tight text-text-primary",
|
|
50
|
-
children:
|
|
51
|
-
}), /* @__PURE__ */
|
|
52
|
+
children: b("ggnomad.event.details", "Details")
|
|
53
|
+
}), /* @__PURE__ */ y("dl", {
|
|
52
54
|
className: "space-y-2 text-sm",
|
|
53
55
|
children: [
|
|
54
|
-
/* @__PURE__ */
|
|
55
|
-
label:
|
|
56
|
-
value:
|
|
56
|
+
/* @__PURE__ */ v(r, {
|
|
57
|
+
label: b("ggnomad.event.category", "Category"),
|
|
58
|
+
value: T.category
|
|
57
59
|
}),
|
|
58
|
-
/* @__PURE__ */
|
|
59
|
-
label:
|
|
60
|
-
value: new Date(
|
|
60
|
+
/* @__PURE__ */ v(r, {
|
|
61
|
+
label: b("ggnomad.event.starts", "Starts"),
|
|
62
|
+
value: new Date(T.startsAt).toLocaleString()
|
|
61
63
|
}),
|
|
62
|
-
/* @__PURE__ */
|
|
63
|
-
label:
|
|
64
|
-
value:
|
|
64
|
+
/* @__PURE__ */ v(r, {
|
|
65
|
+
label: b("ggnomad.event.ends", "Ends"),
|
|
66
|
+
value: T.endsAt ? new Date(T.endsAt).toLocaleString() : b("ggnomad.event.na", "N/A")
|
|
65
67
|
}),
|
|
66
|
-
/* @__PURE__ */
|
|
67
|
-
label:
|
|
68
|
-
value: `${
|
|
68
|
+
/* @__PURE__ */ v(r, {
|
|
69
|
+
label: b("ggnomad.event.price", "Price"),
|
|
70
|
+
value: `${T.currency} ${Number(T.price).toFixed(2)}`
|
|
69
71
|
})
|
|
70
72
|
]
|
|
71
73
|
})]
|
|
72
|
-
}), /* @__PURE__ */
|
|
73
|
-
title:
|
|
74
|
-
children: /* @__PURE__ */
|
|
74
|
+
}), /* @__PURE__ */ v(n, {
|
|
75
|
+
title: b("ggnomad.event.performers", "Performers"),
|
|
76
|
+
children: /* @__PURE__ */ v("ul", {
|
|
75
77
|
className: "space-y-2 text-sm",
|
|
76
|
-
children: (
|
|
78
|
+
children: (T.performers ?? []).map((e) => /* @__PURE__ */ y("li", {
|
|
77
79
|
className: "flex items-start gap-2 text-text-primary",
|
|
78
|
-
children: [/* @__PURE__ */
|
|
80
|
+
children: [/* @__PURE__ */ v(s, {
|
|
79
81
|
className: "mt-0.5 size-4 flex-shrink-0 text-action-primary-bg",
|
|
80
82
|
"aria-hidden": "true"
|
|
81
83
|
}), e]
|
|
@@ -83,14 +85,14 @@ function v() {
|
|
|
83
85
|
})
|
|
84
86
|
})]
|
|
85
87
|
})
|
|
86
|
-
] }) : /* @__PURE__ */
|
|
88
|
+
] }) : /* @__PURE__ */ v(m, {
|
|
87
89
|
illustration: "empty-data",
|
|
88
|
-
title:
|
|
89
|
-
description:
|
|
90
|
+
title: b("ggnomad.event.notFoundTitle", "No event found"),
|
|
91
|
+
description: b("ggnomad.event.notFoundDescription", "This event is unavailable or may have been removed.")
|
|
90
92
|
})
|
|
91
93
|
});
|
|
92
94
|
}
|
|
93
95
|
//#endregion
|
|
94
|
-
export {
|
|
96
|
+
export { b as EventDetailsPage };
|
|
95
97
|
|
|
96
98
|
//# sourceMappingURL=EventDetailsPage.js.map
|