@burdenoff/microfe-tours 2026.1014.1 → 2026.1114.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 (120) hide show
  1. package/dist/constants/featureFlags.d.ts +24 -0
  2. package/dist/constants/featureFlags.js +1 -2
  3. package/dist/constants/featureFlags.js.map +1 -1
  4. package/dist/constants/permissions.d.ts +48 -0
  5. package/dist/constants/permissions.js +1 -2
  6. package/dist/constants/permissions.js.map +1 -1
  7. package/dist/dev/main.d.ts +0 -0
  8. package/dist/generated/global-operations.d.ts +1318 -0
  9. package/dist/generated/global-operations.js +2 -3
  10. package/dist/generated/global-operations.js.map +1 -1
  11. package/dist/generated/global-types.d.ts +26469 -0
  12. package/dist/generated/wspace-operations.d.ts +1 -0
  13. package/dist/generated/wspace-types.d.ts +108647 -0
  14. package/dist/index.d.ts +5 -0
  15. package/dist/test/happydom.d.ts +1 -0
  16. package/dist/test/testing-library.d.ts +1 -0
  17. package/dist/tours/ToursAdminRoot.d.ts +14 -0
  18. package/dist/tours/ToursAdminRoot.js +14 -13
  19. package/dist/tours/ToursAdminRoot.js.map +1 -1
  20. package/dist/tours/ToursAdminRoutes.d.ts +11 -0
  21. package/dist/tours/ToursAdminRoutes.js +12 -13
  22. package/dist/tours/ToursAdminRoutes.js.map +1 -1
  23. package/dist/tours/ToursRoot.d.ts +9 -0
  24. package/dist/tours/ToursRoot.js +1 -1
  25. package/dist/tours/ToursUserRoot.d.ts +13 -0
  26. package/dist/tours/ToursUserRoot.js +14 -13
  27. package/dist/tours/ToursUserRoot.js.map +1 -1
  28. package/dist/tours/ToursUserRoutes.d.ts +14 -0
  29. package/dist/tours/ToursUserRoutes.js +13 -13
  30. package/dist/tours/ToursUserRoutes.js.map +1 -1
  31. package/dist/tours/components/MarkdownEditor.d.ts +12 -0
  32. package/dist/tours/components/MarkdownEditor.js +1 -2
  33. package/dist/tours/components/MarkdownEditor.js.map +1 -1
  34. package/dist/tours/components/RouteHelpers.d.ts +31 -0
  35. package/dist/tours/components/RouteHelpers.js +1 -2
  36. package/dist/tours/components/RouteHelpers.js.map +1 -1
  37. package/dist/tours/components/ToursAdminNavRegistrar.d.ts +23 -0
  38. package/dist/tours/components/ToursAdminNavRegistrar.js +4 -3
  39. package/dist/tours/components/ToursAdminNavRegistrar.js.map +1 -1
  40. package/dist/tours/hooks/useMediaLibrary.d.ts +20 -0
  41. package/dist/tours/hooks/useMediaLibrary.js +1 -2
  42. package/dist/tours/hooks/useMediaLibrary.js.map +1 -1
  43. package/dist/tours/hooks/useStepSequencer.d.ts +18 -0
  44. package/dist/tours/hooks/useStepSequencer.js +16 -13
  45. package/dist/tours/hooks/useStepSequencer.js.map +1 -1
  46. package/dist/tours/hooks/useTourAnalytics.d.ts +30 -0
  47. package/dist/tours/hooks/useTourAnalytics.js +1 -2
  48. package/dist/tours/hooks/useTourAnalytics.js.map +1 -1
  49. package/dist/tours/hooks/useTourBuilder.d.ts +59 -0
  50. package/dist/tours/hooks/useTourBuilder.js +10 -6
  51. package/dist/tours/hooks/useTourBuilder.js.map +1 -1
  52. package/dist/tours/hooks/useTourLifecycle.d.ts +21 -0
  53. package/dist/tours/hooks/useTourLifecycle.js +4 -5
  54. package/dist/tours/hooks/useTourLifecycle.js.map +1 -1
  55. package/dist/tours/hooks/useTourPreview.d.ts +31 -0
  56. package/dist/tours/hooks/useTourPreview.js +5 -6
  57. package/dist/tours/hooks/useTourPreview.js.map +1 -1
  58. package/dist/tours/hooks/useToursList.d.ts +15 -0
  59. package/dist/tours/hooks/useToursList.js +1 -2
  60. package/dist/tours/hooks/useToursList.js.map +1 -1
  61. package/dist/tours/hooks/useToursPermissionAccess.d.ts +15 -0
  62. package/dist/tours/hooks/useToursPermissionAccess.js +1 -2
  63. package/dist/tours/hooks/useToursPermissionAccess.js.map +1 -1
  64. package/dist/tours/i18n.d.ts +413 -0
  65. package/dist/tours/i18n.js +1 -2
  66. package/dist/tours/i18n.js.map +1 -1
  67. package/dist/tours/index.d.ts +21 -0
  68. package/dist/tours/index.js +6 -5
  69. package/dist/tours/pages/AnalyticsDashboardPage.d.ts +5 -0
  70. package/dist/tours/pages/AnalyticsDashboardPage.js +6 -7
  71. package/dist/tours/pages/AnalyticsDashboardPage.js.map +1 -1
  72. package/dist/tours/pages/MediaLibraryPage.d.ts +5 -0
  73. package/dist/tours/pages/MediaLibraryPage.js +10 -11
  74. package/dist/tours/pages/MediaLibraryPage.js.map +1 -1
  75. package/dist/tours/pages/MyToursPage.d.ts +10 -0
  76. package/dist/tours/pages/MyToursPage.js +5 -6
  77. package/dist/tours/pages/MyToursPage.js.map +1 -1
  78. package/dist/tours/pages/MyToursProgressPage.d.ts +5 -0
  79. package/dist/tours/pages/MyToursProgressPage.js +5 -6
  80. package/dist/tours/pages/MyToursProgressPage.js.map +1 -1
  81. package/dist/tours/pages/SchedulerPage.d.ts +5 -0
  82. package/dist/tours/pages/SchedulerPage.js +10 -11
  83. package/dist/tours/pages/SchedulerPage.js.map +1 -1
  84. package/dist/tours/pages/StepEditorPage.d.ts +6 -0
  85. package/dist/tours/pages/StepEditorPage.js +302 -309
  86. package/dist/tours/pages/StepEditorPage.js.map +1 -1
  87. package/dist/tours/pages/SystemAnalyticsPage.d.ts +5 -0
  88. package/dist/tours/pages/SystemAnalyticsPage.js +1 -2
  89. package/dist/tours/pages/SystemAnalyticsPage.js.map +1 -1
  90. package/dist/tours/pages/TemplateLibraryPage.d.ts +5 -0
  91. package/dist/tours/pages/TemplateLibraryPage.js +15 -16
  92. package/dist/tours/pages/TemplateLibraryPage.js.map +1 -1
  93. package/dist/tours/pages/TourBuilderPage.d.ts +1 -0
  94. package/dist/tours/pages/TourBuilderPage.js +273 -300
  95. package/dist/tours/pages/TourBuilderPage.js.map +1 -1
  96. package/dist/tours/pages/TourPreviewPage.d.ts +5 -0
  97. package/dist/tours/pages/TourPreviewPage.js +9 -10
  98. package/dist/tours/pages/TourPreviewPage.js.map +1 -1
  99. package/dist/tours/pages/ToursListPage.d.ts +5 -0
  100. package/dist/tours/pages/ToursListPage.js +157 -157
  101. package/dist/tours/pages/ToursListPage.js.map +1 -1
  102. package/dist/tours/providers/ToursProvider.d.ts +25 -0
  103. package/dist/tours/providers/ToursProvider.js +1 -2
  104. package/dist/tours/providers/ToursProvider.js.map +1 -1
  105. package/dist/tours/store/toursStore.d.ts +40 -0
  106. package/dist/tours/store/toursStore.js +5 -6
  107. package/dist/tours/store/toursStore.js.map +1 -1
  108. package/dist/tours/telemetry.d.ts +7 -0
  109. package/dist/tours/telemetry.js +1 -2
  110. package/dist/tours/telemetry.js.map +1 -1
  111. package/dist/tours/types/index.d.ts +348 -0
  112. package/dist/tours/utils/index.d.ts +28 -0
  113. package/dist/tours/utils/index.js +3 -4
  114. package/dist/tours/utils/index.js.map +1 -1
  115. package/dist/tours/utils/joinPath.d.ts +8 -0
  116. package/dist/tours/utils/joinPath.js +1 -2
  117. package/dist/tours/utils/joinPath.js.map +1 -1
  118. package/dist/utils/nativeBridge.d.ts +2 -0
  119. package/dist/utils/nativeBridge.js +1 -2
  120. package/package.json +62 -51
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Tours Microfrontend - Root Export
3
+ * Re-exports from the main tours module for build-time composition
4
+ */
5
+ export * from './tours';
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,14 @@
1
+ import { FC } from 'react';
2
+ import { ToursRootProps } from './types';
3
+ /**
4
+ * Admin-facing root component for the Tours microfrontend.
5
+ *
6
+ * Mounts the full authoring suite (CRUD, builder, step editor, media
7
+ * library, analytics, templates, scheduler, preview). Nav-item registration
8
+ * lives in `ToursAdminNavRegistrar` so it can consult feature flags from
9
+ * inside the provider subtree; only items whose flags are enabled are
10
+ * registered with the shell. Consumed by `admin-app`.
11
+ *
12
+ * Pair with `ToursUserRoot` for user shells.
13
+ */
14
+ export declare const ToursAdminRoot: FC<ToursRootProps>;
@@ -1,25 +1,27 @@
1
- import { useTr as e } from "./i18n.js";
2
- import { ToursAdminNavRegistrar as t } from "./components/ToursAdminNavRegistrar.js";
3
- import { ToursProvider as n } from "./providers/ToursProvider.js";
4
- import { useToursStore as r } from "./store/toursStore.js";
5
- import { ToursAdminRoutes as i } from "./ToursAdminRoutes.js";
1
+ import { ToursAdminNavRegistrar as e } from "./components/ToursAdminNavRegistrar.js";
2
+ import { ToursProvider as t } from "./providers/ToursProvider.js";
3
+ import { useToursStore as n } from "./store/toursStore.js";
4
+ import { ToursAdminRoutes as r } from "./ToursAdminRoutes.js";
5
+ import { useTr as i } from "./i18n.js";
6
6
  import { useEffect as a } from "react";
