@burdenoff/microfe-buildmyiq 2026.716.1 → 2026.720.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/buildmyiq/BuildmyiqAdminRoot.d.ts.map +1 -1
- package/dist/buildmyiq/BuildmyiqAdminRoot.js +149 -135
- package/dist/buildmyiq/BuildmyiqAdminRoot.js.map +1 -1
- package/dist/buildmyiq/BuildmyiqRoot.d.ts.map +1 -1
- package/dist/buildmyiq/BuildmyiqRoot.js +59 -54
- package/dist/buildmyiq/BuildmyiqRoot.js.map +1 -1
- package/dist/buildmyiq/BuildmyiqRoutes.js.map +1 -1
- package/dist/buildmyiq/components/Avatar.js.map +1 -1
- package/dist/buildmyiq/components/BuiDiscussion.js.map +1 -1
- package/dist/buildmyiq/components/BuiPermissionButton.d.ts +25 -0
- package/dist/buildmyiq/components/BuiPermissionButton.d.ts.map +1 -0
- package/dist/buildmyiq/components/BuiPermissionButton.js +15 -0
- package/dist/buildmyiq/components/BuiPermissionButton.js.map +1 -0
- package/dist/buildmyiq/components/BuiTags.js.map +1 -1
- package/dist/buildmyiq/components/DataTable.js.map +1 -1
- package/dist/buildmyiq/components/EmptyState.js.map +1 -1
- package/dist/buildmyiq/components/EvidenceUpload.js.map +1 -1
- package/dist/buildmyiq/components/FileLink.js.map +1 -1
- package/dist/buildmyiq/components/FilterTabs.js.map +1 -1
- package/dist/buildmyiq/components/MetricsCard.js.map +1 -1
- package/dist/buildmyiq/components/MiniBar.js.map +1 -1
- package/dist/buildmyiq/components/PageLayout.js.map +1 -1
- package/dist/buildmyiq/components/Pagination.js.map +1 -1
- package/dist/buildmyiq/components/StateViews.js.map +1 -1
- package/dist/buildmyiq/components/StatusBadge.js.map +1 -1
- package/dist/buildmyiq/components/Tabs.js.map +1 -1
- package/dist/buildmyiq/components/Viz.js.map +1 -1
- package/dist/buildmyiq/components/index.d.ts +1 -0
- package/dist/buildmyiq/components/index.d.ts.map +1 -1
- package/dist/buildmyiq/components/index.js +1 -0
- package/dist/buildmyiq/context/BuildmyiqContext.js.map +1 -1
- package/dist/buildmyiq/hooks/index.d.ts +1 -0
- package/dist/buildmyiq/hooks/index.d.ts.map +1 -1
- package/dist/buildmyiq/hooks/index.js +1 -0
- package/dist/buildmyiq/hooks/useBuiComments.js.map +1 -1
- package/dist/buildmyiq/hooks/useBuiFiles.js.map +1 -1
- package/dist/buildmyiq/hooks/useBuiPermissions.d.ts +54 -0
- package/dist/buildmyiq/hooks/useBuiPermissions.d.ts.map +1 -0
- package/dist/buildmyiq/hooks/useBuiPermissions.js +2 -0
- package/dist/buildmyiq/hooks/useBuiTags.js.map +1 -1
- package/dist/buildmyiq/hooks/useBuildmyiqApi.js.map +1 -1
- package/dist/buildmyiq/hooks/useCursorPager.js.map +1 -1
- package/dist/buildmyiq/pages/DashboardPage.js.map +1 -1
- package/dist/buildmyiq/pages/_shared/CourseCard.js.map +1 -1
- package/dist/buildmyiq/pages/assessments/AssessmentDetailPage.js.map +1 -1
- package/dist/buildmyiq/pages/assessments/AssessmentsPage.js.map +1 -1
- package/dist/buildmyiq/pages/assessments/TakeAssessmentPage.js.map +1 -1
- package/dist/buildmyiq/pages/authoring/AuthoringPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/authoring/AuthoringPage.js +97 -86
- package/dist/buildmyiq/pages/authoring/AuthoringPage.js.map +1 -1
- package/dist/buildmyiq/pages/authoring/CourseBuilderPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/authoring/CourseBuilderPage.js +159 -118
- package/dist/buildmyiq/pages/authoring/CourseBuilderPage.js.map +1 -1
- package/dist/buildmyiq/pages/authoring/_forms.js.map +1 -1
- package/dist/buildmyiq/pages/catalog/CatalogPage.js.map +1 -1
- package/dist/buildmyiq/pages/catalog/CourseDetailPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/catalog/CourseDetailPage.js +75 -69
- package/dist/buildmyiq/pages/catalog/CourseDetailPage.js.map +1 -1
- package/dist/buildmyiq/pages/certificates/CertificatesPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/certificates/CertificatesPage.js +186 -170
- package/dist/buildmyiq/pages/certificates/CertificatesPage.js.map +1 -1
- package/dist/buildmyiq/pages/cohorts/CohortsPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/cohorts/CohortsPage.js +177 -166
- package/dist/buildmyiq/pages/cohorts/CohortsPage.js.map +1 -1
- package/dist/buildmyiq/pages/insights/InsightsPage.js.map +1 -1
- package/dist/buildmyiq/pages/learning/LessonPlayerPage.js.map +1 -1
- package/dist/buildmyiq/pages/learning/MyLearningPage.js.map +1 -1
- package/dist/buildmyiq/pages/paths/PathsPage.js.map +1 -1
- package/dist/buildmyiq/pages/skills/SkillsPage.js.map +1 -1
- package/dist/buildmyiq/pages/submissions/SubmissionsPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/submissions/SubmissionsPage.js +160 -154
- package/dist/buildmyiq/pages/submissions/SubmissionsPage.js.map +1 -1
- package/dist/buildmyiq/shared/hooks/useTr.js.map +1 -1
- package/dist/buildmyiq/types/index.js.map +1 -1
- package/dist/buildmyiq/utils/cn.js.map +1 -1
- package/dist/buildmyiq/utils/formatters.js.map +1 -1
- package/dist/buildmyiq/utils/navigation.js.map +1 -1
- package/dist/node_modules/clsx/dist/clsx.js.map +1 -1
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +1 -1
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -1
- package/dist/services/createSandboxAssistantTransport.js.map +1 -1
- package/dist/utils/pageContext.d.ts.map +1 -1
- package/dist/utils/pageContext.js +7 -2
- package/dist/utils/pageContext.js.map +1 -1
- package/package.json +2 -2
|
@@ -6,31 +6,32 @@ import { formatDuration as r, formatEnumLabel as i, formatHours as a } from "../
|
|
|
6
6
|
import { StatusBadge as o } from "../../components/StatusBadge.js";
|
|
7
7
|
import { EmptyState as s } from "../../components/EmptyState.js";
|
|
8
8
|
import { ErrorView as c, LoadingView as l, Section as u } from "../../components/StateViews.js";
|
|
9
|
-
import { MiniBar as
|
|
10
|
-
import { useBuiAssessments as
|
|
9
|
+
import { MiniBar as ee } from "../../components/MiniBar.js";
|
|
10
|
+
import { useBuiAssessments as d, useBuiCourse as f, useBuiEnroll as p, useBuiEnrollments as te, useBuiModules as m } from "../../hooks/useBuildmyiqApi.js";
|
|
11
11
|
import "../../hooks/index.js";
|
|
12
|
-
import { BuiDiscussion as
|
|
12
|
+
import { BuiDiscussion as h } from "../../components/BuiDiscussion.js";
|
|
13
13
|
import { BuiTags as ne } from "../../components/BuiTags.js";
|
|
14
|
+
import { BuiPermissionButton as re } from "../../components/BuiPermissionButton.js";
|
|
14
15
|
import "../../components/index.js";
|
|
15
16
|
import { useMemo as g, useState as _ } from "react";
|
|
16
|
-
import { ArrowLeft as
|
|
17
|
+
import { ArrowLeft as ie, ArrowRight as v, BookOpen as ae, ChevronDown as oe, ClipboardCheck as y, ClipboardList as b, Clock as x, FileText as S, GraduationCap as se, Layers as C, ListChecks as w, MousePointerClick as T, PlayCircle as E, Radio as D, Sparkles as O, Video as k } from "lucide-react";
|
|
17
18
|
import { useParams as A } from "react-router-dom";
|
|
18
19
|
import { jsx as j, jsxs as M } from "react/jsx-runtime";
|
|
19
20
|
import { Button as N, GlassCard as P } from "@burdenoff/fe-libs/ui";
|
|
20
21
|
//#region src/buildmyiq/pages/catalog/CourseDetailPage.tsx
|
|
21
|
-
var F = "buildmyiq-course",
|
|
22
|
-
ARTICLE:
|
|
22
|
+
var F = "buildmyiq-course", ce = "__ungrouped__", I = {
|
|
23
|
+
ARTICLE: S,
|
|
23
24
|
VIDEO: k,
|
|
24
|
-
QUIZ:
|
|
25
|
-
ASSIGNMENT:
|
|
26
|
-
INTERACTIVE:
|
|
27
|
-
LIVE_SESSION:
|
|
25
|
+
QUIZ: w,
|
|
26
|
+
ASSIGNMENT: b,
|
|
27
|
+
INTERACTIVE: T,
|
|
28
|
+
LIVE_SESSION: D
|
|
28
29
|
}, L = ({ lesson: e, index: t }) => /* @__PURE__ */ M("li", {
|
|
29
30
|
className: "flex items-center gap-3 px-4 py-3 transition-colors hover:bg-bg-sunken/60",
|
|
30
31
|
children: [
|
|
31
32
|
/* @__PURE__ */ j("span", {
|
|
32
33
|
className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary",
|
|
33
|
-
children: /* @__PURE__ */ j(I[e.type] ??
|
|
34
|
+
children: /* @__PURE__ */ j(I[e.type] ?? S, { className: "h-4 w-4" })
|
|
34
35
|
}),
|
|
35
36
|
/* @__PURE__ */ M("div", {
|
|
36
37
|
className: "min-w-0 flex-1",
|
|
@@ -48,7 +49,7 @@ var F = "buildmyiq-course", se = "__ungrouped__", I = {
|
|
|
48
49
|
e.durationMin > 0 && /* @__PURE__ */ M("span", {
|
|
49
50
|
className: "hidden shrink-0 items-center gap-1 text-xs tabular-nums text-text-secondary sm:inline-flex",
|
|
50
51
|
children: [
|
|
51
|
-
/* @__PURE__ */ j(
|
|
52
|
+
/* @__PURE__ */ j(x, { className: "h-3.5 w-3.5" }),
|
|
52
53
|
" ",
|
|
53
54
|
r(e.durationMin)
|
|
54
55
|
]
|
|
@@ -65,11 +66,11 @@ var F = "buildmyiq-course", se = "__ungrouped__", I = {
|
|
|
65
66
|
"aria-hidden": !0
|
|
66
67
|
}), t]
|
|
67
68
|
}), z = () => {
|
|
68
|
-
let { courseId: i } = A(), { currentUser:
|
|
69
|
+
let { courseId: i } = A(), { currentUser: b, navigateTo: S } = e(), w = b?.id, T = f(i), D = m(i), k = d(i), I = te({
|
|
69
70
|
courseId: i,
|
|
70
|
-
learnerId:
|
|
71
|
-
enabled: !!(i &&
|
|
72
|
-
}), z = p(), [B, V] = _(null), [H, U] = _(null), W =
|
|
71
|
+
learnerId: w,
|
|
72
|
+
enabled: !!(i && w)
|
|
73
|
+
}), z = p(), [B, V] = _(null), [H, U] = _(null), W = T.data ?? null, G = D.data ?? [], K = k.data ?? [], q = I.data?.items ?? [], J = g(() => q.find((e) => e.status !== "DROPPED" && e.status !== "EXPIRED"), [q]), Y = g(() => (W?.lessons ?? []).filter((e) => !e.moduleId), [W]), X = g(() => {
|
|
73
74
|
let e = G.map((e) => ({
|
|
74
75
|
id: e.id,
|
|
75
76
|
title: e.title,
|
|
@@ -77,23 +78,23 @@ var F = "buildmyiq-course", se = "__ungrouped__", I = {
|
|
|
77
78
|
lessons: e.lessons ?? []
|
|
78
79
|
}));
|
|
79
80
|
return Y.length > 0 && e.push({
|
|
80
|
-
id:
|
|
81
|
+
id: ce,
|
|
81
82
|
title: "Additional lessons",
|
|
82
83
|
lessons: Y
|
|
83
84
|
}), e;
|
|
84
|
-
}, [G, Y]), Z = W?.lessons?.length ?? 0,
|
|
85
|
+
}, [G, Y]), Z = W?.lessons?.length ?? 0, le = K.filter((e) => e.status === "PUBLISHED").length, Q = H ?? new Set(X.slice(0, 1).map((e) => e.id)), $ = X.length > 0 && X.every((e) => Q.has(e.id)), ue = (e) => {
|
|
85
86
|
let t = new Set(Q);
|
|
86
87
|
t.has(e) ? t.delete(e) : t.add(e), U(t);
|
|
87
88
|
};
|
|
88
|
-
return i ?
|
|
89
|
+
return i ? T.isLoading ? /* @__PURE__ */ j(n, {
|
|
89
90
|
title: "Course",
|
|
90
91
|
children: /* @__PURE__ */ j(l, { label: "Loading course…" })
|
|
91
|
-
}) :
|
|
92
|
+
}) : T.isError ? /* @__PURE__ */ j(n, {
|
|
92
93
|
title: "Course",
|
|
93
94
|
children: /* @__PURE__ */ j(c, {
|
|
94
95
|
title: "Couldn't load this course",
|
|
95
|
-
message:
|
|
96
|
-
onRetry: () =>
|
|
96
|
+
message: T.error instanceof Error ? T.error.message : void 0,
|
|
97
|
+
onRetry: () => T.refetch()
|
|
97
98
|
})
|
|
98
99
|
}) : W ? /* @__PURE__ */ j(n, {
|
|
99
100
|
title: W.title,
|
|
@@ -108,7 +109,7 @@ var F = "buildmyiq-course", se = "__ungrouped__", I = {
|
|
|
108
109
|
children: [
|
|
109
110
|
/* @__PURE__ */ j("button", {
|
|
110
111
|
type: "button",
|
|
111
|
-
onClick: () =>
|
|
112
|
+
onClick: () => S("catalog"),
|
|
112
113
|
className: "shrink-0 font-medium text-[var(--color-accent-teal)] transition-colors hover:opacity-80",
|
|
113
114
|
children: "Catalog"
|
|
114
115
|
}),
|
|
@@ -123,9 +124,9 @@ var F = "buildmyiq-course", se = "__ungrouped__", I = {
|
|
|
123
124
|
]
|
|
124
125
|
}), /* @__PURE__ */ M("button", {
|
|
125
126
|
type: "button",
|
|
126
|
-
onClick: () =>
|
|
127
|
+
onClick: () => S("catalog"),
|
|
127
128
|
className: "inline-flex shrink-0 items-center gap-1.5 text-sm text-text-secondary transition-colors hover:text-text-primary",
|
|
128
|
-
children: [/* @__PURE__ */ j(
|
|
129
|
+
children: [/* @__PURE__ */ j(ie, { className: "h-4 w-4" }), " Back to catalog"]
|
|
129
130
|
})]
|
|
130
131
|
}),
|
|
131
132
|
/* @__PURE__ */ M("div", {
|
|
@@ -143,7 +144,7 @@ var F = "buildmyiq-course", se = "__ungrouped__", I = {
|
|
|
143
144
|
className: "h-full w-full object-cover"
|
|
144
145
|
}) : /* @__PURE__ */ j("div", {
|
|
145
146
|
className: "flex h-full min-h-[11rem] w-full items-center justify-center bg-[var(--color-accent-soft)]",
|
|
146
|
-
children: /* @__PURE__ */ j(
|
|
147
|
+
children: /* @__PURE__ */ j(ae, { className: "h-10 w-10 text-text-secondary opacity-60" })
|
|
147
148
|
})
|
|
148
149
|
}), /* @__PURE__ */ M("div", {
|
|
149
150
|
className: "flex flex-col gap-4 p-6",
|
|
@@ -163,11 +164,11 @@ var F = "buildmyiq-course", se = "__ungrouped__", I = {
|
|
|
163
164
|
className: "flex flex-wrap gap-2",
|
|
164
165
|
children: [
|
|
165
166
|
W.skill?.name && /* @__PURE__ */ j(R, {
|
|
166
|
-
icon:
|
|
167
|
+
icon: O,
|
|
167
168
|
children: W.skill.name
|
|
168
169
|
}),
|
|
169
170
|
/* @__PURE__ */ M(R, {
|
|
170
|
-
icon:
|
|
171
|
+
icon: C,
|
|
171
172
|
children: [
|
|
172
173
|
G.length,
|
|
173
174
|
" module",
|
|
@@ -179,7 +180,7 @@ var F = "buildmyiq-course", se = "__ungrouped__", I = {
|
|
|
179
180
|
]
|
|
180
181
|
}),
|
|
181
182
|
W.estimatedHours > 0 && /* @__PURE__ */ j(R, {
|
|
182
|
-
icon:
|
|
183
|
+
icon: x,
|
|
183
184
|
children: a(W.estimatedHours)
|
|
184
185
|
})
|
|
185
186
|
]
|
|
@@ -208,8 +209,8 @@ var F = "buildmyiq-course", se = "__ungrouped__", I = {
|
|
|
208
209
|
/* @__PURE__ */ M(N, {
|
|
209
210
|
size: "lg",
|
|
210
211
|
className: "w-full",
|
|
211
|
-
onClick: () =>
|
|
212
|
-
children: [/* @__PURE__ */ j(
|
|
212
|
+
onClick: () => S(`learning/${J.id}`),
|
|
213
|
+
children: [/* @__PURE__ */ j(E, { className: "mr-2 h-4 w-4" }), " Continue learning"]
|
|
213
214
|
}),
|
|
214
215
|
/* @__PURE__ */ M("div", {
|
|
215
216
|
className: "flex flex-col gap-1.5",
|
|
@@ -222,7 +223,7 @@ var F = "buildmyiq-course", se = "__ungrouped__", I = {
|
|
|
222
223
|
className: "font-semibold tabular-nums text-text-primary",
|
|
223
224
|
children: [J.progressPct, "%"]
|
|
224
225
|
})]
|
|
225
|
-
}), /* @__PURE__ */ j(
|
|
226
|
+
}), /* @__PURE__ */ j(ee, { value: J.progressPct })]
|
|
226
227
|
}),
|
|
227
228
|
/* @__PURE__ */ M("div", {
|
|
228
229
|
className: "flex items-center justify-between border-t border-border-seam pt-3 text-xs",
|
|
@@ -235,30 +236,35 @@ var F = "buildmyiq-course", se = "__ungrouped__", I = {
|
|
|
235
236
|
}) : /* @__PURE__ */ M("div", {
|
|
236
237
|
className: "flex flex-col gap-4",
|
|
237
238
|
children: [
|
|
238
|
-
/* @__PURE__ */
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
239
|
+
/* @__PURE__ */ j(re, {
|
|
240
|
+
resource: "buildmyiq-enrollment",
|
|
241
|
+
action: "create",
|
|
242
|
+
deniedBehavior: "tooltip",
|
|
243
|
+
children: /* @__PURE__ */ M(N, {
|
|
244
|
+
size: "lg",
|
|
245
|
+
className: "w-full",
|
|
246
|
+
onClick: async () => {
|
|
247
|
+
if (!(!i || !w)) {
|
|
248
|
+
V(null);
|
|
249
|
+
try {
|
|
250
|
+
S(`learning/${(await z.mutateAsync({
|
|
251
|
+
courseId: i,
|
|
252
|
+
learnerId: w
|
|
253
|
+
})).id}`);
|
|
254
|
+
} catch (e) {
|
|
255
|
+
V(e instanceof Error ? e.message : "Could not enrol in this course.");
|
|
256
|
+
}
|
|
251
257
|
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
258
|
+
},
|
|
259
|
+
disabled: z.isPending || !w,
|
|
260
|
+
children: [
|
|
261
|
+
/* @__PURE__ */ j(se, { className: "mr-2 h-4 w-4" }),
|
|
262
|
+
z.isPending ? "Enrolling…" : "Enrol in this course",
|
|
263
|
+
!z.isPending && /* @__PURE__ */ j(v, { className: "ml-2 h-4 w-4" })
|
|
264
|
+
]
|
|
265
|
+
})
|
|
260
266
|
}),
|
|
261
|
-
!
|
|
267
|
+
!w && /* @__PURE__ */ j("p", {
|
|
262
268
|
className: "text-xs text-text-tertiary",
|
|
263
269
|
children: "Sign in to your workspace to enrol."
|
|
264
270
|
}),
|
|
@@ -273,7 +279,7 @@ var F = "buildmyiq-course", se = "__ungrouped__", I = {
|
|
|
273
279
|
className: "flex items-center justify-between",
|
|
274
280
|
children: [/* @__PURE__ */ M("dt", {
|
|
275
281
|
className: "inline-flex items-center gap-1.5 text-text-secondary",
|
|
276
|
-
children: [/* @__PURE__ */ j(
|
|
282
|
+
children: [/* @__PURE__ */ j(C, { className: "h-3.5 w-3.5 text-text-tertiary" }), " Lessons"]
|
|
277
283
|
}), /* @__PURE__ */ j("dd", {
|
|
278
284
|
className: "font-medium tabular-nums text-text-primary",
|
|
279
285
|
children: Z
|
|
@@ -283,7 +289,7 @@ var F = "buildmyiq-course", se = "__ungrouped__", I = {
|
|
|
283
289
|
className: "flex items-center justify-between",
|
|
284
290
|
children: [/* @__PURE__ */ M("dt", {
|
|
285
291
|
className: "inline-flex items-center gap-1.5 text-text-secondary",
|
|
286
|
-
children: [/* @__PURE__ */ j(
|
|
292
|
+
children: [/* @__PURE__ */ j(x, { className: "h-3.5 w-3.5 text-text-tertiary" }), " Duration"]
|
|
287
293
|
}), /* @__PURE__ */ j("dd", {
|
|
288
294
|
className: "font-medium text-text-primary",
|
|
289
295
|
children: a(W.estimatedHours)
|
|
@@ -293,10 +299,10 @@ var F = "buildmyiq-course", se = "__ungrouped__", I = {
|
|
|
293
299
|
className: "flex items-center justify-between",
|
|
294
300
|
children: [/* @__PURE__ */ M("dt", {
|
|
295
301
|
className: "inline-flex items-center gap-1.5 text-text-secondary",
|
|
296
|
-
children: [/* @__PURE__ */ j(
|
|
302
|
+
children: [/* @__PURE__ */ j(y, { className: "h-3.5 w-3.5 text-text-tertiary" }), " Assessments"]
|
|
297
303
|
}), /* @__PURE__ */ j("dd", {
|
|
298
304
|
className: "font-medium tabular-nums text-text-primary",
|
|
299
|
-
children:
|
|
305
|
+
children: le
|
|
300
306
|
})]
|
|
301
307
|
})
|
|
302
308
|
]
|
|
@@ -318,10 +324,10 @@ var F = "buildmyiq-course", se = "__ungrouped__", I = {
|
|
|
318
324
|
className: "text-sm font-medium text-[var(--color-accent-teal)] transition-colors hover:opacity-80",
|
|
319
325
|
children: $ ? "Collapse all" : "Expand all"
|
|
320
326
|
}) : void 0,
|
|
321
|
-
children:
|
|
327
|
+
children: D.isLoading ? /* @__PURE__ */ j(l, { label: "Loading curriculum…" }) : D.isError ? /* @__PURE__ */ j(c, {
|
|
322
328
|
title: "Couldn't load the curriculum",
|
|
323
|
-
message:
|
|
324
|
-
onRetry: () =>
|
|
329
|
+
message: D.error instanceof Error ? D.error.message : void 0,
|
|
330
|
+
onRetry: () => D.refetch()
|
|
325
331
|
}) : X.length === 0 ? /* @__PURE__ */ j(s, {
|
|
326
332
|
illustration: "empty-data",
|
|
327
333
|
title: "No lessons yet",
|
|
@@ -334,7 +340,7 @@ var F = "buildmyiq-course", se = "__ungrouped__", I = {
|
|
|
334
340
|
className: t("overflow-hidden rounded-xl border bg-bg-surface transition-colors", r ? "border-[var(--color-accent-soft)]" : "border-border-seam"),
|
|
335
341
|
children: [/* @__PURE__ */ M("button", {
|
|
336
342
|
type: "button",
|
|
337
|
-
onClick: () =>
|
|
343
|
+
onClick: () => ue(e.id),
|
|
338
344
|
"aria-expanded": r,
|
|
339
345
|
"aria-controls": i,
|
|
340
346
|
className: "flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-bg-sunken/50",
|
|
@@ -361,7 +367,7 @@ var F = "buildmyiq-course", se = "__ungrouped__", I = {
|
|
|
361
367
|
e.lessons.length === 1 ? "" : "s"
|
|
362
368
|
]
|
|
363
369
|
}),
|
|
364
|
-
/* @__PURE__ */ j(
|
|
370
|
+
/* @__PURE__ */ j(oe, {
|
|
365
371
|
className: t("h-4 w-4 shrink-0 text-text-tertiary transition-transform", r && "rotate-180"),
|
|
366
372
|
"aria-hidden": !0
|
|
367
373
|
})
|
|
@@ -391,7 +397,7 @@ var F = "buildmyiq-course", se = "__ungrouped__", I = {
|
|
|
391
397
|
onRetry: () => k.refetch()
|
|
392
398
|
}) : K.length === 0 ? /* @__PURE__ */ j(s, {
|
|
393
399
|
illustration: "empty-data",
|
|
394
|
-
icon: /* @__PURE__ */ j(
|
|
400
|
+
icon: /* @__PURE__ */ j(y, { className: "h-9 w-9" }),
|
|
395
401
|
title: "No assessments yet",
|
|
396
402
|
description: "This course doesn't have any quizzes or exams attached."
|
|
397
403
|
}) : /* @__PURE__ */ j("ul", {
|
|
@@ -402,7 +408,7 @@ var F = "buildmyiq-course", se = "__ungrouped__", I = {
|
|
|
402
408
|
className: "flex items-start gap-3",
|
|
403
409
|
children: [/* @__PURE__ */ j("span", {
|
|
404
410
|
className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary",
|
|
405
|
-
children: /* @__PURE__ */ j(
|
|
411
|
+
children: /* @__PURE__ */ j(y, { className: "h-4 w-4" })
|
|
406
412
|
}), /* @__PURE__ */ M("div", {
|
|
407
413
|
className: "min-w-0 flex-1",
|
|
408
414
|
children: [
|
|
@@ -429,7 +435,7 @@ var F = "buildmyiq-course", se = "__ungrouped__", I = {
|
|
|
429
435
|
] }),
|
|
430
436
|
e.timeLimitMin > 0 && /* @__PURE__ */ M("span", {
|
|
431
437
|
className: "inline-flex items-center gap-1",
|
|
432
|
-
children: [/* @__PURE__ */ j(
|
|
438
|
+
children: [/* @__PURE__ */ j(x, { className: "h-3.5 w-3.5" }), r(e.timeLimitMin)]
|
|
433
439
|
})
|
|
434
440
|
]
|
|
435
441
|
})
|
|
@@ -439,13 +445,13 @@ var F = "buildmyiq-course", se = "__ungrouped__", I = {
|
|
|
439
445
|
className: "mt-3 flex justify-end",
|
|
440
446
|
children: /* @__PURE__ */ j(N, {
|
|
441
447
|
size: "sm",
|
|
442
|
-
onClick: () =>
|
|
448
|
+
onClick: () => S(`assessments/${e.id}/take`),
|
|
443
449
|
children: "Take"
|
|
444
450
|
})
|
|
445
451
|
})]
|
|
446
452
|
}, e.id))
|
|
447
453
|
})
|
|
448
|
-
}) }), /* @__PURE__ */ j("div", { children: /* @__PURE__ */ j(
|
|
454
|
+
}) }), /* @__PURE__ */ j("div", { children: /* @__PURE__ */ j(h, {
|
|
449
455
|
entityType: F,
|
|
450
456
|
entityId: i,
|
|
451
457
|
description: "Ask questions and discuss this course with your workspace."
|
|
@@ -465,7 +471,7 @@ var F = "buildmyiq-course", se = "__ungrouped__", I = {
|
|
|
465
471
|
title: "Course not found",
|
|
466
472
|
description: "This course may have been unpublished or removed.",
|
|
467
473
|
action: /* @__PURE__ */ j(N, {
|
|
468
|
-
onClick: () =>
|
|
474
|
+
onClick: () => S("catalog"),
|
|
469
475
|
children: "Back to catalog"
|
|
470
476
|
})
|
|
471
477
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CourseDetailPage.js","names":[],"sources":["../../../../src/buildmyiq/pages/catalog/CourseDetailPage.tsx"],"sourcesContent":["/**\n * CourseDetailPage — a single published course: hero, curriculum, assessments,\n * discussion + tags, and the enrol / continue-learning call to action.\n * @module buildmyiq/pages/catalog\n *\n * All data is live: `buiCourse` (hero + flat lessons), `buiListModules`\n * (curriculum), `buiListAssessments` (course exams), and `buiListEnrollments`\n * (to decide Enrol vs Continue). Enrolling calls `buiEnroll` then routes the\n * learner straight into their new enrollment. Every list has its own\n * loading / error / empty state.\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport type { LucideIcon } from 'lucide-react';\nimport {\n ArrowLeft,\n ArrowRight,\n BookOpen,\n ChevronDown,\n ClipboardCheck,\n ClipboardList,\n Clock,\n FileText,\n GraduationCap,\n Layers,\n ListChecks,\n MousePointerClick,\n PlayCircle,\n Radio,\n Sparkles,\n Video,\n} from 'lucide-react';\nimport { Button, GlassCard } from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n StatusBadge,\n Section,\n LoadingView,\n ErrorView,\n EmptyState,\n MiniBar,\n BuiDiscussion,\n BuiTags,\n} from '../../components';\nimport { cn } from '../../utils/cn';\nimport { useBuildmyiq } from '../../context';\nimport {\n useBuiCourse,\n useBuiModules,\n useBuiAssessments,\n useBuiEnrollments,\n useBuiEnroll,\n} from '../../hooks';\nimport { formatHours, formatDuration, formatEnumLabel } from '../../utils/formatters';\nimport type { BuiLesson, BuiLessonType } from '../../types';\n\nconst COURSE_ENTITY = 'buildmyiq-course';\nconst UNGROUPED_ID = '__ungrouped__';\n\n/** A curriculum group is either a real module or the \"additional lessons\" bucket. */\ninterface CurriculumGroup {\n id: string;\n title: string;\n description?: string | null;\n lessons: BuiLesson[];\n}\n\n/** Lesson-type → icon, so learners can scan a module at a glance. */\nconst LESSON_ICON: Record<BuiLessonType, LucideIcon> = {\n ARTICLE: FileText,\n VIDEO: Video,\n QUIZ: ListChecks,\n ASSIGNMENT: ClipboardList,\n INTERACTIVE: MousePointerClick,\n LIVE_SESSION: Radio,\n};\n\nconst LessonRow: FC<{ lesson: BuiLesson; index: number }> = ({ lesson, index }) => {\n const Icon = LESSON_ICON[lesson.type] ?? FileText;\n return (\n <li className=\"flex items-center gap-3 px-4 py-3 transition-colors hover:bg-bg-sunken/60\">\n <span className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <Icon className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n <span className=\"mr-1.5 tabular-nums text-text-tertiary\">{index + 1}.</span>\n {lesson.title}\n </p>\n <p className=\"text-xs text-text-tertiary\">{formatEnumLabel(lesson.type)}</p>\n </div>\n {lesson.durationMin > 0 && (\n <span className=\"hidden shrink-0 items-center gap-1 text-xs tabular-nums text-text-secondary sm:inline-flex\">\n <Clock className=\"h-3.5 w-3.5\" /> {formatDuration(lesson.durationMin)}\n </span>\n )}\n {lesson.isPreview && (\n <span className=\"shrink-0 rounded-full border border-[var(--color-accent-teal)] px-2 py-0.5 text-xs font-medium text-[var(--color-accent-teal)]\">\n Preview\n </span>\n )}\n </li>\n );\n};\n\n/** A single hero meta pill (skill / structure / duration). */\nconst MetaPill: FC<{ icon: LucideIcon; children: ReactNode }> = ({ icon: Icon, children }) => (\n <span className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam bg-bg-surface px-3 py-1.5 text-xs font-medium text-text-secondary\">\n <Icon className=\"h-3.5 w-3.5 text-text-tertiary\" aria-hidden />\n {children}\n </span>\n);\n\nexport const CourseDetailPage: FC = () => {\n const { courseId } = useParams<{ courseId: string }>();\n const { currentUser, navigateTo } = useBuildmyiq();\n const learnerId = currentUser?.id;\n\n const courseQuery = useBuiCourse(courseId);\n const modulesQuery = useBuiModules(courseId);\n const assessmentsQuery = useBuiAssessments(courseId);\n const enrollmentsQuery = useBuiEnrollments({\n courseId,\n learnerId,\n enabled: Boolean(courseId && learnerId),\n });\n\n const enroll = useBuiEnroll();\n const [enrolError, setEnrolError] = useState<string | null>(null);\n // null = \"not yet touched\" → default-open the first module only.\n const [openGroups, setOpenGroups] = useState<Set<string> | null>(null);\n\n const course = courseQuery.data ?? null;\n const modules = modulesQuery.data ?? [];\n const assessments = assessmentsQuery.data ?? [];\n const enrollments = enrollmentsQuery.data?.items ?? [];\n\n // An existing, non-terminal enrollment means \"Continue\" instead of \"Enrol\".\n const activeEnrollment = useMemo(\n () => enrollments.find((e) => e.status !== 'DROPPED' && e.status !== 'EXPIRED'),\n [enrollments]\n );\n\n // Lessons attached directly to the course (no module) — surfaced as their own group.\n const ungroupedLessons = useMemo(\n () => (course?.lessons ?? []).filter((lesson) => !lesson.moduleId),\n [course]\n );\n\n // Unified, ordered list of curriculum groups (modules + the leftovers bucket).\n const groups: CurriculumGroup[] = useMemo(() => {\n const moduleGroups: CurriculumGroup[] = modules.map((m) => ({\n id: m.id,\n title: m.title,\n description: m.description,\n lessons: m.lessons ?? [],\n }));\n if (ungroupedLessons.length > 0) {\n moduleGroups.push({\n id: UNGROUPED_ID,\n title: 'Additional lessons',\n lessons: ungroupedLessons,\n });\n }\n return moduleGroups;\n }, [modules, ungroupedLessons]);\n\n const totalLessons = course?.lessons?.length ?? 0;\n const publishedAssessments = assessments.filter((a) => a.status === 'PUBLISHED').length;\n\n // Effective open-set: default to the first group when the learner hasn't toggled.\n const effectiveOpen = openGroups ?? new Set(groups.slice(0, 1).map((g) => g.id));\n const allOpen = groups.length > 0 && groups.every((g) => effectiveOpen.has(g.id));\n\n const toggleGroup = (id: string): void => {\n const next = new Set(effectiveOpen);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n setOpenGroups(next);\n };\n\n const toggleAll = (): void => {\n setOpenGroups(allOpen ? new Set() : new Set(groups.map((g) => g.id)));\n };\n\n const handleEnrol = async (): Promise<void> => {\n if (!courseId || !learnerId) return;\n setEnrolError(null);\n try {\n const created = await enroll.mutateAsync({ courseId, learnerId });\n navigateTo(`learning/${created.id}`);\n } catch (err) {\n setEnrolError(err instanceof Error ? err.message : 'Could not enrol in this course.');\n }\n };\n\n // Top-level page states.\n if (!courseId) {\n return (\n <PageLayout title=\"Course\">\n <ErrorView title=\"Missing course\" message=\"No course was specified.\" />\n </PageLayout>\n );\n }\n\n if (courseQuery.isLoading) {\n return (\n <PageLayout title=\"Course\">\n <LoadingView label=\"Loading course…\" />\n </PageLayout>\n );\n }\n\n if (courseQuery.isError) {\n return (\n <PageLayout title=\"Course\">\n <ErrorView\n title=\"Couldn't load this course\"\n message={courseQuery.error instanceof Error ? courseQuery.error.message : undefined}\n onRetry={() => courseQuery.refetch()}\n />\n </PageLayout>\n );\n }\n\n if (!course) {\n return (\n <PageLayout title=\"Course\">\n <EmptyState\n illustration=\"empty-search\"\n title=\"Course not found\"\n description=\"This course may have been unpublished or removed.\"\n action={<Button onClick={() => navigateTo('catalog')}>Back to catalog</Button>}\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout title={course.title}>\n <div className=\"mx-auto flex w-full max-w-6xl flex-col gap-6\">\n {/* Breadcrumb + back */}\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <nav className=\"flex min-w-0 items-center gap-1.5 text-sm\" aria-label=\"Breadcrumb\">\n <button\n type=\"button\"\n onClick={() => navigateTo('catalog')}\n className=\"shrink-0 font-medium text-[var(--color-accent-teal)] transition-colors hover:opacity-80\"\n >\n Catalog\n </button>\n <span className=\"shrink-0 text-text-tertiary\">/</span>\n <span className=\"truncate text-text-secondary\">{course.title}</span>\n </nav>\n <button\n type=\"button\"\n onClick={() => navigateTo('catalog')}\n className=\"inline-flex shrink-0 items-center gap-1.5 text-sm text-text-secondary transition-colors hover:text-text-primary\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back to catalog\n </button>\n </div>\n\n {/* Hero: cover + info (left) and enrol / progress card (right) */}\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-[minmax(0,1fr)_20rem]\">\n <GlassCard treatment=\"plain\" className=\"overflow-hidden\">\n <div className=\"grid grid-cols-1 sm:grid-cols-[minmax(0,15rem)_minmax(0,1fr)]\">\n <div className=\"relative h-44 w-full overflow-hidden bg-bg-sunken sm:h-full\">\n {course.cover?.downloadUrl ? (\n <img\n src={course.cover.downloadUrl}\n alt=\"\"\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"flex h-full min-h-[11rem] w-full items-center justify-center bg-[var(--color-accent-soft)]\">\n <BookOpen className=\"h-10 w-10 text-text-secondary opacity-60\" />\n </div>\n )}\n </div>\n\n <div className=\"flex flex-col gap-4 p-6\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <StatusBadge status={course.level} />\n <StatusBadge status={course.status} />\n </div>\n\n <div>\n <h1 className=\"text-2xl font-semibold tracking-tight text-text-primary\">\n {course.title}\n </h1>\n {course.summary && (\n <p className=\"mt-2 text-sm text-text-secondary\">{course.summary}</p>\n )}\n </div>\n\n <div className=\"flex flex-wrap gap-2\">\n {course.skill?.name && <MetaPill icon={Sparkles}>{course.skill.name}</MetaPill>}\n <MetaPill icon={Layers}>\n {modules.length} module{modules.length === 1 ? '' : 's'} · {totalLessons} lesson\n {totalLessons === 1 ? '' : 's'}\n </MetaPill>\n {course.estimatedHours > 0 && (\n <MetaPill icon={Clock}>{formatHours(course.estimatedHours)}</MetaPill>\n )}\n </div>\n\n {course.tags.length > 0 && (\n <div className=\"flex flex-wrap gap-1.5\">\n {course.tags.map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs text-text-secondary\"\n >\n {tag}\n </span>\n ))}\n </div>\n )}\n\n {course.description && (\n <p className=\"whitespace-pre-wrap text-sm leading-relaxed text-text-secondary\">\n {course.description}\n </p>\n )}\n </div>\n </div>\n </GlassCard>\n\n {/* Enrol / progress card */}\n <GlassCard treatment=\"glass\" glow className=\"h-fit p-5 lg:sticky lg:top-4\">\n {activeEnrollment ? (\n <div className=\"flex flex-col gap-4\">\n <Button\n size=\"lg\"\n className=\"w-full\"\n onClick={() => navigateTo(`learning/${activeEnrollment.id}`)}\n >\n <PlayCircle className=\"mr-2 h-4 w-4\" /> Continue learning\n </Button>\n <div className=\"flex flex-col gap-1.5\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-secondary\">Your progress</span>\n <span className=\"font-semibold tabular-nums text-text-primary\">\n {activeEnrollment.progressPct}%\n </span>\n </div>\n <MiniBar value={activeEnrollment.progressPct} />\n </div>\n <div className=\"flex items-center justify-between border-t border-border-seam pt-3 text-xs\">\n <span className=\"text-text-secondary\">Enrollment</span>\n <StatusBadge status={activeEnrollment.status} />\n </div>\n </div>\n ) : (\n <div className=\"flex flex-col gap-4\">\n <Button\n size=\"lg\"\n className=\"w-full\"\n onClick={handleEnrol}\n disabled={enroll.isPending || !learnerId}\n >\n <GraduationCap className=\"mr-2 h-4 w-4\" />\n {enroll.isPending ? 'Enrolling…' : 'Enrol in this course'}\n {!enroll.isPending && <ArrowRight className=\"ml-2 h-4 w-4\" />}\n </Button>\n {!learnerId && (\n <p className=\"text-xs text-text-tertiary\">Sign in to your workspace to enrol.</p>\n )}\n {enrolError && <p className=\"text-xs text-status-error-text\">{enrolError}</p>}\n <dl className=\"flex flex-col gap-2.5 border-t border-border-seam pt-3 text-sm\">\n <div className=\"flex items-center justify-between\">\n <dt className=\"inline-flex items-center gap-1.5 text-text-secondary\">\n <Layers className=\"h-3.5 w-3.5 text-text-tertiary\" /> Lessons\n </dt>\n <dd className=\"font-medium tabular-nums text-text-primary\">{totalLessons}</dd>\n </div>\n {course.estimatedHours > 0 && (\n <div className=\"flex items-center justify-between\">\n <dt className=\"inline-flex items-center gap-1.5 text-text-secondary\">\n <Clock className=\"h-3.5 w-3.5 text-text-tertiary\" /> Duration\n </dt>\n <dd className=\"font-medium text-text-primary\">\n {formatHours(course.estimatedHours)}\n </dd>\n </div>\n )}\n <div className=\"flex items-center justify-between\">\n <dt className=\"inline-flex items-center gap-1.5 text-text-secondary\">\n <ClipboardCheck className=\"h-3.5 w-3.5 text-text-tertiary\" /> Assessments\n </dt>\n <dd className=\"font-medium tabular-nums text-text-primary\">\n {publishedAssessments}\n </dd>\n </div>\n </dl>\n </div>\n )}\n </GlassCard>\n </div>\n\n {/* Curriculum (left) + Assessments & Discussion (right) */}\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-[minmax(0,1.7fr)_minmax(0,1fr)]\">\n {/* Curriculum */}\n <div>\n <Section\n title=\"Curriculum\"\n description=\"Modules and lessons in this course.\"\n action={\n groups.length > 1 ? (\n <button\n type=\"button\"\n onClick={toggleAll}\n className=\"text-sm font-medium text-[var(--color-accent-teal)] transition-colors hover:opacity-80\"\n >\n {allOpen ? 'Collapse all' : 'Expand all'}\n </button>\n ) : undefined\n }\n >\n {modulesQuery.isLoading ? (\n <LoadingView label=\"Loading curriculum…\" />\n ) : modulesQuery.isError ? (\n <ErrorView\n title=\"Couldn't load the curriculum\"\n message={\n modulesQuery.error instanceof Error ? modulesQuery.error.message : undefined\n }\n onRetry={() => modulesQuery.refetch()}\n />\n ) : groups.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No lessons yet\"\n description=\"The instructor hasn't added any modules or lessons to this course.\"\n />\n ) : (\n <div className=\"flex flex-col gap-3\">\n {groups.map((group, index) => {\n const open = effectiveOpen.has(group.id);\n const panelId = `curriculum-panel-${group.id}`;\n return (\n <div\n key={group.id}\n className={cn(\n 'overflow-hidden rounded-xl border bg-bg-surface transition-colors',\n open ? 'border-[var(--color-accent-soft)]' : 'border-border-seam'\n )}\n >\n <button\n type=\"button\"\n onClick={() => toggleGroup(group.id)}\n aria-expanded={open}\n aria-controls={panelId}\n className=\"flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-bg-sunken/50\"\n >\n <span className=\"flex h-7 w-7 shrink-0 items-center justify-center rounded-lg bg-action-primary-bg text-xs font-semibold text-action-primary-text\">\n {index + 1}\n </span>\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"truncate font-semibold text-text-primary\">\n {group.title}\n </h3>\n {group.description && open && (\n <p className=\"mt-0.5 line-clamp-2 text-sm text-text-secondary\">\n {group.description}\n </p>\n )}\n </div>\n <span className=\"shrink-0 text-xs text-text-tertiary\">\n {group.lessons.length} lesson{group.lessons.length === 1 ? '' : 's'}\n </span>\n <ChevronDown\n className={cn(\n 'h-4 w-4 shrink-0 text-text-tertiary transition-transform',\n open && 'rotate-180'\n )}\n aria-hidden\n />\n </button>\n {open &&\n (group.lessons.length === 0 ? (\n <p className=\"border-t border-border-seam px-4 py-3 text-sm text-text-tertiary\">\n No lessons in this module yet.\n </p>\n ) : (\n <ul\n id={panelId}\n className=\"divide-y divide-border-seam border-t border-border-seam\"\n >\n {group.lessons.map((lesson, li) => (\n <LessonRow key={lesson.id} lesson={lesson} index={li} />\n ))}\n </ul>\n ))}\n </div>\n );\n })}\n </div>\n )}\n </Section>\n </div>\n\n {/* Assessments + Discussion */}\n <div className=\"flex flex-col gap-6\">\n <div>\n <Section title=\"Assessments\" description=\"Quizzes and exams for this course.\">\n {assessmentsQuery.isLoading ? (\n <LoadingView label=\"Loading assessments…\" />\n ) : assessmentsQuery.isError ? (\n <ErrorView\n title=\"Couldn't load assessments\"\n message={\n assessmentsQuery.error instanceof Error\n ? assessmentsQuery.error.message\n : undefined\n }\n onRetry={() => assessmentsQuery.refetch()}\n />\n ) : assessments.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<ClipboardCheck className=\"h-9 w-9\" />}\n title=\"No assessments yet\"\n description=\"This course doesn't have any quizzes or exams attached.\"\n />\n ) : (\n <ul className=\"flex flex-col gap-3\">\n {assessments.map((assessment) => (\n <li\n key={assessment.id}\n className=\"rounded-xl border border-border-seam bg-bg-surface p-4\"\n >\n <div className=\"flex items-start gap-3\">\n <span className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <ClipboardCheck className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"font-medium text-text-primary\">{assessment.title}</p>\n <div className=\"mt-1.5 flex flex-wrap items-center gap-1.5\">\n <StatusBadge status={assessment.type} />\n <StatusBadge status={assessment.status} />\n </div>\n <div className=\"mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-text-secondary\">\n <span>Pass ≥ {assessment.passingScore}%</span>\n <span>\n {assessment.maxAttempts} attempt\n {assessment.maxAttempts === 1 ? '' : 's'}\n </span>\n {assessment.timeLimitMin > 0 && (\n <span className=\"inline-flex items-center gap-1\">\n <Clock className=\"h-3.5 w-3.5\" />\n {formatDuration(assessment.timeLimitMin)}\n </span>\n )}\n </div>\n </div>\n </div>\n {assessment.status === 'PUBLISHED' && activeEnrollment && (\n <div className=\"mt-3 flex justify-end\">\n <Button\n size=\"sm\"\n onClick={() => navigateTo(`assessments/${assessment.id}/take`)}\n >\n Take\n </Button>\n </div>\n )}\n </li>\n ))}\n </ul>\n )}\n </Section>\n </div>\n\n {/* Discussion (platform wspace-conversations) */}\n <div>\n <BuiDiscussion\n entityType={COURSE_ENTITY}\n entityId={courseId}\n description=\"Ask questions and discuss this course with your workspace.\"\n />\n </div>\n </div>\n </div>\n\n {/* Tags (platform wspace-tags) — full width */}\n <div>\n <BuiTags entityType={COURSE_ENTITY} entityId={courseId} />\n </div>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CourseDetailPage.js","names":[],"sources":["../../../../src/buildmyiq/pages/catalog/CourseDetailPage.tsx"],"sourcesContent":["/**\n * CourseDetailPage — a single published course: hero, curriculum, assessments,\n * discussion + tags, and the enrol / continue-learning call to action.\n * @module buildmyiq/pages/catalog\n *\n * All data is live: `buiCourse` (hero + flat lessons), `buiListModules`\n * (curriculum), `buiListAssessments` (course exams), and `buiListEnrollments`\n * (to decide Enrol vs Continue). Enrolling calls `buiEnroll` then routes the\n * learner straight into their new enrollment. Every list has its own\n * loading / error / empty state.\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { useMemo, useState } from 'react';\nimport { useParams } from 'react-router-dom';\nimport type { LucideIcon } from 'lucide-react';\nimport {\n ArrowLeft,\n ArrowRight,\n BookOpen,\n ChevronDown,\n ClipboardCheck,\n ClipboardList,\n Clock,\n FileText,\n GraduationCap,\n Layers,\n ListChecks,\n MousePointerClick,\n PlayCircle,\n Radio,\n Sparkles,\n Video,\n} from 'lucide-react';\nimport { Button, GlassCard } from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n StatusBadge,\n Section,\n LoadingView,\n ErrorView,\n EmptyState,\n MiniBar,\n BuiDiscussion,\n BuiTags,\n} from '../../components';\nimport { cn } from '../../utils/cn';\nimport { useBuildmyiq } from '../../context';\nimport {\n useBuiCourse,\n useBuiModules,\n useBuiAssessments,\n useBuiEnrollments,\n useBuiEnroll,\n} from '../../hooks';\nimport { formatHours, formatDuration, formatEnumLabel } from '../../utils/formatters';\nimport type { BuiLesson, BuiLessonType } from '../../types';\nimport { BuiPermissionButton } from '../../components/BuiPermissionButton';\n\nconst COURSE_ENTITY = 'buildmyiq-course';\nconst UNGROUPED_ID = '__ungrouped__';\n\n/** A curriculum group is either a real module or the \"additional lessons\" bucket. */\ninterface CurriculumGroup {\n id: string;\n title: string;\n description?: string | null;\n lessons: BuiLesson[];\n}\n\n/** Lesson-type → icon, so learners can scan a module at a glance. */\nconst LESSON_ICON: Record<BuiLessonType, LucideIcon> = {\n ARTICLE: FileText,\n VIDEO: Video,\n QUIZ: ListChecks,\n ASSIGNMENT: ClipboardList,\n INTERACTIVE: MousePointerClick,\n LIVE_SESSION: Radio,\n};\n\nconst LessonRow: FC<{ lesson: BuiLesson; index: number }> = ({ lesson, index }) => {\n const Icon = LESSON_ICON[lesson.type] ?? FileText;\n return (\n <li className=\"flex items-center gap-3 px-4 py-3 transition-colors hover:bg-bg-sunken/60\">\n <span className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <Icon className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n <span className=\"mr-1.5 tabular-nums text-text-tertiary\">{index + 1}.</span>\n {lesson.title}\n </p>\n <p className=\"text-xs text-text-tertiary\">{formatEnumLabel(lesson.type)}</p>\n </div>\n {lesson.durationMin > 0 && (\n <span className=\"hidden shrink-0 items-center gap-1 text-xs tabular-nums text-text-secondary sm:inline-flex\">\n <Clock className=\"h-3.5 w-3.5\" /> {formatDuration(lesson.durationMin)}\n </span>\n )}\n {lesson.isPreview && (\n <span className=\"shrink-0 rounded-full border border-[var(--color-accent-teal)] px-2 py-0.5 text-xs font-medium text-[var(--color-accent-teal)]\">\n Preview\n </span>\n )}\n </li>\n );\n};\n\n/** A single hero meta pill (skill / structure / duration). */\nconst MetaPill: FC<{ icon: LucideIcon; children: ReactNode }> = ({ icon: Icon, children }) => (\n <span className=\"inline-flex items-center gap-1.5 rounded-lg border border-border-seam bg-bg-surface px-3 py-1.5 text-xs font-medium text-text-secondary\">\n <Icon className=\"h-3.5 w-3.5 text-text-tertiary\" aria-hidden />\n {children}\n </span>\n);\n\nexport const CourseDetailPage: FC = () => {\n const { courseId } = useParams<{ courseId: string }>();\n const { currentUser, navigateTo } = useBuildmyiq();\n const learnerId = currentUser?.id;\n\n const courseQuery = useBuiCourse(courseId);\n const modulesQuery = useBuiModules(courseId);\n const assessmentsQuery = useBuiAssessments(courseId);\n const enrollmentsQuery = useBuiEnrollments({\n courseId,\n learnerId,\n enabled: Boolean(courseId && learnerId),\n });\n\n const enroll = useBuiEnroll();\n const [enrolError, setEnrolError] = useState<string | null>(null);\n // null = \"not yet touched\" → default-open the first module only.\n const [openGroups, setOpenGroups] = useState<Set<string> | null>(null);\n\n const course = courseQuery.data ?? null;\n const modules = modulesQuery.data ?? [];\n const assessments = assessmentsQuery.data ?? [];\n const enrollments = enrollmentsQuery.data?.items ?? [];\n\n // An existing, non-terminal enrollment means \"Continue\" instead of \"Enrol\".\n const activeEnrollment = useMemo(\n () => enrollments.find((e) => e.status !== 'DROPPED' && e.status !== 'EXPIRED'),\n [enrollments]\n );\n\n // Lessons attached directly to the course (no module) — surfaced as their own group.\n const ungroupedLessons = useMemo(\n () => (course?.lessons ?? []).filter((lesson) => !lesson.moduleId),\n [course]\n );\n\n // Unified, ordered list of curriculum groups (modules + the leftovers bucket).\n const groups: CurriculumGroup[] = useMemo(() => {\n const moduleGroups: CurriculumGroup[] = modules.map((m) => ({\n id: m.id,\n title: m.title,\n description: m.description,\n lessons: m.lessons ?? [],\n }));\n if (ungroupedLessons.length > 0) {\n moduleGroups.push({\n id: UNGROUPED_ID,\n title: 'Additional lessons',\n lessons: ungroupedLessons,\n });\n }\n return moduleGroups;\n }, [modules, ungroupedLessons]);\n\n const totalLessons = course?.lessons?.length ?? 0;\n const publishedAssessments = assessments.filter((a) => a.status === 'PUBLISHED').length;\n\n // Effective open-set: default to the first group when the learner hasn't toggled.\n const effectiveOpen = openGroups ?? new Set(groups.slice(0, 1).map((g) => g.id));\n const allOpen = groups.length > 0 && groups.every((g) => effectiveOpen.has(g.id));\n\n const toggleGroup = (id: string): void => {\n const next = new Set(effectiveOpen);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n setOpenGroups(next);\n };\n\n const toggleAll = (): void => {\n setOpenGroups(allOpen ? new Set() : new Set(groups.map((g) => g.id)));\n };\n\n const handleEnrol = async (): Promise<void> => {\n if (!courseId || !learnerId) return;\n setEnrolError(null);\n try {\n const created = await enroll.mutateAsync({ courseId, learnerId });\n navigateTo(`learning/${created.id}`);\n } catch (err) {\n setEnrolError(err instanceof Error ? err.message : 'Could not enrol in this course.');\n }\n };\n\n // Top-level page states.\n if (!courseId) {\n return (\n <PageLayout title=\"Course\">\n <ErrorView title=\"Missing course\" message=\"No course was specified.\" />\n </PageLayout>\n );\n }\n\n if (courseQuery.isLoading) {\n return (\n <PageLayout title=\"Course\">\n <LoadingView label=\"Loading course…\" />\n </PageLayout>\n );\n }\n\n if (courseQuery.isError) {\n return (\n <PageLayout title=\"Course\">\n <ErrorView\n title=\"Couldn't load this course\"\n message={courseQuery.error instanceof Error ? courseQuery.error.message : undefined}\n onRetry={() => courseQuery.refetch()}\n />\n </PageLayout>\n );\n }\n\n if (!course) {\n return (\n <PageLayout title=\"Course\">\n <EmptyState\n illustration=\"empty-search\"\n title=\"Course not found\"\n description=\"This course may have been unpublished or removed.\"\n action={<Button onClick={() => navigateTo('catalog')}>Back to catalog</Button>}\n />\n </PageLayout>\n );\n }\n\n return (\n <PageLayout title={course.title}>\n <div className=\"mx-auto flex w-full max-w-6xl flex-col gap-6\">\n {/* Breadcrumb + back */}\n <div className=\"flex flex-wrap items-center justify-between gap-3\">\n <nav className=\"flex min-w-0 items-center gap-1.5 text-sm\" aria-label=\"Breadcrumb\">\n <button\n type=\"button\"\n onClick={() => navigateTo('catalog')}\n className=\"shrink-0 font-medium text-[var(--color-accent-teal)] transition-colors hover:opacity-80\"\n >\n Catalog\n </button>\n <span className=\"shrink-0 text-text-tertiary\">/</span>\n <span className=\"truncate text-text-secondary\">{course.title}</span>\n </nav>\n <button\n type=\"button\"\n onClick={() => navigateTo('catalog')}\n className=\"inline-flex shrink-0 items-center gap-1.5 text-sm text-text-secondary transition-colors hover:text-text-primary\"\n >\n <ArrowLeft className=\"h-4 w-4\" /> Back to catalog\n </button>\n </div>\n\n {/* Hero: cover + info (left) and enrol / progress card (right) */}\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-[minmax(0,1fr)_20rem]\">\n <GlassCard treatment=\"plain\" className=\"overflow-hidden\">\n <div className=\"grid grid-cols-1 sm:grid-cols-[minmax(0,15rem)_minmax(0,1fr)]\">\n <div className=\"relative h-44 w-full overflow-hidden bg-bg-sunken sm:h-full\">\n {course.cover?.downloadUrl ? (\n <img\n src={course.cover.downloadUrl}\n alt=\"\"\n className=\"h-full w-full object-cover\"\n />\n ) : (\n <div className=\"flex h-full min-h-[11rem] w-full items-center justify-center bg-[var(--color-accent-soft)]\">\n <BookOpen className=\"h-10 w-10 text-text-secondary opacity-60\" />\n </div>\n )}\n </div>\n\n <div className=\"flex flex-col gap-4 p-6\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <StatusBadge status={course.level} />\n <StatusBadge status={course.status} />\n </div>\n\n <div>\n <h1 className=\"text-2xl font-semibold tracking-tight text-text-primary\">\n {course.title}\n </h1>\n {course.summary && (\n <p className=\"mt-2 text-sm text-text-secondary\">{course.summary}</p>\n )}\n </div>\n\n <div className=\"flex flex-wrap gap-2\">\n {course.skill?.name && <MetaPill icon={Sparkles}>{course.skill.name}</MetaPill>}\n <MetaPill icon={Layers}>\n {modules.length} module{modules.length === 1 ? '' : 's'} · {totalLessons} lesson\n {totalLessons === 1 ? '' : 's'}\n </MetaPill>\n {course.estimatedHours > 0 && (\n <MetaPill icon={Clock}>{formatHours(course.estimatedHours)}</MetaPill>\n )}\n </div>\n\n {course.tags.length > 0 && (\n <div className=\"flex flex-wrap gap-1.5\">\n {course.tags.map((tag) => (\n <span\n key={tag}\n className=\"rounded-full bg-bg-sunken px-2.5 py-0.5 text-xs text-text-secondary\"\n >\n {tag}\n </span>\n ))}\n </div>\n )}\n\n {course.description && (\n <p className=\"whitespace-pre-wrap text-sm leading-relaxed text-text-secondary\">\n {course.description}\n </p>\n )}\n </div>\n </div>\n </GlassCard>\n\n {/* Enrol / progress card */}\n <GlassCard treatment=\"glass\" glow className=\"h-fit p-5 lg:sticky lg:top-4\">\n {activeEnrollment ? (\n <div className=\"flex flex-col gap-4\">\n <Button\n size=\"lg\"\n className=\"w-full\"\n onClick={() => navigateTo(`learning/${activeEnrollment.id}`)}\n >\n <PlayCircle className=\"mr-2 h-4 w-4\" /> Continue learning\n </Button>\n <div className=\"flex flex-col gap-1.5\">\n <div className=\"flex items-center justify-between text-sm\">\n <span className=\"text-text-secondary\">Your progress</span>\n <span className=\"font-semibold tabular-nums text-text-primary\">\n {activeEnrollment.progressPct}%\n </span>\n </div>\n <MiniBar value={activeEnrollment.progressPct} />\n </div>\n <div className=\"flex items-center justify-between border-t border-border-seam pt-3 text-xs\">\n <span className=\"text-text-secondary\">Enrollment</span>\n <StatusBadge status={activeEnrollment.status} />\n </div>\n </div>\n ) : (\n <div className=\"flex flex-col gap-4\">\n <BuiPermissionButton\n resource=\"buildmyiq-enrollment\"\n action=\"create\"\n deniedBehavior=\"tooltip\"\n >\n <Button\n size=\"lg\"\n className=\"w-full\"\n onClick={handleEnrol}\n disabled={enroll.isPending || !learnerId}\n >\n <GraduationCap className=\"mr-2 h-4 w-4\" />\n {enroll.isPending ? 'Enrolling…' : 'Enrol in this course'}\n {!enroll.isPending && <ArrowRight className=\"ml-2 h-4 w-4\" />}\n </Button>\n </BuiPermissionButton>\n {!learnerId && (\n <p className=\"text-xs text-text-tertiary\">Sign in to your workspace to enrol.</p>\n )}\n {enrolError && <p className=\"text-xs text-status-error-text\">{enrolError}</p>}\n <dl className=\"flex flex-col gap-2.5 border-t border-border-seam pt-3 text-sm\">\n <div className=\"flex items-center justify-between\">\n <dt className=\"inline-flex items-center gap-1.5 text-text-secondary\">\n <Layers className=\"h-3.5 w-3.5 text-text-tertiary\" /> Lessons\n </dt>\n <dd className=\"font-medium tabular-nums text-text-primary\">{totalLessons}</dd>\n </div>\n {course.estimatedHours > 0 && (\n <div className=\"flex items-center justify-between\">\n <dt className=\"inline-flex items-center gap-1.5 text-text-secondary\">\n <Clock className=\"h-3.5 w-3.5 text-text-tertiary\" /> Duration\n </dt>\n <dd className=\"font-medium text-text-primary\">\n {formatHours(course.estimatedHours)}\n </dd>\n </div>\n )}\n <div className=\"flex items-center justify-between\">\n <dt className=\"inline-flex items-center gap-1.5 text-text-secondary\">\n <ClipboardCheck className=\"h-3.5 w-3.5 text-text-tertiary\" /> Assessments\n </dt>\n <dd className=\"font-medium tabular-nums text-text-primary\">\n {publishedAssessments}\n </dd>\n </div>\n </dl>\n </div>\n )}\n </GlassCard>\n </div>\n\n {/* Curriculum (left) + Assessments & Discussion (right) */}\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-[minmax(0,1.7fr)_minmax(0,1fr)]\">\n {/* Curriculum */}\n <div>\n <Section\n title=\"Curriculum\"\n description=\"Modules and lessons in this course.\"\n action={\n groups.length > 1 ? (\n <button\n type=\"button\"\n onClick={toggleAll}\n className=\"text-sm font-medium text-[var(--color-accent-teal)] transition-colors hover:opacity-80\"\n >\n {allOpen ? 'Collapse all' : 'Expand all'}\n </button>\n ) : undefined\n }\n >\n {modulesQuery.isLoading ? (\n <LoadingView label=\"Loading curriculum…\" />\n ) : modulesQuery.isError ? (\n <ErrorView\n title=\"Couldn't load the curriculum\"\n message={\n modulesQuery.error instanceof Error ? modulesQuery.error.message : undefined\n }\n onRetry={() => modulesQuery.refetch()}\n />\n ) : groups.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No lessons yet\"\n description=\"The instructor hasn't added any modules or lessons to this course.\"\n />\n ) : (\n <div className=\"flex flex-col gap-3\">\n {groups.map((group, index) => {\n const open = effectiveOpen.has(group.id);\n const panelId = `curriculum-panel-${group.id}`;\n return (\n <div\n key={group.id}\n className={cn(\n 'overflow-hidden rounded-xl border bg-bg-surface transition-colors',\n open ? 'border-[var(--color-accent-soft)]' : 'border-border-seam'\n )}\n >\n <button\n type=\"button\"\n onClick={() => toggleGroup(group.id)}\n aria-expanded={open}\n aria-controls={panelId}\n className=\"flex w-full items-center gap-3 px-4 py-3 text-left transition-colors hover:bg-bg-sunken/50\"\n >\n <span className=\"flex h-7 w-7 shrink-0 items-center justify-center rounded-lg bg-action-primary-bg text-xs font-semibold text-action-primary-text\">\n {index + 1}\n </span>\n <div className=\"min-w-0 flex-1\">\n <h3 className=\"truncate font-semibold text-text-primary\">\n {group.title}\n </h3>\n {group.description && open && (\n <p className=\"mt-0.5 line-clamp-2 text-sm text-text-secondary\">\n {group.description}\n </p>\n )}\n </div>\n <span className=\"shrink-0 text-xs text-text-tertiary\">\n {group.lessons.length} lesson{group.lessons.length === 1 ? '' : 's'}\n </span>\n <ChevronDown\n className={cn(\n 'h-4 w-4 shrink-0 text-text-tertiary transition-transform',\n open && 'rotate-180'\n )}\n aria-hidden\n />\n </button>\n {open &&\n (group.lessons.length === 0 ? (\n <p className=\"border-t border-border-seam px-4 py-3 text-sm text-text-tertiary\">\n No lessons in this module yet.\n </p>\n ) : (\n <ul\n id={panelId}\n className=\"divide-y divide-border-seam border-t border-border-seam\"\n >\n {group.lessons.map((lesson, li) => (\n <LessonRow key={lesson.id} lesson={lesson} index={li} />\n ))}\n </ul>\n ))}\n </div>\n );\n })}\n </div>\n )}\n </Section>\n </div>\n\n {/* Assessments + Discussion */}\n <div className=\"flex flex-col gap-6\">\n <div>\n <Section title=\"Assessments\" description=\"Quizzes and exams for this course.\">\n {assessmentsQuery.isLoading ? (\n <LoadingView label=\"Loading assessments…\" />\n ) : assessmentsQuery.isError ? (\n <ErrorView\n title=\"Couldn't load assessments\"\n message={\n assessmentsQuery.error instanceof Error\n ? assessmentsQuery.error.message\n : undefined\n }\n onRetry={() => assessmentsQuery.refetch()}\n />\n ) : assessments.length === 0 ? (\n <EmptyState\n illustration=\"empty-data\"\n icon={<ClipboardCheck className=\"h-9 w-9\" />}\n title=\"No assessments yet\"\n description=\"This course doesn't have any quizzes or exams attached.\"\n />\n ) : (\n <ul className=\"flex flex-col gap-3\">\n {assessments.map((assessment) => (\n <li\n key={assessment.id}\n className=\"rounded-xl border border-border-seam bg-bg-surface p-4\"\n >\n <div className=\"flex items-start gap-3\">\n <span className=\"flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-[var(--color-accent-soft)] text-text-primary\">\n <ClipboardCheck className=\"h-4 w-4\" />\n </span>\n <div className=\"min-w-0 flex-1\">\n <p className=\"font-medium text-text-primary\">{assessment.title}</p>\n <div className=\"mt-1.5 flex flex-wrap items-center gap-1.5\">\n <StatusBadge status={assessment.type} />\n <StatusBadge status={assessment.status} />\n </div>\n <div className=\"mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-text-secondary\">\n <span>Pass ≥ {assessment.passingScore}%</span>\n <span>\n {assessment.maxAttempts} attempt\n {assessment.maxAttempts === 1 ? '' : 's'}\n </span>\n {assessment.timeLimitMin > 0 && (\n <span className=\"inline-flex items-center gap-1\">\n <Clock className=\"h-3.5 w-3.5\" />\n {formatDuration(assessment.timeLimitMin)}\n </span>\n )}\n </div>\n </div>\n </div>\n {assessment.status === 'PUBLISHED' && activeEnrollment && (\n <div className=\"mt-3 flex justify-end\">\n <Button\n size=\"sm\"\n onClick={() => navigateTo(`assessments/${assessment.id}/take`)}\n >\n Take\n </Button>\n </div>\n )}\n </li>\n ))}\n </ul>\n )}\n </Section>\n </div>\n\n {/* Discussion (platform wspace-conversations) */}\n <div>\n <BuiDiscussion\n entityType={COURSE_ENTITY}\n entityId={courseId}\n description=\"Ask questions and discuss this course with your workspace.\"\n />\n </div>\n </div>\n </div>\n\n {/* Tags (platform wspace-tags) — full width */}\n <div>\n <BuiTags entityType={COURSE_ENTITY} entityId={courseId} />\n </div>\n </div>\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CertificatesPage.d.ts","sourceRoot":"","sources":["../../../../src/buildmyiq/pages/certificates/CertificatesPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAqB,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"CertificatesPage.d.ts","sourceRoot":"","sources":["../../../../src/buildmyiq/pages/certificates/CertificatesPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAqB,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAoEnD,eAAO,MAAM,gBAAgB,EAAE,EA4f9B,CAAC"}
|