@burdenoff/microfe-ggnomad 2026.608.1 → 2026.626.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/ggnomad/components/DetailRow.js +18 -0
  2. package/dist/ggnomad/components/DetailRow.js.map +1 -0
  3. package/dist/ggnomad/components/EntityCard.js +50 -0
  4. package/dist/ggnomad/components/EntityCard.js.map +1 -0
  5. package/dist/ggnomad/components/SectionCard.js +21 -0
  6. package/dist/ggnomad/components/SectionCard.js.map +1 -0
  7. package/dist/ggnomad/components/StatCard.js +27 -0
  8. package/dist/ggnomad/components/StatCard.js.map +1 -0
  9. package/dist/ggnomad/components/StatusBadge.js +65 -0
  10. package/dist/ggnomad/components/StatusBadge.js.map +1 -0
  11. package/dist/ggnomad/components/index.js +5 -0
  12. package/dist/ggnomad/pages/ActivityDetailsPage.js +84 -37
  13. package/dist/ggnomad/pages/ActivityDetailsPage.js.map +1 -1
  14. package/dist/ggnomad/pages/AnalyticsPage.js +64 -55
  15. package/dist/ggnomad/pages/AnalyticsPage.js.map +1 -1
  16. package/dist/ggnomad/pages/BookingsPage.js +108 -60
  17. package/dist/ggnomad/pages/BookingsPage.js.map +1 -1
  18. package/dist/ggnomad/pages/DashboardPage.js +135 -110
  19. package/dist/ggnomad/pages/DashboardPage.js.map +1 -1
  20. package/dist/ggnomad/pages/EntertainmentPage.js +40 -33
  21. package/dist/ggnomad/pages/EntertainmentPage.js.map +1 -1
  22. package/dist/ggnomad/pages/EventDetailsPage.js +86 -39
  23. package/dist/ggnomad/pages/EventDetailsPage.js.map +1 -1
  24. package/dist/ggnomad/pages/FavoritesPage.js +44 -27
  25. package/dist/ggnomad/pages/FavoritesPage.js.map +1 -1
  26. package/dist/ggnomad/pages/HospitalityPage.js +45 -42
  27. package/dist/ggnomad/pages/HospitalityPage.js.map +1 -1
  28. package/dist/ggnomad/pages/PageShell.js +33 -11
  29. package/dist/ggnomad/pages/PageShell.js.map +1 -1
  30. package/dist/ggnomad/pages/PropertyDetailsPage.js +95 -45
  31. package/dist/ggnomad/pages/PropertyDetailsPage.js.map +1 -1
  32. package/dist/ggnomad/pages/ProviderPage.js +65 -29
  33. package/dist/ggnomad/pages/ProviderPage.js.map +1 -1
  34. package/dist/ggnomad/pages/SearchPage.js +57 -48
  35. package/dist/ggnomad/pages/SearchPage.js.map +1 -1
  36. package/dist/ggnomad/pages/TourDetailsPage.js +86 -39
  37. package/dist/ggnomad/pages/TourDetailsPage.js.map +1 -1
  38. package/dist/ggnomad/pages/TourismPage.js +64 -63
  39. package/dist/ggnomad/pages/TourismPage.js.map +1 -1
  40. package/dist/ggnomad/pages/TravelPage.js +43 -27
  41. package/dist/ggnomad/pages/TravelPage.js.map +1 -1
  42. package/dist/ggnomad/utils/cn.js +10 -0
  43. package/dist/ggnomad/utils/cn.js.map +1 -0
  44. package/package.json +2 -2