7
7
  import { ErrorBoundary as o } from "@burdenoff/fe-libs/shared/components";
8
8
  import { FeatureFlagProvider as s } from "@burdenoff/fe-libs/shared/feature-flags";
9
9
  import { jsx as c, jsxs as l } from "react/jsx-runtime";
10
- //#region src/tours/ToursAdminRoot.tsx
10
+ // src/tours/ToursAdminRoot.tsx
11
11
  var u = (u) => {
12
- let d = e(), { basePath: f = "/", registerNavItems: p, registerFooterItems: m, workspaceId: h } = u, g = r((e) => e.resetTransientState);
12
+ let d = i(), { basePath: f = "/", registerNavItems: p, registerFooterItems: m, workspaceId: h } = u, g = n((e) => e.resetTransientState);
13
13
  return a(() => () => {
14
14
  g();
15
15
  }, [g]), a(() => {
16
- if (m) return m([{
16
+ if (!m) return;
17
+ let e = [{
17
18
  id: "tours-status",
18
19
  label: d("toursShell.productToursReady", "Product Tours Ready"),
19
20
  type: "status",
20
21
  order: 10,
21
22
  section: "left"
22
- }]);
23
+ }];
24
+ return m(e);
23
25
  }, [m, d]), /* @__PURE__ */ c(s, {
24
26
  workspaceId: h ?? null,
25
27
  gateway: "global",
@@ -29,18 +31,17 @@ var u = (u) => {
29
31
  className: "p-6 text-center text-text-secondary",
30
32
  children: d("toursShell.moduleError", "Something went wrong loading this module. Please refresh the page.")
31
33
  }),
32
- children: [p && /* @__PURE__ */ c(t, {
34
+ children: [p && /* @__PURE__ */ c(e, {
33
35
  basePath: f,
34
36
  registerNavItems: p
35
- }), /* @__PURE__ */ c(n, {
37
+ }), /* @__PURE__ */ c(t, {
36
38
  ...u,
37
39
  variant: "admin",
38
- children: /* @__PURE__ */ c(i, {})
40
+ children: /* @__PURE__ */ c(r, {})
39
41
  })]
40
42
  })
41
43
  });
42
44
  };
43
- //#endregion
44
45
  export { u as ToursAdminRoot };
45
46
 
46
47
  //# sourceMappingURL=ToursAdminRoot.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ToursAdminRoot.js","names":[],"sources":["../../src/tours/ToursAdminRoot.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useEffect } from 'react';\nimport { ErrorBoundary } from '@burdenoff/fe-libs/shared/components';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { ToursAdminNavRegistrar } from './components/ToursAdminNavRegistrar';\nimport { ToursProvider } from './providers/ToursProvider';\nimport { useToursStore } from './store/toursStore';\nimport { ToursAdminRoutes } from './ToursAdminRoutes';\nimport { useTr } from './i18n';\nimport type { FooterItem, ToursRootProps } from './types';\n\n/**\n * Admin-facing root component for the Tours microfrontend.\n *\n * Mounts the full authoring suite (CRUD, builder, step editor, media\n * library, analytics, templates, scheduler, preview). Nav-item registration\n * lives in `ToursAdminNavRegistrar` so it can consult feature flags from\n * inside the provider subtree; only items whose flags are enabled are\n * registered with the shell. Consumed by `admin-app`.\n *\n * Pair with `ToursUserRoot` for user shells.\n */\nexport const ToursAdminRoot: FC<ToursRootProps> = (props) => {\n const tr = useTr();\n const { basePath = '/', registerNavItems, registerFooterItems, workspaceId } = props;\n const resetTransientState = useToursStore((state) => state.resetTransientState);\n\n useEffect(() => {\n // Reset only when the MFE unmounts. Including shell props here would run\n // cleanup on prop changes and could silently discard in-progress drafts.\n return () => {\n resetTransientState();\n };\n }, [resetTransientState]);\n\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const footerItems: FooterItem[] = [\n {\n id: 'tours-status',\n label: tr('toursShell.productToursReady', 'Product Tours Ready'),\n type: 'status',\n order: 10,\n section: 'left',\n },\n ];\n\n return registerFooterItems(footerItems);\n }, [registerFooterItems, tr]);\n\n return (\n <FeatureFlagProvider workspaceId={workspaceId ?? null} gateway=\"global\" defaultEnabled={false}>\n <ErrorBoundary\n fallback={\n <div className=\"p-6 text-center text-text-secondary\">\n {tr(\n 'toursShell.moduleError',\n 'Something went wrong loading this module. Please refresh the page.'\n )}\n </div>\n }\n >\n {registerNavItems && (\n <ToursAdminNavRegistrar basePath={basePath} registerNavItems={registerNavItems} />\n )}\n <ToursProvider {...props} variant=\"admin\">\n <ToursAdminRoutes />\n </ToursProvider>\n </ErrorBoundary>\n </FeatureFlagProvider>\n );\n};\n"],"mappings":";;;;;;;;;;AAsBA,IAAa,KAAsC,MAAU;CAC3D,IAAM,IAAK,GAAO,EACZ,EAAE,cAAW,KAAK,qBAAkB,wBAAqB,mBAAgB,GACzE,IAAsB,GAAe,MAAU,EAAM,oBAAoB;AA0B/E,QAxBA,cAGe;AACX,KAAqB;IAEtB,CAAC,EAAoB,CAAC,EAEzB,QAAgB;AACT,QAYL,QAAO,EAV2B,CAChC;GACE,IAAI;GACJ,OAAO,EAAG,gCAAgC,sBAAsB;GAChE,MAAM;GACN,OAAO;GACP,SAAS;GACV,CACF,CAEsC;IACtC,CAAC,GAAqB,EAAG,CAAC,EAG3B,kBAAC,GAAD;EAAqB,aAAa,KAAe;EAAM,SAAQ;EAAS,gBAAgB;YACtF,kBAAC,GAAD;GACE,UACE,kBAAC,OAAD;IAAK,WAAU;cACZ,EACC,0BACA,qEACD;IACG,CAAA;aAPV,CAUG,KACC,kBAAC,GAAD;IAAkC;IAA4B;IAAoB,CAAA,EAEpF,kBAAC,GAAD;IAAe,GAAI;IAAO,SAAQ;cAChC,kBAAC,GAAD,EAAoB,CAAA;IACN,CAAA,CACF;;EACI,CAAA"}
