@burdenoff/microfe-tours 2026.608.1 → 2026.625.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.
@@ -17,10 +17,10 @@ function g() {
17
17
  }, [e, r]), /* @__PURE__ */ d("div", {
18
18
  className: "flex flex-col items-center justify-center min-h-[300px] text-center p-6 gap-2",
19
19
  children: [/* @__PURE__ */ u("h2", {
20
- className: "text-lg font-semibold text-foreground",
20
+ className: "text-lg font-semibold text-text-primary",
21
21
  children: n("myTours.notFoundTitle")
22
22
  }), /* @__PURE__ */ u("p", {
23
- className: "text-sm text-muted-foreground max-w-md",
23
+ className: "text-sm text-text-secondary max-w-md",
24
24
  children: n("myTours.notFoundBody")
25
25
  })]
26
26
  });
@@ -53,7 +53,7 @@ function v() {
53
53
  return /* @__PURE__ */ u("output", {
54
54
  className: "flex min-h-[300px] items-center justify-center p-6 text-center",
55
55
  children: /* @__PURE__ */ u("span", {
56
- className: "max-w-md text-sm text-muted-foreground",
56
+ className: "max-w-md text-sm text-text-secondary",
57
57
  children: "Tours are not available for this workspace right now."
58
58
  })
59
59
  });
@@ -1 +1 @@
1
- {"version":3,"file":"ToursUserRoutes.js","names":[],"sources":["../../src/tours/ToursUserRoutes.tsx"],"sourcesContent":["import { lazy, Suspense, useEffect } from 'react';\nimport { Route, Routes, useNavigate } from 'react-router-dom';\nimport { FeatureGate } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { TOURS_FEATURES } from '../constants/featureFlags';\nimport { LazyErrorBoundary, PageLoader } from './components/RouteHelpers';\nimport { useToursContext } from './providers/ToursProvider';\nimport { useToursI18n } from './i18n';\n\n// Eager — primary landing\nimport { MyToursPage } from './pages/MyToursPage';\n\n// Lazy — secondary surface\nconst MyToursProgressPage = lazy(() =>\n import('./pages/MyToursProgressPage').then((m) => ({ default: m.MyToursProgressPage }))\n);\n\n/**\n * 404-style landing shown briefly before redirecting any unknown\n * `/tours/*` path back to the user landing. Gives bookmarkers a signal\n * that the page they tried to load doesn't exist under the user surface,\n * rather than silently snapping them back to `/tours`.\n *\n * Note: react-router's relative `navigate('')` / `navigate('.')` resolves\n * to the current pathname, so for a `*` wildcard matched at\n * `/tours/new` those calls would keep the browser on `/tours/new`. We\n * redirect absolutely via `useToursContext().basePath` so the redirect\n * lands on the landing regardless of how deep the unknown URL was.\n */\nfunction UserRouteNotFound() {\n const navigate = useNavigate();\n const { tr } = useToursI18n();\n const { basePath } = useToursContext();\n\n useEffect(() => {\n const target = basePath && basePath.length > 0 ? basePath : '/';\n const id = window.setTimeout(() => navigate(target, { replace: true }), 1500);\n return () => window.clearTimeout(id);\n }, [navigate, basePath]);\n\n return (\n <div className=\"flex flex-col items-center justify-center min-h-[300px] text-center p-6 gap-2\">\n <h2 className=\"text-lg font-semibold text-foreground\">{tr('myTours.notFoundTitle')}</h2>\n <p className=\"text-sm text-muted-foreground max-w-md\">{tr('myTours.notFoundBody')}</p>\n </div>\n );\n}\n\n/**\n * User-facing routes for the Tours microfrontend.\n *\n * Exposes a tight surface — browse available tours and review own progress.\n * Tour playback itself is handled by the SDK overlay, not by routed pages.\n *\n * Routes (relative to basePath, e.g. `/tours`):\n * - `/` → MyToursPage (browse + status)\n * - `/progress` → MyToursProgressPage (own progress, grouped)\n * - `*` → UserRouteNotFound, then absolute navigate to basePath\n * (collapses any admin-style URL like `/new`, `/media`,\n * `/templates` after briefly signalling unavailability)\n */\nexport function ToursUserRoutes() {\n return (\n <FeatureGate flag={TOURS_FEATURES.USER} defaultValue={true} fallback={<UserToursUnavailable />}>\n <LazyErrorBoundary>\n <Suspense fallback={<PageLoader />}>\n <Routes>\n <Route index element={<MyToursPage />} />\n <Route path=\"progress\" element={<MyToursProgressPage />} />\n <Route path=\"*\" element={<UserRouteNotFound />} />\n </Routes>\n </Suspense>\n </LazyErrorBoundary>\n </FeatureGate>\n );\n}\n\nfunction UserToursUnavailable() {\n return (\n <output className=\"flex min-h-[300px] items-center justify-center p-6 text-center\">\n <span className=\"max-w-md text-sm text-muted-foreground\">\n Tours are not available for this workspace right now.\n </span>\n </output>\n );\n}\n"],"mappings":";;;;;;;;;;AAYA,IAAM,IAAsB,QAC1B,OAAO,kCAA+B,MAAM,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,CACxF;AAcD,SAAS,IAAoB;CAC3B,IAAM,IAAW,GAAa,EACxB,EAAE,UAAO,GAAc,EACvB,EAAE,gBAAa,GAAiB;AAQtC,QANA,QAAgB;EACd,IAAM,IAAS,KAAY,EAAS,SAAS,IAAI,IAAW,KACtD,IAAK,OAAO,iBAAiB,EAAS,GAAQ,EAAE,SAAS,IAAM,CAAC,EAAE,KAAK;AAC7E,eAAa,OAAO,aAAa,EAAG;IACnC,CAAC,GAAU,EAAS,CAAC,EAGtB,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,MAAD;GAAI,WAAU;aAAyC,EAAG,wBAAwB;GAAM,CAAA,EACxF,kBAAC,KAAD;GAAG,WAAU;aAA0C,EAAG,uBAAuB;GAAK,CAAA,CAClF;;;AAiBV,SAAgB,IAAkB;AAChC,QACE,kBAAC,GAAD;EAAa,MAAM,EAAe;EAAM,cAAc;EAAM,UAAU,kBAAC,GAAD,EAAwB,CAAA;YAC5F,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;GAAU,UAAU,kBAAC,GAAD,EAAc,CAAA;aAChC,kBAAC,GAAD,EAAA,UAAA;IACE,kBAAC,GAAD;KAAO,OAAA;KAAM,SAAS,kBAAC,GAAD,EAAe,CAAA;KAAI,CAAA;IACzC,kBAAC,GAAD;KAAO,MAAK;KAAW,SAAS,kBAAC,GAAD,EAAuB,CAAA;KAAI,CAAA;IAC3D,kBAAC,GAAD;KAAO,MAAK;KAAI,SAAS,kBAAC,GAAD,EAAqB,CAAA;KAAI,CAAA;IAC3C,EAAA,CAAA;GACA,CAAA,EACO,CAAA;EACR,CAAA;;AAIlB,SAAS,IAAuB;AAC9B,QACE,kBAAC,UAAD;EAAQ,WAAU;YAChB,kBAAC,QAAD;GAAM,WAAU;aAAyC;GAElD,CAAA;EACA,CAAA"}
1
+ {"version":3,"file":"ToursUserRoutes.js","names":[],"sources":["../../src/tours/ToursUserRoutes.tsx"],"sourcesContent":["import { lazy, Suspense, useEffect } from 'react';\nimport { Route, Routes, useNavigate } from 'react-router-dom';\nimport { FeatureGate } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { TOURS_FEATURES } from '../constants/featureFlags';\nimport { LazyErrorBoundary, PageLoader } from './components/RouteHelpers';\nimport { useToursContext } from './providers/ToursProvider';\nimport { useToursI18n } from './i18n';\n\n// Eager — primary landing\nimport { MyToursPage } from './pages/MyToursPage';\n\n// Lazy — secondary surface\nconst MyToursProgressPage = lazy(() =>\n import('./pages/MyToursProgressPage').then((m) => ({ default: m.MyToursProgressPage }))\n);\n\n/**\n * 404-style landing shown briefly before redirecting any unknown\n * `/tours/*` path back to the user landing. Gives bookmarkers a signal\n * that the page they tried to load doesn't exist under the user surface,\n * rather than silently snapping them back to `/tours`.\n *\n * Note: react-router's relative `navigate('')` / `navigate('.')` resolves\n * to the current pathname, so for a `*` wildcard matched at\n * `/tours/new` those calls would keep the browser on `/tours/new`. We\n * redirect absolutely via `useToursContext().basePath` so the redirect\n * lands on the landing regardless of how deep the unknown URL was.\n */\nfunction UserRouteNotFound() {\n const navigate = useNavigate();\n const { tr } = useToursI18n();\n const { basePath } = useToursContext();\n\n useEffect(() => {\n const target = basePath && basePath.length > 0 ? basePath : '/';\n const id = window.setTimeout(() => navigate(target, { replace: true }), 1500);\n return () => window.clearTimeout(id);\n }, [navigate, basePath]);\n\n return (\n <div className=\"flex flex-col items-center justify-center min-h-[300px] text-center p-6 gap-2\">\n <h2 className=\"text-lg font-semibold text-text-primary\">{tr('myTours.notFoundTitle')}</h2>\n <p className=\"text-sm text-text-secondary max-w-md\">{tr('myTours.notFoundBody')}</p>\n </div>\n );\n}\n\n/**\n * User-facing routes for the Tours microfrontend.\n *\n * Exposes a tight surface — browse available tours and review own progress.\n * Tour playback itself is handled by the SDK overlay, not by routed pages.\n *\n * Routes (relative to basePath, e.g. `/tours`):\n * - `/` → MyToursPage (browse + status)\n * - `/progress` → MyToursProgressPage (own progress, grouped)\n * - `*` → UserRouteNotFound, then absolute navigate to basePath\n * (collapses any admin-style URL like `/new`, `/media`,\n * `/templates` after briefly signalling unavailability)\n */\nexport function ToursUserRoutes() {\n return (\n <FeatureGate flag={TOURS_FEATURES.USER} defaultValue={true} fallback={<UserToursUnavailable />}>\n <LazyErrorBoundary>\n <Suspense fallback={<PageLoader />}>\n <Routes>\n <Route index element={<MyToursPage />} />\n <Route path=\"progress\" element={<MyToursProgressPage />} />\n <Route path=\"*\" element={<UserRouteNotFound />} />\n </Routes>\n </Suspense>\n </LazyErrorBoundary>\n </FeatureGate>\n );\n}\n\nfunction UserToursUnavailable() {\n return (\n <output className=\"flex min-h-[300px] items-center justify-center p-6 text-center\">\n <span className=\"max-w-md text-sm text-text-secondary\">\n Tours are not available for this workspace right now.\n </span>\n </output>\n );\n}\n"],"mappings":";;;;;;;;;;AAYA,IAAM,IAAsB,QAC1B,OAAO,kCAA+B,MAAM,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,CACxF;AAcD,SAAS,IAAoB;CAC3B,IAAM,IAAW,GAAa,EACxB,EAAE,UAAO,GAAc,EACvB,EAAE,gBAAa,GAAiB;AAQtC,QANA,QAAgB;EACd,IAAM,IAAS,KAAY,EAAS,SAAS,IAAI,IAAW,KACtD,IAAK,OAAO,iBAAiB,EAAS,GAAQ,EAAE,SAAS,IAAM,CAAC,EAAE,KAAK;AAC7E,eAAa,OAAO,aAAa,EAAG;IACnC,CAAC,GAAU,EAAS,CAAC,EAGtB,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,MAAD;GAAI,WAAU;aAA2C,EAAG,wBAAwB;GAAM,CAAA,EAC1F,kBAAC,KAAD;GAAG,WAAU;aAAwC,EAAG,uBAAuB;GAAK,CAAA,CAChF;;;AAiBV,SAAgB,IAAkB;AAChC,QACE,kBAAC,GAAD;EAAa,MAAM,EAAe;EAAM,cAAc;EAAM,UAAU,kBAAC,GAAD,EAAwB,CAAA;YAC5F,kBAAC,GAAD,EAAA,UACE,kBAAC,GAAD;GAAU,UAAU,kBAAC,GAAD,EAAc,CAAA;aAChC,kBAAC,GAAD,EAAA,UAAA;IACE,kBAAC,GAAD;KAAO,OAAA;KAAM,SAAS,kBAAC,GAAD,EAAe,CAAA;KAAI,CAAA;IACzC,kBAAC,GAAD;KAAO,MAAK;KAAW,SAAS,kBAAC,GAAD,EAAuB,CAAA;KAAI,CAAA;IAC3D,kBAAC,GAAD;KAAO,MAAK;KAAI,SAAS,kBAAC,GAAD,EAAqB,CAAA;KAAI,CAAA;IAC3C,EAAA,CAAA;GACA,CAAA,EACO,CAAA;EACR,CAAA;;AAIlB,SAAS,IAAuB;AAC9B,QACE,kBAAC,UAAD;EAAQ,WAAU;YAChB,kBAAC,QAAD;GAAM,WAAU;aAAuC;GAEhD,CAAA;EACA,CAAA"}
@@ -39,28 +39,28 @@ var c = 3, l = class extends e {
39
39
  className: "flex flex-col items-center justify-center min-h-[400px] gap-4 text-center p-6",
40
40
  children: [
41
41
  /* @__PURE__ */ a("div", {
42
- className: "p-3 rounded-full bg-destructive/10",
43
- children: /* @__PURE__ */ a(n, { className: "size-8 text-destructive" })
42
+ className: "p-3 rounded-full bg-status-error-bg/10",
43
+ children: /* @__PURE__ */ a(n, { className: "size-8 text-status-error-text" })
44
44
  }),
45
45
  /* @__PURE__ */ o("div", {
46
46
  className: "space-y-2",
47
47
  children: [/* @__PURE__ */ a("h3", {
48
- className: "text-lg font-semibold text-foreground",
48
+ className: "text-lg font-semibold text-text-primary",
49
49
  children: "Failed to load page"
50
50
  }), /* @__PURE__ */ a("p", {
51
- className: "text-sm text-muted-foreground max-w-md",
51
+ className: "text-sm text-text-secondary max-w-md",
52
52
  children: e ? "The page chunk could not be loaded. This may be due to a network issue." : "The page chunk could not be loaded after multiple attempts. Please refresh the page."
53
53
  })]
54
54
  }),
55
55
  e ? /* @__PURE__ */ o("button", {
56
56
  type: "button",
57
57
  onClick: this.handleRetry,
58
- className: "inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-md bg-primary text-primary-foreground hover:bg-primary/90 transition-colors",
58
+ className: "inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90 transition-colors",
59
59
  children: [/* @__PURE__ */ a(i, { className: "size-4" }), "Try Again"]
60
60
  }) : /* @__PURE__ */ o("button", {
61
61
  type: "button",
62
62
  onClick: () => window.location.reload(),
63
- className: "inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-md bg-primary text-primary-foreground hover:bg-primary/90 transition-colors",
63
+ className: "inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90 transition-colors",
64
64
  children: [/* @__PURE__ */ a(i, { className: "size-4" }), "Refresh Page"]
65
65
  })
66
66
  ]
@@ -1 +1 @@
1
- {"version":3,"file":"RouteHelpers.js","names":[],"sources":["../../../src/tours/components/RouteHelpers.tsx"],"sourcesContent":["import { Component, Fragment, type ReactNode, type ErrorInfo } from 'react';\nimport { Loader2, AlertTriangle, RefreshCw } from 'lucide-react';\n\n/**\n * Loading fallback for lazy-loaded pages.\n * Shared between ToursAdminRoutes and ToursUserRoutes (Section D/E of BOFF-2391).\n */\nexport function PageLoader() {\n return (\n <div className=\"flex items-center justify-center min-h-[200px]\">\n <Loader2 className=\"size-8 animate-spin text-primary\" />\n </div>\n );\n}\n\n/** Max consecutive retries before the error screen suggests a full reload. */\nconst MAX_RETRY_COUNT = 3;\n\n/**\n * Error boundary for lazy-loaded page chunks.\n *\n * React.lazy() caches rejected promises, so resetting error state alone would\n * re-throw the same cached rejection. The retryCount key forces a full remount\n * of the subtree on retry, creating fresh lazy-component instances.\n */\nexport class LazyErrorBoundary extends Component<\n { children: ReactNode },\n { hasError: boolean; error: Error | null; retryCount: number }\n> {\n constructor(props: { children: ReactNode }) {\n super(props);\n this.state = { hasError: false, error: null, retryCount: 0 };\n }\n\n static getDerivedStateFromError(error: Error) {\n return { hasError: true, error };\n }\n\n componentDidCatch(error: Error, errorInfo: ErrorInfo) {\n // TODO: forward to error monitoring service (Sentry, etc.)\n // Log the full error server-side / in the console; do NOT surface the raw\n // message to users since chunk-load errors can include CDN paths, build\n // hashes, or deployment details that shouldn't appear in the UI.\n console.error('[Tours] Lazy page load failed:', error, errorInfo);\n }\n\n handleRetry = () => {\n this.setState((prev) => ({\n hasError: false,\n error: null,\n retryCount: prev.retryCount + 1,\n }));\n };\n\n render() {\n if (this.state.hasError) {\n const canRetry = this.state.retryCount < MAX_RETRY_COUNT;\n return (\n <div className=\"flex flex-col items-center justify-center min-h-[400px] gap-4 text-center p-6\">\n <div className=\"p-3 rounded-full bg-destructive/10\">\n <AlertTriangle className=\"size-8 text-destructive\" />\n </div>\n <div className=\"space-y-2\">\n <h3 className=\"text-lg font-semibold text-foreground\">Failed to load page</h3>\n <p className=\"text-sm text-muted-foreground max-w-md\">\n {canRetry\n ? 'The page chunk could not be loaded. This may be due to a network issue.'\n : 'The page chunk could not be loaded after multiple attempts. Please refresh the page.'}\n </p>\n </div>\n {canRetry ? (\n <button\n type=\"button\"\n onClick={this.handleRetry}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-md bg-primary text-primary-foreground hover:bg-primary/90 transition-colors\"\n >\n <RefreshCw className=\"size-4\" />\n Try Again\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={() => window.location.reload()}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-md bg-primary text-primary-foreground hover:bg-primary/90 transition-colors\"\n >\n <RefreshCw className=\"size-4\" />\n Refresh Page\n </button>\n )}\n </div>\n );\n }\n\n return <Fragment key={this.state.retryCount}>{this.props.children}</Fragment>;\n }\n}\n"],"mappings":";;;;AAOA,SAAgB,IAAa;AAC3B,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAS,WAAU,oCAAqC,CAAA;EACpD,CAAA;;AAKV,IAAM,IAAkB,GASX,IAAb,cAAuC,EAGrC;CACA,YAAY,GAAgC;AAE1C,EADA,MAAM,EAAM,EACZ,KAAK,QAAQ;GAAE,UAAU;GAAO,OAAO;GAAM,YAAY;GAAG;;CAG9D,OAAO,yBAAyB,GAAc;AAC5C,SAAO;GAAE,UAAU;GAAM;GAAO;;CAGlC,kBAAkB,GAAc,GAAsB;AAKpD,UAAQ,MAAM,kCAAkC,GAAO,EAAU;;CAGnE,oBAAoB;AAClB,OAAK,UAAU,OAAU;GACvB,UAAU;GACV,OAAO;GACP,YAAY,EAAK,aAAa;GAC/B,EAAE;;CAGL,SAAS;AACP,MAAI,KAAK,MAAM,UAAU;GACvB,IAAM,IAAW,KAAK,MAAM,aAAa;AACzC,UACE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD,EAAe,WAAU,2BAA4B,CAAA;MACjD,CAAA;KACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBAAwC;OAAwB,CAAA,EAC9E,kBAAC,KAAD;OAAG,WAAU;iBACV,IACG,4EACA;OACF,CAAA,CACA;;KACL,IACC,kBAAC,UAAD;MACE,MAAK;MACL,SAAS,KAAK;MACd,WAAU;gBAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA,EAAA,YAEzB;UAET,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,OAAO,SAAS,QAAQ;MACvC,WAAU;gBAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA,EAAA,eAEzB;;KAEP;;;AAIV,SAAO,kBAAC,GAAD,EAAA,UAAuC,KAAK,MAAM,UAAoB,EAAvD,KAAK,MAAM,WAA4C"}
1
+ {"version":3,"file":"RouteHelpers.js","names":[],"sources":["../../../src/tours/components/RouteHelpers.tsx"],"sourcesContent":["import { Component, Fragment, type ReactNode, type ErrorInfo } from 'react';\nimport { Loader2, AlertTriangle, RefreshCw } from 'lucide-react';\n\n/**\n * Loading fallback for lazy-loaded pages.\n * Shared between ToursAdminRoutes and ToursUserRoutes (Section D/E of BOFF-2391).\n */\nexport function PageLoader() {\n return (\n <div className=\"flex items-center justify-center min-h-[200px]\">\n <Loader2 className=\"size-8 animate-spin text-primary\" />\n </div>\n );\n}\n\n/** Max consecutive retries before the error screen suggests a full reload. */\nconst MAX_RETRY_COUNT = 3;\n\n/**\n * Error boundary for lazy-loaded page chunks.\n *\n * React.lazy() caches rejected promises, so resetting error state alone would\n * re-throw the same cached rejection. The retryCount key forces a full remount\n * of the subtree on retry, creating fresh lazy-component instances.\n */\nexport class LazyErrorBoundary extends Component<\n { children: ReactNode },\n { hasError: boolean; error: Error | null; retryCount: number }\n> {\n constructor(props: { children: ReactNode }) {\n super(props);\n this.state = { hasError: false, error: null, retryCount: 0 };\n }\n\n static getDerivedStateFromError(error: Error) {\n return { hasError: true, error };\n }\n\n componentDidCatch(error: Error, errorInfo: ErrorInfo) {\n // TODO: forward to error monitoring service (Sentry, etc.)\n // Log the full error server-side / in the console; do NOT surface the raw\n // message to users since chunk-load errors can include CDN paths, build\n // hashes, or deployment details that shouldn't appear in the UI.\n console.error('[Tours] Lazy page load failed:', error, errorInfo);\n }\n\n handleRetry = () => {\n this.setState((prev) => ({\n hasError: false,\n error: null,\n retryCount: prev.retryCount + 1,\n }));\n };\n\n render() {\n if (this.state.hasError) {\n const canRetry = this.state.retryCount < MAX_RETRY_COUNT;\n return (\n <div className=\"flex flex-col items-center justify-center min-h-[400px] gap-4 text-center p-6\">\n <div className=\"p-3 rounded-full bg-status-error-bg/10\">\n <AlertTriangle className=\"size-8 text-status-error-text\" />\n </div>\n <div className=\"space-y-2\">\n <h3 className=\"text-lg font-semibold text-text-primary\">Failed to load page</h3>\n <p className=\"text-sm text-text-secondary max-w-md\">\n {canRetry\n ? 'The page chunk could not be loaded. This may be due to a network issue.'\n : 'The page chunk could not be loaded after multiple attempts. Please refresh the page.'}\n </p>\n </div>\n {canRetry ? (\n <button\n type=\"button\"\n onClick={this.handleRetry}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90 transition-colors\"\n >\n <RefreshCw className=\"size-4\" />\n Try Again\n </button>\n ) : (\n <button\n type=\"button\"\n onClick={() => window.location.reload()}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-md bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90 transition-colors\"\n >\n <RefreshCw className=\"size-4\" />\n Refresh Page\n </button>\n )}\n </div>\n );\n }\n\n return <Fragment key={this.state.retryCount}>{this.props.children}</Fragment>;\n }\n}\n"],"mappings":";;;;AAOA,SAAgB,IAAa;AAC3B,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,GAAD,EAAS,WAAU,oCAAqC,CAAA;EACpD,CAAA;;AAKV,IAAM,IAAkB,GASX,IAAb,cAAuC,EAGrC;CACA,YAAY,GAAgC;AAE1C,EADA,MAAM,EAAM,EACZ,KAAK,QAAQ;GAAE,UAAU;GAAO,OAAO;GAAM,YAAY;GAAG;;CAG9D,OAAO,yBAAyB,GAAc;AAC5C,SAAO;GAAE,UAAU;GAAM;GAAO;;CAGlC,kBAAkB,GAAc,GAAsB;AAKpD,UAAQ,MAAM,kCAAkC,GAAO,EAAU;;CAGnE,oBAAoB;AAClB,OAAK,UAAU,OAAU;GACvB,UAAU;GACV,OAAO;GACP,YAAY,EAAK,aAAa;GAC/B,EAAE;;CAGL,SAAS;AACP,MAAI,KAAK,MAAM,UAAU;GACvB,IAAM,IAAW,KAAK,MAAM,aAAa;AACzC,UACE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,GAAD,EAAe,WAAU,iCAAkC,CAAA;MACvD,CAAA;KACN,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,MAAD;OAAI,WAAU;iBAA0C;OAAwB,CAAA,EAChF,kBAAC,KAAD;OAAG,WAAU;iBACV,IACG,4EACA;OACF,CAAA,CACA;;KACL,IACC,kBAAC,UAAD;MACE,MAAK;MACL,SAAS,KAAK;MACd,WAAU;gBAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA,EAAA,YAEzB;UAET,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,OAAO,SAAS,QAAQ;MACvC,WAAU;gBAHZ,CAKE,kBAAC,GAAD,EAAW,WAAU,UAAW,CAAA,EAAA,eAEzB;;KAEP;;;AAIV,SAAO,kBAAC,GAAD,EAAA,UAAuC,KAAK,MAAM,UAAoB,EAAvD,KAAK,MAAM,WAA4C"}
@@ -6,71 +6,74 @@ import { useCallback as o, useEffect as s } from "react";
6
6
  import { BarChart3 as c, Timer as l, TrendingUp as u, Users as d } from "lucide-react";
7
7
  import { jsx as f, jsxs as p } from "react/jsx-runtime";
8
8
  import { useParams as m } from "react-router-dom";
9
- import { useI18n as h } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
9
+ import { EmphasisPanel as h, GlassCard as g } from "@burdenoff/fe-libs/ui";
10
+ import { useI18n as _ } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
10
11
  //#region src/tours/pages/AnalyticsDashboardPage.tsx
11
- function g() {
12
- let { t: g } = h(), _ = o((e, t) => {
13
- let n = g(e);
12
+ function v() {
13
+ let { t: v } = _(), y = o((e, t) => {
14
+ let n = v(e);
14
15
  return n === e ? t : n;
15
- }, [g]), { tourId: v } = m(), { hasPermission: y, isLoading: b } = e(), x = y(t.TOUR_ANALYTICS_READ), { analytics: S, loading: C, fetchAnalytics: w, getCompletionRate: T, getSkipRate: E } = n(v);
16
+ }, [v]), { tourId: b } = m(), { hasPermission: x, isLoading: S } = e(), C = x(t.TOUR_ANALYTICS_READ), { analytics: w, loading: T, fetchAnalytics: E, getCompletionRate: D, getSkipRate: O } = n(b);
16
17
  return s(() => {
17
- b || !x || w();
18
+ S || !C || E();
18
19
  }, [
19
- w,
20
- b,
21
- x
22
- ]), !b && !x ? /* @__PURE__ */ f("div", {
20
+ E,
21
+ S,
22
+ C
23
+ ]), !S && !C ? /* @__PURE__ */ f("div", {
23
24
  className: "flex items-center justify-center h-full p-3 sm:p-6",
24
25
  children: /* @__PURE__ */ p("div", {
25
26
  className: "text-center",
26
27
  children: [/* @__PURE__ */ f("h2", {
27
- className: "text-lg font-semibold text-foreground mb-2",
28
- children: _("analyticsDashboard.accessDenied", "Access Denied")
28
+ className: "text-lg font-semibold text-text-primary mb-2",
29
+ children: y("analyticsDashboard.accessDenied", "Access Denied")
29
30
  }), /* @__PURE__ */ f("p", {
30
- className: "text-sm text-muted-foreground",
31
- children: _("analyticsDashboard.noPermission", "You do not have permission to view tour analytics.")
31
+ className: "text-sm text-text-secondary",
32
+ children: y("analyticsDashboard.noPermission", "You do not have permission to view tour analytics.")
32
33
  })]
33
34
  })
34
- }) : C ? /* @__PURE__ */ f("div", {
35
+ }) : T ? /* @__PURE__ */ f("div", {
35
36
  className: "flex items-center justify-center h-full",
36
37
  children: /* @__PURE__ */ f("p", {
37
- className: "text-muted-foreground",
38
- children: _("analyticsDashboard.loading", "Loading analytics...")
38
+ className: "text-text-secondary",
39
+ children: y("analyticsDashboard.loading", "Loading analytics...")
39
40
  })
40
- }) : S ? /* @__PURE__ */ p("div", {
41
+ }) : w ? /* @__PURE__ */ p("div", {
41
42
  className: "space-y-4 sm:space-y-6 p-3 sm:p-6",
42
43
  children: [
43
44
  /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ f("h1", {
44
45
  className: "text-xl sm:text-2xl font-bold",
45
- children: _("analyticsDashboard.title", "Tour Analytics")
46
+ children: y("analyticsDashboard.title", "Tour Analytics")
46
47
  }), /* @__PURE__ */ f("p", {
47
- className: "text-sm text-muted-foreground",
48
- children: _("analyticsDashboard.subtitle", "Performance metrics and insights")
48
+ className: "text-sm text-text-secondary",
49
+ children: y("analyticsDashboard.subtitle", "Performance metrics and insights")
49
50
  })] }),
50
51
  /* @__PURE__ */ p("div", {
51
52
  className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4",
52
53
  children: [
53
- /* @__PURE__ */ p("div", {
54
- className: "border border-border rounded-md p-4 bg-card",
54
+ /* @__PURE__ */ p(g, {
55
+ treatment: "glass",
56
+ className: "p-4",
55
57
  children: [/* @__PURE__ */ p("div", {
56
58
  className: "flex items-center gap-3 mb-2",
57
59
  children: [/* @__PURE__ */ f("div", {
58
- className: "p-2 bg-primary/10 rounded-md",
60
+ className: "p-2 bg-action-primary-bg/10 rounded-md",
59
61
  children: /* @__PURE__ */ f(d, {
60
62
  className: "text-primary",
61
63
  size: 20
62
64
  })
63
65
  }), /* @__PURE__ */ f("span", {
64
- className: "text-sm text-muted-foreground",
65
- children: _("analyticsDashboard.totalViews", "Total Views")
66
+ className: "text-sm text-text-secondary",
67
+ children: y("analyticsDashboard.totalViews", "Total Views")
66
68
  })]
67
69
  }), /* @__PURE__ */ f("div", {
68
- className: "text-2xl font-bold",
69
- children: i(S.totalViews)
70
+ className: "text-2xl font-bold tabular-nums",
71
+ children: i(w.totalViews)
70
72
  })]
71
73
  }),
72
- /* @__PURE__ */ p("div", {
73
- className: "border border-border rounded-md p-4 bg-card",
74
+ /* @__PURE__ */ p(g, {
75
+ treatment: "metric",
76
+ className: "p-4",
74
77
  children: [/* @__PURE__ */ p("div", {
75
78
  className: "flex items-center gap-3 mb-2",
76
79
  children: [/* @__PURE__ */ f("div", {
@@ -80,16 +83,17 @@ function g() {
80
83
  size: 20
81
84
  })
82
85
  }), /* @__PURE__ */ f("span", {
83
- className: "text-sm text-muted-foreground",
84
- children: _("analyticsDashboard.completionRate", "Completion Rate")
86
+ className: "text-sm text-text-secondary",
87
+ children: y("analyticsDashboard.completionRate", "Completion Rate")
85
88
  })]
86
89
  }), /* @__PURE__ */ f("div", {
87
- className: "text-2xl font-bold",
88
- children: a(T())
90
+ className: "text-2xl font-bold tabular-nums",
91
+ children: a(D())
89
92
  })]
90
93
  }),
91
- /* @__PURE__ */ p("div", {
92
- className: "border border-border rounded-md p-4 bg-card",
94
+ /* @__PURE__ */ p(g, {
95
+ treatment: "metric",
96
+ className: "p-4",
93
97
  children: [/* @__PURE__ */ p("div", {
94
98
  className: "flex items-center gap-3 mb-2",
95
99
  children: [/* @__PURE__ */ f("div", {
@@ -99,16 +103,17 @@ function g() {
99
103
  size: 20
100
104
  })
101
105
  }), /* @__PURE__ */ f("span", {
102
- className: "text-sm text-muted-foreground",
103
- children: _("analyticsDashboard.skipRate", "Skip Rate")
106
+ className: "text-sm text-text-secondary",
107
+ children: y("analyticsDashboard.skipRate", "Skip Rate")
104
108
  })]
105
109
  }), /* @__PURE__ */ f("div", {
106
- className: "text-2xl font-bold",
107
- children: a(E())
110
+ className: "text-2xl font-bold tabular-nums",
111
+ children: a(O())
108
112
  })]
109
113
  }),
110
- /* @__PURE__ */ p("div", {
111
- className: "border border-border rounded-md p-4 bg-card",
114
+ /* @__PURE__ */ p(g, {
115
+ treatment: "metric",
116
+ className: "p-4",
112
117
  children: [/* @__PURE__ */ p("div", {
113
118
  className: "flex items-center gap-3 mb-2",
114
119
  children: [/* @__PURE__ */ f("div", {
@@ -118,30 +123,30 @@ function g() {
118
123
  size: 20
119
124
  })
120
125
  }), /* @__PURE__ */ f("span", {
121
- className: "text-sm text-muted-foreground",
122
- children: _("analyticsDashboard.avgDuration", "Avg Duration")
126
+ className: "text-sm text-text-secondary",
127
+ children: y("analyticsDashboard.avgDuration", "Avg Duration")
123
128
  })]
124
129
  }), /* @__PURE__ */ f("div", {
125
- className: "text-2xl font-bold",
126
- children: r(S.averageDuration)
130
+ className: "text-2xl font-bold tabular-nums",
131
+ children: r(w.averageDuration)
127
132
  })]
128
133
  })
129
134
  ]
130
135
  }),
131
- /* @__PURE__ */ p("div", {
132
- className: "border border-border rounded-md p-4 sm:p-6 bg-card",
136
+ /* @__PURE__ */ p(h, {
137
+ className: "p-4 sm:p-6",
133
138
  children: [/* @__PURE__ */ f("h2", {
134
139
  className: "text-lg font-semibold mb-4",
135
- children: _("analyticsDashboard.deviceBreakdown", "Device Breakdown")
140
+ children: y("analyticsDashboard.deviceBreakdown", "Device Breakdown")
136
141
  }), /* @__PURE__ */ f("div", {
137
142
  className: "space-y-3",
138
- children: S.byDeviceType.map((e) => /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ p("div", {
143
+ children: w.byDeviceType.map((e) => /* @__PURE__ */ p("div", { children: [/* @__PURE__ */ p("div", {
139
144
  className: "flex items-center justify-between mb-2",
140
145
  children: [/* @__PURE__ */ f("span", {
141
146
  className: "text-sm capitalize",
142
147
  children: e.deviceType
143
148
  }), /* @__PURE__ */ p("span", {
144
- className: "text-sm text-muted-foreground",
149
+ className: "text-sm text-text-secondary tabular-nums",
145
150
  children: [
146
151
  i(e.count),
147
152
  " views (",
@@ -150,64 +155,65 @@ function g() {
150
155
  ]
151
156
  })]
152
157
  }), /* @__PURE__ */ f("div", {
153
- className: "w-full bg-muted rounded-full h-2",
158
+ className: "w-full bg-bg-sunken rounded-full h-2",
154
159
  children: /* @__PURE__ */ f("div", {
155
- className: "bg-primary h-2 rounded-full",
160
+ className: "bg-action-primary-bg h-2 rounded-full shadow-[var(--shadow-pop)]",
156
161
  style: { width: `${e.completionRate}%` }
157
162
  })
158
163
  })] }, e.deviceType))
159
164
  })]
160
165
  }),
161
- S.stepAnalytics && S.stepAnalytics.length > 0 && /* @__PURE__ */ p("div", {
162
- className: "border border-border rounded-md p-4 sm:p-6 bg-card",
166
+ w.stepAnalytics && w.stepAnalytics.length > 0 && /* @__PURE__ */ p(g, {
167
+ treatment: "plain",
168
+ className: "p-4 sm:p-6 border-border-seam",
163
169
  children: [/* @__PURE__ */ f("h2", {
164
170
  className: "text-lg font-semibold mb-4",
165
- children: _("analyticsDashboard.stepPerformance", "Step Performance")
171
+ children: y("analyticsDashboard.stepPerformance", "Step Performance")
166
172
  }), /* @__PURE__ */ f("div", {
167
173
  className: "overflow-x-auto",
168
174
  children: /* @__PURE__ */ p("table", {
169
175
  className: "w-full",
170
176
  children: [/* @__PURE__ */ f("thead", { children: /* @__PURE__ */ p("tr", {
171
- className: "border-b border-border",
177
+ className: "border-b border-border-seam",
172
178
  children: [
173
179
  /* @__PURE__ */ f("th", {
174
- className: "text-left py-2 px-4 text-sm font-medium",
175
- children: _("analyticsDashboard.tableStep", "Step")
180
+ className: "text-left py-2 px-4 text-sm font-medium text-text-secondary",
181
+ children: y("analyticsDashboard.tableStep", "Step")
176
182
  }),
177
183
  /* @__PURE__ */ f("th", {
178
- className: "text-right py-2 px-4 text-sm font-medium",
179
- children: _("analyticsDashboard.tableViews", "Views")
184
+ className: "text-right py-2 px-4 text-sm font-medium text-text-secondary",
185
+ children: y("analyticsDashboard.tableViews", "Views")
180
186
  }),
181
187
  /* @__PURE__ */ f("th", {
182
- className: "text-right py-2 px-4 text-sm font-medium",
183
- children: _("analyticsDashboard.tableCompletions", "Completions")
188
+ className: "text-right py-2 px-4 text-sm font-medium text-text-secondary",
189
+ children: y("analyticsDashboard.tableCompletions", "Completions")
184
190
  }),
185
191
  /* @__PURE__ */ f("th", {
186
- className: "text-right py-2 px-4 text-sm font-medium",
187
- children: _("analyticsDashboard.tableDropOffRate", "Drop-off Rate")
192
+ className: "text-right py-2 px-4 text-sm font-medium text-text-secondary",
193
+ children: y("analyticsDashboard.tableDropOffRate", "Drop-off Rate")
188
194
  })
189
195
  ]
190
- }) }), /* @__PURE__ */ f("tbody", { children: S.stepAnalytics.map((e) => /* @__PURE__ */ p("tr", {
191
- className: "border-b border-border",
196
+ }) }), /* @__PURE__ */ f("tbody", { children: w.stepAnalytics.map((e) => /* @__PURE__ */ p("tr", {
197
+ className: "border-b border-border-seam",
192
198
  children: [
193
199
  /* @__PURE__ */ p("td", {
194
200
  className: "py-2 px-4 text-sm",
195
201
  children: [
196
- _("analyticsDashboard.stepLabel", "Step"),
202
+ y("analyticsDashboard.stepLabel", "Step"),
197
203
  " ",
198
204
  e.stepOrder
199
205
  ]
200
206
  }),
201
207
  /* @__PURE__ */ f("td", {
202
- className: "text-right py-2 px-4 text-sm",
208
+ className: "text-right py-2 px-4 text-sm tabular-nums",
203
209
  children: i(e.views)
204
210
  }),
205
211
  /* @__PURE__ */ f("td", {
206
- className: "text-right py-2 px-4 text-sm",
212
+ className: "text-right py-2 px-4 text-sm tabular-nums",
207
213
  children: i(e.completions)
208
214
  }),
209
215
  /* @__PURE__ */ f("td", {
210
- className: "text-right py-2 px-4 text-sm",
216
+ className: "text-right py-2 px-4 text-sm tabular-nums",
211
217
  children: a(e.dropOffRate)
212
218
  })
213
219
  ]
@@ -219,12 +225,12 @@ function g() {
219
225
  }) : /* @__PURE__ */ f("div", {
220
226
  className: "flex items-center justify-center h-full",
221
227
  children: /* @__PURE__ */ f("p", {
222
- className: "text-muted-foreground",
223
- children: _("analyticsDashboard.noData", "No analytics data available")
228
+ className: "text-text-secondary",
229
+ children: y("analyticsDashboard.noData", "No analytics data available")
224
230
  })
225
231
  });
226
232
  }
227
233
  //#endregion
228
- export { g as AnalyticsDashboardPage };
234
+ export { v as AnalyticsDashboardPage };
229
235
 
230
236
  //# sourceMappingURL=AnalyticsDashboardPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AnalyticsDashboardPage.js","names":[],"sources":["../../../src/tours/pages/AnalyticsDashboardPage.tsx"],"sourcesContent":["import { useCallback, useEffect } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { BarChart3, TrendingUp, Users, Timer } from 'lucide-react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useTourAnalytics } from '../hooks/useTourAnalytics';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport { formatNumber, formatPercentage, formatDuration } from '../utils';\n\n/**\n * Analytics dashboard page\n * Display tour performance metrics and charts\n */\nexport function AnalyticsDashboardPage() {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n const { tourId } = useParams();\n const { hasPermission, isLoading: permissionsLoading } = useToursPermissionAccess();\n const canViewAnalytics = hasPermission(TOUR_PERMISSIONS.TOUR_ANALYTICS_READ);\n\n const { analytics, loading, fetchAnalytics, getCompletionRate, getSkipRate } =\n useTourAnalytics(tourId);\n\n useEffect(() => {\n if (permissionsLoading || !canViewAnalytics) return;\n fetchAnalytics();\n }, [fetchAnalytics, permissionsLoading, canViewAnalytics]);\n\n if (!permissionsLoading && !canViewAnalytics) {\n return (\n <div className=\"flex items-center justify-center h-full p-3 sm:p-6\">\n <div className=\"text-center\">\n <h2 className=\"text-lg font-semibold text-foreground mb-2\">\n {tr('analyticsDashboard.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-muted-foreground\">\n {tr(\n 'analyticsDashboard.noPermission',\n 'You do not have permission to view tour analytics.'\n )}\n </p>\n </div>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <p className=\"text-muted-foreground\">\n {tr('analyticsDashboard.loading', 'Loading analytics...')}\n </p>\n </div>\n );\n }\n\n if (!analytics) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <p className=\"text-muted-foreground\">\n {tr('analyticsDashboard.noData', 'No analytics data available')}\n </p>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div>\n <h1 className=\"text-xl sm:text-2xl font-bold\">\n {tr('analyticsDashboard.title', 'Tour Analytics')}\n </h1>\n <p className=\"text-sm text-muted-foreground\">\n {tr('analyticsDashboard.subtitle', 'Performance metrics and insights')}\n </p>\n </div>\n\n {/* Summary Cards */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n <div className=\"border border-border rounded-md p-4 bg-card\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 bg-primary/10 rounded-md\">\n <Users className=\"text-primary\" size={20} />\n </div>\n <span className=\"text-sm text-muted-foreground\">\n {tr('analyticsDashboard.totalViews', 'Total Views')}\n </span>\n </div>\n <div className=\"text-2xl font-bold\">{formatNumber(analytics.totalViews)}</div>\n </div>\n\n <div className=\"border border-border rounded-md p-4 bg-card\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 bg-success/10 rounded-md\">\n <TrendingUp className=\"text-success\" size={20} />\n </div>\n <span className=\"text-sm text-muted-foreground\">\n {tr('analyticsDashboard.completionRate', 'Completion Rate')}\n </span>\n </div>\n <div className=\"text-2xl font-bold\">{formatPercentage(getCompletionRate())}</div>\n </div>\n\n <div className=\"border border-border rounded-md p-4 bg-card\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 bg-warning/10 rounded-md\">\n <BarChart3 className=\"text-warning\" size={20} />\n </div>\n <span className=\"text-sm text-muted-foreground\">\n {tr('analyticsDashboard.skipRate', 'Skip Rate')}\n </span>\n </div>\n <div className=\"text-2xl font-bold\">{formatPercentage(getSkipRate())}</div>\n </div>\n\n <div className=\"border border-border rounded-md p-4 bg-card\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 bg-info/10 rounded-md\">\n <Timer className=\"text-info\" size={20} />\n </div>\n <span className=\"text-sm text-muted-foreground\">\n {tr('analyticsDashboard.avgDuration', 'Avg Duration')}\n </span>\n </div>\n <div className=\"text-2xl font-bold\">{formatDuration(analytics.averageDuration)}</div>\n </div>\n </div>\n\n {/* Device Breakdown */}\n <div className=\"border border-border rounded-md p-4 sm:p-6 bg-card\">\n <h2 className=\"text-lg font-semibold mb-4\">\n {tr('analyticsDashboard.deviceBreakdown', 'Device Breakdown')}\n </h2>\n <div className=\"space-y-3\">\n {analytics.byDeviceType.map((device) => (\n <div key={device.deviceType}>\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm capitalize\">{device.deviceType}</span>\n <span className=\"text-sm text-muted-foreground\">\n {formatNumber(device.count)} views ({formatPercentage(device.completionRate)})\n </span>\n </div>\n <div className=\"w-full bg-muted rounded-full h-2\">\n <div\n className=\"bg-primary h-2 rounded-full\"\n style={{ width: `${device.completionRate}%` }}\n />\n </div>\n </div>\n ))}\n </div>\n </div>\n\n {/* Step Analytics */}\n {analytics.stepAnalytics && analytics.stepAnalytics.length > 0 && (\n <div className=\"border border-border rounded-md p-4 sm:p-6 bg-card\">\n <h2 className=\"text-lg font-semibold mb-4\">\n {tr('analyticsDashboard.stepPerformance', 'Step Performance')}\n </h2>\n <div className=\"overflow-x-auto\">\n <table className=\"w-full\">\n <thead>\n <tr className=\"border-b border-border\">\n <th className=\"text-left py-2 px-4 text-sm font-medium\">\n {tr('analyticsDashboard.tableStep', 'Step')}\n </th>\n <th className=\"text-right py-2 px-4 text-sm font-medium\">\n {tr('analyticsDashboard.tableViews', 'Views')}\n </th>\n <th className=\"text-right py-2 px-4 text-sm font-medium\">\n {tr('analyticsDashboard.tableCompletions', 'Completions')}\n </th>\n <th className=\"text-right py-2 px-4 text-sm font-medium\">\n {tr('analyticsDashboard.tableDropOffRate', 'Drop-off Rate')}\n </th>\n </tr>\n </thead>\n <tbody>\n {analytics.stepAnalytics.map((step) => (\n <tr key={step.stepId} className=\"border-b border-border\">\n <td className=\"py-2 px-4 text-sm\">\n {tr('analyticsDashboard.stepLabel', 'Step')} {step.stepOrder}\n </td>\n <td className=\"text-right py-2 px-4 text-sm\">{formatNumber(step.views)}</td>\n <td className=\"text-right py-2 px-4 text-sm\">\n {formatNumber(step.completions)}\n </td>\n <td className=\"text-right py-2 px-4 text-sm\">\n {formatPercentage(step.dropOffRate)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </div>\n )}\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AnalyticsDashboardPage.js","names":[],"sources":["../../../src/tours/pages/AnalyticsDashboardPage.tsx"],"sourcesContent":["import { useCallback, useEffect } from 'react';\nimport { useParams } from 'react-router-dom';\nimport { BarChart3, TrendingUp, Users, Timer } from 'lucide-react';\nimport { GlassCard, EmphasisPanel } from '@burdenoff/fe-libs/ui';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\nimport { useTourAnalytics } from '../hooks/useTourAnalytics';\nimport { useToursPermissionAccess } from '../hooks/useToursPermissionAccess';\nimport { TOUR_PERMISSIONS } from '../../constants/permissions';\nimport { formatNumber, formatPercentage, formatDuration } from '../utils';\n\n/**\n * Analytics dashboard page\n * Display tour performance metrics and charts\n */\nexport function AnalyticsDashboardPage() {\n const { t } = useI18n();\n const tr = useCallback(\n (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n },\n [t]\n );\n\n const { tourId } = useParams();\n const { hasPermission, isLoading: permissionsLoading } = useToursPermissionAccess();\n const canViewAnalytics = hasPermission(TOUR_PERMISSIONS.TOUR_ANALYTICS_READ);\n\n const { analytics, loading, fetchAnalytics, getCompletionRate, getSkipRate } =\n useTourAnalytics(tourId);\n\n useEffect(() => {\n if (permissionsLoading || !canViewAnalytics) return;\n fetchAnalytics();\n }, [fetchAnalytics, permissionsLoading, canViewAnalytics]);\n\n if (!permissionsLoading && !canViewAnalytics) {\n return (\n <div className=\"flex items-center justify-center h-full p-3 sm:p-6\">\n <div className=\"text-center\">\n <h2 className=\"text-lg font-semibold text-text-primary mb-2\">\n {tr('analyticsDashboard.accessDenied', 'Access Denied')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'analyticsDashboard.noPermission',\n 'You do not have permission to view tour analytics.'\n )}\n </p>\n </div>\n </div>\n );\n }\n\n if (loading) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <p className=\"text-text-secondary\">\n {tr('analyticsDashboard.loading', 'Loading analytics...')}\n </p>\n </div>\n );\n }\n\n if (!analytics) {\n return (\n <div className=\"flex items-center justify-center h-full\">\n <p className=\"text-text-secondary\">\n {tr('analyticsDashboard.noData', 'No analytics data available')}\n </p>\n </div>\n );\n }\n\n return (\n <div className=\"space-y-4 sm:space-y-6 p-3 sm:p-6\">\n {/* Header */}\n <div>\n <h1 className=\"text-xl sm:text-2xl font-bold\">\n {tr('analyticsDashboard.title', 'Tour Analytics')}\n </h1>\n <p className=\"text-sm text-text-secondary\">\n {tr('analyticsDashboard.subtitle', 'Performance metrics and insights')}\n </p>\n </div>\n\n {/* Summary Cards */}\n <div className=\"grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4\">\n <GlassCard treatment=\"glass\" className=\"p-4\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 bg-action-primary-bg/10 rounded-md\">\n <Users className=\"text-primary\" size={20} />\n </div>\n <span className=\"text-sm text-text-secondary\">\n {tr('analyticsDashboard.totalViews', 'Total Views')}\n </span>\n </div>\n <div className=\"text-2xl font-bold tabular-nums\">{formatNumber(analytics.totalViews)}</div>\n </GlassCard>\n\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 bg-success/10 rounded-md\">\n <TrendingUp className=\"text-success\" size={20} />\n </div>\n <span className=\"text-sm text-text-secondary\">\n {tr('analyticsDashboard.completionRate', 'Completion Rate')}\n </span>\n </div>\n <div className=\"text-2xl font-bold tabular-nums\">\n {formatPercentage(getCompletionRate())}\n </div>\n </GlassCard>\n\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 bg-warning/10 rounded-md\">\n <BarChart3 className=\"text-warning\" size={20} />\n </div>\n <span className=\"text-sm text-text-secondary\">\n {tr('analyticsDashboard.skipRate', 'Skip Rate')}\n </span>\n </div>\n <div className=\"text-2xl font-bold tabular-nums\">{formatPercentage(getSkipRate())}</div>\n </GlassCard>\n\n <GlassCard treatment=\"metric\" className=\"p-4\">\n <div className=\"flex items-center gap-3 mb-2\">\n <div className=\"p-2 bg-info/10 rounded-md\">\n <Timer className=\"text-info\" size={20} />\n </div>\n <span className=\"text-sm text-text-secondary\">\n {tr('analyticsDashboard.avgDuration', 'Avg Duration')}\n </span>\n </div>\n <div className=\"text-2xl font-bold tabular-nums\">\n {formatDuration(analytics.averageDuration)}\n </div>\n </GlassCard>\n </div>\n\n {/* Device Breakdown — the key soft-glow data-viz zone */}\n <EmphasisPanel className=\"p-4 sm:p-6\">\n <h2 className=\"text-lg font-semibold mb-4\">\n {tr('analyticsDashboard.deviceBreakdown', 'Device Breakdown')}\n </h2>\n <div className=\"space-y-3\">\n {analytics.byDeviceType.map((device) => (\n <div key={device.deviceType}>\n <div className=\"flex items-center justify-between mb-2\">\n <span className=\"text-sm capitalize\">{device.deviceType}</span>\n <span className=\"text-sm text-text-secondary tabular-nums\">\n {formatNumber(device.count)} views ({formatPercentage(device.completionRate)})\n </span>\n </div>\n <div className=\"w-full bg-bg-sunken rounded-full h-2\">\n <div\n className=\"bg-action-primary-bg h-2 rounded-full shadow-[var(--shadow-pop)]\"\n style={{ width: `${device.completionRate}%` }}\n />\n </div>\n </div>\n ))}\n </div>\n </EmphasisPanel>\n\n {/* Step Analytics */}\n {analytics.stepAnalytics && analytics.stepAnalytics.length > 0 && (\n <GlassCard treatment=\"plain\" className=\"p-4 sm:p-6 border-border-seam\">\n <h2 className=\"text-lg font-semibold mb-4\">\n {tr('analyticsDashboard.stepPerformance', 'Step Performance')}\n </h2>\n <div className=\"overflow-x-auto\">\n <table className=\"w-full\">\n <thead>\n <tr className=\"border-b border-border-seam\">\n <th className=\"text-left py-2 px-4 text-sm font-medium text-text-secondary\">\n {tr('analyticsDashboard.tableStep', 'Step')}\n </th>\n <th className=\"text-right py-2 px-4 text-sm font-medium text-text-secondary\">\n {tr('analyticsDashboard.tableViews', 'Views')}\n </th>\n <th className=\"text-right py-2 px-4 text-sm font-medium text-text-secondary\">\n {tr('analyticsDashboard.tableCompletions', 'Completions')}\n </th>\n <th className=\"text-right py-2 px-4 text-sm font-medium text-text-secondary\">\n {tr('analyticsDashboard.tableDropOffRate', 'Drop-off Rate')}\n </th>\n </tr>\n </thead>\n <tbody>\n {analytics.stepAnalytics.map((step) => (\n <tr key={step.stepId} className=\"border-b border-border-seam\">\n <td className=\"py-2 px-4 text-sm\">\n {tr('analyticsDashboard.stepLabel', 'Step')} {step.stepOrder}\n </td>\n <td className=\"text-right py-2 px-4 text-sm tabular-nums\">\n {formatNumber(step.views)}\n </td>\n <td className=\"text-right py-2 px-4 text-sm tabular-nums\">\n {formatNumber(step.completions)}\n </td>\n <td className=\"text-right py-2 px-4 text-sm tabular-nums\">\n {formatPercentage(step.dropOffRate)}\n </td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n </GlassCard>\n )}\n </div>\n );\n}\n"],"mappings":"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"}