@@ -0,0 +1,18 @@
1
+ import { jsx as e, jsxs as t } from "react/jsx-runtime";
2
+ //#region src/ggnomad/components/DetailRow.tsx
3
+ function n({ label: n, value: r }) {
4
+ return /* @__PURE__ */ t("div", {
5
+ className: "flex items-center justify-between gap-3",
6
+ children: [/* @__PURE__ */ e("dt", {
7
+ className: "text-text-secondary",
8
+ children: n
9
+ }), /* @__PURE__ */ e("dd", {
10
+ className: "font-medium tabular-nums text-text-primary",
11
+ children: r
12
+ })]
13
+ });
14
+ }
15
+ //#endregion
16
+ export { n as DetailRow };
17
+
18
+ //# sourceMappingURL=DetailRow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DetailRow.js","names":[],"sources":["../../../src/ggnomad/components/DetailRow.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\n\n/** A single label/value pair inside a details list (low-contrast, tabular). */\nexport function DetailRow({ label, value }: { label: string; value: ReactNode }) {\n return (\n <div className=\"flex items-center justify-between gap-3\">\n <dt className=\"text-text-secondary\">{label}</dt>\n <dd className=\"font-medium tabular-nums text-text-primary\">{value}</dd>\n </div>\n );\n}\n"],"mappings":";;AAGA,SAAgB,EAAU,EAAE,UAAO,YAA8C;AAC/E,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,MAAD;GAAI,WAAU;aAAuB;GAAW,CAAA,EAChD,kBAAC,MAAD;GAAI,WAAU;aAA8C;GAAW,CAAA,CACnE"}
@@ -0,0 +1,50 @@
1
+ import { cn as e } from "../utils/cn.js";
2
+ import { Link as t } from "react-router-dom";
3
+ import { GlassCard as n } from "@burdenoff/fe-libs/ui";
4
+ import { Fragment as r, jsx as i, jsxs as a } from "react/jsx-runtime";
5
+ //#region src/ggnomad/components/EntityCard.tsx
6
+ function o({ title: o, meta: s, price: c, badge: l, footer: u, to: d, className: f }) {
7
+ let p = /* @__PURE__ */ a(r, { children: [
8
+ /* @__PURE__ */ a("div", {
9
+ className: "flex items-start justify-between gap-3",
10
+ children: [/* @__PURE__ */ a("div", {
11
+ className: "min-w-0",
12
+ children: [/* @__PURE__ */ i("p", {
13
+ className: "truncate font-medium text-text-primary",
14
+ children: o
15
+ }), s ? /* @__PURE__ */ i("p", {
16
+ className: "mt-0.5 text-sm text-text-secondary",
17
+ children: s
18
+ }) : null]
19
+ }), l ? /* @__PURE__ */ i("div", {
20
+ className: "flex-shrink-0",
21
+ children: l
22
+ }) : null]
23
+ }),
24
+ c ? /* @__PURE__ */ i("p", {
25
+ className: "mt-2 text-sm font-medium tabular-nums text-text-primary",
26
+ children: c
27
+ }) : null,
28
+ u ? /* @__PURE__ */ i("div", {
29
+ className: "mt-2 text-sm text-text-secondary",
30
+ children: u
31
+ }) : null
32
+ ] });
33
+ return d ? /* @__PURE__ */ i(t, {
34
+ to: d,
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",
36
+ children: /* @__PURE__ */ i(n, {
37
+ treatment: "plain",
38
+ className: e("border-border-seam p-4", f),
39
+ children: p
40
+ })
41
+ }) : /* @__PURE__ */ i(n, {
42
+ treatment: "plain",
43
+ className: e("border-border-seam p-4", f),
44
+ children: p
45
+ });
46
+ }
47
+ //#endregion
48
+ export { o as EntityCard };
49
+
50
+ //# sourceMappingURL=EntityCard.js.map
@@ -0,0 +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 * A calm, low-contrast tile for an inventory item (property, tour, event,\n * provider). Hover only lifts the shadow / seam — never moves. When `to` is set\n * the entire surface is a single accessible link.\n */\nexport function EntityCard({ title, meta, price, badge, footer, to, className }: EntityCardProps) {\n const body = (\n <>\n <div className=\"flex items-start justify-between gap-3\">\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 {badge ? <div className=\"flex-shrink-0\">{badge}</div> : 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=\"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 treatment=\"plain\" className={cn('border-border-seam p-4', className)}>\n {body}\n </GlassCard>\n </Link>\n );\n }\n\n return (\n <GlassCard treatment=\"plain\" className={cn('border-border-seam p-4', className)}>\n {body}\n </GlassCard>\n );\n}\n"],"mappings":";;;;;AAuBA,SAAgB,EAAW,EAAE,UAAO,SAAM,UAAO,UAAO,WAAQ,OAAI,gBAA8B;CAChG,IAAM,IACJ,kBAAA,GAAA,EAAA,UAAA;EACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,KAAD;KAAG,WAAU;eAA0C;KAAU,CAAA,EAChE,IAAO,kBAAC,KAAD;KAAG,WAAU;eAAsC;KAAS,CAAA,GAAG,KACnE;OACL,IAAQ,kBAAC,OAAD;IAAK,WAAU;cAAiB;IAAY,CAAA,GAAG,KACpD;;EACL,IACC,kBAAC,KAAD;GAAG,WAAU;aAA2D;GAAU,CAAA,GAChF;EACH,IAAS,kBAAC,OAAD;GAAK,WAAU;aAAoC;GAAa,CAAA,GAAG;EAC5E,EAAA,CAAA;AAgBL,QAbI,IAEA,kBAAC,GAAD;EACM;EACJ,WAAU;YAEV,kBAAC,GAAD;GAAW,WAAU;GAAQ,WAAW,EAAG,0BAA0B,EAAU;aAC5E;GACS,CAAA;EACP,CAAA,GAKT,kBAAC,GAAD;EAAW,WAAU;EAAQ,WAAW,EAAG,0BAA0B,EAAU;YAC5E;EACS,CAAA"}
@@ -0,0 +1,21 @@
1
+ import { cn as e } from "../utils/cn.js";
2
+ import { GlassCard as t } from "@burdenoff/fe-libs/ui";
3
+ import { jsx as n, jsxs as r } from "react/jsx-runtime";
4
+ //#region src/ggnomad/components/SectionCard.tsx
5
+ function i({ title: i, action: a, className: o, treatment: s = "plain", children: c }) {
6
+ return /* @__PURE__ */ r(t, {
7
+ treatment: s,
8
+ className: e("border-border-seam p-5", o),
9
+ children: [i || a ? /* @__PURE__ */ r("div", {
10
+ className: "mb-4 flex items-center justify-between gap-3",
11
+ children: [i ? /* @__PURE__ */ n("h2", {
12
+ className: "text-lg font-medium tracking-tight text-text-primary",
13
+ children: i
14
+ }) : /* @__PURE__ */ n("span", {}), a]
15
+ }) : null, c]
16
+ });
17
+ }
18
+ //#endregion
19
+ export { i as SectionCard };
20
+
21
+ //# sourceMappingURL=SectionCard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SectionCard.js","names":[],"sources":["../../../src/ggnomad/components/SectionCard.tsx"],"sourcesContent":["import type { PropsWithChildren, ReactNode } from 'react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\n\ntype SectionCardProps = PropsWithChildren<{\n title?: ReactNode;\n action?: ReactNode;\n className?: string;\n treatment?: 'plain' | 'glass' | 'metric' | 'insight';\n}>;\n\n/** A titled glass surface used for grouped content blocks. */\nexport function SectionCard({\n title,\n action,\n className,\n treatment = 'plain',\n children,\n}: SectionCardProps) {\n return (\n <GlassCard treatment={treatment} className={cn('border-border-seam p-5', className)}>\n {title || action ? (\n <div className=\"mb-4 flex items-center justify-between gap-3\">\n {title ? (\n <h2 className=\"text-lg font-medium tracking-tight text-text-primary\">{title}</h2>\n ) : (\n <span />\n )}\n {action}\n </div>\n ) : null}\n {children}\n </GlassCard>\n );\n}\n"],"mappings":";;;;AAYA,SAAgB,EAAY,EAC1B,UACA,WACA,cACA,eAAY,SACZ,eACmB;AACnB,QACE,kBAAC,GAAD;EAAsB;EAAW,WAAW,EAAG,0BAA0B,EAAU;YAAnF,CACG,KAAS,IACR,kBAAC,OAAD;GAAK,WAAU;aAAf,CACG,IACC,kBAAC,MAAD;IAAI,WAAU;cAAwD;IAAW,CAAA,GAEjF,kBAAC,QAAD,EAAQ,CAAA,EAET,EACG;OACJ,MACH,EACS"}
@@ -0,0 +1,27 @@
1
+ import { GlassCard as e, GradientText as t } from "@burdenoff/fe-libs/ui";
2
+ import { jsx as n, jsxs as r } from "react/jsx-runtime";
3
+ //#region src/ggnomad/components/StatCard.tsx
4
+ function i({ label: i, value: a, icon: o, hero: s = !1 }) {
5
+ return /* @__PURE__ */ r(e, {
6
+ treatment: s ? "glass" : "metric",
7
+ glow: s,
8
+ className: "border-border-seam p-4",
9
+ children: [/* @__PURE__ */ r("div", {
10
+ className: "flex items-start justify-between gap-3",
11
+ children: [/* @__PURE__ */ n("p", {
12
+ className: "text-sm font-medium text-text-secondary",
13
+ children: i
14
+ }), o ? /* @__PURE__ */ n("span", {
15
+ className: "flex size-9 flex-shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary [&>svg]:size-4",
16
+ children: o
17
+ }) : null]
18
+ }), /* @__PURE__ */ n("p", {
19
+ className: "mt-2 text-2xl font-semibold tabular-nums text-text-primary",
20
+ children: s ? /* @__PURE__ */ n(t, { children: a }) : a
21
+ })]
22
+ });
23
+ }
24
+ //#endregion
25
+ export { i as StatCard };
26
+
27
+ //# sourceMappingURL=StatCard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StatCard.js","names":[],"sources":["../../../src/ggnomad/components/StatCard.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { GlassCard, GradientText } from '@burdenoff/fe-libs/ui';\n\ntype StatCardProps = {\n label: string;\n value: ReactNode;\n icon?: ReactNode;\n /** The single most important metric on a page reads as the glass hero tile. */\n hero?: boolean;\n};\n\n/**\n * KPI / metric tile. Neutral metric tiles use the GlassCard \"metric\" treatment;\n * the page's hero metric uses the \"glass\" treatment with a soft accent glow and\n * a gradient numeral. Numbers are tabular for calm vertical alignment.\n */\nexport function StatCard({ label, value, icon, hero = false }: StatCardProps) {\n return (\n <GlassCard treatment={hero ? 'glass' : 'metric'} glow={hero} className=\"border-border-seam p-4\">\n <div className=\"flex items-start justify-between gap-3\">\n <p className=\"text-sm font-medium text-text-secondary\">{label}</p>\n {icon ? (\n <span className=\"flex size-9 flex-shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary [&>svg]:size-4\">\n {icon}\n </span>\n ) : null}\n </div>\n <p className=\"mt-2 text-2xl font-semibold tabular-nums text-text-primary\">\n {hero ? <GradientText>{value}</GradientText> : value}\n </p>\n </GlassCard>\n );\n}\n"],"mappings":";;;AAgBA,SAAgB,EAAS,EAAE,UAAO,UAAO,SAAM,UAAO,MAAwB;AAC5E,QACE,kBAAC,GAAD;EAAW,WAAW,IAAO,UAAU;EAAU,MAAM;EAAM,WAAU;YAAvE,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,KAAD;IAAG,WAAU;cAA2C;IAAU,CAAA,EACjE,IACC,kBAAC,QAAD;IAAM,WAAU;cACb;IACI,CAAA,GACL,KACA;MACN,kBAAC,KAAD;GAAG,WAAU;aACV,IAAO,kBAAC,GAAD,EAAA,UAAe,GAAqB,CAAA,GAAG;GAC7C,CAAA,CACM"}
@@ -0,0 +1,65 @@
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/StatusBadge.tsx
4
+ var r = {
5
+ success: {
6
+ dot: "bg-status-success-text",
7
+ text: "text-status-success-text",
8
+ bg: "bg-status-success-bg-subtle"
9
+ },
10
+ warning: {
11
+ dot: "bg-status-warning-text",
12
+ text: "text-status-warning-text",
13
+ bg: "bg-status-warning-bg-subtle"
14
+ },
15
+ danger: {
16
+ dot: "bg-status-error-text",
17
+ text: "text-status-error-text",
18
+ bg: "bg-status-error-bg-subtle"
19
+ },
20
+ info: {
21
+ dot: "bg-status-info-text",
22
+ text: "text-status-info-text",
23
+ bg: "bg-status-info-bg-subtle"
24
+ },
25
+ neutral: {
26
+ dot: "bg-text-secondary",
27
+ text: "text-text-secondary",
28
+ bg: "bg-bg-sunken"
29
+ }
30
+ };
31
+ function i(e) {
32
+ let t = e.toUpperCase();
33
+ return [
34
+ "CONFIRMED",
35
+ "ACTIVE",
36
+ "COMPLETED",
37
+ "PAID",
38
+ "APPROVED"
39
+ ].includes(t) ? "success" : [
40
+ "PENDING",
41
+ "PROCESSING",
42
+ "AWAITING",
43
+ "IN_PROGRESS",
44
+ "ONBOARDING"
45
+ ].includes(t) ? "warning" : [
46
+ "CANCELLED",
47
+ "FAILED",
48
+ "REJECTED",
49
+ "SUSPENDED"
50
+ ].includes(t) ? "danger" : "info";
51
+ }
52
+ function a({ status: a, live: o = !1, className: s }) {
53
+ let c = r[i(a)];
54
+ return /* @__PURE__ */ n("span", {
55
+ className: e("inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-xs font-medium", c.bg, c.text, s),
56
+ children: [/* @__PURE__ */ t("span", {
57
+ className: e("size-1.5 rounded-full", c.dot, o ? "motion-safe:animate-vc-glow-pulse" : ""),
58
+ "aria-hidden": "true"
59
+ }), a]
60
+ });
61
+ }
62
+ //#endregion
63
+ export { a as StatusBadge, i as toneForStatus };
64
+
65
+ //# sourceMappingURL=StatusBadge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"StatusBadge.js","names":[],"sources":["../../../src/ggnomad/components/StatusBadge.tsx"],"sourcesContent":["import { cn } from '../utils/cn';\n\ntype Tone = 'success' | 'warning' | 'danger' | 'info' | 'neutral';\n\nconst TONE: Record<Tone, { dot: string; text: string; bg: string }> = {\n success: {\n dot: 'bg-status-success-text',\n text: 'text-status-success-text',\n bg: 'bg-status-success-bg-subtle',\n },\n warning: {\n dot: 'bg-status-warning-text',\n text: 'text-status-warning-text',\n bg: 'bg-status-warning-bg-subtle',\n },\n danger: {\n dot: 'bg-status-error-text',\n text: 'text-status-error-text',\n bg: 'bg-status-error-bg-subtle',\n },\n info: {\n dot: 'bg-status-info-text',\n text: 'text-status-info-text',\n bg: 'bg-status-info-bg-subtle',\n },\n neutral: {\n dot: 'bg-text-secondary',\n text: 'text-text-secondary',\n bg: 'bg-bg-sunken',\n },\n};\n\n/** Maps a free-form status string to a semantic tone. */\nexport function toneForStatus(status: string): Tone {\n const s = status.toUpperCase();\n if (['CONFIRMED', 'ACTIVE', 'COMPLETED', 'PAID', 'APPROVED'].includes(s)) {\n return 'success';\n }\n if (['PENDING', 'PROCESSING', 'AWAITING', 'IN_PROGRESS', 'ONBOARDING'].includes(s)) {\n return 'warning';\n }\n if (['CANCELLED', 'FAILED', 'REJECTED', 'SUSPENDED'].includes(s)) {\n return 'danger';\n }\n return 'info';\n}\n\ntype StatusBadgeProps = {\n status: string;\n /** Pulses the dot for live/active states (opacity only, reduced-motion-safe). */\n live?: boolean;\n className?: string;\n};\n\nexport function StatusBadge({ status, live = false, className }: StatusBadgeProps) {\n const tone = TONE[toneForStatus(status)];\n return (\n <span\n className={cn(\n 'inline-flex items-center gap-1.5 rounded-full px-2.5 py-0.5 text-xs font-medium',\n tone.bg,\n tone.text,\n className\n )}\n >\n <span\n className={cn(\n 'size-1.5 rounded-full',\n tone.dot,\n live ? 'motion-safe:animate-vc-glow-pulse' : ''\n )}\n aria-hidden=\"true\"\n />\n {status}\n </span>\n );\n}\n"],"mappings":";;;AAIA,IAAM,IAAgE;CACpE,SAAS;EACP,KAAK;EACL,MAAM;EACN,IAAI;EACL;CACD,SAAS;EACP,KAAK;EACL,MAAM;EACN,IAAI;EACL;CACD,QAAQ;EACN,KAAK;EACL,MAAM;EACN,IAAI;EACL;CACD,MAAM;EACJ,KAAK;EACL,MAAM;EACN,IAAI;EACL;CACD,SAAS;EACP,KAAK;EACL,MAAM;EACN,IAAI;EACL;CACF;AAGD,SAAgB,EAAc,GAAsB;CAClD,IAAM,IAAI,EAAO,aAAa;AAU9B,QATI;EAAC;EAAa;EAAU;EAAa;EAAQ;EAAW,CAAC,SAAS,EAAE,GAC/D,YAEL;EAAC;EAAW;EAAc;EAAY;EAAe;EAAa,CAAC,SAAS,EAAE,GACzE,YAEL;EAAC;EAAa;EAAU;EAAY;EAAY,CAAC,SAAS,EAAE,GACvD,WAEF;;AAUT,SAAgB,EAAY,EAAE,WAAQ,UAAO,IAAO,gBAA+B;CACjF,IAAM,IAAO,EAAK,EAAc,EAAO;AACvC,QACE,kBAAC,QAAD;EACE,WAAW,EACT,mFACA,EAAK,IACL,EAAK,MACL,EACD;YANH,CAQE,kBAAC,QAAD;GACE,WAAW,EACT,yBACA,EAAK,KACL,IAAO,sCAAsC,GAC9C;GACD,eAAY;GACZ,CAAA,EACD,EACI"}
@@ -0,0 +1,5 @@
1
+ import "./StatCard.js";
2
+ import "./StatusBadge.js";
3
+ import "./SectionCard.js";
4
+ import "./EntityCard.js";
5
+ import "./DetailRow.js";
@@ -1,46 +1,93 @@
1
1
  import { GGNOMAD_ACTIVITY_QUERY as e } from "../graphql.js";