1
+ {"version":3,"file":"ToursAdminRoot.js","names":["ToursAdminRoot","tr","useTr","props","resetTransientState","useToursStore","state","useEffect","registerFooterItems","footerItems","FeatureFlagProvider","workspaceId","ErrorBoundary","registerNavItems","ToursAdminNavRegistrar","ToursProvider","ToursAdminRoutes"],"sources":["../../src/tours/ToursAdminRoot.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useEffect } from 'react';\nimport { ErrorBoundary } from '@burdenoff/fe-libs/shared/components';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { ToursAdminNavRegistrar } from './components/ToursAdminNavRegistrar';\nimport { ToursProvider } from './providers/ToursProvider';\nimport { useToursStore } from './store/toursStore';\nimport { ToursAdminRoutes } from './ToursAdminRoutes';\nimport { useTr } from './i18n';\nimport type { FooterItem, ToursRootProps } from './types';\n\n/**\n * Admin-facing root component for the Tours microfrontend.\n *\n * Mounts the full authoring suite (CRUD, builder, step editor, media\n * library, analytics, templates, scheduler, preview). Nav-item registration\n * lives in `ToursAdminNavRegistrar` so it can consult feature flags from\n * inside the provider subtree; only items whose flags are enabled are\n * registered with the shell. Consumed by `admin-app`.\n *\n * Pair with `ToursUserRoot` for user shells.\n */\nexport const ToursAdminRoot: FC<ToursRootProps> = (props) => {\n const tr = useTr();\n const { basePath = '/', registerNavItems, registerFooterItems, workspaceId } = props;\n const resetTransientState = useToursStore((state) => state.resetTransientState);\n\n useEffect(() => {\n // Reset only when the MFE unmounts. Including shell props here would run\n // cleanup on prop changes and could silently discard in-progress drafts.\n return () => {\n resetTransientState();\n };\n }, [resetTransientState]);\n\n useEffect(() => {\n if (!registerFooterItems) return;\n\n const footerItems: FooterItem[] = [\n {\n id: 'tours-status',\n label: tr('toursShell.productToursReady', 'Product Tours Ready'),\n type: 'status',\n order: 10,\n section: 'left',\n },\n ];\n\n return registerFooterItems(footerItems);\n }, [registerFooterItems, tr]);\n\n return (\n <FeatureFlagProvider workspaceId={workspaceId ?? null} gateway=\"global\" defaultEnabled={false}>\n <ErrorBoundary\n fallback={\n <div className=\"p-6 text-center text-text-secondary\">\n {tr(\n 'toursShell.moduleError',\n 'Something went wrong loading this module. Please refresh the page.'\n )}\n </div>\n }\n >\n {registerNavItems && (\n <ToursAdminNavRegistrar basePath={basePath} registerNavItems={registerNavItems} />\n )}\n <ToursProvider {...props} variant=\"admin\">\n <ToursAdminRoutes />\n </ToursProvider>\n </ErrorBoundary>\n </FeatureFlagProvider>\n );\n};\n"],"mappings":";;;;;;;;;;AAsBA,IAAaA,KAAsC,MAAU;CAC3D,IAAMC,IAAKC,EAAM,GACX,EAAE,cAAW,KAAK,qBAAkB,wBAAqB,mBAAgBC,GACzEC,IAAsBC,GAAe,MAAUC,EAAM,mBAAmB;CA0B9E,OAxBAC,cAGe;EACX,EAAoB;CACtB,GACC,CAACH,CAAmB,CAAC,GAExBG,QAAgB;EACd,IAAI,CAACC,GAAqB;EAE1B,IAAMC,IAA4B,CAChC;GACE,IAAI;GACJ,OAAOR,EAAG,gCAAgC,qBAAqB;GAC/D,MAAM;GACN,OAAO;GACP,SAAS;EACX,CACF;EAEA,OAAOO,EAAoBC,CAAW;CACxC,GAAG,CAACD,GAAqBP,CAAE,CAAC,GAG1B,kBAACS,GAAD;EAAqB,aAAaC,KAAe;EAAM,SAAQ;EAAS,gBAAgB;EACtF,UAAA,kBAACC,GAAD;GACE,UACE,kBAAC,OAAD;IAAK,WAAU;IACZ,UAAAX,EACC,0BACA,oEACF;GACG,CAAA;GAPT,UAAA,CAUGY,KACC,kBAACC,GAAD;IAAkC;IAA4B;GAAmB,CAAA,GAEnF,kBAACC,GAAD;IAAe,GAAIZ;IAAO,SAAQ;IAChC,UAAA,kBAACa,GAAD,CAAmB,CAAA;GACN,CAAA,CACF;;CACI,CAAA;AAEzB"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Admin-facing routes for the Tours microfrontend.
3
+ *
4
+ * Exposes the full authoring surface — CRUD, builder, step editor, media
5
+ * library, analytics, templates, scheduler, preview. Mounted by
6
+ * `ToursAdminRoot` from `admin-app`.
7
+ *
8
+ * `ToursRoutes` is kept as a back-compat alias for callers that haven't
9
+ * migrated yet.
10
+ */
11
+ export declare function ToursAdminRoutes(): import('react').JSX.Element;
@@ -1,29 +1,29 @@
1
1
  import { TOURS_FEATURES as e } from "../constants/featureFlags.js";
2
- import { useTr as t } from "./i18n.js";
3
- import { LazyErrorBoundary as n, PageLoader as r } from "./components/RouteHelpers.js";
2
+ import { LazyErrorBoundary as t, PageLoader as n } from "./components/RouteHelpers.js";
3
+ import { useTr as r } from "./i18n.js";
4
4
  import { ToursListPage as i } from "./pages/ToursListPage.js";
5
5
  import { Suspense as a, lazy as o, useEffect as s } from "react";
6
6
  import { FeatureGate as c } from "@burdenoff/fe-libs/shared/feature-flags";
7
7
  import { jsx as l, jsxs as u } from "react/jsx-runtime";
8
8
  import { Navigate as d, Route as f, Routes as p, useNavigate as m } from "react-router";
9
- //#region src/tours/ToursAdminRoutes.tsx
9
+ // src/tours/ToursAdminRoutes.tsx
10
10
  var h = o(() => import("./pages/TourBuilderPage.js").then((e) => ({ default: e.TourBuilderPage }))), g = o(() => import("./pages/StepEditorPage.js").then((e) => ({ default: e.StepEditorPage }))), _ = o(() => import("./pages/MediaLibraryPage.js").then((e) => ({ default: e.MediaLibraryPage }))), v = o(() => import("./pages/AnalyticsDashboardPage.js").then((e) => ({ default: e.AnalyticsDashboardPage }))), y = o(() => import("./pages/SystemAnalyticsPage.js").then((e) => ({ default: e.SystemAnalyticsPage }))), b = o(() => import("./pages/TemplateLibraryPage.js").then((e) => ({ default: e.TemplateLibraryPage }))), x = o(() => import("./pages/SchedulerPage.js").then((e) => ({ default: e.SchedulerPage }))), S = o(() => import("./pages/TourPreviewPage.js").then((e) => ({ default: e.TourPreviewPage }))), C = 2e3;
11
11
  function w({ redirectTo: e }) {
12
- let n = m(), r = t();
12
+ let t = m(), n = r();
13
13
  return s(() => {
14
- let t = setTimeout(() => {
15
- n(e, { replace: !0 });
14
+ let n = setTimeout(() => {
15
+ t(e, { replace: !0 });
16
16
  }, C);
17
- return () => clearTimeout(t);
18
- }, [n, e]), /* @__PURE__ */ l("div", {
17
+ return () => clearTimeout(n);
18
+ }, [t, e]), /* @__PURE__ */ l("div", {
19
19
  className: "flex h-full items-center justify-center p-8 text-center",
20
20
  role: "alert",
21
21
  children: /* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("h2", {
22
22
  className: "text-lg font-semibold mb-2 text-text-primary",
23
- children: r("featureDisabled.title", "Feature Not Available")
23
+ children: n("featureDisabled.title", "Feature Not Available")
24
24
  }), /* @__PURE__ */ l("p", {
25
25
  className: "text-sm text-text-secondary",
26
- children: r("featureDisabled.description", "This feature is not currently enabled. Redirecting you to the tours list…")
26
+ children: n("featureDisabled.description", "This feature is not currently enabled. Redirecting you to the tours list…")
27
27
  })] })
28
28
  });
29
29
  }
@@ -36,8 +36,8 @@ function T(e, t, { defaultValue: n = !1, redirectTo: r = "../list" } = {}) {
36
36
  });
37
37
  }
