@burdenoff/microfe-buildmyiq 2026.706.11 → 2026.709.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.
@@ -1,88 +1,106 @@
1
1
  import { useBuildmyiq as e } from "./context/BuildmyiqContext.js";
2
- import { DashboardPage as t } from "./pages/DashboardPage.js";
3
- import { Suspense as n, lazy as r } from "react";
4
- import { Navigate as i, Route as a, Routes as o, useLocation as s } from "react-router-dom";
5
- import { jsx as c, jsxs as l } from "react/jsx-runtime";
6
- import { Spinner as u } from "@burdenoff/fe-libs/ui";
2
+ import "./context/index.js";
3
+ import { useTr as t } from "./shared/hooks/useTr.js";
4
+ import { DashboardPage as n } from "./pages/DashboardPage.js";
5
+ import { Suspense as r, lazy as i } from "react";
6
+ import { FeatureGate as a } from "@burdenoff/fe-libs/shared/feature-flags";
7
+ import { Navigate as o, Route as s, Routes as c, useLocation as l } from "react-router-dom";
8
+ import { jsx as u, jsxs as d } from "react/jsx-runtime";
9
+ import { Spinner as f } from "@burdenoff/fe-libs/ui";
7
10
  //#region src/buildmyiq/BuildmyiqRoutes.tsx