2
2
  import { PageShell as t } from "./PageShell.js";
3
- import { useParams as n } from "react-router-dom";
4
- import { useQuery as r } from "@apollo/client/react";
5
- import { jsx as i, jsxs as a } from "react/jsx-runtime";
3
+ import { SectionCard as n } from "../components/SectionCard.js";
4
+ import { DetailRow as r } from "../components/DetailRow.js";
5
+ import "../components/index.js";
6
+ import { Activity as i, CalendarCheck as a, Compass as o, ShieldCheck as s } from "lucide-react";
7
+ import { useI18n as c } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
8
+ import { useNavigate as l, useParams as u } from "react-router-dom";
9
+ import { useQuery as d } from "@apollo/client/react";
10
+ import { GlassCard as f, IllustratedEmptyState as p, NextSteps as m, PagePurpose as h } from "@burdenoff/fe-libs/ui";
11
+ import { Fragment as g, jsx as _, jsxs as v } from "react/jsx-runtime";
6
12
  //#region src/ggnomad/pages/ActivityDetailsPage.tsx
7
- function o() {
8
- let { id: o = "" } = n(), { data: s } = r(e, {
9
- variables: { id: o },
10
- skip: !o
11
- }), c = s?.ggnomadActivity;
12
- return /* @__PURE__ */ i(t, {
13
- title: c?.name ?? "Activity details",
14
- subtitle: "Safety, inclusions and participation details",
15
- children: c ? /* @__PURE__ */ a("div", {
16
- className: "space-y-3 text-sm",
17
- children: [/* @__PURE__ */ a("div", {
18
- className: "rounded-lg border p-4",
19
- children: [
20
- /* @__PURE__ */ a("p", { children: ["Category: ", c.category] }),
21
- /* @__PURE__ */ a("p", { children: ["Duration: ", c.duration] }),
22
- /* @__PURE__ */ a("p", { children: ["Max participants: ", c.maxParticipants] }),
23
- /* @__PURE__ */ a("p", { children: [
24
- "Price: ",
25
- c.currency,
26
- " ",
27
- Number(c.price).toFixed(2)
28
- ] })
29
- ]
30
- }), /* @__PURE__ */ a("div", {
31
- className: "rounded-lg border p-4",
32
- children: [/* @__PURE__ */ i("p", {
33
- className: "font-medium",
34
- children: "Safety information"
35
- }), /* @__PURE__ */ i("p", { children: c.safetyInfo ?? "Standard provider safety policy applies." })]
36
- })]
37
- }) : /* @__PURE__ */ i("div", {
38
- className: "rounded-lg border p-4 text-muted-foreground",
39
- children: "No activity found."
13
+ function y() {
14
+ let { t: y } = c(), b = l(), { id: x = "" } = u(), { data: S } = d(e, {
15
+ variables: { id: x },
16
+ skip: !x
17
+ }), C = S?.ggnomadActivity;
18
+ return /* @__PURE__ */ _(t, {
19
+ title: C?.name ?? y("ggnomad.activity.fallbackTitle", "Activity details"),
20
+ subtitle: y("ggnomad.activity.subtitle", "Safety, inclusions and participation details"),
21
+ children: C ? /* @__PURE__ */ v(g, { children: [
22
+ /* @__PURE__ */ _(h, {
23
+ icon: /* @__PURE__ */ _(i, { className: "size-4" }),
24
+ children: y("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.")
25
+ }),
26
+ /* @__PURE__ */ _(m, {
27
+ storageKey: "ggnomad-activity-detail",
28
+ steps: [{
29
+ id: "book",
30
+ label: y("ggnomad.activity.next.book", "Book this activity"),
31
+ description: y("ggnomad.activity.next.bookDesc", "Add it to your itinerary and track it under Bookings."),
32
+ icon: /* @__PURE__ */ _(a, { className: "size-4" }),
33
+ onClick: () => b("../../../bookings")
34
+ }, {
35
+ id: "browse",
36
+ label: y("ggnomad.activity.next.browse", "Explore more experiences"),
37
+ description: y("ggnomad.activity.next.browseDesc", "Return to Tourism to compare tours and activities."),
38
+ icon: /* @__PURE__ */ _(o, { className: "size-4" }),
39
+ onClick: () => b("../..")
40
+ }]
41
+ }),
42
+ /* @__PURE__ */ v("div", {
43
+ className: "grid gap-4 lg:grid-cols-2",
44
+ children: [/* @__PURE__ */ v(f, {
45
+ treatment: "glass",
46
+ glow: !0,
47
+ className: "border-border-seam p-5",
48
+ children: [/* @__PURE__ */ _("h2", {
49
+ className: "mb-4 text-lg font-medium tracking-tight text-text-primary",
50
+ children: y("ggnomad.activity.details", "Details")
51
+ }), /* @__PURE__ */ v("dl", {
52
+ className: "space-y-2 text-sm",
53
+ children: [
54
+ /* @__PURE__ */ _(r, {
55
+ label: y("ggnomad.activity.category", "Category"),
56
+ value: C.category
57
+ }),
58
+ /* @__PURE__ */ _(r, {
59
+ label: y("ggnomad.activity.duration", "Duration"),
60
+ value: C.duration
61
+ }),
62
+ /* @__PURE__ */ _(r, {
63
+ label: y("ggnomad.activity.maxParticipants", "Max participants"),
64
+ value: String(C.maxParticipants)
65
+ }),
66
+ /* @__PURE__ */ _(r, {
67
+ label: y("ggnomad.activity.price", "Price"),
68
+ value: `${C.currency} ${Number(C.price).toFixed(2)}`
69
+ })
70
+ ]
71
+ })]
72
+ }), /* @__PURE__ */ _(n, {
73
+ title: y("ggnomad.activity.safety", "Safety information"),
74
+ children: /* @__PURE__ */ v("div", {
75
+ className: "flex items-start gap-2 text-sm text-text-primary",
76
+ children: [/* @__PURE__ */ _(s, {
77
+ className: "mt-0.5 size-4 flex-shrink-0 text-status-success-text",
78
+ "aria-hidden": "true"
79
+ }), /* @__PURE__ */ _("p", { children: C.safetyInfo ?? y("ggnomad.activity.defaultSafety", "Standard provider safety policy applies.") })]
80
+ })
81
+ })]
82
+ })
83
+ ] }) : /* @__PURE__ */ _(p, {
84
+ illustration: "empty-data",
85
+ title: y("ggnomad.activity.notFoundTitle", "No activity found"),
86
+ description: y("ggnomad.activity.notFoundDescription", "This activity is unavailable or may have been removed.")
40
87
  })
41
88
  });
42
89
  }
43
90
  //#endregion
44
- export { o as ActivityDetailsPage };
91
+ export { y as ActivityDetailsPage };
45
92
 
46
93
  //# 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 { useParams } from 'react-router-dom';\n\nimport { GGNOMAD_ACTIVITY_QUERY } from '../graphql';\nimport { PageShell } from './PageShell';\n\nexport function ActivityDetailsPage() {\n const { id = '' } = useParams();\n const { data } = useQuery<{\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 }>(GGNOMAD_ACTIVITY_QUERY, { variables: { id }, skip: !id });\n const activity = data?.ggnomadActivity;\n\n return (\n <PageShell\n title={activity?.name ?? 'Activity details'}\n subtitle=\"Safety, inclusions and participation details\"\n >\n {!activity ? (\n <div className=\"rounded-lg border p-4 text-muted-foreground\">No activity found.</div>\n ) : (\n <div className=\"space-y-3 text-sm\">\n <div className=\"rounded-lg border p-4\">\n <p>Category: {activity.category}</p>\n <p>Duration: {activity.duration}</p>\n <p>Max participants: {activity.maxParticipants}</p>\n <p>\n Price: {activity.currency} {Number(activity.price).toFixed(2)}\n </p>\n </div>\n <div className=\"rounded-lg border p-4\">\n <p className=\"font-medium\">Safety information</p>\n <p>{activity.safetyInfo ?? 'Standard provider safety policy applies.'}</p>\n </div>\n </div>\n )}\n </PageShell>\n );\n}\n"],"mappings":";;;;;;AAMA,SAAgB,IAAsB;CACpC,IAAM,EAAE,QAAK,OAAO,GAAW,EACzB,EAAE,YAAS,EAUd,GAAwB;EAAE,WAAW,EAAE,OAAI;EAAE,MAAM,CAAC;EAAI,CAAC,EACtD,IAAW,GAAM;AAEvB,QACE,kBAAC,GAAD;EACE,OAAO,GAAU,QAAQ;EACzB,UAAS;YAEP,IAGA,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,KAAD,EAAA,UAAA,CAAG,cAAW,EAAS,SAAa,EAAA,CAAA;KACpC,kBAAC,KAAD,EAAA,UAAA,CAAG,cAAW,EAAS,SAAa,EAAA,CAAA;KACpC,kBAAC,KAAD,EAAA,UAAA,CAAG,sBAAmB,EAAS,gBAAoB,EAAA,CAAA;KACnD,kBAAC,KAAD,EAAA,UAAA;MAAG;MACO,EAAS;MAAS;MAAE,OAAO,EAAS,MAAM,CAAC,QAAQ,EAAE;MAC3D,EAAA,CAAA;KACA;OACN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,KAAD;KAAG,WAAU;eAAc;KAAsB,CAAA,EACjD,kBAAC,KAAD,EAAA,UAAI,EAAS,cAAc,4CAA+C,CAAA,CACtE;MACF;OAfN,kBAAC,OAAD;GAAK,WAAU;aAA8C;GAAwB,CAAA;EAiB7E,CAAA"}
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 { PageShell } from './PageShell';\nimport { DetailRow, SectionCard } from '../components';\n\nexport function ActivityDetailsPage() {\n const { t } = useI18n();\n const navigate = useNavigate();\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('../../../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('../..'),\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":";;;;;;;;;;;;AAWA,SAAgB,IAAsB;CACpC,IAAM,EAAE,SAAM,GAAS,EACjB,IAAW,GAAa,EACxB,EAAE,QAAK,OAAO,GAAW,EACzB,EAAE,YAAS,EACf,GAcA;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;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,oBAAoB;KAC7C,EACD;KACE,IAAI;KACJ,OAAO,EAAE,gCAAgC,2BAA2B;KACpE,aAAa,EACX,oCACA,qDACD;KACD,MAAM,kBAAC,GAAD,EAAS,WAAU,UAAW,CAAA;KACpC,eAAe,EAAS,QAAQ;KACjC,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,GAhFH,kBAAC,GAAD;GACE,cAAa;GACb,OAAO,EAAE,kCAAkC,oBAAoB;GAC/D,aAAa,EACX,wCACA,yDACD;GACD,CAAA;EA2EM,CAAA"}
@@ -1,64 +1,73 @@
1
1
  import { GGNOMAD_OVERVIEW_QUERY as e } from "../graphql.js";
2
2
  import { PageShell as t } from "./PageShell.js";
3
- import { useQuery as n } from "@apollo/client/react";
4
- import { jsx as r, jsxs as i } from "react/jsx-runtime";
3
+ import { StatCard as n } from "../components/StatCard.js";
4
+ import { SectionCard as r } from "../components/SectionCard.js";
5
+ import "../components/index.js";
6
+ import { BarChart3 as i, CalendarCheck as a, DollarSign as o, Percent as s, Users as c } from "lucide-react";
7
+ import { useI18n as l } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
8
+ import { useQuery as u } from "@apollo/client/react";
9
+ import { EmphasisPanel as d, PagePurpose as f, ShowMore as p } from "@burdenoff/fe-libs/ui";
10
+ import { jsx as m, jsxs as h } from "react/jsx-runtime";
5
11
  //#region src/ggnomad/pages/AnalyticsPage.tsx
6
- function a() {
7
- let { data: a } = n(e), o = a?.ggnomadAnalytics;
8
- return /* @__PURE__ */ i(t, {
9
- title: "Analytics",
10
- subtitle: "GGNomad performance and growth analytics",
11
- children: [/* @__PURE__ */ i("div", {
12
- className: "grid gap-4 md:grid-cols-4",
13
- children: [
14
- /* @__PURE__ */ i("div", {
15
- className: "rounded-lg border p-4",
16
- children: [/* @__PURE__ */ r("p", {
17
- className: "text-sm text-muted-foreground",
18
- children: "Revenue"
19
- }), /* @__PURE__ */ i("p", {
20
- className: "text-xl font-semibold",
21
- children: ["$", Number(o?.totalRevenue ?? 0).toLocaleString()]
22
- })]
23
- }),
24
- /* @__PURE__ */ i("div", {
25
- className: "rounded-lg border p-4",
26
- children: [/* @__PURE__ */ r("p", {
27
- className: "text-sm text-muted-foreground",
28
- children: "Bookings"
29
- }), /* @__PURE__ */ r("p", {
30
- className: "text-xl font-semibold",
31
- children: o?.totalBookings ?? 0
32
- })]
33
- }),
34
- /* @__PURE__ */ i("div", {
35
- className: "rounded-lg border p-4",
36
- children: [/* @__PURE__ */ r("p", {
37
- className: "text-sm text-muted-foreground",
38
- children: "Users"
39
- }), /* @__PURE__ */ r("p", {
40
- className: "text-xl font-semibold",
41
- children: o?.totalUsers ?? 0
42
- })]
43
- }),
44
- /* @__PURE__ */ i("div", {
45
- className: "rounded-lg border p-4",
46
- children: [/* @__PURE__ */ r("p", {
47
- className: "text-sm text-muted-foreground",
48
- children: "Conversion"
49
- }), /* @__PURE__ */ i("p", {
50
- className: "text-xl font-semibold",
51
- children: [Number(o?.conversionRate ?? 0).toFixed(2), "%"]
52
- })]
12
+ function g() {
13
+ let { t: g } = l(), { data: _ } = u(e), v = _?.ggnomadAnalytics;
14
+ return /* @__PURE__ */ h(t, {
15
+ title: g("ggnomad.analytics.title", "Analytics"),
16
+ titleAccent: g("ggnomad.analytics.title.accent", "Analytics"),
17
+ subtitle: g("ggnomad.analytics.subtitle", "GGNomad performance and growth analytics"),
18
+ children: [
19
+ /* @__PURE__ */ m(f, {
20
+ icon: /* @__PURE__ */ m(i, { className: "size-4" }),
21
+ children: g("ggnomad.analytics.purpose", "Understand how the marketplace is performing — total revenue, bookings, active users and conversion rate at a glance, with a category breakdown to see which domains (stays, tours, events) are driving growth.")
22
+ }),
23
+ /* @__PURE__ */ h(d, { children: [/* @__PURE__ */ m("p", {
24
+ className: "text-sm text-text-secondary",
25
+ children: g("ggnomad.analytics.totalRevenue", "Total revenue")
26
+ }), /* @__PURE__ */ h("p", {
27
+ className: "mt-1 text-3xl font-semibold tabular-nums text-text-primary",
28
+ children: ["$", Number(v?.totalRevenue ?? 0).toLocaleString()]
29
+ })] }),
30
+ /* @__PURE__ */ h("section", {
31
+ className: "grid gap-4 sm:grid-cols-2 xl:grid-cols-4",
32
+ children: [
33
+ /* @__PURE__ */ m(n, {
34
+ hero: !0,
35
+ label: g("ggnomad.analytics.conversion", "Conversion"),
36
+ value: `${Number(v?.conversionRate ?? 0).toFixed(2)}%`,
37
+ icon: /* @__PURE__ */ m(s, {})
38
+ }),
39
+ /* @__PURE__ */ m(n, {
40
+ label: g("ggnomad.analytics.revenue", "Revenue"),
41
+ value: `$${Number(v?.totalRevenue ?? 0).toLocaleString()}`,
42
+ icon: /* @__PURE__ */ m(o, {})
43
+ }),
44
+ /* @__PURE__ */ m(n, {
45
+ label: g("ggnomad.analytics.bookings", "Bookings"),
46
+ value: Number(v?.totalBookings ?? 0).toLocaleString(),
47
+ icon: /* @__PURE__ */ m(a, {})
48
+ }),
49
+ /* @__PURE__ */ m(n, {
50
+ label: g("ggnomad.analytics.users", "Users"),
51
+ value: Number(v?.totalUsers ?? 0).toLocaleString(),
52
+ icon: /* @__PURE__ */ m(c, {})
53
+ })
54
+ ]
55
+ }),
56
+ /* @__PURE__ */ m(r, {
57
+ title: g("ggnomad.analytics.revenueByCategory", "Revenue by category"),
58
+ children: /* @__PURE__ */ m(p, {
59
+ collapsedHeight: 140,
60
+ label: g("ggnomad.analytics.rawData", "Raw breakdown"),
61
+ children: /* @__PURE__ */ m("pre", {
62
+ className: "overflow-auto rounded-lg border border-border-seam bg-bg-sunken/40 p-4 text-xs text-text-secondary",
63
+ children: JSON.stringify(v?.revenueByCategory ?? {}, null, 2)
64
+ })
53
65
  })
54
- ]
55
- }), /* @__PURE__ */ r("pre", {
56
- className: "overflow-auto rounded-lg border p-4 text-xs",
57
- children: JSON.stringify(o?.revenueByCategory ?? {}, null, 2)
58
- })]
66
+ })
67
+ ]
59
68
  });
60
69
  }
61
70
  //#endregion
62
- export { a as AnalyticsPage };
71
+ export { g as AnalyticsPage };
63
72
 
64
73
  //# sourceMappingURL=AnalyticsPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AnalyticsPage.js","names":[],"sources":["../../../src/ggnomad/pages/AnalyticsPage.tsx"],"sourcesContent":["import { useQuery } from '@apollo/client/react';\n\nimport { GGNOMAD_OVERVIEW_QUERY } from '../graphql';\nimport { PageShell } from './PageShell';\n\nexport function AnalyticsPage() {\n const { data } = useQuery<{\n ggnomadAnalytics: {\n totalRevenue: number;\n totalBookings: number;\n totalUsers: number;\n conversionRate: number;\n revenueByCategory: Record<string, unknown>;\n };\n }>(GGNOMAD_OVERVIEW_QUERY);\n const analytics = data?.ggnomadAnalytics;\n\n return (\n <PageShell title=\"Analytics\" subtitle=\"GGNomad performance and growth analytics\">\n <div className=\"grid gap-4 md:grid-cols-4\">\n <div className=\"rounded-lg border p-4\">\n <p className=\"text-sm text-muted-foreground\">Revenue</p>\n <p className=\"text-xl font-semibold\">\n ${Number(analytics?.totalRevenue ?? 0).toLocaleString()}\n </p>\n </div>\n <div className=\"rounded-lg border p-4\">\n <p className=\"text-sm text-muted-foreground\">Bookings</p>\n <p className=\"text-xl font-semibold\">{analytics?.totalBookings ?? 0}</p>\n </div>\n <div className=\"rounded-lg border p-4\">\n <p className=\"text-sm text-muted-foreground\">Users</p>\n <p className=\"text-xl font-semibold\">{analytics?.totalUsers ?? 0}</p>\n </div>\n <div className=\"rounded-lg border p-4\">\n <p className=\"text-sm text-muted-foreground\">Conversion</p>\n <p className=\"text-xl font-semibold\">\n {Number(analytics?.conversionRate ?? 0).toFixed(2)}%\n </p>\n </div>\n </div>\n <pre className=\"overflow-auto rounded-lg border p-4 text-xs\">\n {JSON.stringify(analytics?.revenueByCategory ?? {}, null, 2)}\n </pre>\n </PageShell>\n );\n}\n"],"mappings":";;;;;AAKA,SAAgB,IAAgB;CAC9B,IAAM,EAAE,YAAS,EAQd,EAAuB,EACpB,IAAY,GAAM;AAExB,QACE,kBAAC,GAAD;EAAW,OAAM;EAAY,UAAS;YAAtC,CACE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAgC;MAAW,CAAA,EACxD,kBAAC,KAAD;MAAG,WAAU;gBAAb,CAAqC,KACjC,OAAO,GAAW,gBAAgB,EAAE,CAAC,gBAAgB,CACrD;QACA;;IACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAgC;MAAY,CAAA,EACzD,kBAAC,KAAD;MAAG,WAAU;gBAAyB,GAAW,iBAAiB;MAAM,CAAA,CACpE;;IACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAgC;MAAS,CAAA,EACtD,kBAAC,KAAD;MAAG,WAAU;gBAAyB,GAAW,cAAc;MAAM,CAAA,CACjE;;IACN,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,KAAD;MAAG,WAAU;gBAAgC;MAAc,CAAA,EAC3D,kBAAC,KAAD;MAAG,WAAU;gBAAb,CACG,OAAO,GAAW,kBAAkB,EAAE,CAAC,QAAQ,EAAE,EAAC,IACjD;QACA;;IACF;MACN,kBAAC,OAAD;GAAK,WAAU;aACZ,KAAK,UAAU,GAAW,qBAAqB,EAAE,EAAE,MAAM,EAAE;GACxD,CAAA,CACI"}
1
+ {"version":3,"file":"AnalyticsPage.js","names":[],"sources":["../../../src/ggnomad/pages/AnalyticsPage.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 { BarChart3, CalendarCheck, DollarSign, Percent, Users } from 'lucide-react';\nimport { EmphasisPanel, PagePurpose, ShowMore } from '@burdenoff/fe-libs/ui';\n\nimport { GGNOMAD_OVERVIEW_QUERY } from '../graphql';\nimport { PageShell } from './PageShell';\nimport { SectionCard, StatCard } from '../components';\n\nexport function AnalyticsPage() {\n const { t } = useI18n();\n const { data } = useQuery(\n GGNOMAD_OVERVIEW_QUERY as TypedDocumentNode<\n {\n ggnomadAnalytics: {\n totalRevenue: number;\n totalBookings: number;\n totalUsers: number;\n conversionRate: number;\n revenueByCategory: Record<string, unknown>;\n };\n },\n Record<string, never>\n >\n );\n const analytics = data?.ggnomadAnalytics;\n\n return (\n <PageShell\n title={t('ggnomad.analytics.title', 'Analytics')}\n titleAccent={t('ggnomad.analytics.title.accent', 'Analytics')}\n subtitle={t('ggnomad.analytics.subtitle', 'GGNomad performance and growth analytics')}\n >\n <PagePurpose icon={<BarChart3 className=\"size-4\" />}>\n {t(\n 'ggnomad.analytics.purpose',\n 'Understand how the marketplace is performing — total revenue, bookings, active users and conversion rate at a glance, with a category breakdown to see which domains (stays, tours, events) are driving growth.'\n )}\n </PagePurpose>\n\n <EmphasisPanel>\n <p className=\"text-sm text-text-secondary\">\n {t('ggnomad.analytics.totalRevenue', 'Total revenue')}\n </p>\n <p className=\"mt-1 text-3xl font-semibold tabular-nums text-text-primary\">\n ${Number(analytics?.totalRevenue ?? 0).toLocaleString()}\n </p>\n </EmphasisPanel>\n\n <section className=\"grid gap-4 sm:grid-cols-2 xl:grid-cols-4\">\n <StatCard\n hero\n label={t('ggnomad.analytics.conversion', 'Conversion')}\n value={`${Number(analytics?.conversionRate ?? 0).toFixed(2)}%`}\n icon={<Percent />}\n />\n <StatCard\n label={t('ggnomad.analytics.revenue', 'Revenue')}\n value={`$${Number(analytics?.totalRevenue ?? 0).toLocaleString()}`}\n icon={<DollarSign />}\n />\n <StatCard\n label={t('ggnomad.analytics.bookings', 'Bookings')}\n value={Number(analytics?.totalBookings ?? 0).toLocaleString()}\n icon={<CalendarCheck />}\n />\n <StatCard\n label={t('ggnomad.analytics.users', 'Users')}\n value={Number(analytics?.totalUsers ?? 0).toLocaleString()}\n icon={<Users />}\n />\n </section>\n\n <SectionCard title={t('ggnomad.analytics.revenueByCategory', 'Revenue by category')}>\n <ShowMore collapsedHeight={140} label={t('ggnomad.analytics.rawData', 'Raw breakdown')}>\n <pre className=\"overflow-auto rounded-lg border border-border-seam bg-bg-sunken/40 p-4 text-xs text-text-secondary\">\n {JSON.stringify(analytics?.revenueByCategory ?? {}, null, 2)}\n </pre>\n </ShowMore>\n </SectionCard>\n </PageShell>\n );\n}\n"],"mappings":";;;;;;;;;;;AAUA,SAAgB,IAAgB;CAC9B,IAAM,EAAE,SAAM,GAAS,EACjB,EAAE,YAAS,EACf,EAYD,EACK,IAAY,GAAM;AAExB,QACE,kBAAC,GAAD;EACE,OAAO,EAAE,2BAA2B,YAAY;EAChD,aAAa,EAAE,kCAAkC,YAAY;EAC7D,UAAU,EAAE,8BAA8B,2CAA2C;YAHvF;GAKE,kBAAC,GAAD;IAAa,MAAM,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA;cAChD,EACC,6BACA,kNACD;IACW,CAAA;GAEd,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,KAAD;IAAG,WAAU;cACV,EAAE,kCAAkC,gBAAgB;IACnD,CAAA,EACJ,kBAAC,KAAD;IAAG,WAAU;cAAb,CAA0E,KACtE,OAAO,GAAW,gBAAgB,EAAE,CAAC,gBAAgB,CACrD;MACU,EAAA,CAAA;GAEhB,kBAAC,WAAD;IAAS,WAAU;cAAnB;KACE,kBAAC,GAAD;MACE,MAAA;MACA,OAAO,EAAE,gCAAgC,aAAa;MACtD,OAAO,GAAG,OAAO,GAAW,kBAAkB,EAAE,CAAC,QAAQ,EAAE,CAAC;MAC5D,MAAM,kBAAC,GAAD,EAAW,CAAA;MACjB,CAAA;KACF,kBAAC,GAAD;MACE,OAAO,EAAE,6BAA6B,UAAU;MAChD,OAAO,IAAI,OAAO,GAAW,gBAAgB,EAAE,CAAC,gBAAgB;MAChE,MAAM,kBAAC,GAAD,EAAc,CAAA;MACpB,CAAA;KACF,kBAAC,GAAD;MACE,OAAO,EAAE,8BAA8B,WAAW;MAClD,OAAO,OAAO,GAAW,iBAAiB,EAAE,CAAC,gBAAgB;MAC7D,MAAM,kBAAC,GAAD,EAAiB,CAAA;MACvB,CAAA;KACF,kBAAC,GAAD;MACE,OAAO,EAAE,2BAA2B,QAAQ;MAC5C,OAAO,OAAO,GAAW,cAAc,EAAE,CAAC,gBAAgB;MAC1D,MAAM,kBAAC,GAAD,EAAS,CAAA;MACf,CAAA;KACM;;GAEV,kBAAC,GAAD;IAAa,OAAO,EAAE,uCAAuC,sBAAsB;cACjF,kBAAC,GAAD;KAAU,iBAAiB;KAAK,OAAO,EAAE,6BAA6B,gBAAgB;eACpF,kBAAC,OAAD;MAAK,WAAU;gBACZ,KAAK,UAAU,GAAW,qBAAqB,EAAE,EAAE,MAAM,EAAE;MACxD,CAAA;KACG,CAAA;IACC,CAAA;GACJ"}