38
38
  function E() {
39
- return /* @__PURE__ */ l(n, { children: /* @__PURE__ */ l(a, {
40
- fallback: /* @__PURE__ */ l(r, {}),
39
+ return /* @__PURE__ */ l(t, { children: /* @__PURE__ */ l(a, {
40
+ fallback: /* @__PURE__ */ l(n, {}),
41
41
  children: /* @__PURE__ */ u(p, { children: [
42
42
  /* @__PURE__ */ l(f, {
43
43
  index: !0,
@@ -104,7 +104,6 @@ function E() {
104
104
  ] })
105
105
  }) });
106
106
  }
107
- //#endregion
108
107
  export { E as ToursAdminRoutes };
109
108
 
110
109
  //# sourceMappingURL=ToursAdminRoutes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ToursAdminRoutes.js","names":[],"sources":["../../src/tours/ToursAdminRoutes.tsx"],"sourcesContent":["import { lazy, Suspense, useEffect } from 'react';\nimport { Navigate, Route, Routes, useNavigate } from 'react-router';\nimport { FeatureGate } from '@burdenoff/fe-libs/shared/feature-flags';\n\nimport { TOURS_FEATURES, type ToursFeatureKey } from '../constants/featureFlags';\nimport { LazyErrorBoundary, PageLoader } from './components/RouteHelpers';\nimport { useTr } from './i18n';\n// Eager load - main entry point for the microfrontend\nimport { ToursListPage } from './pages/ToursListPage';\n\n// Lazy loaded pages - loaded on demand for better initial bundle size\nconst TourBuilderPage = lazy(() =>\n import('./pages/TourBuilderPage').then((m) => ({ default: m.TourBuilderPage }))\n);\nconst StepEditorPage = lazy(() =>\n import('./pages/StepEditorPage').then((m) => ({ default: m.StepEditorPage }))\n);\nconst MediaLibraryPage = lazy(() =>\n import('./pages/MediaLibraryPage').then((m) => ({ default: m.MediaLibraryPage }))\n);\nconst AnalyticsDashboardPage = lazy(() =>\n import('./pages/AnalyticsDashboardPage').then((m) => ({ default: m.AnalyticsDashboardPage }))\n);\nconst SystemAnalyticsPage = lazy(() =>\n import('./pages/SystemAnalyticsPage').then((m) => ({ default: m.SystemAnalyticsPage }))\n);\nconst TemplateLibraryPage = lazy(() =>\n import('./pages/TemplateLibraryPage').then((m) => ({ default: m.TemplateLibraryPage }))\n);\nconst SchedulerPage = lazy(() =>\n import('./pages/SchedulerPage').then((m) => ({ default: m.SchedulerPage }))\n);\nconst TourPreviewPage = lazy(() =>\n import('./pages/TourPreviewPage').then((m) => ({ default: m.TourPreviewPage }))\n);\n\n/**\n * Fallback shown when a feature-gated route is disabled. Auto-redirects to\n * the given path after a short delay so users are never stranded on a dead\n * screen when an admin flips a flag off mid-session. The visible message is\n * kept for ~2s so screen readers reliably process the `role=\"alert\"` live\n * region before the navigation fires (NVDA/JAWS typically need 1–2 seconds\n * to announce a live region, especially if the user is mid-interaction).\n */\nconst FEATURE_DISABLED_REDIRECT_DELAY_MS = 2000;\n\nfunction FeatureDisabledFallback({ redirectTo }: { redirectTo: string }) {\n const navigate = useNavigate();\n const tr = useTr();\n\n useEffect(() => {\n const timer = setTimeout(() => {\n navigate(redirectTo, { replace: true });\n }, FEATURE_DISABLED_REDIRECT_DELAY_MS);\n return () => clearTimeout(timer);\n }, [navigate, redirectTo]);\n\n return (\n <div className=\"flex h-full items-center justify-center p-8 text-center\" role=\"alert\">\n <div>\n <h2 className=\"text-lg font-semibold mb-2 text-text-primary\">\n {tr('featureDisabled.title', 'Feature Not Available')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'featureDisabled.description',\n 'This feature is not currently enabled. Redirecting you to the tours list…'\n )}\n </p>\n </div>\n </div>\n );\n}\n\n/**\n * Helper: wraps a page element in a FeatureGate.\n *\n * defaultValue=false aligns with the backend seed (`defaultValue: false,\n * previewable: true`) — when a flag can't be resolved (missing from seed or\n * gateway unreachable), the feature stays hidden rather than leaking an\n * unreleased surface to users.\n *\n * `redirectTo` is resolved relative to the gated route's URL. All current\n * gated routes sit one level below the tours basePath, so `../list` lands\n * on `/<basePath>/list` (the default admin landing page).\n */\nfunction gated(\n flag: ToursFeatureKey,\n element: React.ReactNode,\n {\n defaultValue = false,\n redirectTo = '../list',\n }: { defaultValue?: boolean; redirectTo?: string } = {}\n): React.ReactElement {\n return (\n <FeatureGate\n flag={flag}\n defaultValue={defaultValue}\n fallback={<FeatureDisabledFallback redirectTo={redirectTo} />}\n >\n {element}\n </FeatureGate>\n );\n}\n\n/**\n * Admin-facing routes for the Tours microfrontend.\n *\n * Exposes the full authoring surface — CRUD, builder, step editor, media\n * library, analytics, templates, scheduler, preview. Mounted by\n * `ToursAdminRoot` from `admin-app`.\n *\n * `ToursRoutes` is kept as a back-compat alias for callers that haven't\n * migrated yet.\n */\nexport function ToursAdminRoutes() {\n return (\n <LazyErrorBoundary>\n <Suspense fallback={<PageLoader />}>\n <Routes>\n {/* Default redirect */}\n <Route index element={<Navigate to=\"list\" replace />} />\n\n {/* Tour List - Eager loaded (main entry point) */}\n <Route path=\"list\" element={<ToursListPage />} />\n\n {/* Tour Builder - Lazy loaded */}\n <Route path=\"new\" element={<TourBuilderPage />} />\n <Route path=\":tourId/edit\" element={<TourBuilderPage />} />\n\n {/* Step Editor - Lazy loaded */}\n <Route path=\":tourId/steps\" element={<StepEditorPage />} />\n <Route path=\":tourId/steps/:stepId\" element={<StepEditorPage />} />\n\n {/* Media Library - gated by tours.media (feature is Partial: upload storage URL is hardcoded, real pipeline pending) */}\n <Route path=\"media\" element={gated(TOURS_FEATURES.MEDIA, <MediaLibraryPage />)} />\n\n {/* System Analytics - un-gated (feature is 100% E2E complete) */}\n <Route path=\"analytics\" element={<SystemAnalyticsPage />} />\n\n {/* Per-tour Analytics - un-gated (feature is 100% E2E complete) */}\n <Route path=\":tourId/analytics\" element={<AnalyticsDashboardPage />} />\n\n {/* Templates - un-gated (feature is 100% E2E complete) */}\n <Route path=\"templates\" element={<TemplateLibraryPage />} />\n <Route path=\"templates/:templateId\" element={<TemplateLibraryPage />} />\n\n {/* Scheduler - un-gated (feature is 100% E2E complete) */}\n <Route path=\":tourId/schedule\" element={<SchedulerPage />} />\n\n {/* Preview - Lazy loaded (not gated) */}\n <Route path=\":tourId/preview\" element={<TourPreviewPage />} />\n\n {/* Catch-all route - redirect to list */}\n <Route path=\"*\" element={<Navigate to=\"list\" replace />} />\n </Routes>\n </Suspense>\n </LazyErrorBoundary>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"ToursAdminRoutes.js","names":["TourBuilderPage","lazy","m","StepEditorPage","MediaLibraryPage","AnalyticsDashboardPage","SystemAnalyticsPage","TemplateLibraryPage","SchedulerPage","TourPreviewPage","FEATURE_DISABLED_REDIRECT_DELAY_MS","FeatureDisabledFallback","navigate","useNavigate","tr","useTr","useEffect","timer","redirectTo","gated","FeatureGate","element","ToursAdminRoutes","LazyErrorBoundary","Suspense","PageLoader","Routes","Route","Navigate","ToursListPage","TOURS_FEATURES"],"sources":["../../src/tours/ToursAdminRoutes.tsx"],"sourcesContent":["import { lazy, Suspense, useEffect } from 'react';\nimport { Navigate, Route, Routes, useNavigate } from 'react-router';\nimport { FeatureGate } from '@burdenoff/fe-libs/shared/feature-flags';\n\nimport { TOURS_FEATURES, type ToursFeatureKey } from '../constants/featureFlags';\nimport { LazyErrorBoundary, PageLoader } from './components/RouteHelpers';\nimport { useTr } from './i18n';\n// Eager load - main entry point for the microfrontend\nimport { ToursListPage } from './pages/ToursListPage';\n\n// Lazy loaded pages - loaded on demand for better initial bundle size\nconst TourBuilderPage = lazy(() =>\n import('./pages/TourBuilderPage').then((m) => ({ default: m.TourBuilderPage }))\n);\nconst StepEditorPage = lazy(() =>\n import('./pages/StepEditorPage').then((m) => ({ default: m.StepEditorPage }))\n);\nconst MediaLibraryPage = lazy(() =>\n import('./pages/MediaLibraryPage').then((m) => ({ default: m.MediaLibraryPage }))\n);\nconst AnalyticsDashboardPage = lazy(() =>\n import('./pages/AnalyticsDashboardPage').then((m) => ({ default: m.AnalyticsDashboardPage }))\n);\nconst SystemAnalyticsPage = lazy(() =>\n import('./pages/SystemAnalyticsPage').then((m) => ({ default: m.SystemAnalyticsPage }))\n);\nconst TemplateLibraryPage = lazy(() =>\n import('./pages/TemplateLibraryPage').then((m) => ({ default: m.TemplateLibraryPage }))\n);\nconst SchedulerPage = lazy(() =>\n import('./pages/SchedulerPage').then((m) => ({ default: m.SchedulerPage }))\n);\nconst TourPreviewPage = lazy(() =>\n import('./pages/TourPreviewPage').then((m) => ({ default: m.TourPreviewPage }))\n);\n\n/**\n * Fallback shown when a feature-gated route is disabled. Auto-redirects to\n * the given path after a short delay so users are never stranded on a dead\n * screen when an admin flips a flag off mid-session. The visible message is\n * kept for ~2s so screen readers reliably process the `role=\"alert\"` live\n * region before the navigation fires (NVDA/JAWS typically need 1–2 seconds\n * to announce a live region, especially if the user is mid-interaction).\n */\nconst FEATURE_DISABLED_REDIRECT_DELAY_MS = 2000;\n\nfunction FeatureDisabledFallback({ redirectTo }: { redirectTo: string }) {\n const navigate = useNavigate();\n const tr = useTr();\n\n useEffect(() => {\n const timer = setTimeout(() => {\n navigate(redirectTo, { replace: true });\n }, FEATURE_DISABLED_REDIRECT_DELAY_MS);\n return () => clearTimeout(timer);\n }, [navigate, redirectTo]);\n\n return (\n <div className=\"flex h-full items-center justify-center p-8 text-center\" role=\"alert\">\n <div>\n <h2 className=\"text-lg font-semibold mb-2 text-text-primary\">\n {tr('featureDisabled.title', 'Feature Not Available')}\n </h2>\n <p className=\"text-sm text-text-secondary\">\n {tr(\n 'featureDisabled.description',\n 'This feature is not currently enabled. Redirecting you to the tours list…'\n )}\n </p>\n </div>\n </div>\n );\n}\n\n/**\n * Helper: wraps a page element in a FeatureGate.\n *\n * defaultValue=false aligns with the backend seed (`defaultValue: false,\n * previewable: true`) — when a flag can't be resolved (missing from seed or\n * gateway unreachable), the feature stays hidden rather than leaking an\n * unreleased surface to users.\n *\n * `redirectTo` is resolved relative to the gated route's URL. All current\n * gated routes sit one level below the tours basePath, so `../list` lands\n * on `/<basePath>/list` (the default admin landing page).\n */\nfunction gated(\n flag: ToursFeatureKey,\n element: React.ReactNode,\n {\n defaultValue = false,\n redirectTo = '../list',\n }: { defaultValue?: boolean; redirectTo?: string } = {}\n): React.ReactElement {\n return (\n <FeatureGate\n flag={flag}\n defaultValue={defaultValue}\n fallback={<FeatureDisabledFallback redirectTo={redirectTo} />}\n >\n {element}\n </FeatureGate>\n );\n}\n\n/**\n * Admin-facing routes for the Tours microfrontend.\n *\n * Exposes the full authoring surface — CRUD, builder, step editor, media\n * library, analytics, templates, scheduler, preview. Mounted by\n * `ToursAdminRoot` from `admin-app`.\n *\n * `ToursRoutes` is kept as a back-compat alias for callers that haven't\n * migrated yet.\n */\nexport function ToursAdminRoutes() {\n return (\n <LazyErrorBoundary>\n <Suspense fallback={<PageLoader />}>\n <Routes>\n {/* Default redirect */}\n <Route index element={<Navigate to=\"list\" replace />} />\n\n {/* Tour List - Eager loaded (main entry point) */}\n <Route path=\"list\" element={<ToursListPage />} />\n\n {/* Tour Builder - Lazy loaded */}\n <Route path=\"new\" element={<TourBuilderPage />} />\n <Route path=\":tourId/edit\" element={<TourBuilderPage />} />\n\n {/* Step Editor - Lazy loaded */}\n <Route path=\":tourId/steps\" element={<StepEditorPage />} />\n <Route path=\":tourId/steps/:stepId\" element={<StepEditorPage />} />\n\n {/* Media Library - gated by tours.media (feature is Partial: upload storage URL is hardcoded, real pipeline pending) */}\n <Route path=\"media\" element={gated(TOURS_FEATURES.MEDIA, <MediaLibraryPage />)} />\n\n {/* System Analytics - un-gated (feature is 100% E2E complete) */}\n <Route path=\"analytics\" element={<SystemAnalyticsPage />} />\n\n {/* Per-tour Analytics - un-gated (feature is 100% E2E complete) */}\n <Route path=\":tourId/analytics\" element={<AnalyticsDashboardPage />} />\n\n {/* Templates - un-gated (feature is 100% E2E complete) */}\n <Route path=\"templates\" element={<TemplateLibraryPage />} />\n <Route path=\"templates/:templateId\" element={<TemplateLibraryPage />} />\n\n {/* Scheduler - un-gated (feature is 100% E2E complete) */}\n <Route path=\":tourId/schedule\" element={<SchedulerPage />} />\n\n {/* Preview - Lazy loaded (not gated) */}\n <Route path=\":tourId/preview\" element={<TourPreviewPage />} />\n\n {/* Catch-all route - redirect to list */}\n <Route path=\"*\" element={<Navigate to=\"list\" replace />} />\n </Routes>\n </Suspense>\n </LazyErrorBoundary>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Back-compat alias. Use `ToursAdminRoot` directly in new code.
3
+ *
4
+ * `ToursRoot` historically rendered the admin authoring surface; admin-app
5
+ * (and any other shell that imported it) keeps working while we migrate
6
+ * callers to the explicit `ToursAdminRoot` / `ToursUserRoot` naming
7
+ * introduced in BOFF-2391.
8
+ */
9
+ export { ToursAdminRoot as ToursRoot } from './ToursAdminRoot';
@@ -1 +1 @@
1
- import "./ToursAdminRoot.js";
1
+ export { ToursAdminRoot as ToursRoot } from "./ToursAdminRoot.js";
@@ -0,0 +1,13 @@
1
+ import { FC } from 'react';
2
+ import { ToursRootProps } from './types';
3
+ /**
4
+ * User-facing root component for the Tours microfrontend.
5
+ *
6
+ * Mounts the read-only "My Tours" + "My Progress" surface for product
7
+ * user shells (vibecontrols-app, and later workspaces-app / bigconsole-app).
8
+ * Registers exactly two nav items; never exposes authoring or governance
9
+ * pages.
10
+ *
11
+ * Pair with `ToursAdminRoot` for the admin shell.
12
+ */
13
+ export declare const ToursUserRoot: FC<ToursRootProps>;
@@ -1,20 +1,21 @@
1
- import { joinPath as e } from "./utils/joinPath.js";
2
- import { useTr as t } from "./i18n.js";
3
- import { ToursProvider as n } from "./providers/ToursProvider.js";
4
- import { useToursStore as r } from "./store/toursStore.js";
5
- import { ToursUserRoutes as i } from "./ToursUserRoutes.js";
1
+ import { ToursProvider as e } from "./providers/ToursProvider.js";
2
+ import { useToursStore as t } from "./store/toursStore.js";
3
+ import { ToursUserRoutes as n } from "./ToursUserRoutes.js";
4
+ import { useTr as r } from "./i18n.js";
5
+ import { joinPath as i } from "./utils/joinPath.js";
6
6
  import { useEffect as a } from "react";
7
7
  import { ErrorBoundary as o } from "@burdenoff/fe-libs/shared/components";
8
8
  import { FeatureFlagProvider as s } from "@burdenoff/fe-libs/shared/feature-flags";
9
9
  import { Compass as c, ListChecks as l } from "lucide-react";
10
10
  import { jsx as u } from "react/jsx-runtime";
11
- //#region src/tours/ToursUserRoot.tsx
11
+ // src/tours/ToursUserRoot.tsx
12
12
  var d = (d) => {
13
- let f = t(), { basePath: p = "/", registerNavItems: m, workspaceId: h } = d, g = r((e) => e.resetTransientState);
13
+ let f = r(), { basePath: p = "/", registerNavItems: m, workspaceId: h } = d, g = t((e) => e.resetTransientState);
14
14
  return a(() => () => {
15
15
  g();
16
16
  }, [g]), a(() => {
17
- if (m) return m([{
17
+ if (!m) return;
18
+ let e = [{
18
19
  id: "tours-my",
19
20
  label: f("myTours.title", "My Tours"),
20
21
  path: p,
@@ -24,11 +25,12 @@ var d = (d) => {
24
25
  }, {
25
26
  id: "tours-my-progress",
26
27
  label: f("myToursProgress.title", "My Progress"),
27
- path: e(p, "progress"),
28
+ path: i(p, "progress"),
28
29
  icon: /* @__PURE__ */ u(l, { size: 20 }),
29
30
  order: 20,
30
31
  group: "tours"
31
- }]);
32
+ }];
33
+ return m(e);
32
34
  }, [
33
35
  m,
34
36
  p,
@@ -42,15 +44,14 @@ var d = (d) => {
42
44
  className: "p-6 text-center text-text-secondary",
43
45
  children: f("toursShell.moduleError", "Something went wrong loading this module. Please refresh the page.")
44
46
  }),
45
- children: /* @__PURE__ */ u(n, {
47
+ children: /* @__PURE__ */ u(e, {
46
48
  ...d,
47
49
  variant: "user",
48
- children: /* @__PURE__ */ u(i, {}, `${d.currentUser?.id ?? "anonymous"}:${h ?? "global"}`)
50
+ children: /* @__PURE__ */ u(n, {}, `${d.currentUser?.id ?? "anonymous"}:${h ?? "global"}`)
49
51
  })
50
52
  })
51
53
  });
52
54
  };
53
- //#endregion
54
55
  export { d as ToursUserRoot };
55
56
 
56
57
  //# sourceMappingURL=ToursUserRoot.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ToursUserRoot.js","names":[],"sources":["../../src/tours/ToursUserRoot.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useEffect } from 'react';\nimport { ErrorBoundary } from '@burdenoff/fe-libs/shared/components';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { Compass, ListChecks } from 'lucide-react';\nimport { ToursProvider } from './providers/ToursProvider';\nimport { useToursStore } from './store/toursStore';\nimport { ToursUserRoutes } from './ToursUserRoutes';\nimport { useTr } from './i18n';\nimport type { NavItem, ToursRootProps } from './types';\nimport { joinPath } from './utils/joinPath';\n\n/**\n * User-facing root component for the Tours microfrontend.\n *\n * Mounts the read-only \"My Tours\" + \"My Progress\" surface for product\n * user shells (vibecontrols-app, and later workspaces-app / bigconsole-app).\n * Registers exactly two nav items; never exposes authoring or governance\n * pages.\n *\n * Pair with `ToursAdminRoot` for the admin shell.\n */\nexport const ToursUserRoot: FC<ToursRootProps> = (props) => {\n const tr = useTr();\n const { basePath = '/', registerNavItems, workspaceId } = props;\n const resetTransientState = useToursStore((state) => state.resetTransientState);\n\n useEffect(() => {\n return () => {\n resetTransientState();\n };\n }, [resetTransientState]);\n\n useEffect(() => {\n if (!registerNavItems) return;\n\n const navItems: NavItem[] = [\n {\n id: 'tours-my',\n label: tr('myTours.title', 'My Tours'),\n path: basePath,\n icon: <Compass size={20} />,\n order: 10,\n group: 'tours',\n },\n {\n id: 'tours-my-progress',\n label: tr('myToursProgress.title', 'My Progress'),\n path: joinPath(basePath, 'progress'),\n icon: <ListChecks size={20} />,\n order: 20,\n group: 'tours',\n },\n ];\n\n return registerNavItems(navItems);\n }, [registerNavItems, basePath, tr]);\n\n return (\n <FeatureFlagProvider workspaceId={workspaceId ?? null} gateway=\"global\" defaultEnabled={true}>\n <ErrorBoundary\n fallback={\n <div className=\"p-6 text-center text-text-secondary\">\n {tr(\n 'toursShell.moduleError',\n 'Something went wrong loading this module. Please refresh the page.'\n )}\n </div>\n }\n >\n <ToursProvider {...props} variant=\"user\">\n <ToursUserRoutes\n key={`${props.currentUser?.id ?? 'anonymous'}:${workspaceId ?? 'global'}`}\n />\n </ToursProvider>\n </ErrorBoundary>\n </FeatureFlagProvider>\n );\n};\n"],"mappings":";;;;;;;;;;;AAsBA,IAAa,KAAqC,MAAU;CAC1D,IAAM,IAAK,GAAO,EACZ,EAAE,cAAW,KAAK,qBAAkB,mBAAgB,GACpD,IAAsB,GAAe,MAAU,EAAM,oBAAoB;AAiC/E,QA/BA,cACe;AACX,KAAqB;IAEtB,CAAC,EAAoB,CAAC,EAEzB,QAAgB;AACT,QAqBL,QAAO,EAnBqB,CAC1B;GACE,IAAI;GACJ,OAAO,EAAG,iBAAiB,WAAW;GACtC,MAAM;GACN,MAAM,kBAAC,GAAD,EAAS,MAAM,IAAM,CAAA;GAC3B,OAAO;GACP,OAAO;GACR,EACD;GACE,IAAI;GACJ,OAAO,EAAG,yBAAyB,cAAc;GACjD,MAAM,EAAS,GAAU,WAAW;GACpC,MAAM,kBAAC,GAAD,EAAY,MAAM,IAAM,CAAA;GAC9B,OAAO;GACP,OAAO;GACR,CACF,CAEgC;IAChC;EAAC;EAAkB;EAAU;EAAG,CAAC,EAGlC,kBAAC,GAAD;EAAqB,aAAa,KAAe;EAAM,SAAQ;EAAS,gBAAgB;YACtF,kBAAC,GAAD;GACE,UACE,kBAAC,OAAD;IAAK,WAAU;cACZ,EACC,0BACA,qEACD;IACG,CAAA;aAGR,kBAAC,GAAD;IAAe,GAAI;IAAO,SAAQ;cAChC,kBAAC,GAAD,EAEE,EADK,GAAG,EAAM,aAAa,MAAM,YAAY,GAAG,KAAe,WAC/D;IACY,CAAA;GACF,CAAA;EACI,CAAA"}
1
+ {"version":3,"file":"ToursUserRoot.js","names":["ToursUserRoot","tr","useTr","props","resetTransientState","useToursStore","state","useEffect","registerNavItems","navItems","basePath","Compass","joinPath","ListChecks","FeatureFlagProvider","workspaceId","ErrorBoundary","ToursProvider","ToursUserRoutes"],"sources":["../../src/tours/ToursUserRoot.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useEffect } from 'react';\nimport { ErrorBoundary } from '@burdenoff/fe-libs/shared/components';\nimport { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { Compass, ListChecks } from 'lucide-react';\nimport { ToursProvider } from './providers/ToursProvider';\nimport { useToursStore } from './store/toursStore';\nimport { ToursUserRoutes } from './ToursUserRoutes';\nimport { useTr } from './i18n';\nimport type { NavItem, ToursRootProps } from './types';\nimport { joinPath } from './utils/joinPath';\n\n/**\n * User-facing root component for the Tours microfrontend.\n *\n * Mounts the read-only \"My Tours\" + \"My Progress\" surface for product\n * user shells (vibecontrols-app, and later workspaces-app / bigconsole-app).\n * Registers exactly two nav items; never exposes authoring or governance\n * pages.\n *\n * Pair with `ToursAdminRoot` for the admin shell.\n */\nexport const ToursUserRoot: FC<ToursRootProps> = (props) => {\n const tr = useTr();\n const { basePath = '/', registerNavItems, workspaceId } = props;\n const resetTransientState = useToursStore((state) => state.resetTransientState);\n\n useEffect(() => {\n return () => {\n resetTransientState();\n };\n }, [resetTransientState]);\n\n useEffect(() => {\n if (!registerNavItems) return;\n\n const navItems: NavItem[] = [\n {\n id: 'tours-my',\n label: tr('myTours.title', 'My Tours'),\n path: basePath,\n icon: <Compass size={20} />,\n order: 10,\n group: 'tours',\n },\n {\n id: 'tours-my-progress',\n label: tr('myToursProgress.title', 'My Progress'),\n path: joinPath(basePath, 'progress'),\n icon: <ListChecks size={20} />,\n order: 20,\n group: 'tours',\n },\n ];\n\n return registerNavItems(navItems);\n }, [registerNavItems, basePath, tr]);\n\n return (\n <FeatureFlagProvider workspaceId={workspaceId ?? null} gateway=\"global\" defaultEnabled={true}>\n <ErrorBoundary\n fallback={\n <div className=\"p-6 text-center text-text-secondary\">\n {tr(\n 'toursShell.moduleError',\n 'Something went wrong loading this module. Please refresh the page.'\n )}\n </div>\n }\n >\n <ToursProvider {...props} variant=\"user\">\n <ToursUserRoutes\n key={`${props.currentUser?.id ?? 'anonymous'}:${workspaceId ?? 'global'}`}\n />\n </ToursProvider>\n </ErrorBoundary>\n </FeatureFlagProvider>\n );\n};\n"],"mappings":";;;;;;;;;;;AAsBA,IAAaA,KAAqC,MAAU;CAC1D,IAAMC,IAAKC,EAAM,GACX,EAAE,cAAW,KAAK,qBAAkB,mBAAgBC,GACpDC,IAAsBC,GAAe,MAAUC,EAAM,mBAAmB;CAiC9E,OA/BAC,cACe;EACX,EAAoB;CACtB,GACC,CAACH,CAAmB,CAAC,GAExBG,QAAgB;EACd,IAAI,CAACC,GAAkB;EAEvB,IAAMC,IAAsB,CAC1B;GACE,IAAI;GACJ,OAAOR,EAAG,iBAAiB,UAAU;GACrC,MAAMS;GACN,MAAM,kBAACC,GAAD,EAAS,MAAM,GAAK,CAAA;GAC1B,OAAO;GACP,OAAO;EACT,GACA;GACE,IAAI;GACJ,OAAOV,EAAG,yBAAyB,aAAa;GAChD,MAAMW,EAASF,GAAU,UAAU;GACnC,MAAM,kBAACG,GAAD,EAAY,MAAM,GAAK,CAAA;GAC7B,OAAO;GACP,OAAO;EACT,CACF;EAEA,OAAOL,EAAiBC,CAAQ;CAClC,GAAG;EAACD;EAAkBE;EAAUT;CAAE,CAAC,GAGjC,kBAACa,GAAD;EAAqB,aAAaC,KAAe;EAAM,SAAQ;EAAS,gBAAgB;EACtF,UAAA,kBAACC,GAAD;GACE,UACE,kBAAC,OAAD;IAAK,WAAU;IACZ,UAAAf,EACC,0BACA,oEACF;GACG,CAAA;GAGP,UAAA,kBAACgB,GAAD;IAAe,GAAId;IAAO,SAAQ;IAChC,UAAA,kBAACe,GAAD,CAEC,GADM,GAAGf,EAAM,aAAa,MAAM,YAAY,GAAGY,KAAe,UAChE;GACY,CAAA;EACF,CAAA;CACI,CAAA;AAEzB"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * User-facing routes for the Tours microfrontend.
3
+ *
4
+ * Exposes a tight surface — browse available tours and review own progress.
5
+ * Tour playback itself is handled by the SDK overlay, not by routed pages.
6
+ *
7
+ * Routes (relative to basePath, e.g. `/tours`):
8
+ * - `/` → MyToursPage (browse + status)
9
+ * - `/progress` → MyToursProgressPage (own progress, grouped)
10
+ * - `*` → UserRouteNotFound, then absolute navigate to basePath
11
+ * (collapses any admin-style URL like `/new`, `/media`,
12
+ * `/templates` after briefly signalling unavailability)
13
+ */
14
+ export declare function ToursUserRoutes(): import('react').JSX.Element;
@@ -1,27 +1,27 @@
1
1
  import { TOURS_FEATURES as e } from "../constants/featureFlags.js";
2
- import { useToursI18n as t, useTr as n } from "./i18n.js";
2
+ import { LazyErrorBoundary as t, PageLoader as n } from "./components/RouteHelpers.js";
3
3
  import { useToursContext as r } from "./providers/ToursProvider.js";
4
- import { LazyErrorBoundary as i, PageLoader as a } from "./components/RouteHelpers.js";
4
+ import { useToursI18n as i, useTr as a } from "./i18n.js";
5
5
  import { MyToursPage as o } from "./pages/MyToursPage.js";
6
6
  import { Suspense as s, lazy as c, useEffect as l } from "react";
7
7
  import { FeatureGate as u } from "@burdenoff/fe-libs/shared/feature-flags";
8
8
  import { jsx as d, jsxs as f } from "react/jsx-runtime";
9
9
  import { Route as p, Routes as m, useNavigate as h } from "react-router";
10
- //#region src/tours/ToursUserRoutes.tsx
10
+ // src/tours/ToursUserRoutes.tsx
11
11
  var g = c(() => import("./pages/MyToursProgressPage.js").then((e) => ({ default: e.MyToursProgressPage })));
12
12
  function _() {
13
- let e = h(), { tr: n } = t(), { basePath: i } = r();
13
+ let e = h(), { tr: t } = i(), { basePath: n } = r();
14
14
  return l(() => {
15
- let t = i && i.length > 0 ? i : "/", n = window.setTimeout(() => e(t, { replace: !0 }), 1500);
16
- return () => window.clearTimeout(n);
17
- }, [e, i]), /* @__PURE__ */ f("div", {
15
+ let t = n && n.length > 0 ? n : "/", r = window.setTimeout(() => e(t, { replace: !0 }), 1500);
16
+ return () => window.clearTimeout(r);
17
+ }, [e, n]), /* @__PURE__ */ f("div", {
18
18
  className: "flex flex-col items-center justify-center min-h-[300px] text-center p-6 gap-2",
19
19
  children: [/* @__PURE__ */ d("h2", {
20
20
  className: "text-lg font-semibold text-text-primary",
21
- children: n("myTours.notFoundTitle")
21
+ children: t("myTours.notFoundTitle")
22
22
  }), /* @__PURE__ */ d("p", {
23
23
  className: "text-sm text-text-secondary max-w-md",
24
- children: n("myTours.notFoundBody")
24
+ children: t("myTours.notFoundBody")
25
25
  })]
26
26
  });
27
27
  }
@@ -30,8 +30,8 @@ function v() {
30
30
  flag: e.USER,
31
31
  defaultValue: !0,
32
32
  fallback: /* @__PURE__ */ d(y, {}),
33
- children: /* @__PURE__ */ d(i, { children: /* @__PURE__ */ d(s, {
34
- fallback: /* @__PURE__ */ d(a, {}),
33
+ children: /* @__PURE__ */ d(t, { children: /* @__PURE__ */ d(s, {
34
+ fallback: /* @__PURE__ */ d(n, {}),
35
35
  children: /* @__PURE__ */ f(m, { children: [
36
36
  /* @__PURE__ */ d(p, {
37
37
  index: !0,
@@ -50,15 +50,15 @@ function v() {
50
50
  });
51
51
  }
52
52
  function y() {
53
+ let e = a();
53
54
  return /* @__PURE__ */ d("output", {
54
55
  className: "flex min-h-[300px] items-center justify-center p-6 text-center",
55
56
  children: /* @__PURE__ */ d("span", {
56
57
  className: "max-w-md text-sm text-text-secondary",
57
- children: n()("userTours.unavailable", "Tours are not available for this workspace right now.")
58
+ children: e("userTours.unavailable", "Tours are not available for this workspace right now.")
58
59
  })
59
60
  });
60
61
  }
61
- //#endregion
62
62
  export { v as ToursUserRoutes };
63
63
 
64
64
  //# sourceMappingURL=ToursUserRoutes.js.map
@@ -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';\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, useTr } 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 const tr = useTr();\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 {tr('userTours.unavailable', '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;AAE9B,QACE,kBAAC,UAAD;EAAQ,WAAU;YAChB,kBAAC,QAAD;GAAM,WAAU;aAHT,GAAO,CAIR,yBAAyB,wDAAwD;GAChF,CAAA;EACA,CAAA"}
1
+ {"version":3,"file":"ToursUserRoutes.js","names":["MyToursProgressPage","lazy","m","UserRouteNotFound","navigate","useNavigate","useToursI18n","useToursContext","useEffect","target","basePath","id","tr","ToursUserRoutes","FeatureGate","TOURS_FEATURES","UserToursUnavailable","LazyErrorBoundary","Suspense","PageLoader","Routes","Route","MyToursPage","useTr"],"sources":["../../src/tours/ToursUserRoutes.tsx"],"sourcesContent":["import { lazy, Suspense, useEffect } from 'react';\nimport { Route, Routes, useNavigate } from 'react-router';\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, useTr } 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 const tr = useTr();\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 {tr('userTours.unavailable', 'Tours are not available for this workspace right now.')}\n </span>\n </output>\n );\n}\n"],"mappings":";;;;;;;;;;AAYA,IAAMA,IAAsBC,QAC1B,OAAO,iCAA8B,CAAC,MAAM,OAAO,EAAE,SAASC,EAAE,oBAAoB,EAAE,CACxF;AAcA,SAASC,IAAoB;CAC3B,IAAMC,IAAWC,EAAY,GACvB,EAAE,UAAOC,EAAa,GACtB,EAAE,gBAAaC,EAAgB;CAQrC,OANAC,QAAgB;EACd,IAAMC,IAASC,KAAYA,EAAS,SAAS,IAAIA,IAAW,KACtDC,IAAK,OAAO,iBAAiBP,EAASK,GAAQ,EAAE,SAAS,GAAK,CAAC,GAAG,IAAI;EAC5E,aAAa,OAAO,aAAaE,CAAE;CACrC,GAAG,CAACP,GAAUM,CAAQ,CAAC,GAGrB,kBAAC,OAAD;EAAK,WAAU;EAAf,UAAA,CACE,kBAAC,MAAD;GAAI,WAAU;GAA2C,UAAAE,EAAG,uBAAuB;EAAM,CAAA,GACzF,kBAAC,KAAD;GAAG,WAAU;GAAwC,UAAAA,EAAG,sBAAsB;EAAK,CAAA,CAChF;;AAET;AAeA,SAAgBC,IAAkB;CAChC,OACE,kBAACC,GAAD;EAAa,MAAMC,EAAe;EAAM,cAAc;EAAM,UAAU,kBAACC,GAAD,CAAuB,CAAA;EAC3F,UAAA,kBAACC,GAAD,EAAA,UACE,kBAACC,GAAD;GAAU,UAAU,kBAACC,GAAD,CAAa,CAAA;GAC/B,UAAA,kBAACC,GAAD,EAAA,UAAA;IACE,kBAACC,GAAD;KAAO,OAAA;KAAM,SAAS,kBAACC,GAAD,CAAc,CAAA;IAAI,CAAA;IACxC,kBAACD,GAAD;KAAO,MAAK;KAAW,SAAS,kBAACrB,GAAD,CAAsB,CAAA;IAAI,CAAA;IAC1D,kBAACqB,GAAD;KAAO,MAAK;KAAI,SAAS,kBAAClB,GAAD,CAAoB,CAAA;IAAI,CAAA;GAC3C,EAAA,CAAA;EACA,CAAA,EACO,CAAA;CACR,CAAA;AAEjB;AAEA,SAASa,IAAuB;CAC9B,IAAMJ,IAAKW,EAAM;CACjB,OACE,kBAAC,UAAD;EAAQ,WAAU;EAChB,UAAA,kBAAC,QAAD;GAAM,WAAU;GACb,UAAAX,EAAG,yBAAyB,uDAAuD;EAChF,CAAA;CACA,CAAA;AAEZ"}
@@ -0,0 +1,12 @@
1
+ export interface MarkdownEditorProps {
2
+ id?: string;
3
+ value: string;
4
+ onChange: (value: string) => void;
5
+ label?: string;
6
+ placeholder?: string;
7
+ rows?: number;
8
+ required?: boolean;
9
+ disabled?: boolean;
10
+ 'aria-label'?: string;
11
+ }
12
+ export declare function MarkdownEditor({ id, value, onChange, label, placeholder, rows, required, disabled, 'aria-label': ariaLabel, }: MarkdownEditorProps): import('react').JSX.Element;
@@ -3,7 +3,7 @@ import { useCallback as t, useRef as n, useState as r } from "react";
3
3
  import { Markdown as i } from "@burdenoff/fe-libs/shared/components";
4
4
  import { Bold as a, Code as o, Italic as s, Link as c, List as l } from "lucide-react";
5
5
  import { jsx as u, jsxs as d } from "react/jsx-runtime";
6
- //#region src/tours/components/MarkdownEditor.tsx
6
+ // src/tours/components/MarkdownEditor.tsx
7
7
  function f({ title: e, onClick: t, children: n }) {
8
8
  return /* @__PURE__ */ u("button", {
9
9
  type: "button",
@@ -143,7 +143,6 @@ function h({ id: h, value: g, onChange: _, label: v, placeholder: y, rows: b = 6
143
143
  })]
144
144
  });
145
145
  }
146
- //#endregion
147
146
  export { h as MarkdownEditor };
148
147
 
149
148
  //# sourceMappingURL=MarkdownEditor.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"MarkdownEditor.js","names":[],"sources":["../../../src/tours/components/MarkdownEditor.tsx"],"sourcesContent":["import { useCallback, useRef, useState } from 'react';\nimport { Bold, Italic, Link, Code, List } from 'lucide-react';\nimport { Markdown } from '@burdenoff/fe-libs/shared/components';\nimport { useTr } from '../i18n';\n\nexport interface MarkdownEditorProps {\n id?: string;\n value: string;\n onChange: (value: string) => void;\n label?: string;\n placeholder?: string;\n rows?: number;\n required?: boolean;\n disabled?: boolean;\n 'aria-label'?: string;\n}\n\ninterface ToolbarButtonProps {\n title: string;\n onClick: () => void;\n children: React.ReactNode;\n}\n\nfunction ToolbarButton({ title, onClick, children }: ToolbarButtonProps) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n title={title}\n aria-label={title}\n className=\"p-2 min-h-[36px] min-w-[36px] rounded-md border border-border-subtle hover:bg-accent text-text-secondary\"\n >\n {children}\n </button>\n );\n}\n\nfunction wrapSelection(\n textarea: HTMLTextAreaElement,\n prefix: string,\n suffix: string = prefix\n): string {\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n const value = textarea.value;\n const selected = value.slice(start, end);\n const replacement = `${prefix}${selected}${suffix}`;\n const newValue = value.slice(0, start) + replacement + value.slice(end);\n\n // Defer cursor positioning so React state flush does not reset it.\n requestAnimationFrame(() => {\n textarea.focus();\n const newCursor = start + prefix.length + selected.length;\n textarea.setSelectionRange(newCursor, newCursor);\n });\n\n return newValue;\n}\n\nfunction insertAtLineStart(textarea: HTMLTextAreaElement, marker: string): string {\n const start = textarea.selectionStart;\n const value = textarea.value;\n const lineStart = value.lastIndexOf('\\n', start - 1) + 1;\n const before = value.slice(0, lineStart);\n const after = value.slice(lineStart);\n\n requestAnimationFrame(() => {\n textarea.focus();\n const newCursor = lineStart + marker.length;\n textarea.setSelectionRange(newCursor, newCursor);\n });\n\n return `${before}${marker}${after}`;\n}\n\nexport function MarkdownEditor({\n id,\n value,\n onChange,\n label,\n placeholder,\n rows = 6,\n required,\n disabled,\n 'aria-label': ariaLabel,\n}: MarkdownEditorProps) {\n const tr = useTr();\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const [activeTab, setActiveTab] = useState<'write' | 'preview'>('write');\n\n const handleBold = useCallback(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n onChange(wrapSelection(textarea, '**'));\n }, [onChange]);\n\n const handleItalic = useCallback(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n onChange(wrapSelection(textarea, '_'));\n }, [onChange]);\n\n const handleLink = useCallback(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n const selected =\n value.slice(start, end) || tr('markdownEditor.linkTextPlaceholder', 'link text');\n const replacement = `[${selected}](url)`;\n const newValue = value.slice(0, start) + replacement + value.slice(end);\n\n requestAnimationFrame(() => {\n textarea.focus();\n const linkStart = start + selected.length + 3;\n const linkEnd = linkStart + 3;\n textarea.setSelectionRange(linkStart, linkEnd);\n });\n\n onChange(newValue);\n }, [onChange, value, tr]);\n\n const handleCode = useCallback(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n onChange(wrapSelection(textarea, '`'));\n }, [onChange]);\n\n const handleList = useCallback(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n onChange(insertAtLineStart(textarea, '- '));\n }, [onChange]);\n\n return (\n <div className=\"space-y-2\">\n {label && (\n <label htmlFor={id} className=\"block text-sm font-medium\">\n {label}\n {required && <span className=\"text-status-error-text ml-1\">*</span>}\n </label>\n )}\n\n <div className=\"border border-border-subtle rounded-md bg-bg-surface overflow-hidden\">\n <div className=\"flex items-center justify-between border-b border-border-subtle px-2 py-1\">\n <div className=\"flex items-center gap-1\">\n <ToolbarButton title={tr('markdownEditor.bold', 'Bold')} onClick={handleBold}>\n <Bold size={16} />\n </ToolbarButton>\n <ToolbarButton title={tr('markdownEditor.italic', 'Italic')} onClick={handleItalic}>\n <Italic size={16} />\n </ToolbarButton>\n <ToolbarButton title={tr('markdownEditor.link', 'Link')} onClick={handleLink}>\n <Link size={16} />\n </ToolbarButton>\n <ToolbarButton\n title={tr('markdownEditor.inlineCode', 'Inline code')}\n onClick={handleCode}\n >\n <Code size={16} />\n </ToolbarButton>\n <ToolbarButton\n title={tr('markdownEditor.bulletList', 'Bullet list')}\n onClick={handleList}\n >\n <List size={16} />\n </ToolbarButton>\n </div>\n\n <div\n className=\"flex items-center gap-1\"\n role=\"tablist\"\n aria-label={tr('markdownEditor.tabsAriaLabel', 'Markdown editor tabs')}\n >\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={activeTab === 'write'}\n onClick={() => setActiveTab('write')}\n className={`px-3 py-1.5 text-sm rounded-md ${\n activeTab === 'write'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'hover:bg-accent text-text-secondary'\n }`}\n >\n {tr('markdownEditor.writeTab', 'Write')}\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={activeTab === 'preview'}\n onClick={() => setActiveTab('preview')}\n className={`px-3 py-1.5 text-sm rounded-md ${\n activeTab === 'preview'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'hover:bg-accent text-text-secondary'\n }`}\n >\n {tr('markdownEditor.previewTab', 'Preview')}\n </button>\n </div>\n </div>\n\n <div className=\"relative\">\n <textarea\n ref={textareaRef}\n id={id}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n rows={rows}\n disabled={disabled}\n required={required}\n placeholder={placeholder}\n aria-label={ariaLabel ?? label}\n className={`w-full px-3 py-2 bg-bg-surface text-text-primary text-sm sm:text-base resize-y min-h-[120px] focus:outline-none ${\n activeTab === 'write' ? 'block' : 'hidden'\n }`}\n />\n {activeTab === 'preview' && (\n <div className=\"w-full px-3 py-2 min-h-[120px] max-h-[24rem] overflow-y-auto text-sm sm:text-base\">\n {value ? (\n <Markdown>{value}</Markdown>\n ) : (\n <p className=\"text-text-secondary italic\">\n {tr('markdownEditor.nothingToPreview', 'Nothing to preview')}\n </p>\n )}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"MarkdownEditor.js","names":["ToolbarButton","title","wrapSelection","prefix","start","textarea","end","value","selected","replacement","suffix","newValue","newCursor","insertAtLineStart","lineStart","before","after","marker","MarkdownEditor","ariaLabel","tr","useTr","textareaRef","useRef","activeTab","setActiveTab","useState","handleBold","useCallback","onChange","handleItalic","handleLink","linkStart","linkEnd","handleCode","handleList","label","id","required","Bold","Italic","Link","Code","List","Markdown"],"sources":["../../../src/tours/components/MarkdownEditor.tsx"],"sourcesContent":["import { useCallback, useRef, useState } from 'react';\nimport { Bold, Italic, Link, Code, List } from 'lucide-react';\nimport { Markdown } from '@burdenoff/fe-libs/shared/components';\nimport { useTr } from '../i18n';\n\nexport interface MarkdownEditorProps {\n id?: string;\n value: string;\n onChange: (value: string) => void;\n label?: string;\n placeholder?: string;\n rows?: number;\n required?: boolean;\n disabled?: boolean;\n 'aria-label'?: string;\n}\n\ninterface ToolbarButtonProps {\n title: string;\n onClick: () => void;\n children: React.ReactNode;\n}\n\nfunction ToolbarButton({ title, onClick, children }: ToolbarButtonProps) {\n return (\n <button\n type=\"button\"\n onClick={onClick}\n title={title}\n aria-label={title}\n className=\"p-2 min-h-[36px] min-w-[36px] rounded-md border border-border-subtle hover:bg-accent text-text-secondary\"\n >\n {children}\n </button>\n );\n}\n\nfunction wrapSelection(\n textarea: HTMLTextAreaElement,\n prefix: string,\n suffix: string = prefix\n): string {\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n const value = textarea.value;\n const selected = value.slice(start, end);\n const replacement = `${prefix}${selected}${suffix}`;\n const newValue = value.slice(0, start) + replacement + value.slice(end);\n\n // Defer cursor positioning so React state flush does not reset it.\n requestAnimationFrame(() => {\n textarea.focus();\n const newCursor = start + prefix.length + selected.length;\n textarea.setSelectionRange(newCursor, newCursor);\n });\n\n return newValue;\n}\n\nfunction insertAtLineStart(textarea: HTMLTextAreaElement, marker: string): string {\n const start = textarea.selectionStart;\n const value = textarea.value;\n const lineStart = value.lastIndexOf('\\n', start - 1) + 1;\n const before = value.slice(0, lineStart);\n const after = value.slice(lineStart);\n\n requestAnimationFrame(() => {\n textarea.focus();\n const newCursor = lineStart + marker.length;\n textarea.setSelectionRange(newCursor, newCursor);\n });\n\n return `${before}${marker}${after}`;\n}\n\nexport function MarkdownEditor({\n id,\n value,\n onChange,\n label,\n placeholder,\n rows = 6,\n required,\n disabled,\n 'aria-label': ariaLabel,\n}: MarkdownEditorProps) {\n const tr = useTr();\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const [activeTab, setActiveTab] = useState<'write' | 'preview'>('write');\n\n const handleBold = useCallback(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n onChange(wrapSelection(textarea, '**'));\n }, [onChange]);\n\n const handleItalic = useCallback(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n onChange(wrapSelection(textarea, '_'));\n }, [onChange]);\n\n const handleLink = useCallback(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n const selected =\n value.slice(start, end) || tr('markdownEditor.linkTextPlaceholder', 'link text');\n const replacement = `[${selected}](url)`;\n const newValue = value.slice(0, start) + replacement + value.slice(end);\n\n requestAnimationFrame(() => {\n textarea.focus();\n const linkStart = start + selected.length + 3;\n const linkEnd = linkStart + 3;\n textarea.setSelectionRange(linkStart, linkEnd);\n });\n\n onChange(newValue);\n }, [onChange, value, tr]);\n\n const handleCode = useCallback(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n onChange(wrapSelection(textarea, '`'));\n }, [onChange]);\n\n const handleList = useCallback(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n onChange(insertAtLineStart(textarea, '- '));\n }, [onChange]);\n\n return (\n <div className=\"space-y-2\">\n {label && (\n <label htmlFor={id} className=\"block text-sm font-medium\">\n {label}\n {required && <span className=\"text-status-error-text ml-1\">*</span>}\n </label>\n )}\n\n <div className=\"border border-border-subtle rounded-md bg-bg-surface overflow-hidden\">\n <div className=\"flex items-center justify-between border-b border-border-subtle px-2 py-1\">\n <div className=\"flex items-center gap-1\">\n <ToolbarButton title={tr('markdownEditor.bold', 'Bold')} onClick={handleBold}>\n <Bold size={16} />\n </ToolbarButton>\n <ToolbarButton title={tr('markdownEditor.italic', 'Italic')} onClick={handleItalic}>\n <Italic size={16} />\n </ToolbarButton>\n <ToolbarButton title={tr('markdownEditor.link', 'Link')} onClick={handleLink}>\n <Link size={16} />\n </ToolbarButton>\n <ToolbarButton\n title={tr('markdownEditor.inlineCode', 'Inline code')}\n onClick={handleCode}\n >\n <Code size={16} />\n </ToolbarButton>\n <ToolbarButton\n title={tr('markdownEditor.bulletList', 'Bullet list')}\n onClick={handleList}\n >\n <List size={16} />\n </ToolbarButton>\n </div>\n\n <div\n className=\"flex items-center gap-1\"\n role=\"tablist\"\n aria-label={tr('markdownEditor.tabsAriaLabel', 'Markdown editor tabs')}\n >\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={activeTab === 'write'}\n onClick={() => setActiveTab('write')}\n className={`px-3 py-1.5 text-sm rounded-md ${\n activeTab === 'write'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'hover:bg-accent text-text-secondary'\n }`}\n >\n {tr('markdownEditor.writeTab', 'Write')}\n </button>\n <button\n type=\"button\"\n role=\"tab\"\n aria-selected={activeTab === 'preview'}\n onClick={() => setActiveTab('preview')}\n className={`px-3 py-1.5 text-sm rounded-md ${\n activeTab === 'preview'\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'hover:bg-accent text-text-secondary'\n }`}\n >\n {tr('markdownEditor.previewTab', 'Preview')}\n </button>\n </div>\n </div>\n\n <div className=\"relative\">\n <textarea\n ref={textareaRef}\n id={id}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n rows={rows}\n disabled={disabled}\n required={required}\n placeholder={placeholder}\n aria-label={ariaLabel ?? label}\n className={`w-full px-3 py-2 bg-bg-surface text-text-primary text-sm sm:text-base resize-y min-h-[120px] focus:outline-none ${\n activeTab === 'write' ? 'block' : 'hidden'\n }`}\n />\n {activeTab === 'preview' && (\n <div className=\"w-full px-3 py-2 min-h-[120px] max-h-[24rem] overflow-y-auto text-sm sm:text-base\">\n {value ? (\n <Markdown>{value}</Markdown>\n ) : (\n <p className=\"text-text-secondary italic\">\n {tr('markdownEditor.nothingToPreview', 'Nothing to preview')}\n </p>\n )}\n </div>\n )}\n </div>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ import { Component, ReactNode, ErrorInfo } from 'react';
2
+ /**
3
+ * Loading fallback for lazy-loaded pages.
4
+ * Shared between ToursAdminRoutes and ToursUserRoutes (Section D/E of BOFF-2391).
5
+ */
6
+ export declare function PageLoader(): import('react').JSX.Element;
7
+ /**
8
+ * Error boundary for lazy-loaded page chunks.
9
+ *
10
+ * React.lazy() caches rejected promises, so resetting error state alone would
11
+ * re-throw the same cached rejection. The retryCount key forces a full remount
12
+ * of the subtree on retry, creating fresh lazy-component instances.
13
+ */
14
+ export declare class LazyErrorBoundary extends Component<{
15
+ children: ReactNode;
16
+ }, {
17
+ hasError: boolean;
18
+ error: Error | null;
19
+ retryCount: number;
20
+ }> {
21
+ constructor(props: {
22
+ children: ReactNode;
23
+ });
24
+ static getDerivedStateFromError(error: Error): {
25
+ hasError: boolean;
26
+ error: Error;
27
+ };
28
+ componentDidCatch(error: Error, errorInfo: ErrorInfo): void;
29
+ handleRetry: () => void;
30
+ render(): import('react').JSX.Element;
31
+ }