8
- var d = r(() => import("./pages/catalog/CatalogPage.js").then((e) => ({ default: e.CatalogPage }))), f = r(() => import("./pages/catalog/CourseDetailPage.js").then((e) => ({ default: e.CourseDetailPage }))), p = r(() => import("./pages/learning/MyLearningPage.js").then((e) => ({ default: e.MyLearningPage }))), m = r(() => import("./pages/learning/LessonPlayerPage.js").then((e) => ({ default: e.LessonPlayerPage }))), h = r(() => import("./pages/authoring/AuthoringPage.js").then((e) => ({ default: e.AuthoringPage }))), g = r(() => import("./pages/authoring/CourseBuilderPage.js").then((e) => ({ default: e.CourseBuilderPage }))), _ = r(() => import("./pages/assessments/AssessmentsPage.js").then((e) => ({ default: e.AssessmentsPage }))), v = r(() => import("./pages/assessments/AssessmentDetailPage.js").then((e) => ({ default: e.AssessmentDetailPage }))), y = r(() => import("./pages/assessments/TakeAssessmentPage.js").then((e) => ({ default: e.TakeAssessmentPage }))), b = r(() => import("./pages/insights/InsightsPage.js").then((e) => ({ default: e.InsightsPage }))), x = r(() => import("./pages/paths/PathsPage.js").then((e) => ({ default: e.PathsPage }))), S = r(() => import("./pages/submissions/SubmissionsPage.js").then((e) => ({ default: e.SubmissionsPage }))), C = r(() => import("./pages/cohorts/CohortsPage.js").then((e) => ({ default: e.CohortsPage }))), w = r(() => import("./pages/skills/SkillsPage.js").then((e) => ({ default: e.SkillsPage }))), T = r(() => import("./pages/certificates/CertificatesPage.js").then((e) => ({ default: e.CertificatesPage }))), E = () => /* @__PURE__ */ c("div", {
11
+ var p = i(() => import("./pages/catalog/CatalogPage.js").then((e) => ({ default: e.CatalogPage }))), m = i(() => import("./pages/catalog/CourseDetailPage.js").then((e) => ({ default: e.CourseDetailPage }))), h = i(() => import("./pages/learning/MyLearningPage.js").then((e) => ({ default: e.MyLearningPage }))), g = i(() => import("./pages/learning/LessonPlayerPage.js").then((e) => ({ default: e.LessonPlayerPage }))), _ = i(() => import("./pages/authoring/AuthoringPage.js").then((e) => ({ default: e.AuthoringPage }))), v = i(() => import("./pages/authoring/CourseBuilderPage.js").then((e) => ({ default: e.CourseBuilderPage }))), y = i(() => import("./pages/assessments/AssessmentsPage.js").then((e) => ({ default: e.AssessmentsPage }))), b = i(() => import("./pages/assessments/AssessmentDetailPage.js").then((e) => ({ default: e.AssessmentDetailPage }))), x = i(() => import("./pages/assessments/TakeAssessmentPage.js").then((e) => ({ default: e.TakeAssessmentPage }))), S = i(() => import("./pages/insights/InsightsPage.js").then((e) => ({ default: e.InsightsPage }))), C = i(() => import("./pages/paths/PathsPage.js").then((e) => ({ default: e.PathsPage }))), w = i(() => import("./pages/submissions/SubmissionsPage.js").then((e) => ({ default: e.SubmissionsPage }))), T = i(() => import("./pages/cohorts/CohortsPage.js").then((e) => ({ default: e.CohortsPage }))), E = i(() => import("./pages/skills/SkillsPage.js").then((e) => ({ default: e.SkillsPage }))), D = i(() => import("./pages/certificates/CertificatesPage.js").then((e) => ({ default: e.CertificatesPage }))), O = () => /* @__PURE__ */ u("div", {
9
12
  className: "flex min-h-[400px] items-center justify-center",
10
- children: /* @__PURE__ */ c(u, { size: "lg" })
11
- }), D = () => {
12
- let { basePath: r, isStandalone: u } = e(), D = s(), O = r === "/" ? "" : (r ?? "").replace(/\/$/, "");
13
- return O && !u && !(D.pathname === O || D.pathname.startsWith(O + "/")) ? null : /* @__PURE__ */ c("div", {
13
+ children: /* @__PURE__ */ u(f, { size: "lg" })
14
+ });
15
+ function k() {
16
+ return /* @__PURE__ */ u("div", {
17
+ className: "flex h-full min-h-[200px] items-center justify-center text-sm text-text-secondary",
18
+ children: t()("buildmyiq.pages.featureDisabled", "This feature is currently disabled. You can enable it from Feature Previews.")
19
+ });
20
+ }
21
+ function A(e, t, n = !0) {
22
+ return /* @__PURE__ */ u(a, {
23
+ flag: e,
24
+ defaultValue: n,
25
+ fallback: /* @__PURE__ */ u(k, {}),
26
+ children: t
27
+ });
28
+ }
29
+ var j = () => {
30
+ let { basePath: t, isStandalone: i } = e(), a = l(), f = t === "/" ? "" : (t ?? "").replace(/\/$/, "");
31
+ return f && !i && !(a.pathname === f || a.pathname.startsWith(f + "/")) ? null : /* @__PURE__ */ u("div", {
14
32
  className: "flex min-h-full flex-col",
15
- children: /* @__PURE__ */ c(n, {
16
- fallback: /* @__PURE__ */ c(E, {}),
17
- children: /* @__PURE__ */ l(o, { children: [
18
- /* @__PURE__ */ c(a, {
33
+ children: /* @__PURE__ */ u(r, {
34
+ fallback: /* @__PURE__ */ u(O, {}),
35
+ children: /* @__PURE__ */ d(c, { children: [
36
+ /* @__PURE__ */ u(s, {
19
37
  index: !0,
20
- element: /* @__PURE__ */ c(t, {})
38
+ element: A("buildmyiq.overview", /* @__PURE__ */ u(n, {}))
21
39
  }),
22
- /* @__PURE__ */ c(a, {
40
+ /* @__PURE__ */ u(s, {
23
41
  path: "catalog",
24
- element: /* @__PURE__ */ c(d, {})
42
+ element: A("buildmyiq.catalog", /* @__PURE__ */ u(p, {}))
25
43
  }),
26
- /* @__PURE__ */ c(a, {
44
+ /* @__PURE__ */ u(s, {
27
45
  path: "catalog/:courseId",
28
- element: /* @__PURE__ */ c(f, {})
46
+ element: A("buildmyiq.catalog", /* @__PURE__ */ u(m, {}))
29
47
  }),
30
- /* @__PURE__ */ c(a, {
48
+ /* @__PURE__ */ u(s, {
31
49
  path: "learning",
32
- element: /* @__PURE__ */ c(p, {})
50
+ element: A("buildmyiq.learning", /* @__PURE__ */ u(h, {}))
33
51
  }),
34
- /* @__PURE__ */ c(a, {
52
+ /* @__PURE__ */ u(s, {
35
53
  path: "learning/:enrollmentId",
36
- element: /* @__PURE__ */ c(m, {})
54
+ element: A("buildmyiq.learning", /* @__PURE__ */ u(g, {}))
37
55
  }),
38
- /* @__PURE__ */ c(a, {
56
+ /* @__PURE__ */ u(s, {
39
57
  path: "paths",
40
- element: /* @__PURE__ */ c(x, {})
58
+ element: A("buildmyiq.paths", /* @__PURE__ */ u(C, {}))
41
59
  }),
42
- /* @__PURE__ */ c(a, {
60
+ /* @__PURE__ */ u(s, {
43
61
  path: "authoring",
44
- element: /* @__PURE__ */ c(h, {})
62
+ element: A("buildmyiq.authoring", /* @__PURE__ */ u(_, {}))
45
63
  }),
46
- /* @__PURE__ */ c(a, {
64
+ /* @__PURE__ */ u(s, {
47
65
  path: "authoring/:courseId",
48
- element: /* @__PURE__ */ c(g, {})
66
+ element: A("buildmyiq.authoring", /* @__PURE__ */ u(v, {}))
49
67
  }),
50
- /* @__PURE__ */ c(a, {
68
+ /* @__PURE__ */ u(s, {
51
69
  path: "assessments",
52
- element: /* @__PURE__ */ c(_, {})
70
+ element: A("buildmyiq.assessments", /* @__PURE__ */ u(y, {}))
53
71
  }),
54
- /* @__PURE__ */ c(a, {
72
+ /* @__PURE__ */ u(s, {
55
73
  path: "assessments/:assessmentId",
56
- element: /* @__PURE__ */ c(v, {})
74
+ element: A("buildmyiq.assessments", /* @__PURE__ */ u(b, {}))
57
75
  }),
58
- /* @__PURE__ */ c(a, {
76
+ /* @__PURE__ */ u(s, {
59
77
  path: "assessments/:assessmentId/take",
60
- element: /* @__PURE__ */ c(y, {})
78
+ element: A("buildmyiq.assessments", /* @__PURE__ */ u(x, {}))
61
79
  }),
62
- /* @__PURE__ */ c(a, {
80
+ /* @__PURE__ */ u(s, {
63
81
  path: "insights",
64
- element: /* @__PURE__ */ c(b, {})
82
+ element: A("buildmyiq.insights", /* @__PURE__ */ u(S, {}))
65
83
  }),
66
- /* @__PURE__ */ c(a, {
84
+ /* @__PURE__ */ u(s, {
67
85
  path: "submissions",
68
- element: /* @__PURE__ */ c(S, {})
86
+ element: A("buildmyiq.submissions", /* @__PURE__ */ u(w, {}))
69
87
  }),
70
- /* @__PURE__ */ c(a, {
88
+ /* @__PURE__ */ u(s, {
71
89
  path: "cohorts",
72
- element: /* @__PURE__ */ c(C, {})
90
+ element: A("buildmyiq.cohorts", /* @__PURE__ */ u(T, {}))
73
91
  }),
74
- /* @__PURE__ */ c(a, {
92
+ /* @__PURE__ */ u(s, {
75
93
  path: "skills",
76
- element: /* @__PURE__ */ c(w, {})
94
+ element: A("buildmyiq.skills", /* @__PURE__ */ u(E, {}))
77
95
  }),
78
- /* @__PURE__ */ c(a, {
96
+ /* @__PURE__ */ u(s, {
79
97
  path: "certificates",
80
- element: /* @__PURE__ */ c(T, {})
98
+ element: A("buildmyiq.certificates", /* @__PURE__ */ u(D, {}))
81
99
  }),
82
- /* @__PURE__ */ c(a, {
100
+ /* @__PURE__ */ u(s, {
83
101
  path: "*",
84
- element: /* @__PURE__ */ c(i, {
85
- to: O || "/",
102
+ element: /* @__PURE__ */ u(o, {
103
+ to: f || "/",
86
104
  replace: !0
87
105
  })
88
106
  })
@@ -91,6 +109,6 @@ var d = r(() => import("./pages/catalog/CatalogPage.js").then((e) => ({ default:
91
109
  });
92
110
  };
93
111
  //#endregion
94
- export { D as BuildmyiqRoutes };
112
+ export { j as BuildmyiqRoutes };
95
113
 
96
114
  //# sourceMappingURL=BuildmyiqRoutes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"BuildmyiqRoutes.js","names":[],"sources":["../../src/buildmyiq/BuildmyiqRoutes.tsx"],"sourcesContent":["/**\n * Buildmyiq Routes\n * @module buildmyiq\n *\n * Domain routes. The dashboard is eager-loaded (primary landing); all other\n * pages are lazy-loaded. Every route maps to a real `bui*` backend capability.\n */\n\nimport { type FC, lazy, Suspense } from 'react';\nimport { Routes, Route, Navigate, useLocation } from 'react-router-dom';\nimport { Spinner } from '@burdenoff/fe-libs/ui';\nimport { useBuildmyiq } from './context/BuildmyiqContext';\n\n// Eager — primary landing\nimport { DashboardPage } from './pages/DashboardPage';\n\n// Catalog (learner-facing course discovery + detail + enrol)\nconst CatalogPage = lazy(() =>\n import('./pages/catalog/CatalogPage').then((m) => ({ default: m.CatalogPage }))\n);\nconst CourseDetailPage = lazy(() =>\n import('./pages/catalog/CourseDetailPage').then((m) => ({ default: m.CourseDetailPage }))\n);\n\n// My Learning (enrollments + progress + lesson player)\nconst MyLearningPage = lazy(() =>\n import('./pages/learning/MyLearningPage').then((m) => ({ default: m.MyLearningPage }))\n);\nconst LessonPlayerPage = lazy(() =>\n import('./pages/learning/LessonPlayerPage').then((m) => ({ default: m.LessonPlayerPage }))\n);\n\n// Authoring (instructor course management + builder)\nconst AuthoringPage = lazy(() =>\n import('./pages/authoring/AuthoringPage').then((m) => ({ default: m.AuthoringPage }))\n);\nconst CourseBuilderPage = lazy(() =>\n import('./pages/authoring/CourseBuilderPage').then((m) => ({ default: m.CourseBuilderPage }))\n);\n\n// Assessments (list + builder/questions/detail)\nconst AssessmentsPage = lazy(() =>\n import('./pages/assessments/AssessmentsPage').then((m) => ({ default: m.AssessmentsPage }))\n);\nconst AssessmentDetailPage = lazy(() =>\n import('./pages/assessments/AssessmentDetailPage').then((m) => ({\n default: m.AssessmentDetailPage,\n }))\n);\n// Learner-facing: take a published assessment (answer + submit)\nconst TakeAssessmentPage = lazy(() =>\n import('./pages/assessments/TakeAssessmentPage').then((m) => ({ default: m.TakeAssessmentPage }))\n);\n\n// Insights (educator analytics, derived from real data)\nconst InsightsPage = lazy(() =>\n import('./pages/insights/InsightsPage').then((m) => ({ default: m.InsightsPage }))\n);\n\n// Skill Journeys (derived guided paths)\nconst PathsPage = lazy(() =>\n import('./pages/paths/PathsPage').then((m) => ({ default: m.PathsPage }))\n);\n\n// Grading (submissions queue)\nconst SubmissionsPage = lazy(() =>\n import('./pages/submissions/SubmissionsPage').then((m) => ({ default: m.SubmissionsPage }))\n);\n\n// Cohorts\nconst CohortsPage = lazy(() =>\n import('./pages/cohorts/CohortsPage').then((m) => ({ default: m.CohortsPage }))\n);\n\n// Skills taxonomy\nconst SkillsPage = lazy(() =>\n import('./pages/skills/SkillsPage').then((m) => ({ default: m.SkillsPage }))\n);\n\n// Certificates\nconst CertificatesPage = lazy(() =>\n import('./pages/certificates/CertificatesPage').then((m) => ({ default: m.CertificatesPage }))\n);\n\nconst PageLoader: FC = () => (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <Spinner size=\"lg\" />\n </div>\n);\n\nexport const BuildmyiqRoutes: FC = () => {\n const { basePath, isStandalone } = useBuildmyiq();\n const location = useLocation();\n\n const normalizedBasePath = basePath === '/' ? '' : (basePath ?? '').replace(/\\/$/, '');\n\n // Guard: only render if the current path matches basePath (integrated mode).\n if (normalizedBasePath && !isStandalone) {\n const pathMatches =\n location.pathname === normalizedBasePath ||\n location.pathname.startsWith(normalizedBasePath + '/');\n if (!pathMatches) {\n return null;\n }\n }\n\n return (\n <div className=\"flex min-h-full flex-col\">\n <Suspense fallback={<PageLoader />}>\n <Routes>\n <Route index element={<DashboardPage />} />\n\n <Route path=\"catalog\" element={<CatalogPage />} />\n <Route path=\"catalog/:courseId\" element={<CourseDetailPage />} />\n\n <Route path=\"learning\" element={<MyLearningPage />} />\n <Route path=\"learning/:enrollmentId\" element={<LessonPlayerPage />} />\n\n <Route path=\"paths\" element={<PathsPage />} />\n\n <Route path=\"authoring\" element={<AuthoringPage />} />\n <Route path=\"authoring/:courseId\" element={<CourseBuilderPage />} />\n\n <Route path=\"assessments\" element={<AssessmentsPage />} />\n <Route path=\"assessments/:assessmentId\" element={<AssessmentDetailPage />} />\n <Route path=\"assessments/:assessmentId/take\" element={<TakeAssessmentPage />} />\n\n <Route path=\"insights\" element={<InsightsPage />} />\n\n <Route path=\"submissions\" element={<SubmissionsPage />} />\n <Route path=\"cohorts\" element={<CohortsPage />} />\n <Route path=\"skills\" element={<SkillsPage />} />\n <Route path=\"certificates\" element={<CertificatesPage />} />\n\n <Route path=\"*\" element={<Navigate to={normalizedBasePath || '/'} replace />} />\n </Routes>\n </Suspense>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"BuildmyiqRoutes.js","names":[],"sources":["../../src/buildmyiq/BuildmyiqRoutes.tsx"],"sourcesContent":["/**\n * Buildmyiq Routes\n * @module buildmyiq\n *\n * Domain routes. The dashboard is eager-loaded (primary landing); all other\n * pages are lazy-loaded. Every route maps to a real `bui*` backend capability.\n * All product routes are gated by feature flags that default to enabled.\n */\n\nimport React, { type FC, lazy, Suspense } from 'react';\nimport { Routes, Route, Navigate, useLocation } from 'react-router-dom';\nimport { FeatureGate } from '@burdenoff/fe-libs/shared/feature-flags';\nimport { Spinner } from '@burdenoff/fe-libs/ui';\nimport { useBuildmyiq } from './context';\nimport { useTr } from './shared/hooks/useTr';\n\n// Eager — primary landing\nimport { DashboardPage } from './pages/DashboardPage';\n\n// Catalog (learner-facing course discovery + detail + enrol)\nconst CatalogPage = lazy(() =>\n import('./pages/catalog/CatalogPage').then((m) => ({ default: m.CatalogPage }))\n);\nconst CourseDetailPage = lazy(() =>\n import('./pages/catalog/CourseDetailPage').then((m) => ({ default: m.CourseDetailPage }))\n);\n\n// My Learning (enrollments + progress + lesson player)\nconst MyLearningPage = lazy(() =>\n import('./pages/learning/MyLearningPage').then((m) => ({ default: m.MyLearningPage }))\n);\nconst LessonPlayerPage = lazy(() =>\n import('./pages/learning/LessonPlayerPage').then((m) => ({ default: m.LessonPlayerPage }))\n);\n\n// Authoring (instructor course management + builder)\nconst AuthoringPage = lazy(() =>\n import('./pages/authoring/AuthoringPage').then((m) => ({ default: m.AuthoringPage }))\n);\nconst CourseBuilderPage = lazy(() =>\n import('./pages/authoring/CourseBuilderPage').then((m) => ({ default: m.CourseBuilderPage }))\n);\n\n// Assessments (list + builder/questions/detail)\nconst AssessmentsPage = lazy(() =>\n import('./pages/assessments/AssessmentsPage').then((m) => ({ default: m.AssessmentsPage }))\n);\nconst AssessmentDetailPage = lazy(() =>\n import('./pages/assessments/AssessmentDetailPage').then((m) => ({\n default: m.AssessmentDetailPage,\n }))\n);\n// Learner-facing: take a published assessment (answer + submit)\nconst TakeAssessmentPage = lazy(() =>\n import('./pages/assessments/TakeAssessmentPage').then((m) => ({ default: m.TakeAssessmentPage }))\n);\n\n// Insights (educator analytics, derived from real data)\nconst InsightsPage = lazy(() =>\n import('./pages/insights/InsightsPage').then((m) => ({ default: m.InsightsPage }))\n);\n\n// Skill Journeys (derived guided paths)\nconst PathsPage = lazy(() =>\n import('./pages/paths/PathsPage').then((m) => ({ default: m.PathsPage }))\n);\n\n// Grading (submissions queue)\nconst SubmissionsPage = lazy(() =>\n import('./pages/submissions/SubmissionsPage').then((m) => ({ default: m.SubmissionsPage }))\n);\n\n// Cohorts\nconst CohortsPage = lazy(() =>\n import('./pages/cohorts/CohortsPage').then((m) => ({ default: m.CohortsPage }))\n);\n\n// Skills taxonomy\nconst SkillsPage = lazy(() =>\n import('./pages/skills/SkillsPage').then((m) => ({ default: m.SkillsPage }))\n);\n\n// Certificates\nconst CertificatesPage = lazy(() =>\n import('./pages/certificates/CertificatesPage').then((m) => ({ default: m.CertificatesPage }))\n);\n\nconst PageLoader: FC = () => (\n <div className=\"flex min-h-[400px] items-center justify-center\">\n <Spinner size=\"lg\" />\n </div>\n);\n\nfunction FeatureDisabledFallback() {\n const tr = useTr();\n return (\n <div className=\"flex h-full min-h-[200px] items-center justify-center text-sm text-text-secondary\">\n {tr(\n 'buildmyiq.pages.featureDisabled',\n 'This feature is currently disabled. You can enable it from Feature Previews.'\n )}\n </div>\n );\n}\n\nfunction gated(flag: string, element: React.ReactNode, defaultValue = true): React.ReactElement {\n return (\n <FeatureGate flag={flag} defaultValue={defaultValue} fallback={<FeatureDisabledFallback />}>\n {element}\n </FeatureGate>\n );\n}\n\nexport const BuildmyiqRoutes: FC = () => {\n const { basePath, isStandalone } = useBuildmyiq();\n const location = useLocation();\n\n const normalizedBasePath = basePath === '/' ? '' : (basePath ?? '').replace(/\\/$/, '');\n\n // Guard: only render if the current path matches basePath (integrated mode).\n if (normalizedBasePath && !isStandalone) {\n const pathMatches =\n location.pathname === normalizedBasePath ||\n location.pathname.startsWith(normalizedBasePath + '/');\n if (!pathMatches) {\n return null;\n }\n }\n\n return (\n <div className=\"flex min-h-full flex-col\">\n <Suspense fallback={<PageLoader />}>\n <Routes>\n <Route index element={gated('buildmyiq.overview', <DashboardPage />)} />\n\n <Route path=\"catalog\" element={gated('buildmyiq.catalog', <CatalogPage />)} />\n <Route\n path=\"catalog/:courseId\"\n element={gated('buildmyiq.catalog', <CourseDetailPage />)}\n />\n\n <Route path=\"learning\" element={gated('buildmyiq.learning', <MyLearningPage />)} />\n <Route\n path=\"learning/:enrollmentId\"\n element={gated('buildmyiq.learning', <LessonPlayerPage />)}\n />\n\n <Route path=\"paths\" element={gated('buildmyiq.paths', <PathsPage />)} />\n\n <Route path=\"authoring\" element={gated('buildmyiq.authoring', <AuthoringPage />)} />\n <Route\n path=\"authoring/:courseId\"\n element={gated('buildmyiq.authoring', <CourseBuilderPage />)}\n />\n\n <Route path=\"assessments\" element={gated('buildmyiq.assessments', <AssessmentsPage />)} />\n <Route\n path=\"assessments/:assessmentId\"\n element={gated('buildmyiq.assessments', <AssessmentDetailPage />)}\n />\n <Route\n path=\"assessments/:assessmentId/take\"\n element={gated('buildmyiq.assessments', <TakeAssessmentPage />)}\n />\n\n <Route path=\"insights\" element={gated('buildmyiq.insights', <InsightsPage />)} />\n\n <Route path=\"submissions\" element={gated('buildmyiq.submissions', <SubmissionsPage />)} />\n <Route path=\"cohorts\" element={gated('buildmyiq.cohorts', <CohortsPage />)} />\n <Route path=\"skills\" element={gated('buildmyiq.skills', <SkillsPage />)} />\n <Route\n path=\"certificates\"\n element={gated('buildmyiq.certificates', <CertificatesPage />)}\n />\n\n <Route path=\"*\" element={<Navigate to={normalizedBasePath || '/'} replace />} />\n </Routes>\n </Suspense>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -1,8 +1,8 @@
1
1
  import { useBuildmyiq as e } from "../context/BuildmyiqContext.js";
2
+ import "../context/index.js";
2
3
  import { formatDateTime as t } from "../utils/formatters.js";
3
4
  import { EmptyState as n } from "./EmptyState.js";
4
5
  import { ErrorView as r, LoadingView as i, Section as a } from "./StateViews.js";
5
- import "../context/index.js";
6
6
  import { useBuiComments as o, useEcoCreateComment as s } from "../hooks/useBuiComments.js";
7
7
  import "../hooks/index.js";
8
8
  import { useState as c } from "react";
@@ -1,6 +1,6 @@
1
1
  import "./utils/navigation.js";
2
2
  import "./context/BuildmyiqContext.js";
3
+ import "./context/index.js";
3
4
  import "./utils/cn.js";
4
5
  import "./utils/formatters.js";
5
- import "./context/index.js";
6
6
  import "./BuildmyiqRoot.js";
@@ -1,25 +1,27 @@
1
1
  import { useBuildmyiq as e } from "../context/BuildmyiqContext.js";
2
- import { cn as t } from "../utils/cn.js";
3
- import { PageLayout as n } from "../components/PageLayout.js";
4
- import { MetricsCard as r } from "../components/MetricsCard.js";
5
- import { formatEnumLabel as i, formatRelativeTime as a } from "../utils/formatters.js";
6
- import { EmptyState as o } from "../components/EmptyState.js";
7
- import { LoadingView as s, Section as c } from "../components/StateViews.js";
8
- import { MiniBar as l } from "../components/MiniBar.js";
9
- import { Avatar as u } from "../components/Avatar.js";
10
- import { Sparkline as d } from "../components/Viz.js";
11
2
  import "../context/index.js";
12
- import { useBuiCertificates as f, useBuiCourses as p, useBuiEnrollments as m } from "../hooks/useBuildmyiqApi.js";
3
+ import { useTr as t } from "../shared/hooks/useTr.js";
4
+ import { cn as n } from "../utils/cn.js";
5
+ import { PageLayout as r } from "../components/PageLayout.js";
6
+ import { MetricsCard as i } from "../components/MetricsCard.js";
7
+ import { formatEnumLabel as a, formatRelativeTime as o } from "../utils/formatters.js";
8
+ import { EmptyState as s } from "../components/EmptyState.js";
9
+ import { LoadingView as c, Section as l } from "../components/StateViews.js";
10
+ import { MiniBar as u } from "../components/MiniBar.js";
11
+ import { Avatar as d } from "../components/Avatar.js";
12
+ import { Sparkline as f } from "../components/Viz.js";
13
+ import { useBuiCertificates as p, useBuiCourses as m, useBuiEnrollments as h } from "../hooks/useBuildmyiqApi.js";
13
14
  import "../hooks/index.js";
14
15
  import "../components/index.js";
15
- import { CourseCard as h } from "./_shared/CourseCard.js";
16
+ import { CourseCard as g } from "./_shared/CourseCard.js";
16
17
  import "./_shared/index.js";
17
- import { useMemo as g } from "react";
18
- import { ArrowRight as _, Award as v, BookOpen as y, CheckCircle2 as b, Clock as x, Play as S } from "lucide-react";
19
- import { jsx as C, jsxs as w } from "react/jsx-runtime";
20
- import { Button as T, GlassCard as E } from "@burdenoff/fe-libs/ui";
18
+ import { useMemo as _ } from "react";
19
+ import { ArrowRight as v, Award as y, BookOpen as b, CheckCircle2 as x, Clock as S, Play as C } from "lucide-react";
20
+ import { jsx as w, jsxs as T } from "react/jsx-runtime";
21
+ import { Button as E, GlassCard as D } from "@burdenoff/fe-libs/ui";
22
+ import { useEventBus as O } from "@burdenoff/fe-libs/shared/events";
21
23
  //#region src/buildmyiq/pages/DashboardPage.tsx
22
- var D = (e, t = 8) => {
24
+ var k = (e, t = 8) => {
23
25
  let n = e.map((e) => e ? new Date(e).getTime() : NaN).filter((e) => !Number.isNaN(e)).sort((e, t) => e - t);
24
26
  if (n.length < 2) return n.length === 1 ? [1] : [];
25
27
  let r = n[0], i = Math.max(1, Date.now() - r), a = [];
@@ -28,187 +30,211 @@ var D = (e, t = 8) => {
28
30
  a.push(n.filter((e) => e <= o).length);
29
31
  }
30
32
  return a;
31
- }, O = ({ tint: e, children: n }) => /* @__PURE__ */ C("span", {
32
- className: t("inline-flex h-10 w-10 items-center justify-center rounded-xl", e),
33
+ }, A = ({ tint: e, children: t }) => /* @__PURE__ */ w("span", {
34
+ className: n("inline-flex h-10 w-10 items-center justify-center rounded-xl", e),
33
35
  "aria-hidden": "true",
34
- children: n
35
- }), k = () => {
36
- let { currentUser: t, navigateTo: k } = e(), A = t?.id, j = p({
36
+ children: t
37
+ }), j = () => {
38
+ let { currentUser: n, navigateTo: j, workspaceId: M } = e(), N = t(), P = O(), F = n?.id, I = m({
37
39
  status: "PUBLISHED",
38
40
  limit: 50
39
- }), M = m({
40
- learnerId: A,
41
+ }), L = h({
42
+ learnerId: F,
41
43
  limit: 50,
42
- enabled: !!A
43
- }), N = f(void 0, A), P = j.data?.items ?? [], F = M.data?.items ?? [], I = N.data ?? [], L = g(() => F.filter((e) => e.status === "ENROLLED" || e.status === "IN_PROGRESS").sort((e, t) => (t.lastActiveAt ?? t.enrolledAt).localeCompare(e.lastActiveAt ?? e.enrolledAt)), [F]), R = F.filter((e) => e.status === "COMPLETED").length, z = t?.firstName || t?.name?.split(" ")[0] || "there", B = [t?.firstName, t?.lastName].filter(Boolean).join(" ").trim(), V = t?.name || B || (z === "there" ? "" : z), H = t?.role ? i(t.role) : t?.email, U = j.isLoading || !!A && M.isLoading, W = g(() => {
44
+ enabled: !!F
45
+ }), R = p(void 0, F), z = I.data?.items ?? [], B = L.data?.items ?? [], V = R.data ?? [], H = _(() => B.filter((e) => e.status === "ENROLLED" || e.status === "IN_PROGRESS").sort((e, t) => (t.lastActiveAt ?? t.enrolledAt).localeCompare(e.lastActiveAt ?? e.enrolledAt)), [B]), U = B.filter((e) => e.status === "COMPLETED").length, W = n?.firstName || n?.name?.split(" ")[0] || "there", G = [n?.firstName, n?.lastName].filter(Boolean).join(" ").trim(), K = n?.name || G || (W === "there" ? "" : W), q = n?.role ? a(n.role) : n?.email, J = I.isLoading || !!F && L.isLoading, Y = _(() => {
44
46
  let e = /* @__PURE__ */ new Map();
45
- for (let t of P) e.set(t.id, t);
47
+ for (let t of z) e.set(t.id, t);
46
48
  return e;
47
- }, [P]), G = g(() => ({
48
- published: D(P.map((e) => e.publishedAt ?? e.createdAt)),
49
- inProgress: D(F.map((e) => e.enrolledAt)),
50
- completed: D(F.filter((e) => e.status === "COMPLETED").map((e) => e.completedAt ?? e.updatedAt)),
51
- certificates: D(I.map((e) => e.issuedAt))
49
+ }, [z]), X = _(() => ({
50
+ published: k(z.map((e) => e.publishedAt ?? e.createdAt)),
51
+ inProgress: k(B.map((e) => e.enrolledAt)),
52
+ completed: k(B.filter((e) => e.status === "COMPLETED").map((e) => e.completedAt ?? e.updatedAt)),
53
+ certificates: k(V.map((e) => e.issuedAt))
52
54
  }), [
53
- P,
54
- F,
55
- I
56
- ]), K = [
55
+ z,
56
+ B,
57
+ V
58
+ ]), Z = [
57
59
  {
58
60
  key: "published",
59
- icon: /* @__PURE__ */ C(y, { className: "h-5 w-5" }),
61
+ icon: /* @__PURE__ */ w(b, { className: "h-5 w-5" }),
60
62
  tint: "bg-[var(--color-accent-soft)] text-[var(--color-accent-primary)]",
61
- value: `${P.length}${j.data?.pageInfo?.hasMore ? "+" : ""}`,
62
- label: "Published courses",
63
+ value: `${z.length}${I.data?.pageInfo?.hasMore ? "+" : ""}`,
64
+ label: N("buildmyiq.dashboard.publishedCourses", "Published courses"),
63
65
  featured: !0,
64
- series: G.published,
66
+ series: X.published,
65
67
  sparkColor: "var(--color-accent-primary)"
66
68
  },
67
69
  {
68
70
  key: "in-progress",
69
- icon: /* @__PURE__ */ C(x, { className: "h-5 w-5" }),
71
+ icon: /* @__PURE__ */ w(S, { className: "h-5 w-5" }),
70
72
  tint: "bg-[color-mix(in_srgb,var(--color-accent-teal)_16%,transparent)] text-[var(--color-accent-teal)]",
71
- value: L.length,
72
- label: "Courses in progress",
73
- series: G.inProgress,
73
+ value: H.length,
74
+ label: N("buildmyiq.dashboard.coursesInProgress", "Courses in progress"),
75
+ series: X.inProgress,
74
76
  sparkColor: "var(--color-accent-teal)"
75
77
  },
76
78
  {
77
79
  key: "completed",
78
- icon: /* @__PURE__ */ C(b, { className: "h-5 w-5" }),
80
+ icon: /* @__PURE__ */ w(x, { className: "h-5 w-5" }),
79
81
  tint: "bg-status-success-bg-subtle text-status-success-text",
80
- value: R,
81
- label: "Completed",
82
- series: G.completed,
82
+ value: U,
83
+ label: N("buildmyiq.dashboard.completed", "Completed"),
84
+ series: X.completed,
83
85
  sparkColor: "var(--color-status-success-text)"
84
86
  },
85
87
  {
86
88
  key: "certificates",
87
- icon: /* @__PURE__ */ C(v, { className: "h-5 w-5" }),
89
+ icon: /* @__PURE__ */ w(y, { className: "h-5 w-5" }),
88
90
  tint: "bg-status-warning-bg-subtle text-status-warning-text",
89
- value: I.length,
90
- label: "Certificates earned",
91
- series: G.certificates,
91
+ value: V.length,
92
+ label: N("buildmyiq.dashboard.certificatesEarned", "Certificates earned"),
93
+ series: X.certificates,
92
94
  sparkColor: "var(--color-status-warning-text)"
93
95
  }
94
96
  ];
95
- return /* @__PURE__ */ C(n, {
96
- title: `Welcome back, ${z}`,
97
- description: "Your learning, teaching and credentialing — all in one workspace.",
97
+ return /* @__PURE__ */ w(r, {
98
+ title: N("buildmyiq.dashboard.welcomeBack", "Welcome back, {{name}}", { name: W }),
99
+ description: N("buildmyiq.dashboard.subtitle", "Your learning, teaching and credentialing — all in one workspace."),
98
100
  titleGradient: !0,
99
101
  aurora: !0,
100
- actions: t ? /* @__PURE__ */ w("div", {
102
+ actions: n ? /* @__PURE__ */ T("div", {
101
103
  className: "flex w-fit items-center gap-2.5 rounded-full border border-border-seam bg-bg-surface/60 py-1.5 pl-1.5 pr-3.5 backdrop-blur",
102
- children: [/* @__PURE__ */ C(u, {
103
- name: V || null,
104
- seed: t.id,
105
- src: t.avatar,
104
+ children: [/* @__PURE__ */ w(d, {
105
+ name: K || null,
106
+ seed: n.id,
107
+ src: n.avatar,
106
108
  size: "sm"
107
- }), (V || H) && /* @__PURE__ */ w("div", {
109
+ }), (K || q) && /* @__PURE__ */ T("div", {
108
110
  className: "min-w-0 leading-tight",
109
- children: [V && /* @__PURE__ */ C("p", {
111
+ children: [K && /* @__PURE__ */ w("p", {
110
112
  className: "truncate text-sm font-medium text-text-primary",
111
- children: V
112
- }), H && /* @__PURE__ */ C("p", {
113
+ children: K
114
+ }), q && /* @__PURE__ */ w("p", {
113
115
  className: "max-w-[10rem] truncate text-xs text-text-secondary",
114
- children: H
116
+ children: q
115
117
  })]
116
118
  })]
117
119
  }) : void 0,
118
- children: U ? /* @__PURE__ */ C(s, { label: "Loading your learning dashboard…" }) : /* @__PURE__ */ w("div", {
120
+ children: J ? /* @__PURE__ */ w(c, { label: N("buildmyiq.dashboard.loading", "Loading your learning dashboard…") }) : /* @__PURE__ */ T("div", {
119
121
  className: "flex flex-col gap-8",
122
+ "data-tour": "buildmyiq-dashboard-content",
120
123
  children: [
121
- /* @__PURE__ */ C("div", {
124
+ /* @__PURE__ */ w("div", {
122
125
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4",
123
- children: K.map((e) => /* @__PURE__ */ C(r, {
124
- icon: /* @__PURE__ */ C(O, {
126
+ "data-tour": "buildmyiq-dashboard-kpis",
127
+ children: Z.map((e) => /* @__PURE__ */ w(i, {
128
+ icon: /* @__PURE__ */ w(A, {
125
129
  tint: e.tint,
126
130
  children: e.icon
127
131
  }),
128
132
  value: e.value,
129
133
  label: e.label,
130
134
  featured: e.featured,
131
- pill: e.series.length >= 2 ? /* @__PURE__ */ C(d, {
135
+ pill: e.series.length >= 2 ? /* @__PURE__ */ w(f, {
132
136
  data: e.series,
133
137
  color: e.sparkColor
134
138
  }) : void 0
135
139
  }, e.key))
136
140
  }),
137
- /* @__PURE__ */ C(c, {
138
- title: "Continue learning",
139
- description: "Pick up where you left off.",
140
- action: /* @__PURE__ */ w(T, {
141
+ /* @__PURE__ */ w(l, {
142
+ title: N("buildmyiq.dashboard.continueLearning", "Continue learning"),
143
+ description: N("buildmyiq.dashboard.continueLearningDescription", "Pick up where you left off."),
144
+ action: /* @__PURE__ */ T(E, {
141
145
  variant: "ghost",
142
146
  size: "sm",
143
- onClick: () => k("learning"),
144
- children: ["My learning ", /* @__PURE__ */ C(_, { className: "ml-1 h-4 w-4" })]
147
+ onClick: () => {
148
+ P.emitCustom("buildmyiq.dashboard.my_learning_clicked", { workspaceId: M }), j("learning");
149
+ },
150
+ "data-tour": "buildmyiq-dashboard-my-learning",
151
+ children: [
152
+ N("buildmyiq.dashboard.myLearning", "My learning"),
153
+ " ",
154
+ /* @__PURE__ */ w(v, { className: "ml-1 h-4 w-4" })
155
+ ]
145
156
  }),
146
- children: L.length === 0 ? /* @__PURE__ */ C(o, {
157
+ children: H.length === 0 ? /* @__PURE__ */ w(s, {
147
158
  illustration: "onboarding",
148
- title: "No active courses yet",
149
- description: "Browse the catalog and enrol in a course to start learning.",
150
- action: /* @__PURE__ */ C(T, {
151
- onClick: () => k("catalog"),
152
- children: "Explore the catalog"
159
+ title: N("buildmyiq.dashboard.noActiveCoursesTitle", "No active courses yet"),
160
+ description: N("buildmyiq.dashboard.noActiveCoursesDescription", "Browse the catalog and enrol in a course to start learning."),
161
+ action: /* @__PURE__ */ w(E, {
162
+ onClick: () => {
163
+ P.emitCustom("buildmyiq.dashboard.explore_catalog_clicked", { workspaceId: M }), j("catalog");
164
+ },
165
+ "data-tour": "buildmyiq-dashboard-explore-catalog",
166
+ children: N("common.explore", "Explore the catalog")
153
167
  })
154
- }) : /* @__PURE__ */ C("div", {
168
+ }) : /* @__PURE__ */ w("div", {
155
169
  className: "grid grid-cols-1 gap-4 md:grid-cols-2",
156
- children: L.slice(0, 4).map((e) => {
157
- let t = W.get(e.courseId), n = t?.title ?? "Course", r = (t) => {
158
- t?.stopPropagation(), k(`learning/${e.id}`);
170
+ "data-tour": "buildmyiq-dashboard-continue-cards",
171
+ children: H.slice(0, 4).map((e) => {
172
+ let t = Y.get(e.courseId), n = t?.title ?? "Course", r = (t) => {
173
+ t?.stopPropagation(), j(`learning/${e.id}`);
159
174
  };
160
- return /* @__PURE__ */ w(E, {
175
+ return /* @__PURE__ */ T(D, {
161
176
  treatment: "plain",
162
177
  className: "group flex cursor-pointer flex-col overflow-hidden transition-shadow hover:shadow-lg sm:flex-row",
163
178
  onClick: () => r(),
164
- children: [/* @__PURE__ */ w("div", {
179
+ children: [/* @__PURE__ */ T("div", {
165
180
  className: "relative h-36 w-full shrink-0 overflow-hidden bg-bg-sunken sm:h-auto sm:w-40",
166
- children: [t?.cover?.downloadUrl ? /* @__PURE__ */ C("img", {
181
+ children: [t?.cover?.downloadUrl ? /* @__PURE__ */ w("img", {
167
182
  src: t.cover.downloadUrl,
168
183
  alt: "",
169
184
  className: "h-full w-full object-cover transition-transform duration-500 group-hover:scale-105"
170
- }) : /* @__PURE__ */ C("div", {
185
+ }) : /* @__PURE__ */ w("div", {
171
186
  className: "flex h-full w-full items-center justify-center bg-gradient-to-br from-[var(--color-accent-soft)] to-bg-sunken",
172
- children: /* @__PURE__ */ C(y, { className: "h-8 w-8 text-text-secondary opacity-60" })
173
- }), /* @__PURE__ */ C("div", {
187
+ children: /* @__PURE__ */ w(b, { className: "h-8 w-8 text-text-secondary opacity-60" })
188
+ }), /* @__PURE__ */ w("div", {
174
189
  className: "absolute inset-0 flex items-center justify-center",
175
- children: /* @__PURE__ */ C("span", {
190
+ children: /* @__PURE__ */ w("span", {
176
191
  className: "inline-flex h-11 w-11 items-center justify-center rounded-full bg-bg-surface/85 shadow-lg backdrop-blur transition-transform duration-200 group-hover:scale-110",
177
- children: /* @__PURE__ */ C(S, { className: "ml-0.5 h-5 w-5 fill-current text-[var(--color-accent-primary)]" })
192
+ children: /* @__PURE__ */ w(C, { className: "ml-0.5 h-5 w-5 fill-current text-[var(--color-accent-primary)]" })
178
193
  })
179
194
  })]
180
- }), /* @__PURE__ */ w("div", {
195
+ }), /* @__PURE__ */ T("div", {
181
196
  className: "flex min-w-0 flex-1 flex-col gap-2 p-4",
182
197
  children: [
183
- /* @__PURE__ */ C("p", {
198
+ /* @__PURE__ */ w("p", {
184
199
  className: "line-clamp-1 font-semibold text-text-primary",
185
200
  children: n
186
201
  }),
187
- t?.summary && /* @__PURE__ */ C("p", {
202
+ t?.summary && /* @__PURE__ */ w("p", {
188
203
  className: "line-clamp-2 text-sm text-text-secondary",
189
204
  children: t.summary
190
205
  }),
191
- /* @__PURE__ */ w("div", {
206
+ /* @__PURE__ */ T("div", {
192
207
  className: "mt-auto space-y-2.5 pt-2",
193
- children: [/* @__PURE__ */ w("div", {
208
+ children: [/* @__PURE__ */ T("div", {
194
209
  className: "flex items-center gap-2",
195
- children: [/* @__PURE__ */ C(l, {
210
+ children: [/* @__PURE__ */ w(u, {
196
211
  value: e.progressPct,
197
212
  className: "flex-1"
198
- }), /* @__PURE__ */ w("span", {
213
+ }), /* @__PURE__ */ T("span", {
199
214
  className: "shrink-0 text-xs font-semibold tabular-nums text-text-secondary",
200
215
  children: [e.progressPct, "%"]
201
216
  })]
202
- }), /* @__PURE__ */ w("div", {
217
+ }), /* @__PURE__ */ T("div", {
203
218
  className: "flex items-center justify-between gap-2",
204
- children: [/* @__PURE__ */ w("span", {
219
+ children: [/* @__PURE__ */ T("span", {
205
220
  className: "truncate text-xs text-text-secondary",
206
- children: ["Active ", a(e.lastActiveAt ?? e.enrolledAt)]
207
- }), /* @__PURE__ */ w(T, {
221
+ children: ["Active ", o(e.lastActiveAt ?? e.enrolledAt)]
222
+ }), /* @__PURE__ */ T(E, {
208
223
  size: "sm",
209
224
  className: "min-h-[40px] shrink-0",
210
- onClick: (e) => r(e),
211
- children: [/* @__PURE__ */ C(S, { className: "mr-1 h-4 w-4 fill-current" }), " Continue"]
225
+ onClick: (t) => {
226
+ P.emitCustom("buildmyiq.dashboard.continue_course_clicked", {
227
+ workspaceId: M,
228
+ enrollmentId: e.id,
229
+ courseId: e.courseId
230
+ }), r(t);
231
+ },
232
+ "data-tour": "buildmyiq-dashboard-continue-button",
233
+ children: [
234
+ /* @__PURE__ */ w(C, { className: "mr-1 h-4 w-4 fill-current" }),
235
+ " ",
236
+ N("common.continue", "Continue")
237
+ ]
212
238
  })]
213
239
  })]
214
240
  })
@@ -218,28 +244,39 @@ var D = (e, t = 8) => {
218
244
  })
219
245
  })
220
246
  }),
221
- /* @__PURE__ */ C(c, {
222
- title: "Explore the catalog",
223
- description: "Newly published courses in your workspace.",
224
- action: /* @__PURE__ */ w(T, {
247
+ /* @__PURE__ */ w(l, {
248
+ title: N("buildmyiq.dashboard.exploreCatalog", "Explore the catalog"),
249
+ description: N("buildmyiq.dashboard.exploreCatalogDescription", "Newly published courses in your workspace."),
250
+ action: /* @__PURE__ */ T(E, {
225
251
  variant: "ghost",
226
252
  size: "sm",
227
- onClick: () => k("catalog"),
228
- children: ["Browse all ", /* @__PURE__ */ C(_, { className: "ml-1 h-4 w-4" })]
253
+ onClick: () => {
254
+ P.emitCustom("buildmyiq.dashboard.browse_all_catalog_clicked", { workspaceId: M }), j("catalog");
255
+ },
256
+ "data-tour": "buildmyiq-dashboard-browse-all",
257
+ children: [
258
+ N("common.browseAll", "Browse all"),
259
+ " ",
260
+ /* @__PURE__ */ w(v, { className: "ml-1 h-4 w-4" })
261
+ ]
229
262
  }),
230
- children: P.length === 0 ? /* @__PURE__ */ C(o, {
263
+ children: z.length === 0 ? /* @__PURE__ */ w(s, {
231
264
  illustration: "empty-data",
232
- title: "No published courses yet",
233
- description: "Create and publish a course in Authoring to populate the catalog.",
234
- action: /* @__PURE__ */ C(T, {
235
- onClick: () => k("authoring"),
236
- children: "Go to Authoring"
265
+ title: N("buildmyiq.dashboard.noPublishedCoursesTitle", "No published courses yet"),
266
+ description: N("buildmyiq.dashboard.noPublishedCoursesDescription", "Create and publish a course in Authoring to populate the catalog."),
267
+ action: /* @__PURE__ */ w(E, {
268
+ onClick: () => {
269
+ P.emitCustom("buildmyiq.dashboard.go_to_authoring_clicked", { workspaceId: M }), j("authoring");
270
+ },
271
+ "data-tour": "buildmyiq-dashboard-go-authoring",
272
+ children: N("common.create", "Go to Authoring")
237
273
  })
238
- }) : /* @__PURE__ */ C("div", {
274
+ }) : /* @__PURE__ */ w("div", {
239
275
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4",
240
- children: P.slice(0, 8).map((e) => /* @__PURE__ */ C(h, {
276
+ "data-tour": "buildmyiq-dashboard-catalog-grid",
277
+ children: z.slice(0, 8).map((e) => /* @__PURE__ */ w(g, {
241
278
  course: e,
242
- onClick: () => k(`catalog/${e.id}`)
279
+ onClick: () => j(`catalog/${e.id}`)
243
280
  }, e.id))
244
281
  })
245
282
  })
@@ -248,6 +285,6 @@ var D = (e, t = 8) => {
248
285
  });
249
286
  };
250
287
  //#endregion
251
- export { k as DashboardPage };
288
+ export { j as DashboardPage };
252
289
 
253
290
  //# sourceMappingURL=DashboardPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../src/buildmyiq/pages/DashboardPage.tsx"],"sourcesContent":["/**\n * DashboardPage — the Buildmyiq landing surface.\n * @module buildmyiq/pages\n *\n * A learner + educator overview built entirely from real `bui*` data:\n * - KPI row: published courses, my active enrollments, completed, certificates\n * — each stat card carries a tinted icon tile and (where derivable) a\n * Sparkline trend built from the REAL timestamps of the underlying rows.\n * - Continue learning: the caller's in-progress enrollments as rich cards with\n * real cover art, summary, a progress bar and a Continue action.\n * - Explore: recently published catalog courses.\n * Every number, row, avatar and trend is derived from live hooks; empty states\n * are honest and no value is fabricated.\n */\n\nimport type { FC, ReactNode, MouseEvent } from 'react';\nimport { useMemo } from 'react';\nimport { BookOpen, Clock, CheckCircle2, Award, ArrowRight, Play } from 'lucide-react';\nimport { Button, GlassCard } from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n MetricsCard,\n EmptyState,\n LoadingView,\n Section,\n MiniBar,\n Avatar,\n Sparkline,\n} from '../components';\nimport { CourseCard } from './_shared';\nimport { cn } from '../utils/cn';\nimport { useBuildmyiq } from '../context';\nimport { useBuiCourses, useBuiEnrollments, useBuiCertificates } from '../hooks';\nimport { formatRelativeTime, formatEnumLabel } from '../utils/formatters';\nimport type { BuiCourse, BuiEnrollment } from '../types';\n\n/**\n * Build a cumulative-count trend series from a set of REAL event timestamps\n * (e.g. `publishedAt`, `enrolledAt`, `issuedAt`). The span from the earliest\n * event to now is split into `buckets` slots and each slot holds the running\n * count of events up to its boundary — a genuine, non-fabricated growth line.\n * Returns fewer than two points when there is not enough history to plot; the\n * caller then omits the sparkline rather than drawing a meaningless flat line.\n */\nconst cumulativeSeries = (\n isoDates: Array<string | null | undefined>,\n buckets = 8\n): number[] => {\n const times = isoDates\n .map((d) => (d ? new Date(d).getTime() : Number.NaN))\n .filter((t) => !Number.isNaN(t))\n .sort((a, b) => a - b);\n if (times.length < 2) return times.length === 1 ? [1] : [];\n const start = times[0];\n const end = Date.now();\n const span = Math.max(1, end - start);\n const series: number[] = [];\n for (let i = 1; i <= buckets; i += 1) {\n const boundary = start + (span * i) / buckets;\n series.push(times.filter((t) => t <= boundary).length);\n }\n return series;\n};\n\n/** A tinted, rounded icon tile used in the KPI stat cards. */\nconst StatIcon: FC<{ tint: string; children: ReactNode }> = ({ tint, children }) => (\n <span\n className={cn('inline-flex h-10 w-10 items-center justify-center rounded-xl', tint)}\n aria-hidden=\"true\"\n >\n {children}\n </span>\n);\n\nexport const DashboardPage: FC = () => {\n const { currentUser, navigateTo } = useBuildmyiq();\n const learnerId = currentUser?.id;\n\n const coursesQuery = useBuiCourses({ status: 'PUBLISHED', limit: 50 });\n const enrollmentsQuery = useBuiEnrollments({ learnerId, limit: 50, enabled: Boolean(learnerId) });\n const certsQuery = useBuiCertificates(undefined, learnerId);\n\n const courses: BuiCourse[] = coursesQuery.data?.items ?? [];\n const enrollments: BuiEnrollment[] = enrollmentsQuery.data?.items ?? [];\n const certificates = certsQuery.data ?? [];\n\n const activeEnrollments = useMemo(\n () =>\n enrollments\n .filter((e) => e.status === 'ENROLLED' || e.status === 'IN_PROGRESS')\n .sort((a, b) =>\n (b.lastActiveAt ?? b.enrolledAt).localeCompare(a.lastActiveAt ?? a.enrolledAt)\n ),\n [enrollments]\n );\n const completedCount = enrollments.filter((e) => e.status === 'COMPLETED').length;\n\n const firstName = currentUser?.firstName || currentUser?.name?.split(' ')[0] || 'there';\n const fullName = [currentUser?.firstName, currentUser?.lastName].filter(Boolean).join(' ').trim();\n const displayName = currentUser?.name || fullName || (firstName === 'there' ? '' : firstName);\n const roleLabel = currentUser?.role ? formatEnumLabel(currentUser.role) : currentUser?.email;\n\n const isLoading = coursesQuery.isLoading || (Boolean(learnerId) && enrollmentsQuery.isLoading);\n\n const courseById = useMemo(() => {\n const map = new Map<string, BuiCourse>();\n for (const c of courses) map.set(c.id, c);\n return map;\n }, [courses]);\n\n // REAL trend lines — cumulative growth from each entity's own timestamps.\n const trends = useMemo(\n () => ({\n published: cumulativeSeries(courses.map((c) => c.publishedAt ?? c.createdAt)),\n inProgress: cumulativeSeries(enrollments.map((e) => e.enrolledAt)),\n completed: cumulativeSeries(\n enrollments\n .filter((e) => e.status === 'COMPLETED')\n .map((e) => e.completedAt ?? e.updatedAt)\n ),\n certificates: cumulativeSeries(certificates.map((c) => c.issuedAt)),\n }),\n [courses, enrollments, certificates]\n );\n\n const kpis: Array<{\n key: string;\n icon: ReactNode;\n tint: string;\n value: string | number;\n label: string;\n featured?: boolean;\n series: number[];\n sparkColor: string;\n }> = [\n {\n key: 'published',\n icon: <BookOpen className=\"h-5 w-5\" />,\n tint: 'bg-[var(--color-accent-soft)] text-[var(--color-accent-primary)]',\n value: `${courses.length}${coursesQuery.data?.pageInfo?.hasMore ? '+' : ''}`,\n label: 'Published courses',\n featured: true,\n series: trends.published,\n sparkColor: 'var(--color-accent-primary)',\n },\n {\n key: 'in-progress',\n icon: <Clock className=\"h-5 w-5\" />,\n tint: 'bg-[color-mix(in_srgb,var(--color-accent-teal)_16%,transparent)] text-[var(--color-accent-teal)]',\n value: activeEnrollments.length,\n label: 'Courses in progress',\n series: trends.inProgress,\n sparkColor: 'var(--color-accent-teal)',\n },\n {\n key: 'completed',\n icon: <CheckCircle2 className=\"h-5 w-5\" />,\n tint: 'bg-status-success-bg-subtle text-status-success-text',\n value: completedCount,\n label: 'Completed',\n series: trends.completed,\n sparkColor: 'var(--color-status-success-text)',\n },\n {\n key: 'certificates',\n icon: <Award className=\"h-5 w-5\" />,\n tint: 'bg-status-warning-bg-subtle text-status-warning-text',\n value: certificates.length,\n label: 'Certificates earned',\n series: trends.certificates,\n sparkColor: 'var(--color-status-warning-text)',\n },\n ];\n\n return (\n <PageLayout\n title={`Welcome back, ${firstName}`}\n description=\"Your learning, teaching and credentialing — all in one workspace.\"\n titleGradient\n aurora\n actions={\n currentUser ? (\n <div className=\"flex w-fit items-center gap-2.5 rounded-full border border-border-seam bg-bg-surface/60 py-1.5 pl-1.5 pr-3.5 backdrop-blur\">\n <Avatar name={displayName || null} seed={currentUser.id} src={currentUser.avatar} size=\"sm\" />\n {(displayName || roleLabel) && (\n <div className=\"min-w-0 leading-tight\">\n {displayName && (\n <p className=\"truncate text-sm font-medium text-text-primary\">{displayName}</p>\n )}\n {roleLabel && (\n <p className=\"max-w-[10rem] truncate text-xs text-text-secondary\">{roleLabel}</p>\n )}\n </div>\n )}\n </div>\n ) : undefined\n }\n >\n {isLoading ? (\n <LoadingView label=\"Loading your learning dashboard…\" />\n ) : (\n <div className=\"flex flex-col gap-8\">\n {/* KPI row */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4\">\n {kpis.map((k) => (\n <MetricsCard\n key={k.key}\n icon={<StatIcon tint={k.tint}>{k.icon}</StatIcon>}\n value={k.value}\n label={k.label}\n featured={k.featured}\n pill={\n k.series.length >= 2 ? (\n <Sparkline data={k.series} color={k.sparkColor} />\n ) : undefined\n }\n />\n ))}\n </div>\n\n {/* Continue learning */}\n <Section\n title=\"Continue learning\"\n description=\"Pick up where you left off.\"\n action={\n <Button variant=\"ghost\" size=\"sm\" onClick={() => navigateTo('learning')}>\n My learning <ArrowRight className=\"ml-1 h-4 w-4\" />\n </Button>\n }\n >\n {activeEnrollments.length === 0 ? (\n <EmptyState\n illustration=\"onboarding\"\n title=\"No active courses yet\"\n description=\"Browse the catalog and enrol in a course to start learning.\"\n action={<Button onClick={() => navigateTo('catalog')}>Explore the catalog</Button>}\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 md:grid-cols-2\">\n {activeEnrollments.slice(0, 4).map((e) => {\n const course = courseById.get(e.courseId);\n const title = course?.title ?? 'Course';\n const goToCourse = (ev?: MouseEvent) => {\n ev?.stopPropagation();\n navigateTo(`learning/${e.id}`);\n };\n return (\n <GlassCard\n key={e.id}\n treatment=\"plain\"\n className=\"group flex cursor-pointer flex-col overflow-hidden transition-shadow hover:shadow-lg sm:flex-row\"\n onClick={() => goToCourse()}\n >\n {/* Cover / play thumbnail */}\n <div className=\"relative h-36 w-full shrink-0 overflow-hidden bg-bg-sunken sm:h-auto sm:w-40\">\n {course?.cover?.downloadUrl ? (\n <img\n src={course.cover.downloadUrl}\n alt=\"\"\n className=\"h-full w-full object-cover transition-transform duration-500 group-hover:scale-105\"\n />\n ) : (\n <div className=\"flex h-full w-full items-center justify-center bg-gradient-to-br from-[var(--color-accent-soft)] to-bg-sunken\">\n <BookOpen className=\"h-8 w-8 text-text-secondary opacity-60\" />\n </div>\n )}\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <span className=\"inline-flex h-11 w-11 items-center justify-center rounded-full bg-bg-surface/85 shadow-lg backdrop-blur transition-transform duration-200 group-hover:scale-110\">\n <Play className=\"ml-0.5 h-5 w-5 fill-current text-[var(--color-accent-primary)]\" />\n </span>\n </div>\n </div>\n\n {/* Content */}\n <div className=\"flex min-w-0 flex-1 flex-col gap-2 p-4\">\n <p className=\"line-clamp-1 font-semibold text-text-primary\">{title}</p>\n {course?.summary && (\n <p className=\"line-clamp-2 text-sm text-text-secondary\">\n {course.summary}\n </p>\n )}\n <div className=\"mt-auto space-y-2.5 pt-2\">\n <div className=\"flex items-center gap-2\">\n <MiniBar value={e.progressPct} className=\"flex-1\" />\n <span className=\"shrink-0 text-xs font-semibold tabular-nums text-text-secondary\">\n {e.progressPct}%\n </span>\n </div>\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"truncate text-xs text-text-secondary\">\n Active {formatRelativeTime(e.lastActiveAt ?? e.enrolledAt)}\n </span>\n <Button\n size=\"sm\"\n className=\"min-h-[40px] shrink-0\"\n onClick={(ev: MouseEvent) => goToCourse(ev)}\n >\n <Play className=\"mr-1 h-4 w-4 fill-current\" /> Continue\n </Button>\n </div>\n </div>\n </div>\n </GlassCard>\n );\n })}\n </div>\n )}\n </Section>\n\n {/* Explore catalog */}\n <Section\n title=\"Explore the catalog\"\n description=\"Newly published courses in your workspace.\"\n action={\n <Button variant=\"ghost\" size=\"sm\" onClick={() => navigateTo('catalog')}>\n Browse all <ArrowRight className=\"ml-1 h-4 w-4\" />\n </Button>\n }\n >\n {courses.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No published courses yet\"\n description=\"Create and publish a course in Authoring to populate the catalog.\"\n action={<Button onClick={() => navigateTo('authoring')}>Go to Authoring</Button>}\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\">\n {courses.slice(0, 8).map((course) => (\n <CourseCard\n key={course.id}\n course={course}\n onClick={() => navigateTo(`catalog/${course.id}`)}\n />\n ))}\n </div>\n )}\n </Section>\n </div>\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"DashboardPage.js","names":[],"sources":["../../../src/buildmyiq/pages/DashboardPage.tsx"],"sourcesContent":["/**\n * DashboardPage — the Buildmyiq landing surface.\n * @module buildmyiq/pages\n *\n * A learner + educator overview built entirely from real `bui*` data:\n * - KPI row: published courses, my active enrollments, completed, certificates\n * — each stat card carries a tinted icon tile and (where derivable) a\n * Sparkline trend built from the REAL timestamps of the underlying rows.\n * - Continue learning: the caller's in-progress enrollments as rich cards with\n * real cover art, summary, a progress bar and a Continue action.\n * - Explore: recently published catalog courses.\n * Every number, row, avatar and trend is derived from live hooks; empty states\n * are honest and no value is fabricated.\n */\n\nimport type { FC, ReactNode, MouseEvent } from 'react';\nimport { useMemo } from 'react';\nimport { BookOpen, Clock, CheckCircle2, Award, ArrowRight, Play } from 'lucide-react';\nimport { Button, GlassCard } from '@burdenoff/fe-libs/ui';\nimport { useEventBus } from '@burdenoff/fe-libs/shared/events';\nimport {\n PageLayout,\n MetricsCard,\n EmptyState,\n LoadingView,\n Section,\n MiniBar,\n Avatar,\n Sparkline,\n} from '../components';\nimport { CourseCard } from './_shared';\nimport { cn } from '../utils/cn';\nimport { useBuildmyiq } from '../context';\nimport { useBuiCourses, useBuiEnrollments, useBuiCertificates } from '../hooks';\nimport { useTr } from '../shared/hooks/useTr';\nimport { formatRelativeTime, formatEnumLabel } from '../utils/formatters';\nimport type { BuiCourse, BuiEnrollment } from '../types';\n\n/**\n * Build a cumulative-count trend series from a set of REAL event timestamps\n * (e.g. `publishedAt`, `enrolledAt`, `issuedAt`). The span from the earliest\n * event to now is split into `buckets` slots and each slot holds the running\n * count of events up to its boundary — a genuine, non-fabricated growth line.\n * Returns fewer than two points when there is not enough history to plot; the\n * caller then omits the sparkline rather than drawing a meaningless flat line.\n */\nconst cumulativeSeries = (isoDates: Array<string | null | undefined>, buckets = 8): number[] => {\n const times = isoDates\n .map((d) => (d ? new Date(d).getTime() : Number.NaN))\n .filter((t) => !Number.isNaN(t))\n .sort((a, b) => a - b);\n if (times.length < 2) return times.length === 1 ? [1] : [];\n const start = times[0];\n const end = Date.now();\n const span = Math.max(1, end - start);\n const series: number[] = [];\n for (let i = 1; i <= buckets; i += 1) {\n const boundary = start + (span * i) / buckets;\n series.push(times.filter((t) => t <= boundary).length);\n }\n return series;\n};\n\n/** A tinted, rounded icon tile used in the KPI stat cards. */\nconst StatIcon: FC<{ tint: string; children: ReactNode }> = ({ tint, children }) => (\n <span\n className={cn('inline-flex h-10 w-10 items-center justify-center rounded-xl', tint)}\n aria-hidden=\"true\"\n >\n {children}\n </span>\n);\n\nexport const DashboardPage: FC = () => {\n const { currentUser, navigateTo, workspaceId } = useBuildmyiq();\n const tr = useTr();\n const bus = useEventBus();\n const learnerId = currentUser?.id;\n\n const coursesQuery = useBuiCourses({ status: 'PUBLISHED', limit: 50 });\n const enrollmentsQuery = useBuiEnrollments({ learnerId, limit: 50, enabled: Boolean(learnerId) });\n const certsQuery = useBuiCertificates(undefined, learnerId);\n\n const courses: BuiCourse[] = coursesQuery.data?.items ?? [];\n const enrollments: BuiEnrollment[] = enrollmentsQuery.data?.items ?? [];\n const certificates = certsQuery.data ?? [];\n\n const activeEnrollments = useMemo(\n () =>\n enrollments\n .filter((e) => e.status === 'ENROLLED' || e.status === 'IN_PROGRESS')\n .sort((a, b) =>\n (b.lastActiveAt ?? b.enrolledAt).localeCompare(a.lastActiveAt ?? a.enrolledAt)\n ),\n [enrollments]\n );\n const completedCount = enrollments.filter((e) => e.status === 'COMPLETED').length;\n\n const firstName = currentUser?.firstName || currentUser?.name?.split(' ')[0] || 'there';\n const fullName = [currentUser?.firstName, currentUser?.lastName].filter(Boolean).join(' ').trim();\n const displayName = currentUser?.name || fullName || (firstName === 'there' ? '' : firstName);\n const roleLabel = currentUser?.role ? formatEnumLabel(currentUser.role) : currentUser?.email;\n\n const isLoading = coursesQuery.isLoading || (Boolean(learnerId) && enrollmentsQuery.isLoading);\n\n const courseById = useMemo(() => {\n const map = new Map<string, BuiCourse>();\n for (const c of courses) map.set(c.id, c);\n return map;\n }, [courses]);\n\n // REAL trend lines — cumulative growth from each entity's own timestamps.\n const trends = useMemo(\n () => ({\n published: cumulativeSeries(courses.map((c) => c.publishedAt ?? c.createdAt)),\n inProgress: cumulativeSeries(enrollments.map((e) => e.enrolledAt)),\n completed: cumulativeSeries(\n enrollments.filter((e) => e.status === 'COMPLETED').map((e) => e.completedAt ?? e.updatedAt)\n ),\n certificates: cumulativeSeries(certificates.map((c) => c.issuedAt)),\n }),\n [courses, enrollments, certificates]\n );\n\n const kpis: Array<{\n key: string;\n icon: ReactNode;\n tint: string;\n value: string | number;\n label: string;\n featured?: boolean;\n series: number[];\n sparkColor: string;\n }> = [\n {\n key: 'published',\n icon: <BookOpen className=\"h-5 w-5\" />,\n tint: 'bg-[var(--color-accent-soft)] text-[var(--color-accent-primary)]',\n value: `${courses.length}${coursesQuery.data?.pageInfo?.hasMore ? '+' : ''}`,\n label: tr('buildmyiq.dashboard.publishedCourses', 'Published courses'),\n featured: true,\n series: trends.published,\n sparkColor: 'var(--color-accent-primary)',\n },\n {\n key: 'in-progress',\n icon: <Clock className=\"h-5 w-5\" />,\n tint: 'bg-[color-mix(in_srgb,var(--color-accent-teal)_16%,transparent)] text-[var(--color-accent-teal)]',\n value: activeEnrollments.length,\n label: tr('buildmyiq.dashboard.coursesInProgress', 'Courses in progress'),\n series: trends.inProgress,\n sparkColor: 'var(--color-accent-teal)',\n },\n {\n key: 'completed',\n icon: <CheckCircle2 className=\"h-5 w-5\" />,\n tint: 'bg-status-success-bg-subtle text-status-success-text',\n value: completedCount,\n label: tr('buildmyiq.dashboard.completed', 'Completed'),\n series: trends.completed,\n sparkColor: 'var(--color-status-success-text)',\n },\n {\n key: 'certificates',\n icon: <Award className=\"h-5 w-5\" />,\n tint: 'bg-status-warning-bg-subtle text-status-warning-text',\n value: certificates.length,\n label: tr('buildmyiq.dashboard.certificatesEarned', 'Certificates earned'),\n series: trends.certificates,\n sparkColor: 'var(--color-status-warning-text)',\n },\n ];\n\n return (\n <PageLayout\n title={tr('buildmyiq.dashboard.welcomeBack', 'Welcome back, {{name}}', { name: firstName })}\n description={tr(\n 'buildmyiq.dashboard.subtitle',\n 'Your learning, teaching and credentialing — all in one workspace.'\n )}\n titleGradient\n aurora\n actions={\n currentUser ? (\n <div className=\"flex w-fit items-center gap-2.5 rounded-full border border-border-seam bg-bg-surface/60 py-1.5 pl-1.5 pr-3.5 backdrop-blur\">\n <Avatar\n name={displayName || null}\n seed={currentUser.id}\n src={currentUser.avatar}\n size=\"sm\"\n />\n {(displayName || roleLabel) && (\n <div className=\"min-w-0 leading-tight\">\n {displayName && (\n <p className=\"truncate text-sm font-medium text-text-primary\">{displayName}</p>\n )}\n {roleLabel && (\n <p className=\"max-w-[10rem] truncate text-xs text-text-secondary\">{roleLabel}</p>\n )}\n </div>\n )}\n </div>\n ) : undefined\n }\n >\n {isLoading ? (\n <LoadingView\n label={tr('buildmyiq.dashboard.loading', 'Loading your learning dashboard…')}\n />\n ) : (\n <div className=\"flex flex-col gap-8\" data-tour=\"buildmyiq-dashboard-content\">\n {/* KPI row */}\n <div\n className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4\"\n data-tour=\"buildmyiq-dashboard-kpis\"\n >\n {kpis.map((k) => (\n <MetricsCard\n key={k.key}\n icon={<StatIcon tint={k.tint}>{k.icon}</StatIcon>}\n value={k.value}\n label={k.label}\n featured={k.featured}\n pill={\n k.series.length >= 2 ? (\n <Sparkline data={k.series} color={k.sparkColor} />\n ) : undefined\n }\n />\n ))}\n </div>\n\n {/* Continue learning */}\n <Section\n title={tr('buildmyiq.dashboard.continueLearning', 'Continue learning')}\n description={tr(\n 'buildmyiq.dashboard.continueLearningDescription',\n 'Pick up where you left off.'\n )}\n action={\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => {\n bus.emitCustom('buildmyiq.dashboard.my_learning_clicked', { workspaceId });\n navigateTo('learning');\n }}\n data-tour=\"buildmyiq-dashboard-my-learning\"\n >\n {tr('buildmyiq.dashboard.myLearning', 'My learning')}{' '}\n <ArrowRight className=\"ml-1 h-4 w-4\" />\n </Button>\n }\n >\n {activeEnrollments.length === 0 ? (\n <EmptyState\n illustration=\"onboarding\"\n title={tr('buildmyiq.dashboard.noActiveCoursesTitle', 'No active courses yet')}\n description={tr(\n 'buildmyiq.dashboard.noActiveCoursesDescription',\n 'Browse the catalog and enrol in a course to start learning.'\n )}\n action={\n <Button\n onClick={() => {\n bus.emitCustom('buildmyiq.dashboard.explore_catalog_clicked', {\n workspaceId,\n });\n navigateTo('catalog');\n }}\n data-tour=\"buildmyiq-dashboard-explore-catalog\"\n >\n {tr('common.explore', 'Explore the catalog')}\n </Button>\n }\n />\n ) : (\n <div\n className=\"grid grid-cols-1 gap-4 md:grid-cols-2\"\n data-tour=\"buildmyiq-dashboard-continue-cards\"\n >\n {activeEnrollments.slice(0, 4).map((e) => {\n const course = courseById.get(e.courseId);\n const title = course?.title ?? 'Course';\n const goToCourse = (ev?: MouseEvent) => {\n ev?.stopPropagation();\n navigateTo(`learning/${e.id}`);\n };\n return (\n <GlassCard\n key={e.id}\n treatment=\"plain\"\n className=\"group flex cursor-pointer flex-col overflow-hidden transition-shadow hover:shadow-lg sm:flex-row\"\n onClick={() => goToCourse()}\n >\n {/* Cover / play thumbnail */}\n <div className=\"relative h-36 w-full shrink-0 overflow-hidden bg-bg-sunken sm:h-auto sm:w-40\">\n {course?.cover?.downloadUrl ? (\n <img\n src={course.cover.downloadUrl}\n alt=\"\"\n className=\"h-full w-full object-cover transition-transform duration-500 group-hover:scale-105\"\n />\n ) : (\n <div className=\"flex h-full w-full items-center justify-center bg-gradient-to-br from-[var(--color-accent-soft)] to-bg-sunken\">\n <BookOpen className=\"h-8 w-8 text-text-secondary opacity-60\" />\n </div>\n )}\n <div className=\"absolute inset-0 flex items-center justify-center\">\n <span className=\"inline-flex h-11 w-11 items-center justify-center rounded-full bg-bg-surface/85 shadow-lg backdrop-blur transition-transform duration-200 group-hover:scale-110\">\n <Play className=\"ml-0.5 h-5 w-5 fill-current text-[var(--color-accent-primary)]\" />\n </span>\n </div>\n </div>\n\n {/* Content */}\n <div className=\"flex min-w-0 flex-1 flex-col gap-2 p-4\">\n <p className=\"line-clamp-1 font-semibold text-text-primary\">{title}</p>\n {course?.summary && (\n <p className=\"line-clamp-2 text-sm text-text-secondary\">\n {course.summary}\n </p>\n )}\n <div className=\"mt-auto space-y-2.5 pt-2\">\n <div className=\"flex items-center gap-2\">\n <MiniBar value={e.progressPct} className=\"flex-1\" />\n <span className=\"shrink-0 text-xs font-semibold tabular-nums text-text-secondary\">\n {e.progressPct}%\n </span>\n </div>\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"truncate text-xs text-text-secondary\">\n Active {formatRelativeTime(e.lastActiveAt ?? e.enrolledAt)}\n </span>\n <Button\n size=\"sm\"\n className=\"min-h-[40px] shrink-0\"\n onClick={(ev: MouseEvent) => {\n bus.emitCustom('buildmyiq.dashboard.continue_course_clicked', {\n workspaceId,\n enrollmentId: e.id,\n courseId: e.courseId,\n });\n goToCourse(ev);\n }}\n data-tour=\"buildmyiq-dashboard-continue-button\"\n >\n <Play className=\"mr-1 h-4 w-4 fill-current\" />{' '}\n {tr('common.continue', 'Continue')}\n </Button>\n </div>\n </div>\n </div>\n </GlassCard>\n );\n })}\n </div>\n )}\n </Section>\n\n {/* Explore catalog */}\n <Section\n title={tr('buildmyiq.dashboard.exploreCatalog', 'Explore the catalog')}\n description={tr(\n 'buildmyiq.dashboard.exploreCatalogDescription',\n 'Newly published courses in your workspace.'\n )}\n action={\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={() => {\n bus.emitCustom('buildmyiq.dashboard.browse_all_catalog_clicked', { workspaceId });\n navigateTo('catalog');\n }}\n data-tour=\"buildmyiq-dashboard-browse-all\"\n >\n {tr('common.browseAll', 'Browse all')} <ArrowRight className=\"ml-1 h-4 w-4\" />\n </Button>\n }\n >\n {courses.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title={tr(\n 'buildmyiq.dashboard.noPublishedCoursesTitle',\n 'No published courses yet'\n )}\n description={tr(\n 'buildmyiq.dashboard.noPublishedCoursesDescription',\n 'Create and publish a course in Authoring to populate the catalog.'\n )}\n action={\n <Button\n onClick={() => {\n bus.emitCustom('buildmyiq.dashboard.go_to_authoring_clicked', {\n workspaceId,\n });\n navigateTo('authoring');\n }}\n data-tour=\"buildmyiq-dashboard-go-authoring\"\n >\n {tr('common.create', 'Go to Authoring')}\n </Button>\n }\n />\n ) : (\n <div\n className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4\"\n data-tour=\"buildmyiq-dashboard-catalog-grid\"\n >\n {courses.slice(0, 8).map((course) => (\n <CourseCard\n key={course.id}\n course={course}\n onClick={() => navigateTo(`catalog/${course.id}`)}\n />\n ))}\n </div>\n )}\n </Section>\n </div>\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
@@ -1,11 +1,11 @@
1
1
  import { useBuildmyiq as e } from "../../context/BuildmyiqContext.js";
2
+ import "../../context/index.js";
2
3
  import { cn as t } from "../../utils/cn.js";
3
4
  import { PageLayout as n } from "../../components/PageLayout.js";
4
5
  import { formatDuration as r, formatEnumLabel as i } from "../../utils/formatters.js";
5
6
  import { StatusBadge as a } from "../../components/StatusBadge.js";
6
7
  import { EmptyState as o } from "../../components/EmptyState.js";
7
8
  import { ErrorView as ee, LoadingView as te, Section as ne } from "../../components/StateViews.js";
8
- import "../../context/index.js";
9
9
  import { useBuiAssessment as re, useBuiCreateQuestion as s, useBuiPublishAssessment as c } from "../../hooks/useBuildmyiqApi.js";
10
10
  import "../../hooks/index.js";
11
11
  import "../../components/index.js";
@@ -1,4 +1,5 @@
1
1
  import { useBuildmyiq as e } from "../../context/BuildmyiqContext.js";
2
+ import "../../context/index.js";
2
3
  import { PageLayout as t } from "../../components/PageLayout.js";
3
4
  import { MetricsCard as n } from "../../components/MetricsCard.js";
4
5
  import { formatEnumLabel as ee, formatRelativeTime as te } from "../../utils/formatters.js";
@@ -7,7 +8,6 @@ import { EmptyState as i } from "../../components/EmptyState.js";
7
8
  import { ErrorView as ne, LoadingView as a } from "../../components/StateViews.js";
8
9
  import { DataTable as o } from "../../components/DataTable.js";
9
10
  import { FilterTabs as s } from "../../components/FilterTabs.js";
10
- import "../../context/index.js";
11
11
  import { useBuiAssessments as c, useBuiCourses as l, useBuiCreateAssessment as u } from "../../hooks/useBuildmyiqApi.js";
12
12
  import "../../hooks/index.js";
13
13
  import "../../components/index.js";
@@ -1,4 +1,5 @@
1
1
  import { useBuildmyiq as e } from "../../context/BuildmyiqContext.js";
2
+ import "../../context/index.js";
2
3
  import { cn as t } from "../../utils/cn.js";
3
4
  import { PageLayout as n } from "../../components/PageLayout.js";
4
5
  import { formatDuration as r, formatEnumLabel as i } from "../../utils/formatters.js";
@@ -6,7 +7,6 @@ import { StatusBadge as a } from "../../components/StatusBadge.js";
6
7
  import { EmptyState as o } from "../../components/EmptyState.js";
7
8
  import { ErrorView as s, LoadingView as c } from "../../components/StateViews.js";
8
9
  import { ProgressRing as l } from "../../components/Viz.js";
9
- import "../../context/index.js";
10
10
  import { useBuiAssessment as u, useBuiEnrollments as d, useBuiSubmitAssessment as f } from "../../hooks/useBuildmyiqApi.js";
11
11
  import "../../hooks/index.js";
12
12
  import "../../components/index.js";
@@ -1,11 +1,11 @@
1
1
  import { useBuildmyiq as e } from "../../context/BuildmyiqContext.js";
2
+ import "../../context/index.js";
2
3
  import { PageLayout as t } from "../../components/PageLayout.js";
3
4
  import { formatEnumLabel as n } from "../../utils/formatters.js";
4
5
  import { EmptyState as r } from "../../components/EmptyState.js";
5
6
  import { ErrorView as i, LoadingView as a } from "../../components/StateViews.js";
6
7
  import { FilterTabs as o } from "../../components/FilterTabs.js";
7
8
  import { Pagination as s } from "../../components/Pagination.js";
8
- import "../../context/index.js";
9
9
  import { useBuiCourses as c, useBuiCreateCourse as l, useBuiSkills as u } from "../../hooks/useBuildmyiqApi.js";
10
10
  import { useCursorPager as d } from "../../hooks/useCursorPager.js";
11
11
  import "../../hooks/index.js";
@@ -1,4 +1,5 @@
1
1
  import { useBuildmyiq as e } from "../../context/BuildmyiqContext.js";
2
+ import "../../context/index.js";
2
3
  import { cn as t } from "../../utils/cn.js";
3
4
  import { PageLayout as n } from "../../components/PageLayout.js";
4
5
  import { formatDate as r, formatDuration as i, formatEnumLabel as a } from "../../utils/formatters.js";
@@ -6,7 +7,6 @@ import { StatusBadge as o } from "../../components/StatusBadge.js";
6
7
  import { EmptyState as s } from "../../components/EmptyState.js";
7
8
  import { ErrorView as c, LoadingView as l, Section as u } from "../../components/StateViews.js";
8
9
  import { Tabs as d } from "../../components/Tabs.js";
9
- import "../../context/index.js";
10
10
  import { useBuiArchiveCourse as f, useBuiAssessments as p, useBuiCourse as m, useBuiCreateLesson as h, useBuiCreateModule as g, useBuiModules as _, useBuiPublishCourse as v, useBuiSkills as y, useBuiUpdateCourse as b } from "../../hooks/useBuildmyiqApi.js";
11
11
  import "../../hooks/index.js";
12
12
  import { EvidenceUpload as x } from "../../components/EvidenceUpload.js";
@@ -1,11 +1,11 @@
1
1
  import { useBuildmyiq as e } from "../../context/BuildmyiqContext.js";
2
+ import "../../context/index.js";
2
3
  import { PageLayout as t } from "../../components/PageLayout.js";
3
4
  import { formatEnumLabel as n } from "../../utils/formatters.js";
4
5
  import { EmptyState as r } from "../../components/EmptyState.js";
5
6
  import { ErrorView as i, LoadingView as a } from "../../components/StateViews.js";
6
7
  import { FilterTabs as o } from "../../components/FilterTabs.js";
7
8
  import { Pagination as s } from "../../components/Pagination.js";
8
- import "../../context/index.js";
9
9
  import { useBuiCourses as c } from "../../hooks/useBuildmyiqApi.js";
10
10
  import { useCursorPager as l } from "../../hooks/useCursorPager.js";
11
11
  import "../../hooks/index.js";
@@ -1,4 +1,5 @@
1
1
  import { useBuildmyiq as e } from "../../context/BuildmyiqContext.js";
2
+ import "../../context/index.js";
2
3
  import { cn as t } from "../../utils/cn.js";
3
4
  import { PageLayout as n } from "../../components/PageLayout.js";
4
5
  import { formatDuration as r, formatEnumLabel as i, formatHours as a } from "../../utils/formatters.js";
@@ -6,7 +7,6 @@ import { StatusBadge as o } from "../../components/StatusBadge.js";
6
7
  import { EmptyState as s } from "../../components/EmptyState.js";
7
8
  import { ErrorView as c, LoadingView as l, Section as u } from "../../components/StateViews.js";
8
9
  import { MiniBar as d } from "../../components/MiniBar.js";
9
- import "../../context/index.js";
10
10
  import { useBuiAssessments as f, useBuiCourse as ee, useBuiEnroll as p, useBuiEnrollments as m, useBuiModules as h } from "../../hooks/useBuildmyiqApi.js";
11
11
  import "../../hooks/index.js";
12
12
  import { BuiDiscussion as te } from "../../components/BuiDiscussion.js";
@@ -1,4 +1,5 @@
1
1
  import { useBuildmyiq as e } from "../../context/BuildmyiqContext.js";
2
+ import "../../context/index.js";
2
3
  import { PageLayout as t } from "../../components/PageLayout.js";
3
4
  import { MetricsCard as n } from "../../components/MetricsCard.js";
4
5
  import { formatDate as r, formatPercent as ee } from "../../utils/formatters.js";
@@ -9,7 +10,6 @@ import { DataTable as ie } from "../../components/DataTable.js";
9
10
  import { FilterTabs as ae } from "../../components/FilterTabs.js";
10
11
  import { Pagination as oe } from "../../components/Pagination.js";
11
12
  import { Avatar as se } from "../../components/Avatar.js";
12
- import "../../context/index.js";
13
13
  import { useBuiCertificates as ce, useBuiCourses as le, useBuiIssueCertificate as ue, useBuiRevokeCertificate as de } from "../../hooks/useBuildmyiqApi.js";
14
14
  import "../../hooks/index.js";
15
15
  import { FileLink as fe } from "../../components/FileLink.js";
@@ -1,4 +1,5 @@
1
1
  import { useBuildmyiq as e } from "../../context/BuildmyiqContext.js";
2
+ import "../../context/index.js";
2
3
  import { PageLayout as t } from "../../components/PageLayout.js";
3
4
  import { MetricsCard as n } from "../../components/MetricsCard.js";
4
5
  import { formatDate as r, formatEnumLabel as i, formatNumber as a } from "../../utils/formatters.js";
@@ -7,7 +8,6 @@ import { EmptyState as te } from "../../components/EmptyState.js";
7
8
  import { ErrorView as ne, LoadingView as re } from "../../components/StateViews.js";
8
9
  import { DataTable as ie } from "../../components/DataTable.js";
9
10
  import { FilterTabs as ae } from "../../components/FilterTabs.js";
10
- import "../../context/index.js";
11
11
  import { useBuiCohorts as o, useBuiCourses as s, useBuiCreateCohort as c } from "../../hooks/useBuildmyiqApi.js";
12
12
  import "../../hooks/index.js";
13
13
  import "../../components/index.js";
@@ -1,4 +1,5 @@
1
1
  import { useBuildmyiq as e } from "../../context/BuildmyiqContext.js";
2
+ import "../../context/index.js";
2
3
  import { cn as t } from "../../utils/cn.js";
3
4
  import { PageLayout as n } from "../../components/PageLayout.js";
4
5
  import { formatDuration as r, formatEnumLabel as i } from "../../utils/formatters.js";
@@ -7,7 +8,6 @@ import { EmptyState as o } from "../../components/EmptyState.js";
7
8
  import { ErrorView as s, LoadingView as c } from "../../components/StateViews.js";
8
9
  import { MiniBar as l } from "../../components/MiniBar.js";
9
10
  import { ProgressRing as u } from "../../components/Viz.js";
10
- import "../../context/index.js";
11
11
  import { useBuiCourse as ee, useBuiEnrollment as te, useBuiRecordLessonProgress as ne, useBuiTransitionEnrollment as re } from "../../hooks/useBuildmyiqApi.js";
12
12
  import "../../hooks/index.js";
13
13
  import { FileLink as d } from "../../components/FileLink.js";
@@ -1,4 +1,5 @@
1
1
  import { useBuildmyiq as e } from "../../context/BuildmyiqContext.js";
2
+ import "../../context/index.js";
2
3
  import { cn as t } from "../../utils/cn.js";
3
4
  import { PageLayout as n } from "../../components/PageLayout.js";
4
5
  import { formatDate as r, formatDateTime as i, formatRelativeTime as a } from "../../utils/formatters.js";
@@ -9,7 +10,6 @@ import { DataTable as d } from "../../components/DataTable.js";
9
10
  import { Tabs as f } from "../../components/Tabs.js";
10
11
  import { MiniBar as p } from "../../components/MiniBar.js";
11
12
  import { ProgressRing as m, Sparkline as h } from "../../components/Viz.js";
12
- import "../../context/index.js";
13
13
  import { useBuiCertificates as g, useBuiCourses as ee, useBuiEnrollments as te, useBuiSubmissions as ne } from "../../hooks/useBuildmyiqApi.js";
14
14
  import "../../hooks/index.js";
15
15
  import { FileLink as _ } from "../../components/FileLink.js";
@@ -1,4 +1,5 @@
1
1
  import { useBuildmyiq as e } from "../../context/BuildmyiqContext.js";
2
+ import "../../context/index.js";
2
3
  import { cn as t } from "../../utils/cn.js";
3
4
  import { PageLayout as n } from "../../components/PageLayout.js";
4
5
  import { formatHours as r } from "../../utils/formatters.js";
@@ -6,7 +7,6 @@ import { StatusBadge as i } from "../../components/StatusBadge.js";
6
7
  import { EmptyState as a } from "../../components/EmptyState.js";
7
8
  import { ErrorView as o, LoadingView as s } from "../../components/StateViews.js";
8
9
  import { ProgressRing as c } from "../../components/Viz.js";
9
- import "../../context/index.js";
10
10
  import { useBuiCourses as l, useBuiEnrollments as u, useBuiSkills as d } from "../../hooks/useBuildmyiqApi.js";
11
11
  import "../../hooks/index.js";
12
12
  import "../../components/index.js";
@@ -1,4 +1,5 @@
1
1
  import { useBuildmyiq as e } from "../../context/BuildmyiqContext.js";
2
+ import "../../context/index.js";
2
3
  import { PageLayout as t } from "../../components/PageLayout.js";
3
4
  import { MetricsCard as n } from "../../components/MetricsCard.js";
4
5
  import { formatDate as r, formatDateTime as i } from "../../utils/formatters.js";
@@ -9,7 +10,6 @@ import { DataTable as ne } from "../../components/DataTable.js";
9
10
  import { Tabs as s } from "../../components/Tabs.js";
10
11
  import { Pagination as c } from "../../components/Pagination.js";
11
12
  import { Avatar as l } from "../../components/Avatar.js";
12
- import "../../context/index.js";
13
13
  import { useBuiGradeSubmission as u, useBuiSubmissions as re } from "../../hooks/useBuildmyiqApi.js";
14
14
  import { useCursorPager as ie } from "../../hooks/useCursorPager.js";
15
15
  import "../../hooks/index.js";
@@ -0,0 +1,14 @@
1
+ import { useCallback as e } from "react";
2
+ import { useI18n as t } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
3
+ //#region src/buildmyiq/shared/hooks/useTr.ts
4
+ function n() {
5
+ let { t: n } = t();
6
+ return e((e, t, r) => {
7
+ let i = n(e, r), a = i === e ? t : i;
8
+ return r ? Object.entries(r).reduce((e, [t, n]) => e.replace(RegExp(`\\{\\{${t}\\}\\}`, "g"), String(n)), a) : a;
9
+ }, [n]);
10
+ }
11
+ //#endregion
12
+ export { n as useTr };
13
+
14
+ //# sourceMappingURL=useTr.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTr.js","names":[],"sources":["../../../../src/buildmyiq/shared/hooks/useTr.ts"],"sourcesContent":["import { useCallback } from 'react';\nimport { useI18n } from '@burdenoff/fe-libs/shared/providers/shell/I18nProvider';\n\n/**\n * useTr — translation hook with fallback semantics.\n *\n * Wraps `useI18n().t()` so that when a key has no translation (i.e. the\n * provider returns the key itself), the caller's English fallback is used\n * instead. Supports optional interpolation params using `{{key}}` placeholders.\n */\nexport function useTr(): (\n key: string,\n fallback: string,\n params?: Record<string, string | number>\n) => string {\n const { t } = useI18n();\n return useCallback(\n (key: string, fallback: string, params?: Record<string, string | number>): string => {\n const translated = t(key, params);\n const raw = translated === key ? fallback : translated;\n if (!params) return raw;\n return Object.entries(params).reduce(\n (str, [k, v]) => str.replace(new RegExp(`\\\\{\\\\{${k}\\\\}\\\\}`, 'g'), String(v)),\n raw\n );\n },\n [t]\n );\n}\n"],"mappings":";;;AAUA,SAAgB,IAIJ;CACV,IAAM,EAAE,SAAM,GAAS;AACvB,QAAO,GACJ,GAAa,GAAkB,MAAqD;EACnF,IAAM,IAAa,EAAE,GAAK,EAAO,EAC3B,IAAM,MAAe,IAAM,IAAW;AAE5C,SADK,IACE,OAAO,QAAQ,EAAO,CAAC,QAC3B,GAAK,CAAC,GAAG,OAAO,EAAI,QAAY,OAAO,SAAS,EAAE,SAAS,IAAI,EAAE,OAAO,EAAE,CAAC,EAC5E,EACD,GAJmB;IAMtB,CAAC,EAAE,CACJ"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-buildmyiq",
3
- "version": "2026.706.11",
3
+ "version": "2026.709.1",
4
4
  "description": "Burdenoff BuildMyIQ Microfrontend — education, learning & development",
5
5
  "type": "module",
6
6
  "files": [