@burdenoff/microfe-ggnomad 2026.705.1 → 2026.705.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/DetailHero.js +22 -0
- package/dist/ggnomad/components/DetailHero.js.map +1 -0
- package/dist/ggnomad/components/EntityCard.js +36 -22
- package/dist/ggnomad/components/EntityCard.js.map +1 -1
- package/dist/ggnomad/components/index.js +1 -0
- package/dist/ggnomad/pages/ActivityDetailsPage.js +60 -55
- package/dist/ggnomad/pages/ActivityDetailsPage.js.map +1 -1
- package/dist/ggnomad/pages/EntertainmentPage.js +2 -0
- package/dist/ggnomad/pages/EntertainmentPage.js.map +1 -1
- package/dist/ggnomad/pages/EventDetailsPage.js +60 -55
- package/dist/ggnomad/pages/EventDetailsPage.js.map +1 -1
- package/dist/ggnomad/pages/HospitalityPage.js +2 -0
- package/dist/ggnomad/pages/HospitalityPage.js.map +1 -1
- package/dist/ggnomad/pages/PropertyDetailsPage.js +65 -60
- package/dist/ggnomad/pages/PropertyDetailsPage.js.map +1 -1
- package/dist/ggnomad/pages/TourDetailsPage.js +60 -55
- package/dist/ggnomad/pages/TourDetailsPage.js.map +1 -1
- package/dist/ggnomad/pages/TourismPage.js +2 -0
- package/dist/ggnomad/pages/TourismPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { cn as e } from "../utils/cn.js";
|
|
2
|
+
import { jsx as t, jsxs as n } from "react/jsx-runtime";
|
|
3
|
+
//#region src/ggnomad/components/DetailHero.tsx
|
|
4
|
+
function r({ image: r, alt: i, className: a }) {
|
|
5
|
+
return /* @__PURE__ */ n("div", {
|
|
6
|
+
className: e("relative mb-5 aspect-[21/9] w-full overflow-hidden rounded-card border border-border-seam", a),
|
|
7
|
+
children: [r ? /* @__PURE__ */ t("img", {
|
|
8
|
+
src: r,
|
|
9
|
+
alt: i ?? "",
|
|
10
|
+
loading: "eager",
|
|
11
|
+
decoding: "async",
|
|
12
|
+
className: "size-full object-cover"
|
|
13
|
+
}) : /* @__PURE__ */ t("div", {
|
|
14
|
+
"aria-hidden": "true",
|
|
15
|
+
className: "size-full bg-gradient-to-br from-action-primary-bg/30 via-bg-sunken to-bg-sunken"
|
|
16
|
+
}), /* @__PURE__ */ t("div", { className: "pointer-events-none absolute inset-0 bg-gradient-to-t from-black/50 via-transparent to-transparent" })]
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { r as DetailHero };
|
|
21
|
+
|
|
22
|
+
//# sourceMappingURL=DetailHero.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DetailHero.js","names":[],"sources":["../../../src/ggnomad/components/DetailHero.tsx"],"sourcesContent":["import { cn } from '../utils/cn';\n\ninterface DetailHeroProps {\n /** Primary cover image URL; a branded gradient is shown when absent. */\n image?: string | null;\n alt?: string;\n className?: string;\n}\n\n/**\n * Full-width hero cover for a listing detail page — a 21:9 photo with a bottom\n * gradient scrim, or a branded gradient fallback when the listing has no media.\n */\nexport function DetailHero({ image, alt, className }: DetailHeroProps) {\n return (\n <div\n className={cn(\n 'relative mb-5 aspect-[21/9] w-full overflow-hidden rounded-card border border-border-seam',\n className\n )}\n >\n {image ? (\n <img\n src={image}\n alt={alt ?? ''}\n loading=\"eager\"\n decoding=\"async\"\n className=\"size-full object-cover\"\n />\n ) : (\n <div\n aria-hidden=\"true\"\n className=\"size-full bg-gradient-to-br from-action-primary-bg/30 via-bg-sunken to-bg-sunken\"\n />\n )}\n <div className=\"pointer-events-none absolute inset-0 bg-gradient-to-t from-black/50 via-transparent to-transparent\" />\n </div>\n );\n}\n"],"mappings":";;;AAaA,SAAgB,EAAW,EAAE,UAAO,QAAK,gBAA8B;AACrE,QACE,kBAAC,OAAD;EACE,WAAW,EACT,6FACA,EACD;YAJH,CAMG,IACC,kBAAC,OAAD;GACE,KAAK;GACL,KAAK,KAAO;GACZ,SAAQ;GACR,UAAS;GACT,WAAU;GACV,CAAA,GAEF,kBAAC,OAAD;GACE,eAAY;GACZ,WAAU;GACV,CAAA,EAEJ,kBAAC,OAAD,EAAK,WAAU,sGAAuG,CAAA,CAClH"}
|
|
@@ -3,22 +3,36 @@ import { Link as t } from "react-router-dom";
|
|
|
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
|
|
6
|
-
function o({ title: o, meta: s, price: c, badge: l, footer: u,
|
|
7
|
-
let
|
|
6
|
+
function o({ title: o, meta: s, price: c, badge: l, footer: u, image: d, imageAlt: f, to: p, className: m }) {
|
|
7
|
+
let h = /* @__PURE__ */ a(r, { children: [
|
|
8
8
|
/* @__PURE__ */ a("div", {
|
|
9
|
-
className: "
|
|
10
|
-
children: [
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
className: "
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
9
|
+
className: "relative -mx-4 -mt-4 mb-3 aspect-[16/10] overflow-hidden",
|
|
10
|
+
children: [
|
|
11
|
+
d ? /* @__PURE__ */ i("img", {
|
|
12
|
+
src: d,
|
|
13
|
+
alt: f ?? "",
|
|
14
|
+
loading: "lazy",
|
|
15
|
+
decoding: "async",
|
|
16
|
+
className: "size-full object-cover transition-transform duration-300 group-hover:scale-[1.04]"
|
|
17
|
+
}) : /* @__PURE__ */ i("div", {
|
|
18
|
+
"aria-hidden": "true",
|
|
19
|
+
className: "size-full bg-gradient-to-br from-action-primary-bg/25 via-bg-sunken to-bg-sunken"
|
|
20
|
+
}),
|
|
21
|
+
/* @__PURE__ */ i("div", { className: "pointer-events-none absolute inset-0 bg-gradient-to-t from-black/45 via-transparent to-transparent" }),
|
|
22
|
+
l ? /* @__PURE__ */ i("div", {
|
|
23
|
+
className: "absolute left-2 top-2",
|
|
24
|
+
children: l
|
|
25
|
+
}) : null
|
|
26
|
+
]
|
|
27
|
+
}),
|
|
28
|
+
/* @__PURE__ */ a("div", {
|
|
29
|
+
className: "min-w-0",
|
|
30
|
+
children: [/* @__PURE__ */ i("p", {
|
|
31
|
+
className: "truncate font-medium text-text-primary",
|
|
32
|
+
children: o
|
|
33
|
+
}), s ? /* @__PURE__ */ i("p", {
|
|
34
|
+
className: "mt-0.5 text-sm text-text-secondary",
|
|
35
|
+
children: s
|
|
22
36
|
}) : null]
|
|
23
37
|
}),
|
|
24
38
|
c ? /* @__PURE__ */ i("p", {
|
|
@@ -30,18 +44,18 @@ function o({ title: o, meta: s, price: c, badge: l, footer: u, to: d, className:
|
|
|
30
44
|
children: u
|
|
31
45
|
}) : null
|
|
32
46
|
] });
|
|
33
|
-
return
|
|
34
|
-
to:
|
|
35
|
-
className: "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",
|
|
47
|
+
return p ? /* @__PURE__ */ i(t, {
|
|
48
|
+
to: p,
|
|
49
|
+
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",
|
|
36
50
|
children: /* @__PURE__ */ i(n, {
|
|
37
51
|
treatment: "plain",
|
|
38
|
-
className: e("border-border-seam p-4",
|
|
39
|
-
children:
|
|
52
|
+
className: e("overflow-hidden border-border-seam p-4", m),
|
|
53
|
+
children: h
|
|
40
54
|
})
|
|
41
55
|
}) : /* @__PURE__ */ i(n, {
|
|
42
56
|
treatment: "plain",
|
|
43
|
-
className: e("border-border-seam p-4",
|
|
44
|
-
children:
|
|
57
|
+
className: e("group overflow-hidden border-border-seam p-4", m),
|
|
58
|
+
children: h
|
|
45
59
|
});
|
|
46
60
|
}
|
|
47
61
|
//#endregion
|
|
@@ -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 /** When provided, the whole card becomes a router Link. */\n to?: string;\n className?: string;\n};\n\n/**\n *
|
|
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;KACV,CAAA,GAEF,kBAAC,OAAD;KACE,eAAY;KACZ,WAAU;KACV,CAAA;IAEJ,kBAAC,OAAD,EAAK,WAAU,sGAAuG,CAAA;IACrH,IAAQ,kBAAC,OAAD;KAAK,WAAU;eAAyB;KAAY,CAAA,GAAG;IAC5D;;EAMJ,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,KAAD;IAAG,WAAU;cAA0C;IAAU,CAAA,EAChE,IAAO,kBAAC,KAAD;IAAG,WAAU;cAAsC;IAAS,CAAA,GAAG,KACnE;;EACL,IACC,kBAAC,KAAD;GAAG,WAAU;aAA2D;GAAU,CAAA,GAChF;EACH,IAAS,kBAAC,OAAD;GAAK,WAAU;aAAoC;GAAa,CAAA,GAAG;EAC5E,EAAA,CAAA;AAmBL,QAhBI,IAEA,kBAAC,GAAD;EACM;EACJ,WAAU;YAEV,kBAAC,GAAD;GACE,WAAU;GACV,WAAW,EAAG,0CAA0C,EAAU;aAEjE;GACS,CAAA;EACP,CAAA,GAKT,kBAAC,GAAD;EACE,WAAU;EACV,WAAW,EAAG,gDAAgD,EAAU;YAEvE;EACS,CAAA"}
|
|
@@ -2,94 +2,99 @@ import { GGNOMAD_ACTIVITY_QUERY as e } from "../graphql.js";
|
|
|
2
2
|
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
|
+
import { DetailHero as i } from "../components/DetailHero.js";
|
|
5
6
|
import "../components/index.js";
|
|
6
|
-
import { useGGNomad as
|
|
7
|
-
import { joinPath as
|
|
8
|
-
import { Activity as
|
|
9
|
-
import { useI18n as
|
|
10
|
-
import { useNavigate as
|
|
11
|
-
import { useQuery as
|
|
12
|
-
import { GlassCard as
|
|
13
|
-
import { Fragment as
|
|
7
|
+
import { useGGNomad as a } from "../providers/GGNomadProvider.js";
|
|
8
|
+
import { joinPath as o } from "../utils/navigation.js";
|
|
9
|
+
import { Activity as s, CalendarCheck as c, Compass as l, ShieldCheck as u } from "lucide-react";
|
|
10
|
+
import { useI18n as d } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
11
|
+
import { useNavigate as f, useParams as p } from "react-router-dom";
|
|
12
|
+
import { useQuery as m } from "@apollo/client/react";
|
|
13
|
+
import { GlassCard as h, IllustratedEmptyState as g, NextSteps as _, PagePurpose as v } from "@burdenoff/fe-libs/ui";
|
|
14
|
+
import { Fragment as y, jsx as b, jsxs as x } from "react/jsx-runtime";
|
|
14
15
|
//#region src/ggnomad/pages/ActivityDetailsPage.tsx
|
|
15
|
-
function
|
|
16
|
-
let { t:
|
|
17
|
-
variables: { id:
|
|
18
|
-
skip: !
|
|
19
|
-
}),
|
|
20
|
-
return /* @__PURE__ */
|
|
21
|
-
title:
|
|
22
|
-
subtitle:
|
|
23
|
-
children:
|
|
24
|
-
/* @__PURE__ */
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
function S() {
|
|
17
|
+
let { t: S } = d(), C = f(), { basePath: w = "/" } = a(), { id: T = "" } = p(), { data: E } = m(e, {
|
|
18
|
+
variables: { id: T },
|
|
19
|
+
skip: !T
|
|
20
|
+
}), D = E?.ggnomadActivity;
|
|
21
|
+
return /* @__PURE__ */ b(t, {
|
|
22
|
+
title: D?.name ?? S("ggnomad.activity.fallbackTitle", "Activity details"),
|
|
23
|
+
subtitle: S("ggnomad.activity.subtitle", "Safety, inclusions and participation details"),
|
|
24
|
+
children: D ? /* @__PURE__ */ x(y, { children: [
|
|
25
|
+
/* @__PURE__ */ b(i, {
|
|
26
|
+
image: D.images?.[0],
|
|
27
|
+
alt: D.name
|
|
27
28
|
}),
|
|
28
|
-
/* @__PURE__ */
|
|
29
|
+
/* @__PURE__ */ b(v, {
|
|
30
|
+
icon: /* @__PURE__ */ b(s, { className: "size-4" }),
|
|
31
|
+
children: S("ggnomad.activity.purpose", "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.")
|
|
32
|
+
}),
|
|
33
|
+
/* @__PURE__ */ b(_, {
|
|
29
34
|
storageKey: "ggnomad-activity-detail",
|
|
30
35
|
steps: [{
|
|
31
36
|
id: "book",
|
|
32
|
-
label:
|
|
33
|
-
description:
|
|
34
|
-
icon: /* @__PURE__ */
|
|
35
|
-
onClick: () =>
|
|
37
|
+
label: S("ggnomad.activity.next.book", "Book this activity"),
|
|
38
|
+
description: S("ggnomad.activity.next.bookDesc", "Add it to your itinerary and track it under Bookings."),
|
|
39
|
+
icon: /* @__PURE__ */ b(c, { className: "size-4" }),
|
|
40
|
+
onClick: () => C(o(w, "bookings"))
|
|
36
41
|
}, {
|
|
37
42
|
id: "browse",
|
|
38
|
-
label:
|
|
39
|
-
description:
|
|
40
|
-
icon: /* @__PURE__ */
|
|
41
|
-
onClick: () =>
|
|
43
|
+
label: S("ggnomad.activity.next.browse", "Explore more experiences"),
|
|
44
|
+
description: S("ggnomad.activity.next.browseDesc", "Return to Tourism to compare tours and activities."),
|
|
45
|
+
icon: /* @__PURE__ */ b(l, { className: "size-4" }),
|
|
46
|
+
onClick: () => C(o(w, "tourism"))
|
|
42
47
|
}]
|
|
43
48
|
}),
|
|
44
|
-
/* @__PURE__ */
|
|
49
|
+
/* @__PURE__ */ x("div", {
|
|
45
50
|
className: "grid gap-4 lg:grid-cols-2",
|
|
46
|
-
children: [/* @__PURE__ */
|
|
51
|
+
children: [/* @__PURE__ */ x(h, {
|
|
47
52
|
treatment: "glass",
|
|
48
53
|
glow: !0,
|
|
49
54
|
className: "border-border-seam p-5",
|
|
50
|
-
children: [/* @__PURE__ */
|
|
55
|
+
children: [/* @__PURE__ */ b("h2", {
|
|
51
56
|
className: "mb-4 text-lg font-medium tracking-tight text-text-primary",
|
|
52
|
-
children:
|
|
53
|
-
}), /* @__PURE__ */
|
|
57
|
+
children: S("ggnomad.activity.details", "Details")
|
|
58
|
+
}), /* @__PURE__ */ x("dl", {
|
|
54
59
|
className: "space-y-2 text-sm",
|
|
55
60
|
children: [
|
|
56
|
-
/* @__PURE__ */
|
|
57
|
-
label:
|
|
58
|
-
value:
|
|
61
|
+
/* @__PURE__ */ b(r, {
|
|
62
|
+
label: S("ggnomad.activity.category", "Category"),
|
|
63
|
+
value: D.category
|
|
59
64
|
}),
|
|
60
|
-
/* @__PURE__ */
|
|
61
|
-
label:
|
|
62
|
-
value:
|
|
65
|
+
/* @__PURE__ */ b(r, {
|
|
66
|
+
label: S("ggnomad.activity.duration", "Duration"),
|
|
67
|
+
value: D.duration
|
|
63
68
|
}),
|
|
64
|
-
/* @__PURE__ */
|
|
65
|
-
label:
|
|
66
|
-
value: String(
|
|
69
|
+
/* @__PURE__ */ b(r, {
|
|
70
|
+
label: S("ggnomad.activity.maxParticipants", "Max participants"),
|
|
71
|
+
value: String(D.maxParticipants)
|
|
67
72
|
}),
|
|
68
|
-
/* @__PURE__ */
|
|
69
|
-
label:
|
|
70
|
-
value: `${
|
|
73
|
+
/* @__PURE__ */ b(r, {
|
|
74
|
+
label: S("ggnomad.activity.price", "Price"),
|
|
75
|
+
value: `${D.currency} ${Number(D.price).toFixed(2)}`
|
|
71
76
|
})
|
|
72
77
|
]
|
|
73
78
|
})]
|
|
74
|
-
}), /* @__PURE__ */
|
|
75
|
-
title:
|
|
76
|
-
children: /* @__PURE__ */
|
|
79
|
+
}), /* @__PURE__ */ b(n, {
|
|
80
|
+
title: S("ggnomad.activity.safety", "Safety information"),
|
|
81
|
+
children: /* @__PURE__ */ x("div", {
|
|
77
82
|
className: "flex items-start gap-2 text-sm text-text-primary",
|
|
78
|
-
children: [/* @__PURE__ */
|
|
83
|
+
children: [/* @__PURE__ */ b(u, {
|
|
79
84
|
className: "mt-0.5 size-4 flex-shrink-0 text-status-success-text",
|
|
80
85
|
"aria-hidden": "true"
|
|
81
|
-
}), /* @__PURE__ */
|
|
86
|
+
}), /* @__PURE__ */ b("p", { children: D.safetyInfo ?? S("ggnomad.activity.defaultSafety", "Standard provider safety policy applies.") })]
|
|
82
87
|
})
|
|
83
88
|
})]
|
|
84
89
|
})
|
|
85
|
-
] }) : /* @__PURE__ */
|
|
90
|
+
] }) : /* @__PURE__ */ b(g, {
|
|
86
91
|
illustration: "empty-data",
|
|
87
|
-
title:
|
|
88
|
-
description:
|
|
92
|
+
title: S("ggnomad.activity.notFoundTitle", "No activity found"),
|
|
93
|
+
description: S("ggnomad.activity.notFoundDescription", "This activity is unavailable or may have been removed.")
|
|
89
94
|
})
|
|
90
95
|
});
|
|
91
96
|
}
|
|
92
97
|
//#endregion
|
|
93
|
-
export {
|
|
98
|
+
export { S as ActivityDetailsPage };
|
|
94
99
|
|
|
95
100
|
//# sourceMappingURL=ActivityDetailsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ActivityDetailsPage.js","names":[],"sources":["../../../src/ggnomad/pages/ActivityDetailsPage.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 { useNavigate, useParams } from 'react-router-dom';\nimport { Activity, CalendarCheck, Compass, 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 { DetailRow, SectionCard } from '../components';\n\nexport function ActivityDetailsPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const { basePath = '/' } = useGGNomad();\n const { id = '' } = useParams();\n const { data } = useQuery(\n GGNOMAD_ACTIVITY_QUERY as TypedDocumentNode<\n {\n ggnomadActivity: {\n name: string;\n category: string;\n duration: string;\n maxParticipants: number;\n currency: string;\n price: number;\n safetyInfo?: 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 <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: 'book',\n label: t('ggnomad.activity.next.book', 'Book this activity'),\n description: t(\n 'ggnomad.activity.next.bookDesc',\n 'Add it to your itinerary and track it under Bookings.'\n ),\n icon: <CalendarCheck className=\"size-4\" />,\n onClick: () => navigate(joinPath(basePath, 'bookings')),\n },\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-2\">\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.activity.details', 'Details')}\n </h2>\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('ggnomad.activity.defaultSafety', 'Standard provider safety policy applies.')}\n </p>\n </div>\n </SectionCard>\n </div>\n </>\n )}\n </PageShell>\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"ActivityDetailsPage.js","names":[],"sources":["../../../src/ggnomad/pages/ActivityDetailsPage.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 { useNavigate, useParams } from 'react-router-dom';\nimport { Activity, CalendarCheck, Compass, 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 { DetailRow, SectionCard, DetailHero } from '../components';\n\nexport function ActivityDetailsPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const { basePath = '/' } = useGGNomad();\n const { id = '' } = useParams();\n const { data } = useQuery(\n GGNOMAD_ACTIVITY_QUERY as TypedDocumentNode<\n {\n ggnomadActivity: {\n name: string;\n category: string;\n duration: string;\n maxParticipants: number;\n currency: string;\n price: number;\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: 'book',\n label: t('ggnomad.activity.next.book', 'Book this activity'),\n description: t(\n 'ggnomad.activity.next.bookDesc',\n 'Add it to your itinerary and track it under Bookings.'\n ),\n icon: <CalendarCheck className=\"size-4\" />,\n onClick: () => navigate(joinPath(basePath, 'bookings')),\n },\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-2\">\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.activity.details', 'Details')}\n </h2>\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('ggnomad.activity.defaultSafety', 'Standard provider safety policy applies.')}\n </p>\n </div>\n </SectionCard>\n </div>\n </>\n )}\n </PageShell>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AAaA,SAAgB,IAAsB;CACpC,IAAM,EAAE,SAAM,GAAS,EACjB,IAAW,GAAa,EACxB,EAAE,cAAW,QAAQ,GAAY,EACjC,EAAE,QAAK,OAAO,GAAW,EACzB,EAAE,YAAS,EACf,GAeA;EAAE,WAAW,EAAE,OAAI;EAAE,MAAM,CAAC;EAAI,CACjC,EACK,IAAW,GAAM;AAEvB,QACE,kBAAC,GAAD;EACE,OAAO,GAAU,QAAQ,EAAE,kCAAkC,mBAAmB;EAChF,UAAU,EAAE,6BAA6B,+CAA+C;YAEtF,IAUA,kBAAA,GAAA,EAAA,UAAA;GACE,kBAAC,GAAD;IAAY,OAAO,EAAS,SAAS;IAAI,KAAK,EAAS;IAAQ,CAAA;GAC/D,kBAAC,GAAD;IAAa,MAAM,kBAAC,GAAD,EAAU,WAAU,UAAW,CAAA;cAC/C,EACC,4BACA,wMACD;IACW,CAAA;GAEd,kBAAC,GAAD;IACE,YAAW;IACX,OAAO,CACL;KACE,IAAI;KACJ,OAAO,EAAE,8BAA8B,qBAAqB;KAC5D,aAAa,EACX,kCACA,wDACD;KACD,MAAM,kBAAC,GAAD,EAAe,WAAU,UAAW,CAAA;KAC1C,eAAe,EAAS,EAAS,GAAU,WAAW,CAAC;KACxD,EACD;KACE,IAAI;KACJ,OAAO,EAAE,gCAAgC,2BAA2B;KACpE,aAAa,EACX,oCACA,qDACD;KACD,MAAM,kBAAC,GAAD,EAAS,WAAU,UAAW,CAAA;KACpC,eAAe,EAAS,EAAS,GAAU,UAAU,CAAC;KACvD,CACF;IACD,CAAA;GAEF,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAW,WAAU;KAAQ,MAAA;KAAK,WAAU;eAA5C,CACE,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAE,4BAA4B,UAAU;MACtC,CAAA,EACL,kBAAC,MAAD;MAAI,WAAU;gBAAd;OACE,kBAAC,GAAD;QACE,OAAO,EAAE,6BAA6B,WAAW;QACjD,OAAO,EAAS;QAChB,CAAA;OACF,kBAAC,GAAD;QACE,OAAO,EAAE,6BAA6B,WAAW;QACjD,OAAO,EAAS;QAChB,CAAA;OACF,kBAAC,GAAD;QACE,OAAO,EAAE,oCAAoC,mBAAmB;QAChE,OAAO,OAAO,EAAS,gBAAgB;QACvC,CAAA;OACF,kBAAC,GAAD;QACE,OAAO,EAAE,0BAA0B,QAAQ;QAC3C,OAAO,GAAG,EAAS,SAAS,GAAG,OAAO,EAAS,MAAM,CAAC,QAAQ,EAAE;QAChE,CAAA;OACC;QACK;QACZ,kBAAC,GAAD;KAAa,OAAO,EAAE,2BAA2B,qBAAqB;eACpE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OACE,WAAU;OACV,eAAY;OACZ,CAAA,EACF,kBAAC,KAAD,EAAA,UACG,EAAS,cACR,EAAE,kCAAkC,2CAA2C,EAC/E,CAAA,CACA;;KACM,CAAA,CACV;;GACL,EAAA,CAAA,GAjFH,kBAAC,GAAD;GACE,cAAa;GACb,OAAO,EAAE,kCAAkC,oBAAoB;GAC/D,aAAa,EACX,wCACA,yDACD;GACD,CAAA;EA4EM,CAAA"}
|
|
@@ -39,6 +39,8 @@ function p() {
|
|
|
39
39
|
className: "grid gap-3 sm:grid-cols-2 xl:grid-cols-3",
|
|
40
40
|
children: _.map((e) => /* @__PURE__ */ d(n, {
|
|
41
41
|
to: e.id,
|
|
42
|
+
image: e.images?.[0],
|
|
43
|
+
imageAlt: e.name,
|
|
42
44
|
title: e.name,
|
|
43
45
|
meta: e.category,
|
|
44
46
|
price: `${e.currency} ${Number(e.price).toFixed(2)}`,
|
|
@@ -1 +1 @@
|
|
|
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,
|
|
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 images?: 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 image={event.images?.[0]}\n imageAlt={event.name}\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,GAcA,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,SAAS;MACtB,UAAU,EAAM;MAChB,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,EARK,EAAM,GAQX,CACF;KACE,CAAA;IACN,kBAAC,GAAD;KACE,SAAS,EAAO,UAAU;KAC1B,kBAAkB,GAAU,MAAM,IAAI,GAAG;KACzC,CAAA;IACQ;KAEJ"}
|
|
@@ -2,82 +2,87 @@ import { GGNOMAD_EVENT_QUERY as e } from "../graphql.js";
|
|
|
2
2
|
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
|
+
import { DetailHero as i } from "../components/DetailHero.js";
|
|
5
6
|
import "../components/index.js";
|
|
6
|
-
import { useGGNomad as
|
|
7
|
-
import { joinPath as
|
|
8
|
-
import { CalendarCheck as
|
|
9
|
-
import { useI18n as
|
|
10
|
-
import { useNavigate as
|
|
11
|
-
import { useQuery as
|
|
12
|
-
import { GlassCard as
|
|
13
|
-
import { Fragment as
|
|
7
|
+
import { useGGNomad as a } from "../providers/GGNomadProvider.js";
|
|
8
|
+
import { joinPath as o } from "../utils/navigation.js";
|
|
9
|
+
import { CalendarCheck as s, Mic2 as c, Ticket as l } from "lucide-react";
|
|
10
|
+
import { useI18n as u } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
11
|
+
import { useNavigate as d, useParams as f } from "react-router-dom";
|
|
12
|
+
import { useQuery as p } from "@apollo/client/react";
|
|
13
|
+
import { GlassCard as m, IllustratedEmptyState as h, NextSteps as g, PagePurpose as _ } from "@burdenoff/fe-libs/ui";
|
|
14
|
+
import { Fragment as v, jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
14
15
|
//#region src/ggnomad/pages/EventDetailsPage.tsx
|
|
15
|
-
function
|
|
16
|
-
let { t:
|
|
17
|
-
variables: { id:
|
|
18
|
-
skip: !
|
|
19
|
-
}),
|
|
20
|
-
return /* @__PURE__ */
|
|
21
|
-
title:
|
|
22
|
-
subtitle:
|
|
23
|
-
children:
|
|
24
|
-
/* @__PURE__ */
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
function x() {
|
|
17
|
+
let { t: x } = u(), S = d(), { basePath: C = "/" } = a(), { id: w = "" } = f(), { data: T } = p(e, {
|
|
18
|
+
variables: { id: w },
|
|
19
|
+
skip: !w
|
|
20
|
+
}), E = T?.ggnomadEvent;
|
|
21
|
+
return /* @__PURE__ */ y(t, {
|
|
22
|
+
title: E?.name ?? x("ggnomad.event.fallbackTitle", "Event details"),
|
|
23
|
+
subtitle: x("ggnomad.event.subtitle", "Live event details, venue and lineup"),
|
|
24
|
+
children: E ? /* @__PURE__ */ b(v, { children: [
|
|
25
|
+
/* @__PURE__ */ y(i, {
|
|
26
|
+
image: E.images?.[0],
|
|
27
|
+
alt: E.name
|
|
27
28
|
}),
|
|
28
|
-
/* @__PURE__ */
|
|
29
|
+
/* @__PURE__ */ y(_, {
|
|
30
|
+
icon: /* @__PURE__ */ y(l, { className: "size-4" }),
|
|
31
|
+
children: x("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.")
|
|
32
|
+
}),
|
|
33
|
+
/* @__PURE__ */ y(g, {
|
|
29
34
|
storageKey: "ggnomad-event-detail",
|
|
30
35
|
steps: [{
|
|
31
36
|
id: "book",
|
|
32
|
-
label:
|
|
33
|
-
description:
|
|
34
|
-
icon: /* @__PURE__ */
|
|
35
|
-
onClick: () =>
|
|
37
|
+
label: x("ggnomad.event.next.book", "Book tickets"),
|
|
38
|
+
description: x("ggnomad.event.next.bookDesc", "Reserve seats and track them under Bookings."),
|
|
39
|
+
icon: /* @__PURE__ */ y(s, { className: "size-4" }),
|
|
40
|
+
onClick: () => S(o(C, "bookings"))
|
|
36
41
|
}, {
|
|
37
42
|
id: "browse",
|
|
38
|
-
label:
|
|
39
|
-
description:
|
|
40
|
-
icon: /* @__PURE__ */
|
|
41
|
-
onClick: () =>
|
|
43
|
+
label: x("ggnomad.event.next.browse", "See more events"),
|
|
44
|
+
description: x("ggnomad.event.next.browseDesc", "Return to Entertainment for other live experiences."),
|
|
45
|
+
icon: /* @__PURE__ */ y(l, { className: "size-4" }),
|
|
46
|
+
onClick: () => S(o(C, "entertainment"))
|
|
42
47
|
}]
|
|
43
48
|
}),
|
|
44
|
-
/* @__PURE__ */
|
|
49
|
+
/* @__PURE__ */ b("div", {
|
|
45
50
|
className: "grid gap-4 lg:grid-cols-2",
|
|
46
|
-
children: [/* @__PURE__ */
|
|
51
|
+
children: [/* @__PURE__ */ b(m, {
|
|
47
52
|
treatment: "glass",
|
|
48
53
|
glow: !0,
|
|
49
54
|
className: "border-border-seam p-5",
|
|
50
|
-
children: [/* @__PURE__ */
|
|
55
|
+
children: [/* @__PURE__ */ y("h2", {
|
|
51
56
|
className: "mb-4 text-lg font-medium tracking-tight text-text-primary",
|
|
52
|
-
children:
|
|
53
|
-
}), /* @__PURE__ */
|
|
57
|
+
children: x("ggnomad.event.details", "Details")
|
|
58
|
+
}), /* @__PURE__ */ b("dl", {
|
|
54
59
|
className: "space-y-2 text-sm",
|
|
55
60
|
children: [
|
|
56
|
-
/* @__PURE__ */
|
|
57
|
-
label:
|
|
58
|
-
value:
|
|
61
|
+
/* @__PURE__ */ y(r, {
|
|
62
|
+
label: x("ggnomad.event.category", "Category"),
|
|
63
|
+
value: E.category
|
|
59
64
|
}),
|
|
60
|
-
/* @__PURE__ */
|
|
61
|
-
label:
|
|
62
|
-
value: new Date(
|
|
65
|
+
/* @__PURE__ */ y(r, {
|
|
66
|
+
label: x("ggnomad.event.starts", "Starts"),
|
|
67
|
+
value: new Date(E.startsAt).toLocaleString()
|
|
63
68
|
}),
|
|
64
|
-
/* @__PURE__ */
|
|
65
|
-
label:
|
|
66
|
-
value:
|
|
69
|
+
/* @__PURE__ */ y(r, {
|
|
70
|
+
label: x("ggnomad.event.ends", "Ends"),
|
|
71
|
+
value: E.endsAt ? new Date(E.endsAt).toLocaleString() : x("ggnomad.event.na", "N/A")
|
|
67
72
|
}),
|
|
68
|
-
/* @__PURE__ */
|
|
69
|
-
label:
|
|
70
|
-
value: `${
|
|
73
|
+
/* @__PURE__ */ y(r, {
|
|
74
|
+
label: x("ggnomad.event.price", "Price"),
|
|
75
|
+
value: `${E.currency} ${Number(E.price).toFixed(2)}`
|
|
71
76
|
})
|
|
72
77
|
]
|
|
73
78
|
})]
|
|
74
|
-
}), /* @__PURE__ */
|
|
75
|
-
title:
|
|
76
|
-
children: /* @__PURE__ */
|
|
79
|
+
}), /* @__PURE__ */ y(n, {
|
|
80
|
+
title: x("ggnomad.event.performers", "Performers"),
|
|
81
|
+
children: /* @__PURE__ */ y("ul", {
|
|
77
82
|
className: "space-y-2 text-sm",
|
|
78
|
-
children: (
|
|
83
|
+
children: (E.performers ?? []).map((e) => /* @__PURE__ */ b("li", {
|
|
79
84
|
className: "flex items-start gap-2 text-text-primary",
|
|
80
|
-
children: [/* @__PURE__ */
|
|
85
|
+
children: [/* @__PURE__ */ y(c, {
|
|
81
86
|
className: "mt-0.5 size-4 flex-shrink-0 text-action-primary-bg",
|
|
82
87
|
"aria-hidden": "true"
|
|
83
88
|
}), e]
|
|
@@ -85,14 +90,14 @@ function b() {
|
|
|
85
90
|
})
|
|
86
91
|
})]
|
|
87
92
|
})
|
|
88
|
-
] }) : /* @__PURE__ */
|
|
93
|
+
] }) : /* @__PURE__ */ y(h, {
|
|
89
94
|
illustration: "empty-data",
|
|
90
|
-
title:
|
|
91
|
-
description:
|
|
95
|
+
title: x("ggnomad.event.notFoundTitle", "No event found"),
|
|
96
|
+
description: x("ggnomad.event.notFoundDescription", "This event is unavailable or may have been removed.")
|
|
92
97
|
})
|
|
93
98
|
});
|
|
94
99
|
}
|
|
95
100
|
//#endregion
|
|
96
|
-
export {
|
|
101
|
+
export { x as EventDetailsPage };
|
|
97
102
|
|
|
98
103
|
//# sourceMappingURL=EventDetailsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EventDetailsPage.js","names":[],"sources":["../../../src/ggnomad/pages/EventDetailsPage.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 { useNavigate, useParams } from 'react-router-dom';\nimport { CalendarCheck, Mic2, 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 { DetailRow, SectionCard } from '../components';\n\nexport function EventDetailsPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const { basePath = '/' } = useGGNomad();\n const { id = '' } = useParams();\n const { data } = useQuery(\n GGNOMAD_EVENT_QUERY as TypedDocumentNode<\n {\n ggnomadEvent: {\n name: string;\n category: string;\n startsAt: string;\n endsAt?: string;\n currency: string;\n price: number;\n performers: 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 <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: 'book',\n label: t('ggnomad.event.next.book', 'Book tickets'),\n description: t(\n 'ggnomad.event.next.bookDesc',\n 'Reserve seats and track them under Bookings.'\n ),\n icon: <CalendarCheck className=\"size-4\" />,\n onClick: () => navigate(joinPath(basePath, 'bookings')),\n },\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-2\">\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.event.details', 'Details')}\n </h2>\n <dl className=\"space-y-2 text-sm\">\n <DetailRow label={t('ggnomad.event.category', 'Category')} value={event.category} />\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 </div>\n </>\n )}\n </PageShell>\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"EventDetailsPage.js","names":[],"sources":["../../../src/ggnomad/pages/EventDetailsPage.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 { useNavigate, useParams } from 'react-router-dom';\nimport { CalendarCheck, Mic2, 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 { DetailRow, SectionCard, DetailHero } from '../components';\n\nexport function EventDetailsPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const { basePath = '/' } = useGGNomad();\n const { id = '' } = useParams();\n const { data } = useQuery(\n GGNOMAD_EVENT_QUERY as TypedDocumentNode<\n {\n ggnomadEvent: {\n name: string;\n category: string;\n startsAt: string;\n endsAt?: string;\n currency: string;\n price: number;\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: 'book',\n label: t('ggnomad.event.next.book', 'Book tickets'),\n description: t(\n 'ggnomad.event.next.bookDesc',\n 'Reserve seats and track them under Bookings.'\n ),\n icon: <CalendarCheck className=\"size-4\" />,\n onClick: () => navigate(joinPath(basePath, 'bookings')),\n },\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-2\">\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.event.details', 'Details')}\n </h2>\n <dl className=\"space-y-2 text-sm\">\n <DetailRow label={t('ggnomad.event.category', 'Category')} value={event.category} />\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 </div>\n </>\n )}\n </PageShell>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AAaA,SAAgB,IAAmB;CACjC,IAAM,EAAE,SAAM,GAAS,EACjB,IAAW,GAAa,EACxB,EAAE,cAAW,QAAQ,GAAY,EACjC,EAAE,QAAK,OAAO,GAAW,EACzB,EAAE,YAAS,EACf,GAeA;EAAE,WAAW,EAAE,OAAI;EAAE,MAAM,CAAC;EAAI,CACjC,EACK,IAAQ,GAAM;AAEpB,QACE,kBAAC,GAAD;EACE,OAAO,GAAO,QAAQ,EAAE,+BAA+B,gBAAgB;EACvE,UAAU,EAAE,0BAA0B,uCAAuC;YAE3E,IAUA,kBAAA,GAAA,EAAA,UAAA;GACE,kBAAC,GAAD;IAAY,OAAO,EAAM,SAAS;IAAI,KAAK,EAAM;IAAQ,CAAA;GACzD,kBAAC,GAAD;IAAa,MAAM,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA;cAC7C,EACC,yBACA,gLACD;IACW,CAAA;GAEd,kBAAC,GAAD;IACE,YAAW;IACX,OAAO,CACL;KACE,IAAI;KACJ,OAAO,EAAE,2BAA2B,eAAe;KACnD,aAAa,EACX,+BACA,+CACD;KACD,MAAM,kBAAC,GAAD,EAAe,WAAU,UAAW,CAAA;KAC1C,eAAe,EAAS,EAAS,GAAU,WAAW,CAAC;KACxD,EACD;KACE,IAAI;KACJ,OAAO,EAAE,6BAA6B,kBAAkB;KACxD,aAAa,EACX,iCACA,sDACD;KACD,MAAM,kBAAC,GAAD,EAAQ,WAAU,UAAW,CAAA;KACnC,eAAe,EAAS,EAAS,GAAU,gBAAgB,CAAC;KAC7D,CACF;IACD,CAAA;GAEF,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAW,WAAU;KAAQ,MAAA;KAAK,WAAU;eAA5C,CACE,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAE,yBAAyB,UAAU;MACnC,CAAA,EACL,kBAAC,MAAD;MAAI,WAAU;gBAAd;OACE,kBAAC,GAAD;QAAW,OAAO,EAAE,0BAA0B,WAAW;QAAE,OAAO,EAAM;QAAY,CAAA;OACpF,kBAAC,GAAD;QACE,OAAO,EAAE,wBAAwB,SAAS;QAC1C,OAAO,IAAI,KAAK,EAAM,SAAS,CAAC,gBAAgB;QAChD,CAAA;OACF,kBAAC,GAAD;QACE,OAAO,EAAE,sBAAsB,OAAO;QACtC,OACE,EAAM,SACF,IAAI,KAAK,EAAM,OAAO,CAAC,gBAAgB,GACvC,EAAE,oBAAoB,MAAM;QAElC,CAAA;OACF,kBAAC,GAAD;QACE,OAAO,EAAE,uBAAuB,QAAQ;QACxC,OAAO,GAAG,EAAM,SAAS,GAAG,OAAO,EAAM,MAAM,CAAC,QAAQ,EAAE;QAC1D,CAAA;OACC;QACK;QACZ,kBAAC,GAAD;KAAa,OAAO,EAAE,4BAA4B,aAAa;eAC7D,kBAAC,MAAD;MAAI,WAAU;iBACV,EAAM,cAAc,EAAE,EAAE,KAAK,MAC7B,kBAAC,MAAD;OAAoB,WAAU;iBAA9B,CACE,kBAAC,GAAD;QACE,WAAU;QACV,eAAY;QACZ,CAAA,EACD,EACE;SANI,EAMJ,CACL;MACC,CAAA;KACO,CAAA,CACV;;GACL,EAAA,CAAA,GAnFH,kBAAC,GAAD;GACE,cAAa;GACb,OAAO,EAAE,+BAA+B,iBAAiB;GACzD,aAAa,EACX,qCACA,sDACD;GACD,CAAA;EA8EM,CAAA"}
|
|
@@ -39,6 +39,8 @@ function m() {
|
|
|
39
39
|
className: "grid gap-3 sm:grid-cols-2 xl:grid-cols-3",
|
|
40
40
|
children: v.map((e) => /* @__PURE__ */ f(n, {
|
|
41
41
|
to: e.id,
|
|
42
|
+
image: e.images?.[0],
|
|
43
|
+
imageAlt: e.name,
|
|
42
44
|
title: e.name,
|
|
43
45
|
meta: e.type,
|
|
44
46
|
price: `${e.currency} ${Number(e.pricePerNight).toFixed(2)} ${m("ggnomad.hospitality.card.perNight", "per night")}`,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HospitalityPage.js","names":[],"sources":["../../../src/ggnomad/pages/HospitalityPage.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 { Hotel, Star } from 'lucide-react';\nimport { GlassCard, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\n\nimport { GGNOMAD_PROPERTIES_QUERY } from '../graphql';\nimport { PageShell } from './PageShell';\nimport { EntityCard, LoadMoreButton } from '../components';\n\nexport function HospitalityPage() {\n const { t } = useI18n();\n const [limit, setLimit] = useState(12);\n const { data } = useQuery(\n GGNOMAD_PROPERTIES_QUERY as TypedDocumentNode<\n {\n ggnomadProperties: Array<{\n id: string;\n name: string;\n type: string;\n pricePerNight: number;\n currency: string;\n rating?: number;\n }>;\n },\n { first: number; skip: number }\n >,\n { variables: { first: limit, skip: 0 } }\n );\n const properties = data?.ggnomadProperties ?? [];\n\n return (\n <PageShell\n title={t('ggnomad.hospitality.title', 'Hospitality')}\n titleAccent={t('ggnomad.hospitality.title.accent', 'Hospitality')}\n subtitle={t(\n 'ggnomad.hospitality.subtitle',\n 'Property discovery powered by live GraphQL data'\n )}\n >\n <PagePurpose icon={<Hotel className=\"size-4\" />}>\n {t(\n 'ggnomad.hospitality.purpose',\n 'Discover places to stay — hotels, homestays and short-stay rentals. Open any property to review its nightly price, guest capacity and rating, then create a booking for the dates you need.'\n )}\n </PagePurpose>\n\n {properties.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('ggnomad.hospitality.emptyTitle', 'No properties available')}\n description={t(\n 'ggnomad.hospitality.emptyDescription',\n 'Properties published by providers will be discoverable 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.hospitality.featuredStays', 'Featured stays')}\n </h2>\n <div className=\"grid gap-3 sm:grid-cols-2 xl:grid-cols-3\">\n {properties.map((property) => (\n <EntityCard\n key={property.id}\n to={property.id}\n title={property.name}\n meta={property.type}\n price={`${property.currency} ${Number(property.pricePerNight).toFixed(2)} ${t(\n 'ggnomad.hospitality.card.perNight',\n 'per night'\n )}`}\n footer={\n <span className=\"inline-flex items-center gap-1 tabular-nums\">\n <Star className=\"size-3.5 text-status-warning-text\" aria-hidden=\"true\" />\n {property.rating ?? t('ggnomad.hospitality.card.noRating', 'N/A')}\n </span>\n }\n />\n ))}\n </div>\n <LoadMoreButton\n visible={properties.length >= limit}\n onLoadMore={() => setLimit((l) => l + 12)}\n />\n </GlassCard>\n )}\n </PageShell>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAWA,SAAgB,IAAkB;CAChC,IAAM,EAAE,SAAM,GAAS,EACjB,CAAC,GAAO,KAAY,EAAS,GAAG,EAChC,EAAE,YAAS,EACf,
|
|
1
|
+
{"version":3,"file":"HospitalityPage.js","names":[],"sources":["../../../src/ggnomad/pages/HospitalityPage.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 { Hotel, Star } from 'lucide-react';\nimport { GlassCard, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\n\nimport { GGNOMAD_PROPERTIES_QUERY } from '../graphql';\nimport { PageShell } from './PageShell';\nimport { EntityCard, LoadMoreButton } from '../components';\n\nexport function HospitalityPage() {\n const { t } = useI18n();\n const [limit, setLimit] = useState(12);\n const { data } = useQuery(\n GGNOMAD_PROPERTIES_QUERY as TypedDocumentNode<\n {\n ggnomadProperties: Array<{\n id: string;\n name: string;\n type: string;\n pricePerNight: number;\n currency: string;\n rating?: number;\n images?: string[];\n }>;\n },\n { first: number; skip: number }\n >,\n { variables: { first: limit, skip: 0 } }\n );\n const properties = data?.ggnomadProperties ?? [];\n\n return (\n <PageShell\n title={t('ggnomad.hospitality.title', 'Hospitality')}\n titleAccent={t('ggnomad.hospitality.title.accent', 'Hospitality')}\n subtitle={t(\n 'ggnomad.hospitality.subtitle',\n 'Property discovery powered by live GraphQL data'\n )}\n >\n <PagePurpose icon={<Hotel className=\"size-4\" />}>\n {t(\n 'ggnomad.hospitality.purpose',\n 'Discover places to stay — hotels, homestays and short-stay rentals. Open any property to review its nightly price, guest capacity and rating, then create a booking for the dates you need.'\n )}\n </PagePurpose>\n\n {properties.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('ggnomad.hospitality.emptyTitle', 'No properties available')}\n description={t(\n 'ggnomad.hospitality.emptyDescription',\n 'Properties published by providers will be discoverable 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.hospitality.featuredStays', 'Featured stays')}\n </h2>\n <div className=\"grid gap-3 sm:grid-cols-2 xl:grid-cols-3\">\n {properties.map((property) => (\n <EntityCard\n key={property.id}\n to={property.id}\n image={property.images?.[0]}\n imageAlt={property.name}\n title={property.name}\n meta={property.type}\n price={`${property.currency} ${Number(property.pricePerNight).toFixed(2)} ${t(\n 'ggnomad.hospitality.card.perNight',\n 'per night'\n )}`}\n footer={\n <span className=\"inline-flex items-center gap-1 tabular-nums\">\n <Star className=\"size-3.5 text-status-warning-text\" aria-hidden=\"true\" />\n {property.rating ?? t('ggnomad.hospitality.card.noRating', 'N/A')}\n </span>\n }\n />\n ))}\n </div>\n <LoadMoreButton\n visible={properties.length >= limit}\n onLoadMore={() => setLimit((l) => l + 12)}\n />\n </GlassCard>\n )}\n </PageShell>\n );\n}\n"],"mappings":";;;;;;;;;;;;AAWA,SAAgB,IAAkB;CAChC,IAAM,EAAE,SAAM,GAAS,EACjB,CAAC,GAAO,KAAY,EAAS,GAAG,EAChC,EAAE,YAAS,EACf,GAcA,EAAE,WAAW;EAAE,OAAO;EAAO,MAAM;EAAG,EAAE,CACzC,EACK,IAAa,GAAM,qBAAqB,EAAE;AAEhD,QACE,kBAAC,GAAD;EACE,OAAO,EAAE,6BAA6B,cAAc;EACpD,aAAa,EAAE,oCAAoC,cAAc;EACjE,UAAU,EACR,gCACA,kDACD;YANH,CAQE,kBAAC,GAAD;GAAa,MAAM,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA;aAC5C,EACC,+BACA,8LACD;GACW,CAAA,EAEb,EAAW,WAAW,IACrB,kBAAC,GAAD;GACE,cAAa;GACb,OAAO,EAAE,kCAAkC,0BAA0B;GACrE,aAAa,EACX,wCACA,+DACD;GACD,CAAA,GAEF,kBAAC,GAAD;GAAW,WAAU;GAAQ,MAAA;GAAK,WAAU;aAA5C;IACE,kBAAC,MAAD;KAAI,WAAU;eACX,EAAE,qCAAqC,iBAAiB;KACtD,CAAA;IACL,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAW,KAAK,MACf,kBAAC,GAAD;MAEE,IAAI,EAAS;MACb,OAAO,EAAS,SAAS;MACzB,UAAU,EAAS;MACnB,OAAO,EAAS;MAChB,MAAM,EAAS;MACf,OAAO,GAAG,EAAS,SAAS,GAAG,OAAO,EAAS,cAAc,CAAC,QAAQ,EAAE,CAAC,GAAG,EAC1E,qCACA,YACD;MACD,QACE,kBAAC,QAAD;OAAM,WAAU;iBAAhB,CACE,kBAAC,GAAD;QAAM,WAAU;QAAoC,eAAY;QAAS,CAAA,EACxE,EAAS,UAAU,EAAE,qCAAqC,MAAM,CAC5D;;MAET,EAhBK,EAAS,GAgBd,CACF;KACE,CAAA;IACN,kBAAC,GAAD;KACE,SAAS,EAAW,UAAU;KAC9B,kBAAkB,GAAU,MAAM,IAAI,GAAG;KACzC,CAAA;IACQ;KAEJ"}
|
|
@@ -2,105 +2,110 @@ import { GGNOMAD_PROPERTY_QUERY as e } from "../graphql.js";
|
|
|
2
2
|
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
|
+
import { DetailHero as i } from "../components/DetailHero.js";
|
|
5
6
|
import "../components/index.js";
|
|
6
|
-
import { useGGNomad as
|
|
7
|
-
import { joinPath as
|
|
8
|
-
import { CalendarCheck as
|
|
9
|
-
import { useI18n as
|
|
10
|
-
import { useNavigate as
|
|
11
|
-
import { useQuery as
|
|
12
|
-
import { GlassCard as
|
|
13
|
-
import { Fragment as
|
|
7
|
+
import { useGGNomad as a } from "../providers/GGNomadProvider.js";
|
|
8
|
+
import { joinPath as o } from "../utils/navigation.js";
|
|
9
|
+
import { CalendarCheck as s, Hotel as c } from "lucide-react";
|
|
10
|
+
import { useI18n as l } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
11
|
+
import { useNavigate as u, useParams as d } from "react-router-dom";
|
|
12
|
+
import { useQuery as f } from "@apollo/client/react";
|
|
13
|
+
import { GlassCard as p, IllustratedEmptyState as m, NextSteps as h, PagePurpose as g } from "@burdenoff/fe-libs/ui";
|
|
14
|
+
import { Fragment as _, jsx as v, jsxs as y } from "react/jsx-runtime";
|
|
14
15
|
//#region src/ggnomad/pages/PropertyDetailsPage.tsx
|
|
15
|
-
function
|
|
16
|
-
let { t:
|
|
17
|
-
variables: { id:
|
|
18
|
-
skip: !
|
|
19
|
-
}),
|
|
20
|
-
return /* @__PURE__ */
|
|
21
|
-
title:
|
|
22
|
-
subtitle:
|
|
23
|
-
children:
|
|
24
|
-
/* @__PURE__ */
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
function b() {
|
|
17
|
+
let { t: b } = l(), x = u(), { basePath: S = "/" } = a(), { id: C = "" } = d(), { data: w } = f(e, {
|
|
18
|
+
variables: { id: C },
|
|
19
|
+
skip: !C
|
|
20
|
+
}), T = w?.ggnomadProperty;
|
|
21
|
+
return /* @__PURE__ */ v(t, {
|
|
22
|
+
title: T?.name ?? b("ggnomad.property.fallbackTitle", "Property details"),
|
|
23
|
+
subtitle: b("ggnomad.property.subtitle", "Detailed hospitality listing view"),
|
|
24
|
+
children: T ? /* @__PURE__ */ y(_, { children: [
|
|
25
|
+
/* @__PURE__ */ v(i, {
|
|
26
|
+
image: T.images?.[0],
|
|
27
|
+
alt: T.name
|
|
27
28
|
}),
|
|
28
|
-
/* @__PURE__ */
|
|
29
|
+
/* @__PURE__ */ v(g, {
|
|
30
|
+
icon: /* @__PURE__ */ v(c, { className: "size-4" }),
|
|
31
|
+
children: b("ggnomad.property.purpose", "Everything you need to decide on this stay — its type, description, nightly price, guest capacity, rating and the provider behind it — before you reserve it.")
|
|
32
|
+
}),
|
|
33
|
+
/* @__PURE__ */ v(h, {
|
|
29
34
|
storageKey: "ggnomad-property-detail",
|
|
30
35
|
steps: [{
|
|
31
36
|
id: "book",
|
|
32
|
-
label:
|
|
33
|
-
description:
|
|
34
|
-
icon: /* @__PURE__ */
|
|
35
|
-
onClick: () =>
|
|
37
|
+
label: b("ggnomad.property.next.book", "Book this stay"),
|
|
38
|
+
description: b("ggnomad.property.next.bookDesc", "Reserve these dates and track it under Bookings."),
|
|
39
|
+
icon: /* @__PURE__ */ v(s, { className: "size-4" }),
|
|
40
|
+
onClick: () => x(o(S, "bookings"))
|
|
36
41
|
}, {
|
|
37
42
|
id: "browse",
|
|
38
|
-
label:
|
|
39
|
-
description:
|
|
40
|
-
icon: /* @__PURE__ */
|
|
41
|
-
onClick: () =>
|
|
43
|
+
label: b("ggnomad.property.next.browse", "Compare other stays"),
|
|
44
|
+
description: b("ggnomad.property.next.browseDesc", "Go back to hospitality listings to compare options."),
|
|
45
|
+
icon: /* @__PURE__ */ v(c, { className: "size-4" }),
|
|
46
|
+
onClick: () => x(o(S, "hospitality"))
|
|
42
47
|
}]
|
|
43
48
|
}),
|
|
44
|
-
/* @__PURE__ */
|
|
49
|
+
/* @__PURE__ */ y("div", {
|
|
45
50
|
className: "grid gap-4 lg:grid-cols-2",
|
|
46
|
-
children: [/* @__PURE__ */
|
|
47
|
-
title:
|
|
51
|
+
children: [/* @__PURE__ */ y(n, {
|
|
52
|
+
title: b("ggnomad.property.about", "About"),
|
|
48
53
|
children: [
|
|
49
|
-
/* @__PURE__ */
|
|
54
|
+
/* @__PURE__ */ v("p", {
|
|
50
55
|
className: "text-sm text-text-secondary",
|
|
51
|
-
children:
|
|
56
|
+
children: b("ggnomad.property.type", "Type")
|
|
52
57
|
}),
|
|
53
|
-
/* @__PURE__ */
|
|
58
|
+
/* @__PURE__ */ v("p", {
|
|
54
59
|
className: "text-text-primary",
|
|
55
|
-
children:
|
|
60
|
+
children: T.type
|
|
56
61
|
}),
|
|
57
|
-
/* @__PURE__ */
|
|
62
|
+
/* @__PURE__ */ v("p", {
|
|
58
63
|
className: "mt-3 text-sm text-text-secondary",
|
|
59
|
-
children:
|
|
64
|
+
children: b("ggnomad.property.description", "Description")
|
|
60
65
|
}),
|
|
61
|
-
/* @__PURE__ */
|
|
66
|
+
/* @__PURE__ */ v("p", {
|
|
62
67
|
className: "text-text-primary",
|
|
63
|
-
children:
|
|
68
|
+
children: T.description ?? b("ggnomad.property.noDescription", "No description")
|
|
64
69
|
})
|
|
65
70
|
]
|
|
66
|
-
}), /* @__PURE__ */
|
|
71
|
+
}), /* @__PURE__ */ y(p, {
|
|
67
72
|
treatment: "glass",
|
|
68
73
|
glow: !0,
|
|
69
74
|
className: "border-border-seam p-5",
|
|
70
|
-
children: [/* @__PURE__ */
|
|
75
|
+
children: [/* @__PURE__ */ v("h2", {
|
|
71
76
|
className: "mb-4 text-lg font-medium tracking-tight text-text-primary",
|
|
72
|
-
children:
|
|
73
|
-
}), /* @__PURE__ */
|
|
77
|
+
children: b("ggnomad.property.details", "Details")
|
|
78
|
+
}), /* @__PURE__ */ y("dl", {
|
|
74
79
|
className: "space-y-2 text-sm",
|
|
75
80
|
children: [
|
|
76
|
-
/* @__PURE__ */
|
|
77
|
-
label:
|
|
78
|
-
value: `${
|
|
81
|
+
/* @__PURE__ */ v(r, {
|
|
82
|
+
label: b("ggnomad.property.price", "Price"),
|
|
83
|
+
value: `${T.currency} ${Number(T.pricePerNight).toFixed(2)}`
|
|
79
84
|
}),
|
|
80
|
-
/* @__PURE__ */
|
|
81
|
-
label:
|
|
82
|
-
value: String(
|
|
85
|
+
/* @__PURE__ */ v(r, {
|
|
86
|
+
label: b("ggnomad.property.guests", "Guests"),
|
|
87
|
+
value: String(T.maxGuests)
|
|
83
88
|
}),
|
|
84
|
-
/* @__PURE__ */
|
|
85
|
-
label:
|
|
86
|
-
value: String(
|
|
89
|
+
/* @__PURE__ */ v(r, {
|
|
90
|
+
label: b("ggnomad.property.rating", "Rating"),
|
|
91
|
+
value: String(T.rating ?? b("ggnomad.property.noRating", "N/A"))
|
|
87
92
|
}),
|
|
88
|
-
/* @__PURE__ */
|
|
89
|
-
label:
|
|
90
|
-
value:
|
|
93
|
+
/* @__PURE__ */ v(r, {
|
|
94
|
+
label: b("ggnomad.property.provider", "Provider"),
|
|
95
|
+
value: T.provider?.name ?? b("ggnomad.property.unassigned", "Unassigned")
|
|
91
96
|
})
|
|
92
97
|
]
|
|
93
98
|
})]
|
|
94
99
|
})]
|
|
95
100
|
})
|
|
96
|
-
] }) : /* @__PURE__ */
|
|
101
|
+
] }) : /* @__PURE__ */ v(m, {
|
|
97
102
|
illustration: "empty-data",
|
|
98
|
-
title:
|
|
99
|
-
description:
|
|
103
|
+
title: b("ggnomad.property.notFoundTitle", "No property found"),
|
|
104
|
+
description: b("ggnomad.property.notFoundDescription", "This property is unavailable or may have been removed.")
|
|
100
105
|
})
|
|
101
106
|
});
|
|
102
107
|
}
|
|
103
108
|
//#endregion
|
|
104
|
-
export {
|
|
109
|
+
export { b as PropertyDetailsPage };
|
|
105
110
|
|
|
106
111
|
//# sourceMappingURL=PropertyDetailsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PropertyDetailsPage.js","names":[],"sources":["../../../src/ggnomad/pages/PropertyDetailsPage.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 { useNavigate, useParams } from 'react-router-dom';\nimport { CalendarCheck, Hotel } from 'lucide-react';\nimport { GlassCard, IllustratedEmptyState, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';\n\nimport { GGNOMAD_PROPERTY_QUERY } from '../graphql';\nimport { useGGNomad } from '../providers/GGNomadProvider';\nimport { joinPath } from '../utils/navigation';\nimport { PageShell } from './PageShell';\nimport { DetailRow, SectionCard } from '../components';\n\nexport function PropertyDetailsPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const { basePath = '/' } = useGGNomad();\n const { id = '' } = useParams();\n const { data } = useQuery(\n GGNOMAD_PROPERTY_QUERY as TypedDocumentNode<\n {\n ggnomadProperty: {\n name: string;\n type: string;\n description?: string;\n currency: string;\n pricePerNight: number;\n maxGuests: number;\n rating?: number;\n provider?: { name: string };\n } | null;\n },\n { id: string }\n >,\n { variables: { id }, skip: !id }\n );\n const property = data?.ggnomadProperty;\n\n return (\n <PageShell\n title={property?.name ?? t('ggnomad.property.fallbackTitle', 'Property details')}\n subtitle={t('ggnomad.property.subtitle', 'Detailed hospitality listing view')}\n >\n {!property ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('ggnomad.property.notFoundTitle', 'No property found')}\n description={t(\n 'ggnomad.property.notFoundDescription',\n 'This property is unavailable or may have been removed.'\n )}\n />\n ) : (\n <>\n <PagePurpose icon={<Hotel className=\"size-4\" />}>\n {t(\n 'ggnomad.property.purpose',\n 'Everything you need to decide on this stay — its type, description, nightly price, guest capacity, rating and the provider behind it — before you reserve it.'\n )}\n </PagePurpose>\n\n <NextSteps\n storageKey=\"ggnomad-property-detail\"\n steps={[\n {\n id: 'book',\n label: t('ggnomad.property.next.book', 'Book this stay'),\n description: t(\n 'ggnomad.property.next.bookDesc',\n 'Reserve these dates and track it under Bookings.'\n ),\n icon: <CalendarCheck className=\"size-4\" />,\n onClick: () => navigate(joinPath(basePath, 'bookings')),\n },\n {\n id: 'browse',\n label: t('ggnomad.property.next.browse', 'Compare other stays'),\n description: t(\n 'ggnomad.property.next.browseDesc',\n 'Go back to hospitality listings to compare options.'\n ),\n icon: <Hotel className=\"size-4\" />,\n onClick: () => navigate(joinPath(basePath, 'hospitality')),\n },\n ]}\n />\n\n <div className=\"grid gap-4 lg:grid-cols-2\">\n <SectionCard title={t('ggnomad.property.about', 'About')}>\n <p className=\"text-sm text-text-secondary\">{t('ggnomad.property.type', 'Type')}</p>\n <p className=\"text-text-primary\">{property.type}</p>\n <p className=\"mt-3 text-sm text-text-secondary\">\n {t('ggnomad.property.description', 'Description')}\n </p>\n <p className=\"text-text-primary\">\n {property.description ?? t('ggnomad.property.noDescription', 'No description')}\n </p>\n </SectionCard>\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.property.details', 'Details')}\n </h2>\n <dl className=\"space-y-2 text-sm\">\n <DetailRow\n label={t('ggnomad.property.price', 'Price')}\n value={`${property.currency} ${Number(property.pricePerNight).toFixed(2)}`}\n />\n <DetailRow\n label={t('ggnomad.property.guests', 'Guests')}\n value={String(property.maxGuests)}\n />\n <DetailRow\n label={t('ggnomad.property.rating', 'Rating')}\n value={String(property.rating ?? t('ggnomad.property.noRating', 'N/A'))}\n />\n <DetailRow\n label={t('ggnomad.property.provider', 'Provider')}\n value={property.provider?.name ?? t('ggnomad.property.unassigned', 'Unassigned')}\n />\n </dl>\n </GlassCard>\n </div>\n </>\n )}\n </PageShell>\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"PropertyDetailsPage.js","names":[],"sources":["../../../src/ggnomad/pages/PropertyDetailsPage.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 { useNavigate, useParams } from 'react-router-dom';\nimport { CalendarCheck, Hotel } from 'lucide-react';\nimport { GlassCard, IllustratedEmptyState, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';\n\nimport { GGNOMAD_PROPERTY_QUERY } from '../graphql';\nimport { useGGNomad } from '../providers/GGNomadProvider';\nimport { joinPath } from '../utils/navigation';\nimport { PageShell } from './PageShell';\nimport { DetailRow, SectionCard, DetailHero } from '../components';\n\nexport function PropertyDetailsPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const { basePath = '/' } = useGGNomad();\n const { id = '' } = useParams();\n const { data } = useQuery(\n GGNOMAD_PROPERTY_QUERY as TypedDocumentNode<\n {\n ggnomadProperty: {\n name: string;\n type: string;\n description?: string;\n currency: string;\n pricePerNight: number;\n maxGuests: number;\n rating?: number;\n provider?: { name: string };\n images?: string[];\n } | null;\n },\n { id: string }\n >,\n { variables: { id }, skip: !id }\n );\n const property = data?.ggnomadProperty;\n\n return (\n <PageShell\n title={property?.name ?? t('ggnomad.property.fallbackTitle', 'Property details')}\n subtitle={t('ggnomad.property.subtitle', 'Detailed hospitality listing view')}\n >\n {!property ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('ggnomad.property.notFoundTitle', 'No property found')}\n description={t(\n 'ggnomad.property.notFoundDescription',\n 'This property is unavailable or may have been removed.'\n )}\n />\n ) : (\n <>\n <DetailHero image={property.images?.[0]} alt={property.name} />\n <PagePurpose icon={<Hotel className=\"size-4\" />}>\n {t(\n 'ggnomad.property.purpose',\n 'Everything you need to decide on this stay — its type, description, nightly price, guest capacity, rating and the provider behind it — before you reserve it.'\n )}\n </PagePurpose>\n\n <NextSteps\n storageKey=\"ggnomad-property-detail\"\n steps={[\n {\n id: 'book',\n label: t('ggnomad.property.next.book', 'Book this stay'),\n description: t(\n 'ggnomad.property.next.bookDesc',\n 'Reserve these dates and track it under Bookings.'\n ),\n icon: <CalendarCheck className=\"size-4\" />,\n onClick: () => navigate(joinPath(basePath, 'bookings')),\n },\n {\n id: 'browse',\n label: t('ggnomad.property.next.browse', 'Compare other stays'),\n description: t(\n 'ggnomad.property.next.browseDesc',\n 'Go back to hospitality listings to compare options.'\n ),\n icon: <Hotel className=\"size-4\" />,\n onClick: () => navigate(joinPath(basePath, 'hospitality')),\n },\n ]}\n />\n\n <div className=\"grid gap-4 lg:grid-cols-2\">\n <SectionCard title={t('ggnomad.property.about', 'About')}>\n <p className=\"text-sm text-text-secondary\">{t('ggnomad.property.type', 'Type')}</p>\n <p className=\"text-text-primary\">{property.type}</p>\n <p className=\"mt-3 text-sm text-text-secondary\">\n {t('ggnomad.property.description', 'Description')}\n </p>\n <p className=\"text-text-primary\">\n {property.description ?? t('ggnomad.property.noDescription', 'No description')}\n </p>\n </SectionCard>\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.property.details', 'Details')}\n </h2>\n <dl className=\"space-y-2 text-sm\">\n <DetailRow\n label={t('ggnomad.property.price', 'Price')}\n value={`${property.currency} ${Number(property.pricePerNight).toFixed(2)}`}\n />\n <DetailRow\n label={t('ggnomad.property.guests', 'Guests')}\n value={String(property.maxGuests)}\n />\n <DetailRow\n label={t('ggnomad.property.rating', 'Rating')}\n value={String(property.rating ?? t('ggnomad.property.noRating', 'N/A'))}\n />\n <DetailRow\n label={t('ggnomad.property.provider', 'Provider')}\n value={property.provider?.name ?? t('ggnomad.property.unassigned', 'Unassigned')}\n />\n </dl>\n </GlassCard>\n </div>\n </>\n )}\n </PageShell>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AAaA,SAAgB,IAAsB;CACpC,IAAM,EAAE,SAAM,GAAS,EACjB,IAAW,GAAa,EACxB,EAAE,cAAW,QAAQ,GAAY,EACjC,EAAE,QAAK,OAAO,GAAW,EACzB,EAAE,YAAS,EACf,GAgBA;EAAE,WAAW,EAAE,OAAI;EAAE,MAAM,CAAC;EAAI,CACjC,EACK,IAAW,GAAM;AAEvB,QACE,kBAAC,GAAD;EACE,OAAO,GAAU,QAAQ,EAAE,kCAAkC,mBAAmB;EAChF,UAAU,EAAE,6BAA6B,oCAAoC;YAE3E,IAUA,kBAAA,GAAA,EAAA,UAAA;GACE,kBAAC,GAAD;IAAY,OAAO,EAAS,SAAS;IAAI,KAAK,EAAS;IAAQ,CAAA;GAC/D,kBAAC,GAAD;IAAa,MAAM,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA;cAC5C,EACC,4BACA,gKACD;IACW,CAAA;GAEd,kBAAC,GAAD;IACE,YAAW;IACX,OAAO,CACL;KACE,IAAI;KACJ,OAAO,EAAE,8BAA8B,iBAAiB;KACxD,aAAa,EACX,kCACA,mDACD;KACD,MAAM,kBAAC,GAAD,EAAe,WAAU,UAAW,CAAA;KAC1C,eAAe,EAAS,EAAS,GAAU,WAAW,CAAC;KACxD,EACD;KACE,IAAI;KACJ,OAAO,EAAE,gCAAgC,sBAAsB;KAC/D,aAAa,EACX,oCACA,sDACD;KACD,MAAM,kBAAC,GAAD,EAAO,WAAU,UAAW,CAAA;KAClC,eAAe,EAAS,EAAS,GAAU,cAAc,CAAC;KAC3D,CACF;IACD,CAAA;GAEF,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAa,OAAO,EAAE,0BAA0B,QAAQ;eAAxD;MACE,kBAAC,KAAD;OAAG,WAAU;iBAA+B,EAAE,yBAAyB,OAAO;OAAK,CAAA;MACnF,kBAAC,KAAD;OAAG,WAAU;iBAAqB,EAAS;OAAS,CAAA;MACpD,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAE,gCAAgC,cAAc;OAC/C,CAAA;MACJ,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAS,eAAe,EAAE,kCAAkC,iBAAiB;OAC5E,CAAA;MACQ;QACd,kBAAC,GAAD;KAAW,WAAU;KAAQ,MAAA;KAAK,WAAU;eAA5C,CACE,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAE,4BAA4B,UAAU;MACtC,CAAA,EACL,kBAAC,MAAD;MAAI,WAAU;gBAAd;OACE,kBAAC,GAAD;QACE,OAAO,EAAE,0BAA0B,QAAQ;QAC3C,OAAO,GAAG,EAAS,SAAS,GAAG,OAAO,EAAS,cAAc,CAAC,QAAQ,EAAE;QACxE,CAAA;OACF,kBAAC,GAAD;QACE,OAAO,EAAE,2BAA2B,SAAS;QAC7C,OAAO,OAAO,EAAS,UAAU;QACjC,CAAA;OACF,kBAAC,GAAD;QACE,OAAO,EAAE,2BAA2B,SAAS;QAC7C,OAAO,OAAO,EAAS,UAAU,EAAE,6BAA6B,MAAM,CAAC;QACvE,CAAA;OACF,kBAAC,GAAD;QACE,OAAO,EAAE,6BAA6B,WAAW;QACjD,OAAO,EAAS,UAAU,QAAQ,EAAE,+BAA+B,aAAa;QAChF,CAAA;OACC;QACK;OACR;;GACL,EAAA,CAAA,GA/EH,kBAAC,GAAD;GACE,cAAa;GACb,OAAO,EAAE,kCAAkC,oBAAoB;GAC/D,aAAa,EACX,wCACA,yDACD;GACD,CAAA;EA0EM,CAAA"}
|
|
@@ -2,82 +2,87 @@ import { GGNOMAD_TOUR_QUERY as e } from "../graphql.js";
|
|
|
2
2
|
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
|
+
import { DetailHero as i } from "../components/DetailHero.js";
|
|
5
6
|
import "../components/index.js";
|
|
6
|
-
import { useGGNomad as
|
|
7
|
-
import { joinPath as
|
|
8
|
-
import { CalendarCheck as
|
|
9
|
-
import { useI18n as
|
|
10
|
-
import { useNavigate as
|
|
11
|
-
import { useQuery as
|
|
12
|
-
import { GlassCard as
|
|
13
|
-
import { Fragment as
|
|
7
|
+
import { useGGNomad as a } from "../providers/GGNomadProvider.js";
|
|
8
|
+
import { joinPath as o } from "../utils/navigation.js";
|
|
9
|
+
import { CalendarCheck as s, CheckCircle2 as c, Compass as l } from "lucide-react";
|
|
10
|
+
import { useI18n as u } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
|
|
11
|
+
import { useNavigate as d, useParams as f } from "react-router-dom";
|
|
12
|
+
import { useQuery as p } from "@apollo/client/react";
|
|
13
|
+
import { GlassCard as m, IllustratedEmptyState as h, NextSteps as g, PagePurpose as _ } from "@burdenoff/fe-libs/ui";
|
|
14
|
+
import { Fragment as v, jsx as y, jsxs as b } from "react/jsx-runtime";
|
|
14
15
|
//#region src/ggnomad/pages/TourDetailsPage.tsx
|
|
15
|
-
function
|
|
16
|
-
let { t:
|
|
17
|
-
variables: { id:
|
|
18
|
-
skip: !
|
|
19
|
-
}),
|
|
20
|
-
return /* @__PURE__ */
|
|
21
|
-
title:
|
|
22
|
-
subtitle:
|
|
23
|
-
children:
|
|
24
|
-
/* @__PURE__ */
|
|
25
|
-
|
|
26
|
-
|
|
16
|
+
function x() {
|
|
17
|
+
let { t: x } = u(), S = d(), { basePath: C = "/" } = a(), { id: w = "" } = f(), { data: T } = p(e, {
|
|
18
|
+
variables: { id: w },
|
|
19
|
+
skip: !w
|
|
20
|
+
}), E = T?.ggnomadTour;
|
|
21
|
+
return /* @__PURE__ */ y(t, {
|
|
22
|
+
title: E?.name ?? x("ggnomad.tour.fallbackTitle", "Tour details"),
|
|
23
|
+
subtitle: x("ggnomad.tour.subtitle", "Tour package details and booking context"),
|
|
24
|
+
children: E ? /* @__PURE__ */ b(v, { children: [
|
|
25
|
+
/* @__PURE__ */ y(i, {
|
|
26
|
+
image: E.images?.[0],
|
|
27
|
+
alt: E.name
|
|
27
28
|
}),
|
|
28
|
-
/* @__PURE__ */
|
|
29
|
+
/* @__PURE__ */ y(_, {
|
|
30
|
+
icon: /* @__PURE__ */ y(l, { className: "size-4" }),
|
|
31
|
+
children: x("ggnomad.tour.purpose", "See exactly what this tour involves — duration, difficulty, your guide, price and the highlights you’ll experience — so you can confirm it fits your trip before booking.")
|
|
32
|
+
}),
|
|
33
|
+
/* @__PURE__ */ y(g, {
|
|
29
34
|
storageKey: "ggnomad-tour-detail",
|
|
30
35
|
steps: [{
|
|
31
36
|
id: "book",
|
|
32
|
-
label:
|
|
33
|
-
description:
|
|
34
|
-
icon: /* @__PURE__ */
|
|
35
|
-
onClick: () =>
|
|
37
|
+
label: x("ggnomad.tour.next.book", "Book this tour"),
|
|
38
|
+
description: x("ggnomad.tour.next.bookDesc", "Add it to your itinerary and track it under Bookings."),
|
|
39
|
+
icon: /* @__PURE__ */ y(s, { className: "size-4" }),
|
|
40
|
+
onClick: () => S(o(C, "bookings"))
|
|
36
41
|
}, {
|
|
37
42
|
id: "browse",
|
|
38
|
-
label:
|
|
39
|
-
description:
|
|
40
|
-
icon: /* @__PURE__ */
|
|
41
|
-
onClick: () =>
|
|
43
|
+
label: x("ggnomad.tour.next.browse", "Explore more experiences"),
|
|
44
|
+
description: x("ggnomad.tour.next.browseDesc", "Return to Tourism to compare tours and activities."),
|
|
45
|
+
icon: /* @__PURE__ */ y(l, { className: "size-4" }),
|
|
46
|
+
onClick: () => S(o(C, "tourism"))
|
|
42
47
|
}]
|
|
43
48
|
}),
|
|
44
|
-
/* @__PURE__ */
|
|
49
|
+
/* @__PURE__ */ b("div", {
|
|
45
50
|
className: "grid gap-4 lg:grid-cols-2",
|
|
46
|
-
children: [/* @__PURE__ */
|
|
51
|
+
children: [/* @__PURE__ */ b(m, {
|
|
47
52
|
treatment: "glass",
|
|
48
53
|
glow: !0,
|
|
49
54
|
className: "border-border-seam p-5",
|
|
50
|
-
children: [/* @__PURE__ */
|
|
55
|
+
children: [/* @__PURE__ */ y("h2", {
|
|
51
56
|
className: "mb-4 text-lg font-medium tracking-tight text-text-primary",
|
|
52
|
-
children:
|
|
53
|
-
}), /* @__PURE__ */
|
|
57
|
+
children: x("ggnomad.tour.details", "Details")
|
|
58
|
+
}), /* @__PURE__ */ b("dl", {
|
|
54
59
|
className: "space-y-2 text-sm",
|
|
55
60
|
children: [
|
|
56
|
-
/* @__PURE__ */
|
|
57
|
-
label:
|
|
58
|
-
value:
|
|
61
|
+
/* @__PURE__ */ y(r, {
|
|
62
|
+
label: x("ggnomad.tour.duration", "Duration"),
|
|
63
|
+
value: E.duration
|
|
59
64
|
}),
|
|
60
|
-
/* @__PURE__ */
|
|
61
|
-
label:
|
|
62
|
-
value:
|
|
65
|
+
/* @__PURE__ */ y(r, {
|
|
66
|
+
label: x("ggnomad.tour.difficulty", "Difficulty"),
|
|
67
|
+
value: E.difficulty ?? x("ggnomad.tour.na", "N/A")
|
|
63
68
|
}),
|
|
64
|
-
/* @__PURE__ */
|
|
65
|
-
label:
|
|
66
|
-
value:
|
|
69
|
+
/* @__PURE__ */ y(r, {
|
|
70
|
+
label: x("ggnomad.tour.guide", "Guide"),
|
|
71
|
+
value: E.guideName ?? x("ggnomad.tour.na", "N/A")
|
|
67
72
|
}),
|
|
68
|
-
/* @__PURE__ */
|
|
69
|
-
label:
|
|
70
|
-
value: `${
|
|
73
|
+
/* @__PURE__ */ y(r, {
|
|
74
|
+
label: x("ggnomad.tour.price", "Price"),
|
|
75
|
+
value: `${E.currency} ${Number(E.price).toFixed(2)}`
|
|
71
76
|
})
|
|
72
77
|
]
|
|
73
78
|
})]
|
|
74
|
-
}), /* @__PURE__ */
|
|
75
|
-
title:
|
|
76
|
-
children: /* @__PURE__ */
|
|
79
|
+
}), /* @__PURE__ */ y(n, {
|
|
80
|
+
title: x("ggnomad.tour.highlights", "Highlights"),
|
|
81
|
+
children: /* @__PURE__ */ y("ul", {
|
|
77
82
|
className: "space-y-2 text-sm",
|
|
78
|
-
children: (
|
|
83
|
+
children: (E.highlights ?? []).map((e) => /* @__PURE__ */ b("li", {
|
|
79
84
|
className: "flex items-start gap-2 text-text-primary",
|
|
80
|
-
children: [/* @__PURE__ */
|
|
85
|
+
children: [/* @__PURE__ */ y(c, {
|
|
81
86
|
className: "mt-0.5 size-4 flex-shrink-0 text-status-success-text",
|
|
82
87
|
"aria-hidden": "true"
|
|
83
88
|
}), e]
|
|
@@ -85,14 +90,14 @@ function b() {
|
|
|
85
90
|
})
|
|
86
91
|
})]
|
|
87
92
|
})
|
|
88
|
-
] }) : /* @__PURE__ */
|
|
93
|
+
] }) : /* @__PURE__ */ y(h, {
|
|
89
94
|
illustration: "empty-data",
|
|
90
|
-
title:
|
|
91
|
-
description:
|
|
95
|
+
title: x("ggnomad.tour.notFoundTitle", "No tour found"),
|
|
96
|
+
description: x("ggnomad.tour.notFoundDescription", "This tour is unavailable or may have been removed.")
|
|
92
97
|
})
|
|
93
98
|
});
|
|
94
99
|
}
|
|
95
100
|
//#endregion
|
|
96
|
-
export {
|
|
101
|
+
export { x as TourDetailsPage };
|
|
97
102
|
|
|
98
103
|
//# sourceMappingURL=TourDetailsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TourDetailsPage.js","names":[],"sources":["../../../src/ggnomad/pages/TourDetailsPage.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 { useNavigate, useParams } from 'react-router-dom';\nimport { CalendarCheck, CheckCircle2, Compass } from 'lucide-react';\nimport { GlassCard, IllustratedEmptyState, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';\n\nimport { GGNOMAD_TOUR_QUERY } from '../graphql';\nimport { useGGNomad } from '../providers/GGNomadProvider';\nimport { joinPath } from '../utils/navigation';\nimport { PageShell } from './PageShell';\nimport { DetailRow, SectionCard } from '../components';\n\nexport function TourDetailsPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const { basePath = '/' } = useGGNomad();\n const { id = '' } = useParams();\n const { data } = useQuery(\n GGNOMAD_TOUR_QUERY as TypedDocumentNode<\n {\n ggnomadTour: {\n name: string;\n duration: string;\n difficulty?: string;\n guideName?: string;\n currency: string;\n price: number;\n highlights: string[];\n } | null;\n },\n { id: string }\n >,\n { variables: { id }, skip: !id }\n );\n const tour = data?.ggnomadTour;\n\n return (\n <PageShell\n title={tour?.name ?? t('ggnomad.tour.fallbackTitle', 'Tour details')}\n subtitle={t('ggnomad.tour.subtitle', 'Tour package details and booking context')}\n >\n {!tour ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('ggnomad.tour.notFoundTitle', 'No tour found')}\n description={t(\n 'ggnomad.tour.notFoundDescription',\n 'This tour is unavailable or may have been removed.'\n )}\n />\n ) : (\n <>\n <PagePurpose icon={<Compass className=\"size-4\" />}>\n {t(\n 'ggnomad.tour.purpose',\n 'See exactly what this tour involves — duration, difficulty, your guide, price and the highlights you’ll experience — so you can confirm it fits your trip before booking.'\n )}\n </PagePurpose>\n\n <NextSteps\n storageKey=\"ggnomad-tour-detail\"\n steps={[\n {\n id: 'book',\n label: t('ggnomad.tour.next.book', 'Book this tour'),\n description: t(\n 'ggnomad.tour.next.bookDesc',\n 'Add it to your itinerary and track it under Bookings.'\n ),\n icon: <CalendarCheck className=\"size-4\" />,\n onClick: () => navigate(joinPath(basePath, 'bookings')),\n },\n {\n id: 'browse',\n label: t('ggnomad.tour.next.browse', 'Explore more experiences'),\n description: t(\n 'ggnomad.tour.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-2\">\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.tour.details', 'Details')}\n </h2>\n <dl className=\"space-y-2 text-sm\">\n <DetailRow label={t('ggnomad.tour.duration', 'Duration')} value={tour.duration} />\n <DetailRow\n label={t('ggnomad.tour.difficulty', 'Difficulty')}\n value={tour.difficulty ?? t('ggnomad.tour.na', 'N/A')}\n />\n <DetailRow\n label={t('ggnomad.tour.guide', 'Guide')}\n value={tour.guideName ?? t('ggnomad.tour.na', 'N/A')}\n />\n <DetailRow\n label={t('ggnomad.tour.price', 'Price')}\n value={`${tour.currency} ${Number(tour.price).toFixed(2)}`}\n />\n </dl>\n </GlassCard>\n <SectionCard title={t('ggnomad.tour.highlights', 'Highlights')}>\n <ul className=\"space-y-2 text-sm\">\n {(tour.highlights ?? []).map((highlight) => (\n <li key={highlight} className=\"flex items-start gap-2 text-text-primary\">\n <CheckCircle2\n className=\"mt-0.5 size-4 flex-shrink-0 text-status-success-text\"\n aria-hidden=\"true\"\n />\n {highlight}\n </li>\n ))}\n </ul>\n </SectionCard>\n </div>\n </>\n )}\n </PageShell>\n );\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"TourDetailsPage.js","names":[],"sources":["../../../src/ggnomad/pages/TourDetailsPage.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 { useNavigate, useParams } from 'react-router-dom';\nimport { CalendarCheck, CheckCircle2, Compass } from 'lucide-react';\nimport { GlassCard, IllustratedEmptyState, NextSteps, PagePurpose } from '@burdenoff/fe-libs/ui';\n\nimport { GGNOMAD_TOUR_QUERY } from '../graphql';\nimport { useGGNomad } from '../providers/GGNomadProvider';\nimport { joinPath } from '../utils/navigation';\nimport { PageShell } from './PageShell';\nimport { DetailRow, SectionCard, DetailHero } from '../components';\n\nexport function TourDetailsPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\n const { basePath = '/' } = useGGNomad();\n const { id = '' } = useParams();\n const { data } = useQuery(\n GGNOMAD_TOUR_QUERY as TypedDocumentNode<\n {\n ggnomadTour: {\n name: string;\n duration: string;\n difficulty?: string;\n guideName?: string;\n currency: string;\n price: number;\n highlights: string[];\n images?: string[];\n } | null;\n },\n { id: string }\n >,\n { variables: { id }, skip: !id }\n );\n const tour = data?.ggnomadTour;\n\n return (\n <PageShell\n title={tour?.name ?? t('ggnomad.tour.fallbackTitle', 'Tour details')}\n subtitle={t('ggnomad.tour.subtitle', 'Tour package details and booking context')}\n >\n {!tour ? (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={t('ggnomad.tour.notFoundTitle', 'No tour found')}\n description={t(\n 'ggnomad.tour.notFoundDescription',\n 'This tour is unavailable or may have been removed.'\n )}\n />\n ) : (\n <>\n <DetailHero image={tour.images?.[0]} alt={tour.name} />\n <PagePurpose icon={<Compass className=\"size-4\" />}>\n {t(\n 'ggnomad.tour.purpose',\n 'See exactly what this tour involves — duration, difficulty, your guide, price and the highlights you’ll experience — so you can confirm it fits your trip before booking.'\n )}\n </PagePurpose>\n\n <NextSteps\n storageKey=\"ggnomad-tour-detail\"\n steps={[\n {\n id: 'book',\n label: t('ggnomad.tour.next.book', 'Book this tour'),\n description: t(\n 'ggnomad.tour.next.bookDesc',\n 'Add it to your itinerary and track it under Bookings.'\n ),\n icon: <CalendarCheck className=\"size-4\" />,\n onClick: () => navigate(joinPath(basePath, 'bookings')),\n },\n {\n id: 'browse',\n label: t('ggnomad.tour.next.browse', 'Explore more experiences'),\n description: t(\n 'ggnomad.tour.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-2\">\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.tour.details', 'Details')}\n </h2>\n <dl className=\"space-y-2 text-sm\">\n <DetailRow label={t('ggnomad.tour.duration', 'Duration')} value={tour.duration} />\n <DetailRow\n label={t('ggnomad.tour.difficulty', 'Difficulty')}\n value={tour.difficulty ?? t('ggnomad.tour.na', 'N/A')}\n />\n <DetailRow\n label={t('ggnomad.tour.guide', 'Guide')}\n value={tour.guideName ?? t('ggnomad.tour.na', 'N/A')}\n />\n <DetailRow\n label={t('ggnomad.tour.price', 'Price')}\n value={`${tour.currency} ${Number(tour.price).toFixed(2)}`}\n />\n </dl>\n </GlassCard>\n <SectionCard title={t('ggnomad.tour.highlights', 'Highlights')}>\n <ul className=\"space-y-2 text-sm\">\n {(tour.highlights ?? []).map((highlight) => (\n <li key={highlight} className=\"flex items-start gap-2 text-text-primary\">\n <CheckCircle2\n className=\"mt-0.5 size-4 flex-shrink-0 text-status-success-text\"\n aria-hidden=\"true\"\n />\n {highlight}\n </li>\n ))}\n </ul>\n </SectionCard>\n </div>\n </>\n )}\n </PageShell>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AAaA,SAAgB,IAAkB;CAChC,IAAM,EAAE,SAAM,GAAS,EACjB,IAAW,GAAa,EACxB,EAAE,cAAW,QAAQ,GAAY,EACjC,EAAE,QAAK,OAAO,GAAW,EACzB,EAAE,YAAS,EACf,GAeA;EAAE,WAAW,EAAE,OAAI;EAAE,MAAM,CAAC;EAAI,CACjC,EACK,IAAO,GAAM;AAEnB,QACE,kBAAC,GAAD;EACE,OAAO,GAAM,QAAQ,EAAE,8BAA8B,eAAe;EACpE,UAAU,EAAE,yBAAyB,2CAA2C;YAE9E,IAUA,kBAAA,GAAA,EAAA,UAAA;GACE,kBAAC,GAAD;IAAY,OAAO,EAAK,SAAS;IAAI,KAAK,EAAK;IAAQ,CAAA;GACvD,kBAAC,GAAD;IAAa,MAAM,kBAAC,GAAD,EAAS,WAAU,UAAW,CAAA;cAC9C,EACC,wBACA,4KACD;IACW,CAAA;GAEd,kBAAC,GAAD;IACE,YAAW;IACX,OAAO,CACL;KACE,IAAI;KACJ,OAAO,EAAE,0BAA0B,iBAAiB;KACpD,aAAa,EACX,8BACA,wDACD;KACD,MAAM,kBAAC,GAAD,EAAe,WAAU,UAAW,CAAA;KAC1C,eAAe,EAAS,EAAS,GAAU,WAAW,CAAC;KACxD,EACD;KACE,IAAI;KACJ,OAAO,EAAE,4BAA4B,2BAA2B;KAChE,aAAa,EACX,gCACA,qDACD;KACD,MAAM,kBAAC,GAAD,EAAS,WAAU,UAAW,CAAA;KACpC,eAAe,EAAS,EAAS,GAAU,UAAU,CAAC;KACvD,CACF;IACD,CAAA;GAEF,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAW,WAAU;KAAQ,MAAA;KAAK,WAAU;eAA5C,CACE,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAE,wBAAwB,UAAU;MAClC,CAAA,EACL,kBAAC,MAAD;MAAI,WAAU;gBAAd;OACE,kBAAC,GAAD;QAAW,OAAO,EAAE,yBAAyB,WAAW;QAAE,OAAO,EAAK;QAAY,CAAA;OAClF,kBAAC,GAAD;QACE,OAAO,EAAE,2BAA2B,aAAa;QACjD,OAAO,EAAK,cAAc,EAAE,mBAAmB,MAAM;QACrD,CAAA;OACF,kBAAC,GAAD;QACE,OAAO,EAAE,sBAAsB,QAAQ;QACvC,OAAO,EAAK,aAAa,EAAE,mBAAmB,MAAM;QACpD,CAAA;OACF,kBAAC,GAAD;QACE,OAAO,EAAE,sBAAsB,QAAQ;QACvC,OAAO,GAAG,EAAK,SAAS,GAAG,OAAO,EAAK,MAAM,CAAC,QAAQ,EAAE;QACxD,CAAA;OACC;QACK;QACZ,kBAAC,GAAD;KAAa,OAAO,EAAE,2BAA2B,aAAa;eAC5D,kBAAC,MAAD;MAAI,WAAU;iBACV,EAAK,cAAc,EAAE,EAAE,KAAK,MAC5B,kBAAC,MAAD;OAAoB,WAAU;iBAA9B,CACE,kBAAC,GAAD;QACE,WAAU;QACV,eAAY;QACZ,CAAA,EACD,EACE;SANI,EAMJ,CACL;MACC,CAAA;KACO,CAAA,CACV;;GACL,EAAA,CAAA,GA/EH,kBAAC,GAAD;GACE,cAAa;GACb,OAAO,EAAE,8BAA8B,gBAAgB;GACvD,aAAa,EACX,oCACA,qDACD;GACD,CAAA;EA0EM,CAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TourismPage.js","names":[],"sources":["../../../src/ggnomad/pages/TourismPage.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 { Compass } from 'lucide-react';\nimport { EmphasisPanel, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\n\nimport { GGNOMAD_ACTIVITIES_QUERY, GGNOMAD_TOURS_QUERY } from '../graphql';\nimport { PageShell } from './PageShell';\nimport { EntityCard, SectionCard, LoadMoreButton } from '../components';\n\ntype TourismItem = {\n id: string;\n name: string;\n duration: string;\n price: number;\n currency: string;\n};\n\nexport function TourismPage() {\n const { t } = useI18n();\n const [tourLimit, setTourLimit] = useState(12);\n const [activityLimit, setActivityLimit] = useState(12);\n const { data: toursData } = useQuery(\n GGNOMAD_TOURS_QUERY as TypedDocumentNode<\n { ggnomadTours: TourismItem[] },\n { first: number; skip: number }\n >,\n {\n variables: { first: tourLimit, skip: 0 },\n }\n );\n const { data: activitiesData } = useQuery(\n GGNOMAD_ACTIVITIES_QUERY as TypedDocumentNode<\n { ggnomadActivities: TourismItem[] },\n { first: number; skip: number }\n >,\n { variables: { first: activityLimit, skip: 0 } }\n );\n const tours = toursData?.ggnomadTours ?? [];\n const activities = activitiesData?.ggnomadActivities ?? [];\n\n const renderList = (items: TourismItem[], basePath: string, emptyLabel: string) => {\n if (items.length === 0) {\n return (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={emptyLabel}\n description={t(\n 'ggnomad.tourism.emptyDescription',\n 'Curated experiences will appear here once published.'\n )}\n />\n );\n }\n return (\n <div className=\"grid gap-3\">\n {items.map((item) => (\n <EntityCard\n key={item.id}\n to={`${basePath}/${item.id}`}\n title={item.name}\n meta={item.duration}\n price={`${item.currency} ${Number(item.price).toFixed(2)}`}\n />\n ))}\n </div>\n );\n };\n\n return (\n <PageShell\n title={t('ggnomad.tourism.title', 'Tourism')}\n titleAccent={t('ggnomad.tourism.title.accent', 'Tourism')}\n subtitle={t('ggnomad.tourism.subtitle', 'Tours and curated activity experiences')}\n >\n <PagePurpose icon={<Compass className=\"size-4\" />}>\n {t(\n 'ggnomad.tourism.purpose',\n 'Plan what you’ll actually do at a destination — guided tours and bookable activities curated by local operators. Open any experience to review duration, price and what’s included, then add it to a booking.'\n )}\n </PagePurpose>\n\n <EmphasisPanel>\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div>\n <p className=\"text-sm text-text-secondary\">{t('ggnomad.tourism.tours', 'Tours')}</p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums text-text-primary\">\n {tours.length}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('ggnomad.tourism.activities', 'Activities')}\n </p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums text-text-primary\">\n {activities.length}\n </p>\n </div>\n </div>\n <p className=\"mt-4 text-sm text-text-secondary\">\n {t(\n 'ggnomad.tourism.emphasisNote',\n 'Curated, bookable experiences across your destinations — pick a tour or activity below to see full details.'\n )}\n </p>\n </EmphasisPanel>\n\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <SectionCard title={t('ggnomad.tourism.tours', 'Tours')}>\n {renderList(tours, 'tours', t('ggnomad.tourism.toursEmpty', 'No tours yet'))}\n <LoadMoreButton
|
|
1
|
+
{"version":3,"file":"TourismPage.js","names":[],"sources":["../../../src/ggnomad/pages/TourismPage.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 { Compass } from 'lucide-react';\nimport { EmphasisPanel, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';\n\nimport { GGNOMAD_ACTIVITIES_QUERY, GGNOMAD_TOURS_QUERY } from '../graphql';\nimport { PageShell } from './PageShell';\nimport { EntityCard, SectionCard, LoadMoreButton } from '../components';\n\ntype TourismItem = {\n images?: string[];\n id: string;\n name: string;\n duration: string;\n price: number;\n currency: string;\n};\n\nexport function TourismPage() {\n const { t } = useI18n();\n const [tourLimit, setTourLimit] = useState(12);\n const [activityLimit, setActivityLimit] = useState(12);\n const { data: toursData } = useQuery(\n GGNOMAD_TOURS_QUERY as TypedDocumentNode<\n { ggnomadTours: TourismItem[] },\n { first: number; skip: number }\n >,\n {\n variables: { first: tourLimit, skip: 0 },\n }\n );\n const { data: activitiesData } = useQuery(\n GGNOMAD_ACTIVITIES_QUERY as TypedDocumentNode<\n { ggnomadActivities: TourismItem[] },\n { first: number; skip: number }\n >,\n { variables: { first: activityLimit, skip: 0 } }\n );\n const tours = toursData?.ggnomadTours ?? [];\n const activities = activitiesData?.ggnomadActivities ?? [];\n\n const renderList = (items: TourismItem[], basePath: string, emptyLabel: string) => {\n if (items.length === 0) {\n return (\n <IllustratedEmptyState\n illustration=\"empty-data\"\n title={emptyLabel}\n description={t(\n 'ggnomad.tourism.emptyDescription',\n 'Curated experiences will appear here once published.'\n )}\n />\n );\n }\n return (\n <div className=\"grid gap-3\">\n {items.map((item) => (\n <EntityCard\n key={item.id}\n to={`${basePath}/${item.id}`}\n image={item.images?.[0]}\n imageAlt={item.name}\n title={item.name}\n meta={item.duration}\n price={`${item.currency} ${Number(item.price).toFixed(2)}`}\n />\n ))}\n </div>\n );\n };\n\n return (\n <PageShell\n title={t('ggnomad.tourism.title', 'Tourism')}\n titleAccent={t('ggnomad.tourism.title.accent', 'Tourism')}\n subtitle={t('ggnomad.tourism.subtitle', 'Tours and curated activity experiences')}\n >\n <PagePurpose icon={<Compass className=\"size-4\" />}>\n {t(\n 'ggnomad.tourism.purpose',\n 'Plan what you’ll actually do at a destination — guided tours and bookable activities curated by local operators. Open any experience to review duration, price and what’s included, then add it to a booking.'\n )}\n </PagePurpose>\n\n <EmphasisPanel>\n <div className=\"grid gap-4 sm:grid-cols-2\">\n <div>\n <p className=\"text-sm text-text-secondary\">{t('ggnomad.tourism.tours', 'Tours')}</p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums text-text-primary\">\n {tours.length}\n </p>\n </div>\n <div>\n <p className=\"text-sm text-text-secondary\">\n {t('ggnomad.tourism.activities', 'Activities')}\n </p>\n <p className=\"mt-1 text-2xl font-semibold tabular-nums text-text-primary\">\n {activities.length}\n </p>\n </div>\n </div>\n <p className=\"mt-4 text-sm text-text-secondary\">\n {t(\n 'ggnomad.tourism.emphasisNote',\n 'Curated, bookable experiences across your destinations — pick a tour or activity below to see full details.'\n )}\n </p>\n </EmphasisPanel>\n\n <div className=\"grid gap-6 lg:grid-cols-2\">\n <SectionCard title={t('ggnomad.tourism.tours', 'Tours')}>\n {renderList(tours, 'tours', t('ggnomad.tourism.toursEmpty', 'No tours yet'))}\n <LoadMoreButton\n visible={tours.length >= tourLimit}\n onLoadMore={() => setTourLimit((l) => l + 12)}\n />\n </SectionCard>\n <SectionCard title={t('ggnomad.tourism.activities', 'Activities')}>\n {renderList(\n activities,\n 'activities',\n t('ggnomad.tourism.activitiesEmpty', 'No activities yet')\n )}\n <LoadMoreButton\n visible={activities.length >= activityLimit}\n onLoadMore={() => setActivityLimit((l) => l + 12)}\n />\n </SectionCard>\n </div>\n </PageShell>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAoBA,SAAgB,IAAc;CAC5B,IAAM,EAAE,SAAM,GAAS,EACjB,CAAC,GAAW,KAAgB,EAAS,GAAG,EACxC,CAAC,GAAe,KAAoB,EAAS,GAAG,EAChD,EAAE,MAAM,MAAc,EAC1B,GAIA,EACE,WAAW;EAAE,OAAO;EAAW,MAAM;EAAG,EACzC,CACF,EACK,EAAE,MAAM,MAAmB,EAC/B,GAIA,EAAE,WAAW;EAAE,OAAO;EAAe,MAAM;EAAG,EAAE,CACjD,EACK,IAAQ,GAAW,gBAAgB,EAAE,EACrC,IAAa,GAAgB,qBAAqB,EAAE,EAEpD,KAAc,GAAsB,GAAkB,MACtD,EAAM,WAAW,IAEjB,kBAAC,GAAD;EACE,cAAa;EACb,OAAO;EACP,aAAa,EACX,oCACA,uDACD;EACD,CAAA,GAIJ,kBAAC,OAAD;EAAK,WAAU;YACZ,EAAM,KAAK,MACV,kBAAC,GAAD;GAEE,IAAI,GAAG,EAAS,GAAG,EAAK;GACxB,OAAO,EAAK,SAAS;GACrB,UAAU,EAAK;GACf,OAAO,EAAK;GACZ,MAAM,EAAK;GACX,OAAO,GAAG,EAAK,SAAS,GAAG,OAAO,EAAK,MAAM,CAAC,QAAQ,EAAE;GACxD,EAPK,EAAK,GAOV,CACF;EACE,CAAA;AAIV,QACE,kBAAC,GAAD;EACE,OAAO,EAAE,yBAAyB,UAAU;EAC5C,aAAa,EAAE,gCAAgC,UAAU;EACzD,UAAU,EAAE,4BAA4B,yCAAyC;YAHnF;GAKE,kBAAC,GAAD;IAAa,MAAM,kBAAC,GAAD,EAAS,WAAU,UAAW,CAAA;cAC9C,EACC,2BACA,gNACD;IACW,CAAA;GAEd,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;KAAG,WAAU;eAA+B,EAAE,yBAAyB,QAAQ;KAAK,CAAA,EACpF,kBAAC,KAAD;KAAG,WAAU;eACV,EAAM;KACL,CAAA,CACA,EAAA,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;KAAG,WAAU;eACV,EAAE,8BAA8B,aAAa;KAC5C,CAAA,EACJ,kBAAC,KAAD;KAAG,WAAU;eACV,EAAW;KACV,CAAA,CACA,EAAA,CAAA,CACF;OACN,kBAAC,KAAD;IAAG,WAAU;cACV,EACC,gCACA,8GACD;IACC,CAAA,CACU,EAAA,CAAA;GAEhB,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAa,OAAO,EAAE,yBAAyB,QAAQ;eAAvD,CACG,EAAW,GAAO,SAAS,EAAE,8BAA8B,eAAe,CAAC,EAC5E,kBAAC,GAAD;MACE,SAAS,EAAM,UAAU;MACzB,kBAAkB,GAAc,MAAM,IAAI,GAAG;MAC7C,CAAA,CACU;QACd,kBAAC,GAAD;KAAa,OAAO,EAAE,8BAA8B,aAAa;eAAjE,CACG,EACC,GACA,cACA,EAAE,mCAAmC,oBAAoB,CAC1D,EACD,kBAAC,GAAD;MACE,SAAS,EAAW,UAAU;MAC9B,kBAAkB,GAAkB,MAAM,IAAI,GAAG;MACjD,CAAA,CACU;OACV;;GACI"}
|