@burdenoff/microfe-buildmyiq 2026.705.13 → 2026.706.10
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/BuildmyiqRoot.d.ts.map +1 -1
- package/dist/buildmyiq/BuildmyiqRoot.js +57 -49
- package/dist/buildmyiq/BuildmyiqRoot.js.map +1 -1
- package/dist/buildmyiq/BuildmyiqRoutes.d.ts.map +1 -1
- package/dist/buildmyiq/BuildmyiqRoutes.js +15 -11
- package/dist/buildmyiq/BuildmyiqRoutes.js.map +1 -1
- package/dist/buildmyiq/components/Avatar.d.ts +15 -0
- package/dist/buildmyiq/components/Avatar.d.ts.map +1 -0
- package/dist/buildmyiq/components/Avatar.js +42 -0
- package/dist/buildmyiq/components/Avatar.js.map +1 -0
- package/dist/buildmyiq/components/Viz.d.ts +25 -0
- package/dist/buildmyiq/components/Viz.d.ts.map +1 -0
- package/dist/buildmyiq/components/Viz.js +78 -0
- package/dist/buildmyiq/components/Viz.js.map +1 -0
- package/dist/buildmyiq/components/index.d.ts +2 -0
- package/dist/buildmyiq/components/index.d.ts.map +1 -1
- package/dist/buildmyiq/components/index.js +2 -0
- package/dist/buildmyiq/pages/DashboardPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/DashboardPage.js +200 -98
- package/dist/buildmyiq/pages/DashboardPage.js.map +1 -1
- package/dist/buildmyiq/pages/assessments/AssessmentDetailPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/assessments/AssessmentDetailPage.js +262 -241
- package/dist/buildmyiq/pages/assessments/AssessmentDetailPage.js.map +1 -1
- package/dist/buildmyiq/pages/assessments/AssessmentsPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/assessments/AssessmentsPage.js +142 -115
- package/dist/buildmyiq/pages/assessments/AssessmentsPage.js.map +1 -1
- package/dist/buildmyiq/pages/assessments/TakeAssessmentPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/assessments/TakeAssessmentPage.js +220 -156
- 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 +51 -46
- 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 +581 -448
- package/dist/buildmyiq/pages/authoring/CourseBuilderPage.js.map +1 -1
- package/dist/buildmyiq/pages/catalog/CatalogPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/catalog/CatalogPage.js +52 -42
- 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 +422 -301
- 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 +237 -151
- 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 +219 -138
- package/dist/buildmyiq/pages/cohorts/CohortsPage.js.map +1 -1
- package/dist/buildmyiq/pages/insights/InsightsPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/insights/InsightsPage.js +276 -152
- package/dist/buildmyiq/pages/insights/InsightsPage.js.map +1 -1
- package/dist/buildmyiq/pages/learning/LessonPlayerPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/learning/LessonPlayerPage.js +237 -174
- package/dist/buildmyiq/pages/learning/LessonPlayerPage.js.map +1 -1
- package/dist/buildmyiq/pages/learning/MyLearningPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/learning/MyLearningPage.js +307 -169
- package/dist/buildmyiq/pages/learning/MyLearningPage.js.map +1 -1
- package/dist/buildmyiq/pages/paths/PathsPage.d.ts +4 -0
- package/dist/buildmyiq/pages/paths/PathsPage.d.ts.map +1 -0
- package/dist/buildmyiq/pages/paths/PathsPage.js +187 -0
- package/dist/buildmyiq/pages/paths/PathsPage.js.map +1 -0
- package/dist/buildmyiq/pages/skills/SkillsPage.d.ts.map +1 -1
- package/dist/buildmyiq/pages/skills/SkillsPage.js +233 -140
- 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 +379 -275
- package/dist/buildmyiq/pages/submissions/SubmissionsPage.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,79 +3,127 @@ import { PageLayout as t } from "../../components/PageLayout.js";
|
|
|
3
3
|
import { MetricsCard as n } from "../../components/MetricsCard.js";
|
|
4
4
|
import { formatEnumLabel as r, formatPercent as i } from "../../utils/formatters.js";
|
|
5
5
|
import { EmptyState as a } from "../../components/EmptyState.js";
|
|
6
|
-
import {
|
|
7
|
-
import { MiniBar as
|
|
8
|
-
import {
|
|
6
|
+
import { ErrorView as o, LoadingView as s, Section as c } from "../../components/StateViews.js";
|
|
7
|
+
import { MiniBar as l } from "../../components/MiniBar.js";
|
|
8
|
+
import { ProgressRing as u, Sparkline as d } from "../../components/Viz.js";
|
|
9
|
+
import { useBuiCertificates as f, useBuiCourses as p, useBuiEnrollments as m, useBuiSubmissions as h } from "../../hooks/useBuildmyiqApi.js";
|
|
9
10
|
import "../../hooks/index.js";
|
|
10
11
|
import "../../components/index.js";
|
|
11
|
-
import { useMemo as
|
|
12
|
-
import { Award as
|
|
13
|
-
import { jsx as
|
|
14
|
-
import { GlassCard as
|
|
12
|
+
import { useMemo as g } from "react";
|
|
13
|
+
import { AlarmClock as _, Award as v, BookOpen as y, CheckCircle2 as b, Clock as x, GraduationCap as S, TrendingUp as C, User as w, Users as T, XCircle as E } from "lucide-react";
|
|
14
|
+
import { jsx as D, jsxs as O } from "react/jsx-runtime";
|
|
15
|
+
import { GlassCard as k } from "@burdenoff/fe-libs/ui";
|
|
15
16
|
//#region src/buildmyiq/pages/insights/InsightsPage.tsx
|
|
16
|
-
var
|
|
17
|
+
var A = [
|
|
17
18
|
"ENROLLED",
|
|
18
19
|
"IN_PROGRESS",
|
|
19
20
|
"COMPLETED",
|
|
20
21
|
"DROPPED",
|
|
21
22
|
"EXPIRED"
|
|
22
|
-
],
|
|
23
|
+
], j = {
|
|
24
|
+
ENROLLED: {
|
|
25
|
+
icon: w,
|
|
26
|
+
bar: "bg-[var(--color-accent-teal)]",
|
|
27
|
+
icon_tint: "text-[var(--color-accent-teal)]"
|
|
28
|
+
},
|
|
29
|
+
IN_PROGRESS: {
|
|
30
|
+
icon: x,
|
|
31
|
+
bar: "bg-[var(--color-accent-primary)]",
|
|
32
|
+
icon_tint: "text-[var(--color-accent-primary)]"
|
|
33
|
+
},
|
|
34
|
+
COMPLETED: {
|
|
35
|
+
icon: b,
|
|
36
|
+
bar: "bg-[var(--color-status-success-text)]",
|
|
37
|
+
icon_tint: "text-[var(--color-status-success-text)]"
|
|
38
|
+
},
|
|
39
|
+
DROPPED: {
|
|
40
|
+
icon: E,
|
|
41
|
+
bar: "bg-[var(--color-status-error-text)]",
|
|
42
|
+
icon_tint: "text-[var(--color-status-error-text)]"
|
|
43
|
+
},
|
|
44
|
+
EXPIRED: {
|
|
45
|
+
icon: _,
|
|
46
|
+
bar: "bg-[var(--color-text-tertiary)]",
|
|
47
|
+
icon_tint: "text-[var(--color-text-tertiary)]"
|
|
48
|
+
}
|
|
49
|
+
}, M = {
|
|
50
|
+
info: "bg-[var(--color-status-info-bg)] text-[var(--color-status-info-text)]",
|
|
51
|
+
success: "bg-[var(--color-status-success-bg)] text-[var(--color-status-success-text)]",
|
|
52
|
+
warning: "bg-[var(--color-status-warning-bg)] text-[var(--color-status-warning-text)]",
|
|
53
|
+
accent: "bg-[var(--color-accent-soft)] text-[var(--color-accent-teal)]"
|
|
54
|
+
}, N = ({ tint: t, children: n }) => /* @__PURE__ */ D("span", {
|
|
55
|
+
className: e("flex h-11 w-11 items-center justify-center rounded-full", M[t]),
|
|
56
|
+
children: n
|
|
57
|
+
}), P = ({ rows: t }) => /* @__PURE__ */ D("div", {
|
|
23
58
|
className: "flex flex-col gap-3",
|
|
24
|
-
children:
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
59
|
+
children: t.map((t) => {
|
|
60
|
+
let n = j[t.status], r = n.icon;
|
|
61
|
+
return /* @__PURE__ */ O("div", {
|
|
62
|
+
className: "flex items-center gap-3",
|
|
63
|
+
children: [
|
|
64
|
+
/* @__PURE__ */ O("span", {
|
|
65
|
+
className: "flex w-24 shrink-0 items-center gap-2 text-sm text-text-secondary sm:w-28",
|
|
66
|
+
children: [/* @__PURE__ */ D(r, { className: e("h-4 w-4 shrink-0", n.icon_tint) }), /* @__PURE__ */ D("span", {
|
|
67
|
+
className: "truncate",
|
|
68
|
+
children: t.label
|
|
69
|
+
})]
|
|
70
|
+
}),
|
|
71
|
+
/* @__PURE__ */ D(l, {
|
|
72
|
+
value: t.pct,
|
|
73
|
+
barClassName: n.bar,
|
|
74
|
+
className: "flex-1",
|
|
75
|
+
height: 8
|
|
76
|
+
}),
|
|
77
|
+
/* @__PURE__ */ D("span", {
|
|
78
|
+
className: "w-9 shrink-0 text-right text-sm tabular-nums text-text-primary",
|
|
79
|
+
children: t.count
|
|
80
|
+
})
|
|
81
|
+
]
|
|
82
|
+
}, t.status);
|
|
83
|
+
})
|
|
84
|
+
}), F = () => {
|
|
85
|
+
let _ = p({ limit: 100 }), b = m({ limit: 100 }), x = h({ limit: 100 }), w = f(), E = g(() => _.data?.items ?? [], [_.data]), j = g(() => b.data?.items ?? [], [b.data]), M = g(() => x.data?.items ?? [], [x.data]), F = w.data ?? [], I = g(() => {
|
|
86
|
+
let e = E.filter((e) => e.status === "PUBLISHED").length, t = j.filter((e) => e.status === "COMPLETED").length, n = j.filter((e) => e.status === "IN_PROGRESS").length, r = j.length > 0 ? t / j.length * 100 : 0, i = j.length > 0 ? j.reduce((e, t) => e + (t.progressPct ?? 0), 0) / j.length : 0, a = M.filter((e) => e.status === "GRADED"), o = a.filter((e) => e.passed === !0).length, s = a.length > 0 ? o / a.length * 100 : 0, c = new Set(j.map((e) => e.learnerId)).size;
|
|
46
87
|
return {
|
|
47
88
|
published: e,
|
|
89
|
+
totalCourses: E.length,
|
|
48
90
|
completed: t,
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
91
|
+
inProgress: n,
|
|
92
|
+
completionRate: r,
|
|
93
|
+
avgProgress: i,
|
|
94
|
+
gradedCount: a.length,
|
|
95
|
+
passed: o,
|
|
96
|
+
passRate: s,
|
|
97
|
+
activeLearners: c
|
|
54
98
|
};
|
|
55
99
|
}, [
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
]),
|
|
60
|
-
let e =
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
COMPLETED: "bg-[var(--color-status-success-text)]",
|
|
64
|
-
DROPPED: "bg-[var(--color-status-error-text)]",
|
|
65
|
-
EXPIRED: "bg-[var(--color-text-tertiary)]"
|
|
66
|
-
};
|
|
67
|
-
return C.map((n) => {
|
|
68
|
-
let i = A.filter((e) => e.status === n).length;
|
|
100
|
+
E,
|
|
101
|
+
j,
|
|
102
|
+
M
|
|
103
|
+
]), L = g(() => {
|
|
104
|
+
let e = j.length;
|
|
105
|
+
return A.map((t) => {
|
|
106
|
+
let n = j.filter((e) => e.status === t).length;
|
|
69
107
|
return {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
108
|
+
status: t,
|
|
109
|
+
label: r(t),
|
|
110
|
+
count: n,
|
|
111
|
+
pct: e > 0 ? n / e * 100 : 0
|
|
74
112
|
};
|
|
75
113
|
}).filter((e) => e.count > 0);
|
|
76
|
-
}, [
|
|
77
|
-
|
|
78
|
-
|
|
114
|
+
}, [j]), R = g(() => {
|
|
115
|
+
if (j.length === 0) return [];
|
|
116
|
+
let e = /* @__PURE__ */ new Map();
|
|
117
|
+
for (let t of j) {
|
|
118
|
+
let n = new Date(t.enrolledAt);
|
|
119
|
+
if (Number.isNaN(n.getTime())) continue;
|
|
120
|
+
let r = `${n.getFullYear()}-${String(n.getMonth() + 1).padStart(2, "0")}`;
|
|
121
|
+
e.set(r, (e.get(r) ?? 0) + 1);
|
|
122
|
+
}
|
|
123
|
+
return [...e.entries()].sort(([e], [t]) => e.localeCompare(t)).map(([, e]) => e);
|
|
124
|
+
}, [j]), z = g(() => {
|
|
125
|
+
let e = new Map(E.map((e) => [e.id, e.title])), t = /* @__PURE__ */ new Map();
|
|
126
|
+
for (let e of j) t.set(e.courseId, (t.get(e.courseId) ?? 0) + 1);
|
|
79
127
|
let n = Math.max(1, ...t.values());
|
|
80
128
|
return [...t.entries()].map(([t, r]) => ({
|
|
81
129
|
courseId: t,
|
|
@@ -83,108 +131,179 @@ var C = [
|
|
|
83
131
|
count: r,
|
|
84
132
|
pct: r / n * 100
|
|
85
133
|
})).sort((e, t) => t.count - e.count).slice(0, 6);
|
|
86
|
-
}, [
|
|
87
|
-
return /* @__PURE__ */
|
|
134
|
+
}, [E, j]), B = _.isLoading || b.isLoading || x.isLoading, V = _.isError || b.isError || x.isError || w.isError, H = E.length > 0 || j.length > 0 || M.length > 0 || F.length > 0;
|
|
135
|
+
return /* @__PURE__ */ D(t, {
|
|
88
136
|
title: "Insights",
|
|
89
137
|
description: "Learning analytics across your workspace — completion, engagement and outcomes.",
|
|
90
138
|
titleGradient: !0,
|
|
91
139
|
aurora: !0,
|
|
92
|
-
children:
|
|
140
|
+
children: B ? /* @__PURE__ */ D(s, { label: "Crunching your learning data…" }) : V ? /* @__PURE__ */ D(o, {
|
|
141
|
+
title: "Couldn't load insights",
|
|
142
|
+
message: "We hit a snag pulling your learning data. Try again in a moment.",
|
|
143
|
+
onRetry: () => {
|
|
144
|
+
_.refetch(), b.refetch(), x.refetch(), w.refetch();
|
|
145
|
+
}
|
|
146
|
+
}) : H ? /* @__PURE__ */ O("div", {
|
|
93
147
|
className: "flex flex-col gap-8",
|
|
94
148
|
children: [
|
|
95
|
-
/* @__PURE__ */
|
|
149
|
+
/* @__PURE__ */ O("div", {
|
|
96
150
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4",
|
|
97
151
|
children: [
|
|
98
|
-
/* @__PURE__ */
|
|
99
|
-
icon: /* @__PURE__ */
|
|
100
|
-
|
|
152
|
+
/* @__PURE__ */ D(n, {
|
|
153
|
+
icon: /* @__PURE__ */ D(N, {
|
|
154
|
+
tint: "info",
|
|
155
|
+
children: /* @__PURE__ */ D(y, { className: "h-5 w-5" })
|
|
156
|
+
}),
|
|
157
|
+
value: I.published,
|
|
101
158
|
label: "Published courses",
|
|
102
|
-
|
|
159
|
+
footer: /* @__PURE__ */ O("span", {
|
|
160
|
+
className: "text-xs text-text-secondary",
|
|
161
|
+
children: [
|
|
162
|
+
"of ",
|
|
163
|
+
I.totalCourses,
|
|
164
|
+
" total"
|
|
165
|
+
]
|
|
166
|
+
})
|
|
103
167
|
}),
|
|
104
|
-
/* @__PURE__ */
|
|
105
|
-
icon: /* @__PURE__ */
|
|
106
|
-
|
|
107
|
-
|
|
168
|
+
/* @__PURE__ */ D(n, {
|
|
169
|
+
icon: /* @__PURE__ */ D(N, {
|
|
170
|
+
tint: "success",
|
|
171
|
+
children: /* @__PURE__ */ D(T, { className: "h-5 w-5" })
|
|
172
|
+
}),
|
|
173
|
+
value: I.activeLearners,
|
|
174
|
+
label: "Active learners",
|
|
175
|
+
footer: /* @__PURE__ */ O("span", {
|
|
176
|
+
className: "text-xs text-text-secondary",
|
|
177
|
+
children: [
|
|
178
|
+
j.length,
|
|
179
|
+
" enrolment",
|
|
180
|
+
j.length === 1 ? "" : "s"
|
|
181
|
+
]
|
|
182
|
+
})
|
|
108
183
|
}),
|
|
109
|
-
/* @__PURE__ */
|
|
110
|
-
icon: /* @__PURE__ */
|
|
111
|
-
|
|
184
|
+
/* @__PURE__ */ D(n, {
|
|
185
|
+
icon: /* @__PURE__ */ D(N, {
|
|
186
|
+
tint: "accent",
|
|
187
|
+
children: /* @__PURE__ */ D(C, { className: "h-5 w-5" })
|
|
188
|
+
}),
|
|
189
|
+
value: i(I.completionRate),
|
|
112
190
|
label: "Completion rate",
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
barClassName: "bg-[var(--color-status-success-text)]"
|
|
191
|
+
footer: /* @__PURE__ */ D(l, {
|
|
192
|
+
value: I.completionRate,
|
|
193
|
+
barClassName: "bg-[var(--color-accent-teal)]"
|
|
117
194
|
})
|
|
118
195
|
}),
|
|
119
|
-
/* @__PURE__ */
|
|
120
|
-
icon: /* @__PURE__ */
|
|
121
|
-
|
|
196
|
+
/* @__PURE__ */ D(n, {
|
|
197
|
+
icon: /* @__PURE__ */ D(N, {
|
|
198
|
+
tint: "warning",
|
|
199
|
+
children: /* @__PURE__ */ D(v, { className: "h-5 w-5" })
|
|
200
|
+
}),
|
|
201
|
+
value: F.length,
|
|
122
202
|
label: "Certificates issued"
|
|
123
203
|
})
|
|
124
204
|
]
|
|
125
205
|
}),
|
|
126
|
-
/* @__PURE__ */
|
|
206
|
+
/* @__PURE__ */ O("div", {
|
|
127
207
|
className: "grid grid-cols-1 gap-6 lg:grid-cols-2",
|
|
128
|
-
children: [/* @__PURE__ */
|
|
208
|
+
children: [/* @__PURE__ */ D(c, {
|
|
129
209
|
title: "Enrolment status",
|
|
130
|
-
description: `${
|
|
131
|
-
children:
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
210
|
+
description: `${j.length} enrolments`,
|
|
211
|
+
children: L.length === 0 ? /* @__PURE__ */ D(k, {
|
|
212
|
+
treatment: "plain",
|
|
213
|
+
className: "p-5",
|
|
214
|
+
children: /* @__PURE__ */ D("p", {
|
|
215
|
+
className: "text-sm text-text-secondary",
|
|
216
|
+
children: "No enrolments yet."
|
|
217
|
+
})
|
|
218
|
+
}) : /* @__PURE__ */ O(k, {
|
|
135
219
|
treatment: "plain",
|
|
136
220
|
className: "p-5",
|
|
137
|
-
children: [/* @__PURE__ */
|
|
138
|
-
className: "mt-4 flex items-center gap-
|
|
139
|
-
children: [/* @__PURE__ */
|
|
140
|
-
className: "text-sm text-text-secondary",
|
|
221
|
+
children: [/* @__PURE__ */ D(P, { rows: L }), /* @__PURE__ */ O("div", {
|
|
222
|
+
className: "mt-4 flex flex-wrap items-center justify-between gap-3 border-t border-border-seam pt-3",
|
|
223
|
+
children: [/* @__PURE__ */ O("span", {
|
|
224
|
+
className: "inline-flex items-center gap-1.5 text-sm text-text-secondary",
|
|
141
225
|
children: [
|
|
142
|
-
"
|
|
143
|
-
" ",
|
|
144
|
-
/* @__PURE__ */
|
|
226
|
+
/* @__PURE__ */ D(S, { className: "h-4 w-4" }),
|
|
227
|
+
" Avg progress",
|
|
228
|
+
/* @__PURE__ */ D("span", {
|
|
145
229
|
className: "font-medium text-text-primary",
|
|
146
|
-
children: i(
|
|
230
|
+
children: i(I.avgProgress)
|
|
147
231
|
})
|
|
148
232
|
]
|
|
233
|
+
}), R.length >= 2 && /* @__PURE__ */ O("span", {
|
|
234
|
+
className: "inline-flex items-center gap-2",
|
|
235
|
+
children: [/* @__PURE__ */ D(d, {
|
|
236
|
+
data: R,
|
|
237
|
+
color: "var(--color-accent-teal)"
|
|
238
|
+
}), /* @__PURE__ */ D("span", {
|
|
239
|
+
className: "text-xs text-text-secondary",
|
|
240
|
+
children: "new / month"
|
|
241
|
+
})]
|
|
149
242
|
})]
|
|
150
243
|
})]
|
|
151
244
|
})
|
|
152
|
-
}), /* @__PURE__ */
|
|
153
|
-
title: "
|
|
154
|
-
description: `${
|
|
155
|
-
children: /* @__PURE__ */
|
|
245
|
+
}), /* @__PURE__ */ D(c, {
|
|
246
|
+
title: "Learning outcomes",
|
|
247
|
+
description: `${M.length} submissions`,
|
|
248
|
+
children: /* @__PURE__ */ O(k, {
|
|
156
249
|
treatment: "plain",
|
|
157
|
-
className: "flex flex-col gap-
|
|
250
|
+
className: "flex flex-col gap-5 p-5",
|
|
158
251
|
children: [
|
|
159
|
-
/* @__PURE__ */
|
|
160
|
-
className: "flex items-center justify-
|
|
161
|
-
children: [/* @__PURE__ */
|
|
162
|
-
className: "
|
|
163
|
-
children:
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
"
|
|
169
|
-
|
|
170
|
-
|
|
252
|
+
/* @__PURE__ */ O("div", {
|
|
253
|
+
className: "flex items-center justify-around gap-4",
|
|
254
|
+
children: [/* @__PURE__ */ O("div", {
|
|
255
|
+
className: "flex flex-col items-center gap-2 text-center",
|
|
256
|
+
children: [/* @__PURE__ */ D(u, {
|
|
257
|
+
value: I.completionRate,
|
|
258
|
+
size: 96,
|
|
259
|
+
stroke: 8,
|
|
260
|
+
color: "var(--color-accent-teal)",
|
|
261
|
+
label: j.length === 0 ? "—" : void 0
|
|
262
|
+
}), /* @__PURE__ */ D("span", {
|
|
263
|
+
className: "text-sm text-text-secondary",
|
|
264
|
+
children: "Completion rate"
|
|
265
|
+
})]
|
|
266
|
+
}), /* @__PURE__ */ O("div", {
|
|
267
|
+
className: "flex flex-col items-center gap-2 text-center",
|
|
268
|
+
children: [/* @__PURE__ */ D(u, {
|
|
269
|
+
value: I.passRate,
|
|
270
|
+
size: 96,
|
|
271
|
+
stroke: 8,
|
|
272
|
+
color: "var(--color-status-success-text)",
|
|
273
|
+
label: I.gradedCount === 0 ? "—" : void 0
|
|
274
|
+
}), /* @__PURE__ */ D("span", {
|
|
275
|
+
className: "text-sm text-text-secondary",
|
|
276
|
+
children: "Pass rate"
|
|
277
|
+
})]
|
|
171
278
|
})]
|
|
172
279
|
}),
|
|
173
|
-
/* @__PURE__ */
|
|
174
|
-
className: "
|
|
175
|
-
children: [/* @__PURE__ */
|
|
176
|
-
className: "
|
|
177
|
-
children: [/* @__PURE__ */
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
})
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
280
|
+
/* @__PURE__ */ O("div", {
|
|
281
|
+
className: "grid grid-cols-2 gap-3 border-t border-border-seam pt-4",
|
|
282
|
+
children: [/* @__PURE__ */ O("div", { children: [/* @__PURE__ */ O("p", {
|
|
283
|
+
className: "text-lg font-semibold tabular-nums text-text-primary",
|
|
284
|
+
children: [I.gradedCount, /* @__PURE__ */ O("span", {
|
|
285
|
+
className: "text-sm font-normal text-text-secondary",
|
|
286
|
+
children: ["/", M.length]
|
|
287
|
+
})]
|
|
288
|
+
}), /* @__PURE__ */ D("p", {
|
|
289
|
+
className: "text-xs text-text-secondary",
|
|
290
|
+
children: "Graded"
|
|
291
|
+
})] }), /* @__PURE__ */ O("div", { children: [/* @__PURE__ */ O("p", {
|
|
292
|
+
className: "text-lg font-semibold tabular-nums text-text-primary",
|
|
293
|
+
children: [I.passed, /* @__PURE__ */ O("span", {
|
|
294
|
+
className: "text-sm font-normal text-text-secondary",
|
|
295
|
+
children: [
|
|
296
|
+
" ",
|
|
297
|
+
"of ",
|
|
298
|
+
I.gradedCount
|
|
299
|
+
]
|
|
300
|
+
})]
|
|
301
|
+
}), /* @__PURE__ */ D("p", {
|
|
302
|
+
className: "text-xs text-text-secondary",
|
|
303
|
+
children: "Passed"
|
|
304
|
+
})] })]
|
|
305
|
+
}),
|
|
306
|
+
I.gradedCount === 0 && /* @__PURE__ */ D("p", {
|
|
188
307
|
className: "text-xs text-text-secondary",
|
|
189
308
|
children: "No submissions have been graded yet — pass rate appears once grading begins."
|
|
190
309
|
})
|
|
@@ -192,44 +311,49 @@ var C = [
|
|
|
192
311
|
})
|
|
193
312
|
})]
|
|
194
313
|
}),
|
|
195
|
-
/* @__PURE__ */
|
|
314
|
+
/* @__PURE__ */ D(c, {
|
|
196
315
|
title: "Top courses by enrolment",
|
|
197
|
-
children:
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
316
|
+
children: z.length === 0 ? /* @__PURE__ */ D(k, {
|
|
317
|
+
treatment: "plain",
|
|
318
|
+
className: "p-5",
|
|
319
|
+
children: /* @__PURE__ */ D("p", {
|
|
320
|
+
className: "text-sm text-text-secondary",
|
|
321
|
+
children: "No enrolments yet."
|
|
322
|
+
})
|
|
323
|
+
}) : /* @__PURE__ */ D(k, {
|
|
201
324
|
treatment: "plain",
|
|
202
325
|
className: "p-5",
|
|
203
|
-
children: /* @__PURE__ */
|
|
204
|
-
className: "flex flex-col gap-
|
|
205
|
-
children:
|
|
326
|
+
children: /* @__PURE__ */ D("div", {
|
|
327
|
+
className: "flex flex-col gap-4",
|
|
328
|
+
children: z.map((t, n) => /* @__PURE__ */ O("div", {
|
|
206
329
|
className: "flex items-center gap-3",
|
|
207
|
-
children: [
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
/* @__PURE__ */
|
|
213
|
-
className: "
|
|
214
|
-
children:
|
|
215
|
-
|
|
216
|
-
|
|
330
|
+
children: [/* @__PURE__ */ D("span", {
|
|
331
|
+
className: e("flex h-6 w-6 shrink-0 items-center justify-center rounded-full text-xs font-semibold tabular-nums", n === 0 ? "bg-[var(--color-accent-soft)] text-[var(--color-accent-teal)]" : "bg-bg-sunken text-text-secondary"),
|
|
332
|
+
children: n + 1
|
|
333
|
+
}), /* @__PURE__ */ O("div", {
|
|
334
|
+
className: "min-w-0 flex-1",
|
|
335
|
+
children: [/* @__PURE__ */ O("div", {
|
|
336
|
+
className: "flex items-center justify-between gap-2",
|
|
337
|
+
children: [/* @__PURE__ */ D("span", {
|
|
338
|
+
className: "truncate text-sm text-text-primary",
|
|
339
|
+
children: t.title
|
|
340
|
+
}), /* @__PURE__ */ D("span", {
|
|
341
|
+
className: "shrink-0 text-sm tabular-nums text-text-secondary",
|
|
342
|
+
children: t.count
|
|
343
|
+
})]
|
|
344
|
+
}), /* @__PURE__ */ D(l, {
|
|
217
345
|
value: t.pct,
|
|
218
346
|
barClassName: "bg-[var(--color-accent-teal)]",
|
|
219
|
-
className: "
|
|
347
|
+
className: "mt-1.5",
|
|
220
348
|
height: 8
|
|
221
|
-
})
|
|
222
|
-
|
|
223
|
-
className: "w-10 shrink-0 text-right text-sm tabular-nums text-text-secondary",
|
|
224
|
-
children: t.count
|
|
225
|
-
})
|
|
226
|
-
]
|
|
349
|
+
})]
|
|
350
|
+
})]
|
|
227
351
|
}, t.courseId))
|
|
228
352
|
})
|
|
229
353
|
})
|
|
230
354
|
})
|
|
231
355
|
]
|
|
232
|
-
}) : /* @__PURE__ */
|
|
356
|
+
}) : /* @__PURE__ */ D(a, {
|
|
233
357
|
illustration: "empty-data",
|
|
234
358
|
title: "No data to analyse yet",
|
|
235
359
|
description: "Publish courses and enrol learners — insights appear here as activity accrues."
|
|
@@ -237,6 +361,6 @@ var C = [
|
|
|
237
361
|
});
|
|
238
362
|
};
|
|
239
363
|
//#endregion
|
|
240
|
-
export {
|
|
364
|
+
export { F as InsightsPage };
|
|
241
365
|
|
|
242
366
|
//# sourceMappingURL=InsightsPage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InsightsPage.js","names":[],"sources":["../../../../src/buildmyiq/pages/insights/InsightsPage.tsx"],"sourcesContent":["/**\n * InsightsPage — educator/admin learning analytics, DERIVED entirely from live\n * `bui*` data (no analytics tables, no fabricated numbers).\n * @module buildmyiq/pages/insights\n *\n * Everything here is computed client-side from real queries:\n * - courses (total / published / by level)\n * - enrollments (status split, completion rate, average progress)\n * - submissions (graded / pass rate)\n * - certificates issued\n * - top courses by enrolment\n * When the workspace has no data yet, the page shows honest zero/empty states.\n */\n\nimport type { FC } from 'react';\nimport { useMemo } from 'react';\nimport { BookOpen, GraduationCap, CheckCircle2, Award, TrendingUp, Users } from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport { PageLayout, MetricsCard, LoadingView, Section, EmptyState, MiniBar } from '../../components';\nimport {\n useBuiCourses,\n useBuiEnrollments,\n useBuiSubmissions,\n useBuiCertificates,\n} from '../../hooks';\nimport { formatEnumLabel, formatPercent } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\nimport type { BuiCourse, BuiEnrollment, BuiSubmission } from '../../types';\n\nconst ENROLLMENT_STATUS_ORDER = ['ENROLLED', 'IN_PROGRESS', 'COMPLETED', 'DROPPED', 'EXPIRED'] as const;\n\ninterface DistRow {\n label: string;\n count: number;\n pct: number;\n tone: string;\n}\n\nconst DistBars: FC<{ rows: DistRow[] }> = ({ rows }) => (\n <div className=\"flex flex-col gap-3\">\n {rows.map((r) => (\n <div key={r.label} className=\"flex items-center gap-3\">\n <span className=\"w-28 shrink-0 text-sm text-text-secondary\">{r.label}</span>\n <MiniBar value={r.pct} barClassName={r.tone} className=\"flex-1\" height={8} />\n <span className=\"w-10 shrink-0 text-right text-sm tabular-nums text-text-primary\">{r.count}</span>\n </div>\n ))}\n </div>\n);\n\nexport const InsightsPage: FC = () => {\n const coursesQuery = useBuiCourses({ limit: 100 });\n const enrollmentsQuery = useBuiEnrollments({ limit: 100 });\n const submissionsQuery = useBuiSubmissions({ limit: 100 });\n const certsQuery = useBuiCertificates();\n\n const courses: BuiCourse[] = useMemo(() => coursesQuery.data?.items ?? [], [coursesQuery.data]);\n const enrollments: BuiEnrollment[] = useMemo(\n () => enrollmentsQuery.data?.items ?? [],\n [enrollmentsQuery.data]\n );\n const submissions: BuiSubmission[] = useMemo(\n () => submissionsQuery.data?.items ?? [],\n [submissionsQuery.data]\n );\n const certificates = certsQuery.data ?? [];\n\n const stats = useMemo(() => {\n const published = courses.filter((c) => c.status === 'PUBLISHED').length;\n const completed = enrollments.filter((e) => e.status === 'COMPLETED').length;\n const completionRate = enrollments.length > 0 ? (completed / enrollments.length) * 100 : 0;\n const avgProgress =\n enrollments.length > 0\n ? enrollments.reduce((s, e) => s + (e.progressPct ?? 0), 0) / enrollments.length\n : 0;\n const graded = submissions.filter((s) => s.status === 'GRADED');\n const passed = graded.filter((s) => s.passed === true).length;\n const passRate = graded.length > 0 ? (passed / graded.length) * 100 : 0;\n const activeLearners = new Set(enrollments.map((e) => e.learnerId)).size;\n return {\n published,\n completed,\n completionRate,\n avgProgress,\n gradedCount: graded.length,\n passRate,\n activeLearners,\n };\n }, [courses, enrollments, submissions]);\n\n const enrollmentDist: DistRow[] = useMemo(() => {\n const total = enrollments.length;\n const tones: Record<string, string> = {\n ENROLLED: 'bg-[var(--color-accent-teal)]',\n IN_PROGRESS: 'bg-[var(--color-accent-teal)]',\n COMPLETED: 'bg-[var(--color-status-success-text)]',\n DROPPED: 'bg-[var(--color-status-error-text)]',\n EXPIRED: 'bg-[var(--color-text-tertiary)]',\n };\n return ENROLLMENT_STATUS_ORDER.map((status) => {\n const count = enrollments.filter((e) => e.status === status).length;\n return {\n label: formatEnumLabel(status),\n count,\n pct: total > 0 ? (count / total) * 100 : 0,\n tone: tones[status] ?? 'bg-accent',\n };\n }).filter((r) => r.count > 0);\n }, [enrollments]);\n\n const topCourses = useMemo(() => {\n const titleById = new Map(courses.map((c) => [c.id, c.title]));\n const counts = new Map<string, number>();\n for (const e of enrollments) counts.set(e.courseId, (counts.get(e.courseId) ?? 0) + 1);\n const max = Math.max(1, ...counts.values());\n return [...counts.entries()]\n .map(([courseId, count]) => ({\n courseId,\n title: titleById.get(courseId) ?? 'Course',\n count,\n pct: (count / max) * 100,\n }))\n .sort((a, b) => b.count - a.count)\n .slice(0, 6);\n }, [courses, enrollments]);\n\n const isLoading =\n coursesQuery.isLoading || enrollmentsQuery.isLoading || submissionsQuery.isLoading;\n\n const hasAnyData =\n courses.length > 0 || enrollments.length > 0 || submissions.length > 0 || certificates.length > 0;\n\n return (\n <PageLayout\n title=\"Insights\"\n description=\"Learning analytics across your workspace — completion, engagement and outcomes.\"\n titleGradient\n aurora\n >\n {isLoading ? (\n <LoadingView label=\"Crunching your learning data…\" />\n ) : !hasAnyData ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No data to analyse yet\"\n description=\"Publish courses and enrol learners — insights appear here as activity accrues.\"\n />\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 <MetricsCard\n icon={<BookOpen className=\"h-5 w-5\" />}\n value={stats.published}\n label=\"Published courses\"\n featured\n />\n <MetricsCard\n icon={<Users className=\"h-5 w-5\" />}\n value={stats.activeLearners}\n label=\"Active learners\"\n />\n <MetricsCard\n icon={<TrendingUp className=\"h-5 w-5\" />}\n value={formatPercent(stats.completionRate)}\n label=\"Completion rate\"\n accent=\"success\"\n footer={<MiniBar value={stats.completionRate} barClassName=\"bg-[var(--color-status-success-text)]\" />}\n />\n <MetricsCard\n icon={<Award className=\"h-5 w-5\" />}\n value={certificates.length}\n label=\"Certificates issued\"\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n {/* Enrollment status distribution */}\n <Section title=\"Enrolment status\" description={`${enrollments.length} enrolments`}>\n {enrollmentDist.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">No enrolments yet.</p>\n ) : (\n <GlassCard treatment=\"plain\" className=\"p-5\">\n <DistBars rows={enrollmentDist} />\n <div className=\"mt-4 flex items-center gap-2 border-t border-border-seam pt-3\">\n <GraduationCap className=\"h-4 w-4 text-text-secondary\" />\n <span className=\"text-sm text-text-secondary\">\n Average progress{' '}\n <span className=\"font-medium text-text-primary\">\n {formatPercent(stats.avgProgress)}\n </span>\n </span>\n </div>\n </GlassCard>\n )}\n </Section>\n\n {/* Assessment outcomes */}\n <Section title=\"Assessment outcomes\" description={`${submissions.length} submissions`}>\n <GlassCard treatment=\"plain\" className=\"flex flex-col gap-4 p-5\">\n <div className=\"flex items-center justify-between\">\n <span className=\"text-sm text-text-secondary\">Graded</span>\n <span className=\"text-sm font-medium tabular-nums text-text-primary\">\n {stats.gradedCount}/{submissions.length}\n </span>\n </div>\n <div>\n <div className=\"mb-1 flex items-center justify-between\">\n <span className=\"inline-flex items-center gap-1.5 text-sm text-text-secondary\">\n <CheckCircle2 className=\"h-4 w-4 text-status-success-text\" /> Pass rate\n </span>\n <span className=\"text-sm font-medium tabular-nums text-text-primary\">\n {formatPercent(stats.passRate)}\n </span>\n </div>\n <MiniBar\n value={stats.passRate}\n barClassName=\"bg-[var(--color-status-success-text)]\"\n height={8}\n />\n </div>\n {stats.gradedCount === 0 && (\n <p className=\"text-xs text-text-secondary\">\n No submissions have been graded yet — pass rate appears once grading begins.\n </p>\n )}\n </GlassCard>\n </Section>\n </div>\n\n {/* Top courses by enrolment */}\n <Section title=\"Top courses by enrolment\">\n {topCourses.length === 0 ? (\n <p className=\"text-sm text-text-secondary\">No enrolments yet.</p>\n ) : (\n <GlassCard treatment=\"plain\" className=\"p-5\">\n <div className=\"flex flex-col gap-3\">\n {topCourses.map((c, i) => (\n <div key={c.courseId} className=\"flex items-center gap-3\">\n <span\n className={cn(\n 'flex h-6 w-6 shrink-0 items-center justify-center rounded-full text-xs font-semibold tabular-nums',\n i === 0 ? 'bg-[var(--color-accent-soft)] text-text-primary' : 'bg-bg-sunken text-text-secondary'\n )}\n >\n {i + 1}\n </span>\n <span className=\"w-48 shrink-0 truncate text-sm text-text-primary\">{c.title}</span>\n <MiniBar value={c.pct} barClassName=\"bg-[var(--color-accent-teal)]\" className=\"flex-1\" height={8} />\n <span className=\"w-10 shrink-0 text-right text-sm tabular-nums text-text-secondary\">\n {c.count}\n </span>\n </div>\n ))}\n </div>\n </GlassCard>\n )}\n </Section>\n </div>\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"InsightsPage.js","names":[],"sources":["../../../../src/buildmyiq/pages/insights/InsightsPage.tsx"],"sourcesContent":["/**\n * InsightsPage — educator/admin learning analytics, DERIVED entirely from live\n * `bui*` data (no analytics tables, no fabricated numbers).\n * @module buildmyiq/pages/insights\n *\n * Everything here is computed client-side from real queries:\n * - courses (total / published)\n * - enrollments (status split, completion rate, average progress, trend by month)\n * - submissions (graded / passed / pass rate)\n * - certificates issued\n * - top courses by enrolment\n * The circular meters (completion + pass rate), the tinted KPI cards, the\n * status-distribution bars, the enrolment sparkline and the ranked top-course\n * bars all read as one tokenized system. When the workspace has no data yet the\n * page shows honest zero/empty states.\n */\n\nimport type { FC, ReactNode } from 'react';\nimport { useMemo } from 'react';\nimport {\n BookOpen,\n Award,\n TrendingUp,\n Users,\n GraduationCap,\n CheckCircle2,\n User,\n Clock,\n XCircle,\n AlarmClock,\n type LucideIcon,\n} from 'lucide-react';\nimport { GlassCard } from '@burdenoff/fe-libs/ui';\nimport {\n PageLayout,\n MetricsCard,\n LoadingView,\n ErrorView,\n Section,\n EmptyState,\n MiniBar,\n ProgressRing,\n Sparkline,\n} from '../../components';\nimport {\n useBuiCourses,\n useBuiEnrollments,\n useBuiSubmissions,\n useBuiCertificates,\n} from '../../hooks';\nimport { formatEnumLabel, formatPercent } from '../../utils/formatters';\nimport { cn } from '../../utils/cn';\nimport type { BuiCourse, BuiEnrollment, BuiSubmission } from '../../types';\n\nconst ENROLLMENT_STATUS_ORDER = ['ENROLLED', 'IN_PROGRESS', 'COMPLETED', 'DROPPED', 'EXPIRED'] as const;\ntype EnrollmentStatusKey = (typeof ENROLLMENT_STATUS_ORDER)[number];\n\n/** Per-status icon + bar/icon tone so the distribution reads as one system. */\nconst STATUS_META: Record<EnrollmentStatusKey, { icon: LucideIcon; bar: string; icon_tint: string }> = {\n ENROLLED: {\n icon: User,\n bar: 'bg-[var(--color-accent-teal)]',\n icon_tint: 'text-[var(--color-accent-teal)]',\n },\n IN_PROGRESS: {\n icon: Clock,\n bar: 'bg-[var(--color-accent-primary)]',\n icon_tint: 'text-[var(--color-accent-primary)]',\n },\n COMPLETED: {\n icon: CheckCircle2,\n bar: 'bg-[var(--color-status-success-text)]',\n icon_tint: 'text-[var(--color-status-success-text)]',\n },\n DROPPED: {\n icon: XCircle,\n bar: 'bg-[var(--color-status-error-text)]',\n icon_tint: 'text-[var(--color-status-error-text)]',\n },\n EXPIRED: {\n icon: AlarmClock,\n bar: 'bg-[var(--color-text-tertiary)]',\n icon_tint: 'text-[var(--color-text-tertiary)]',\n },\n};\n\ntype Tint = 'info' | 'success' | 'warning' | 'accent';\nconst TINT_CLASS: Record<Tint, string> = {\n info: 'bg-[var(--color-status-info-bg)] text-[var(--color-status-info-text)]',\n success: 'bg-[var(--color-status-success-bg)] text-[var(--color-status-success-text)]',\n warning: 'bg-[var(--color-status-warning-bg)] text-[var(--color-status-warning-text)]',\n accent: 'bg-[var(--color-accent-soft)] text-[var(--color-accent-teal)]',\n};\n\n/** Soft tinted circle behind a KPI icon (matches the concept's tinted badges). */\nconst TintedIcon: FC<{ tint: Tint; children: ReactNode }> = ({ tint, children }) => (\n <span className={cn('flex h-11 w-11 items-center justify-center rounded-full', TINT_CLASS[tint])}>\n {children}\n </span>\n);\n\ninterface DistRow {\n status: EnrollmentStatusKey;\n label: string;\n count: number;\n pct: number;\n}\n\nconst DistBars: FC<{ rows: DistRow[] }> = ({ rows }) => (\n <div className=\"flex flex-col gap-3\">\n {rows.map((r) => {\n const meta = STATUS_META[r.status];\n const Icon = meta.icon;\n return (\n <div key={r.status} className=\"flex items-center gap-3\">\n <span className=\"flex w-24 shrink-0 items-center gap-2 text-sm text-text-secondary sm:w-28\">\n <Icon className={cn('h-4 w-4 shrink-0', meta.icon_tint)} />\n <span className=\"truncate\">{r.label}</span>\n </span>\n <MiniBar value={r.pct} barClassName={meta.bar} className=\"flex-1\" height={8} />\n <span className=\"w-9 shrink-0 text-right text-sm tabular-nums text-text-primary\">{r.count}</span>\n </div>\n );\n })}\n </div>\n);\n\nexport const InsightsPage: FC = () => {\n const coursesQuery = useBuiCourses({ limit: 100 });\n const enrollmentsQuery = useBuiEnrollments({ limit: 100 });\n const submissionsQuery = useBuiSubmissions({ limit: 100 });\n const certsQuery = useBuiCertificates();\n\n const courses: BuiCourse[] = useMemo(() => coursesQuery.data?.items ?? [], [coursesQuery.data]);\n const enrollments: BuiEnrollment[] = useMemo(\n () => enrollmentsQuery.data?.items ?? [],\n [enrollmentsQuery.data]\n );\n const submissions: BuiSubmission[] = useMemo(\n () => submissionsQuery.data?.items ?? [],\n [submissionsQuery.data]\n );\n const certificates = certsQuery.data ?? [];\n\n const stats = useMemo(() => {\n const published = courses.filter((c) => c.status === 'PUBLISHED').length;\n const completed = enrollments.filter((e) => e.status === 'COMPLETED').length;\n const inProgress = enrollments.filter((e) => e.status === 'IN_PROGRESS').length;\n const completionRate = enrollments.length > 0 ? (completed / enrollments.length) * 100 : 0;\n const avgProgress =\n enrollments.length > 0\n ? enrollments.reduce((s, e) => s + (e.progressPct ?? 0), 0) / enrollments.length\n : 0;\n const graded = submissions.filter((s) => s.status === 'GRADED');\n const passed = graded.filter((s) => s.passed === true).length;\n const passRate = graded.length > 0 ? (passed / graded.length) * 100 : 0;\n const activeLearners = new Set(enrollments.map((e) => e.learnerId)).size;\n return {\n published,\n totalCourses: courses.length,\n completed,\n inProgress,\n completionRate,\n avgProgress,\n gradedCount: graded.length,\n passed,\n passRate,\n activeLearners,\n };\n }, [courses, enrollments, submissions]);\n\n const enrollmentDist: DistRow[] = useMemo(() => {\n const total = enrollments.length;\n return ENROLLMENT_STATUS_ORDER.map((status) => {\n const count = enrollments.filter((e) => e.status === status).length;\n return {\n status,\n label: formatEnumLabel(status),\n count,\n pct: total > 0 ? (count / total) * 100 : 0,\n };\n }).filter((r) => r.count > 0);\n }, [enrollments]);\n\n /** New enrolments bucketed by calendar month (real `enrolledAt` timestamps). */\n const enrolmentTrend = useMemo(() => {\n if (enrollments.length === 0) return [];\n const byMonth = new Map<string, number>();\n for (const e of enrollments) {\n const d = new Date(e.enrolledAt);\n if (Number.isNaN(d.getTime())) continue;\n const key = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`;\n byMonth.set(key, (byMonth.get(key) ?? 0) + 1);\n }\n return [...byMonth.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([, v]) => v);\n }, [enrollments]);\n\n const topCourses = useMemo(() => {\n const titleById = new Map(courses.map((c) => [c.id, c.title]));\n const counts = new Map<string, number>();\n for (const e of enrollments) counts.set(e.courseId, (counts.get(e.courseId) ?? 0) + 1);\n const max = Math.max(1, ...counts.values());\n return [...counts.entries()]\n .map(([courseId, count]) => ({\n courseId,\n title: titleById.get(courseId) ?? 'Course',\n count,\n pct: (count / max) * 100,\n }))\n .sort((a, b) => b.count - a.count)\n .slice(0, 6);\n }, [courses, enrollments]);\n\n const isLoading =\n coursesQuery.isLoading || enrollmentsQuery.isLoading || submissionsQuery.isLoading;\n const isError =\n coursesQuery.isError ||\n enrollmentsQuery.isError ||\n submissionsQuery.isError ||\n certsQuery.isError;\n\n const hasAnyData =\n courses.length > 0 || enrollments.length > 0 || submissions.length > 0 || certificates.length > 0;\n\n const retry = () => {\n void coursesQuery.refetch();\n void enrollmentsQuery.refetch();\n void submissionsQuery.refetch();\n void certsQuery.refetch();\n };\n\n return (\n <PageLayout\n title=\"Insights\"\n description=\"Learning analytics across your workspace — completion, engagement and outcomes.\"\n titleGradient\n aurora\n >\n {isLoading ? (\n <LoadingView label=\"Crunching your learning data…\" />\n ) : isError ? (\n <ErrorView\n title=\"Couldn't load insights\"\n message=\"We hit a snag pulling your learning data. Try again in a moment.\"\n onRetry={retry}\n />\n ) : !hasAnyData ? (\n <EmptyState\n illustration=\"empty-data\"\n title=\"No data to analyse yet\"\n description=\"Publish courses and enrol learners — insights appear here as activity accrues.\"\n />\n ) : (\n <div className=\"flex flex-col gap-8\">\n {/* KPI row — tinted-icon cards, all values derived from live data */}\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 xl:grid-cols-4\">\n <MetricsCard\n icon={\n <TintedIcon tint=\"info\">\n <BookOpen className=\"h-5 w-5\" />\n </TintedIcon>\n }\n value={stats.published}\n label=\"Published courses\"\n footer={\n <span className=\"text-xs text-text-secondary\">of {stats.totalCourses} total</span>\n }\n />\n <MetricsCard\n icon={\n <TintedIcon tint=\"success\">\n <Users className=\"h-5 w-5\" />\n </TintedIcon>\n }\n value={stats.activeLearners}\n label=\"Active learners\"\n footer={\n <span className=\"text-xs text-text-secondary\">\n {enrollments.length} enrolment{enrollments.length === 1 ? '' : 's'}\n </span>\n }\n />\n <MetricsCard\n icon={\n <TintedIcon tint=\"accent\">\n <TrendingUp className=\"h-5 w-5\" />\n </TintedIcon>\n }\n value={formatPercent(stats.completionRate)}\n label=\"Completion rate\"\n footer={\n <MiniBar value={stats.completionRate} barClassName=\"bg-[var(--color-accent-teal)]\" />\n }\n />\n <MetricsCard\n icon={\n <TintedIcon tint=\"warning\">\n <Award className=\"h-5 w-5\" />\n </TintedIcon>\n }\n value={certificates.length}\n label=\"Certificates issued\"\n />\n </div>\n\n <div className=\"grid grid-cols-1 gap-6 lg:grid-cols-2\">\n {/* Enrolment status distribution */}\n <Section title=\"Enrolment status\" description={`${enrollments.length} enrolments`}>\n {enrollmentDist.length === 0 ? (\n <GlassCard treatment=\"plain\" className=\"p-5\">\n <p className=\"text-sm text-text-secondary\">No enrolments yet.</p>\n </GlassCard>\n ) : (\n <GlassCard treatment=\"plain\" className=\"p-5\">\n <DistBars rows={enrollmentDist} />\n <div className=\"mt-4 flex flex-wrap items-center justify-between gap-3 border-t border-border-seam pt-3\">\n <span className=\"inline-flex items-center gap-1.5 text-sm text-text-secondary\">\n <GraduationCap className=\"h-4 w-4\" /> Avg progress\n <span className=\"font-medium text-text-primary\">\n {formatPercent(stats.avgProgress)}\n </span>\n </span>\n {enrolmentTrend.length >= 2 && (\n <span className=\"inline-flex items-center gap-2\">\n <Sparkline data={enrolmentTrend} color=\"var(--color-accent-teal)\" />\n <span className=\"text-xs text-text-secondary\">new / month</span>\n </span>\n )}\n </div>\n </GlassCard>\n )}\n </Section>\n\n {/* Learning outcomes — paired circular meters */}\n <Section title=\"Learning outcomes\" description={`${submissions.length} submissions`}>\n <GlassCard treatment=\"plain\" className=\"flex flex-col gap-5 p-5\">\n <div className=\"flex items-center justify-around gap-4\">\n <div className=\"flex flex-col items-center gap-2 text-center\">\n <ProgressRing\n value={stats.completionRate}\n size={96}\n stroke={8}\n color=\"var(--color-accent-teal)\"\n label={enrollments.length === 0 ? '—' : undefined}\n />\n <span className=\"text-sm text-text-secondary\">Completion rate</span>\n </div>\n <div className=\"flex flex-col items-center gap-2 text-center\">\n <ProgressRing\n value={stats.passRate}\n size={96}\n stroke={8}\n color=\"var(--color-status-success-text)\"\n label={stats.gradedCount === 0 ? '—' : undefined}\n />\n <span className=\"text-sm text-text-secondary\">Pass rate</span>\n </div>\n </div>\n <div className=\"grid grid-cols-2 gap-3 border-t border-border-seam pt-4\">\n <div>\n <p className=\"text-lg font-semibold tabular-nums text-text-primary\">\n {stats.gradedCount}\n <span className=\"text-sm font-normal text-text-secondary\">\n /{submissions.length}\n </span>\n </p>\n <p className=\"text-xs text-text-secondary\">Graded</p>\n </div>\n <div>\n <p className=\"text-lg font-semibold tabular-nums text-text-primary\">\n {stats.passed}\n <span className=\"text-sm font-normal text-text-secondary\">\n {' '}\n of {stats.gradedCount}\n </span>\n </p>\n <p className=\"text-xs text-text-secondary\">Passed</p>\n </div>\n </div>\n {stats.gradedCount === 0 && (\n <p className=\"text-xs text-text-secondary\">\n No submissions have been graded yet — pass rate appears once grading begins.\n </p>\n )}\n </GlassCard>\n </Section>\n </div>\n\n {/* Top courses by enrolment */}\n <Section title=\"Top courses by enrolment\">\n {topCourses.length === 0 ? (\n <GlassCard treatment=\"plain\" className=\"p-5\">\n <p className=\"text-sm text-text-secondary\">No enrolments yet.</p>\n </GlassCard>\n ) : (\n <GlassCard treatment=\"plain\" className=\"p-5\">\n <div className=\"flex flex-col gap-4\">\n {topCourses.map((c, i) => (\n <div key={c.courseId} className=\"flex items-center gap-3\">\n <span\n className={cn(\n 'flex h-6 w-6 shrink-0 items-center justify-center rounded-full text-xs font-semibold tabular-nums',\n i === 0\n ? 'bg-[var(--color-accent-soft)] text-[var(--color-accent-teal)]'\n : 'bg-bg-sunken text-text-secondary'\n )}\n >\n {i + 1}\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center justify-between gap-2\">\n <span className=\"truncate text-sm text-text-primary\">{c.title}</span>\n <span className=\"shrink-0 text-sm tabular-nums text-text-secondary\">\n {c.count}\n </span>\n </div>\n <MiniBar\n value={c.pct}\n barClassName=\"bg-[var(--color-accent-teal)]\"\n className=\"mt-1.5\"\n height={8}\n />\n </div>\n </div>\n ))}\n </div>\n </GlassCard>\n )}\n </Section>\n </div>\n )}\n </PageLayout>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LessonPlayerPage.d.ts","sourceRoot":"","sources":["../../../../src/buildmyiq/pages/learning/LessonPlayerPage.tsx"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"LessonPlayerPage.d.ts","sourceRoot":"","sources":["../../../../src/buildmyiq/pages/learning/LessonPlayerPage.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AA6DhC,eAAO,MAAM,gBAAgB,EAAE,EAuc9B,CAAC"}
|