@burdenoff/microfe-technospam 2026.704.2 → 2026.705.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/technospam/api/technospamApi.js +233 -150
- package/dist/technospam/api/technospamApi.js.map +1 -1
- package/dist/technospam/components/LoadMore.js +18 -2
- package/dist/technospam/components/LoadMore.js.map +1 -0
- package/dist/technospam/components/MediaCover.js +57 -0
- package/dist/technospam/components/MediaCover.js.map +1 -0
- package/dist/technospam/components/PageLayout.js +1 -1
- package/dist/technospam/components/PageLayout.js.map +1 -1
- package/dist/technospam/components/StatCard.js +19 -11
- package/dist/technospam/components/StatCard.js.map +1 -1
- package/dist/technospam/components/StatusBadge.js +1 -1
- package/dist/technospam/components/StatusBadge.js.map +1 -1
- package/dist/technospam/components/index.js +1 -0
- package/dist/technospam/pages/AdminAdminDashboardPagePage.js +748 -352
- package/dist/technospam/pages/AdminAdminDashboardPagePage.js.map +1 -1
- package/dist/technospam/pages/AnalyticsAnalyticsPagePage.js +167 -127
- package/dist/technospam/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemDetailsPagePage.js +178 -125
- package/dist/technospam/pages/ApplicationsProblemDetailsPagePage.js.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemsPagePage.js +89 -93
- package/dist/technospam/pages/ApplicationsProblemsPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesCourseDetailsPagePage.js +295 -141
- package/dist/technospam/pages/CoursesCourseDetailsPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesCoursesPagePage.js +86 -86
- package/dist/technospam/pages/CoursesCoursesPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesMyLearningPagePage.js +122 -88
- package/dist/technospam/pages/CoursesMyLearningPagePage.js.map +1 -1
- package/dist/technospam/pages/DashboardDashboardPagePage.js +232 -199
- package/dist/technospam/pages/DashboardDashboardPagePage.js.map +1 -1
- package/dist/technospam/pages/HackathonsHackathonDetailsPagePage.js +128 -106
- package/dist/technospam/pages/HackathonsHackathonDetailsPagePage.js.map +1 -1
- package/dist/technospam/pages/HackathonsHackathonsPagePage.js +89 -73
- package/dist/technospam/pages/HackathonsHackathonsPagePage.js.map +1 -1
- package/dist/technospam/pages/MentorshipMentorProfilePagePage.js +171 -115
- package/dist/technospam/pages/MentorshipMentorProfilePagePage.js.map +1 -1
- package/dist/technospam/pages/MentorshipMentorsPagePage.js +214 -151
- package/dist/technospam/pages/MentorshipMentorsPagePage.js.map +1 -1
- package/dist/technospam/pages/MentorshipSessionsPagePage.js +241 -116
- package/dist/technospam/pages/MentorshipSessionsPagePage.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,200 +1,354 @@
|
|
|
1
1
|
import { useTechnoSpam as e } from "../providers/TechnoSpamProvider.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
2
|
+
import { cn as t } from "../utils/cn.js";
|
|
3
|
+
import { PageLayout as n } from "../components/PageLayout.js";
|
|
4
|
+
import { StatCard as r } from "../components/StatCard.js";
|
|
5
|
+
import { MediaCover as i, topicIcon as a } from "../components/MediaCover.js";
|
|
6
|
+
import { formatDuration as o, humanizeEnum as s } from "../utils/format.js";
|
|
7
|
+
import { EmptyView as c, ErrorView as l } from "../components/StateViews.js";
|
|
7
8
|
import "../components/index.js";
|
|
8
|
-
import { notify as
|
|
9
|
-
import { useCourseQuery as
|
|
10
|
-
import { BookOpen as
|
|
11
|
-
import { useSearchParams as
|
|
12
|
-
import { jsx as
|
|
13
|
-
import { Badge as
|
|
9
|
+
import { notify as u } from "../lib/toast.js";
|
|
10
|
+
import { useCertificatesQuery as d, useCourseQuery as f, useEnroll as p, useIssueCertificate as m, useUpdateProgress as ee } from "../api/technospamApi.js";
|
|
11
|
+
import { Award as h, BookOpen as g, CheckCircle2 as _, Clock as v, Eye as y, FileText as b, Layers as x, Lock as S, PlayCircle as C, ShieldCheck as te, Sparkles as w, TrendingUp as T, Trophy as E, Users as D } from "lucide-react";
|
|
12
|
+
import { useSearchParams as O } from "react-router-dom";
|
|
13
|
+
import { jsx as k, jsxs as A } from "react/jsx-runtime";
|
|
14
|
+
import { Badge as j, Button as M, GlassCard as N, Skeleton as P } from "@burdenoff/fe-libs/ui";
|
|
14
15
|
//#region src/technospam/pages/CoursesCourseDetailsPagePage.tsx
|
|
15
|
-
var
|
|
16
|
-
VIDEO:
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
16
|
+
var F = {
|
|
17
|
+
VIDEO: {
|
|
18
|
+
icon: C,
|
|
19
|
+
chip: "bg-accent-primary/15 text-accent-primary",
|
|
20
|
+
badge: "border-accent-primary/25 bg-accent-primary/10 text-accent-primary"
|
|
21
|
+
},
|
|
22
|
+
ARTICLE: {
|
|
23
|
+
icon: b,
|
|
24
|
+
chip: "bg-accent-indigo/15 text-accent-indigo",
|
|
25
|
+
badge: "border-accent-indigo/25 bg-accent-indigo/10 text-accent-indigo"
|
|
26
|
+
},
|
|
27
|
+
INTERACTIVE: {
|
|
28
|
+
icon: w,
|
|
29
|
+
chip: "bg-accent-teal/15 text-accent-teal",
|
|
30
|
+
badge: "border-accent-teal/25 bg-accent-teal/10 text-accent-teal"
|
|
31
|
+
},
|
|
32
|
+
QUIZ: {
|
|
33
|
+
icon: _,
|
|
34
|
+
chip: "bg-accent-purple/15 text-accent-purple",
|
|
35
|
+
badge: "border-accent-purple/25 bg-accent-purple/10 text-accent-purple"
|
|
36
|
+
},
|
|
37
|
+
PROJECT: {
|
|
38
|
+
icon: x,
|
|
39
|
+
chip: "bg-status-warning-bg-subtle text-status-warning-text",
|
|
40
|
+
badge: "border-status-warning-border bg-status-warning-bg-subtle text-status-warning-text"
|
|
41
|
+
}
|
|
21
42
|
};
|
|
22
|
-
function
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
43
|
+
function I({ icon: e, children: t }) {
|
|
44
|
+
return /* @__PURE__ */ A("span", {
|
|
45
|
+
className: "inline-flex items-center gap-1 rounded-full bg-white/15 px-2.5 py-1 text-xs font-medium text-white ring-1 ring-white/25 backdrop-blur-sm",
|
|
46
|
+
children: [e ? /* @__PURE__ */ k(e, { size: 12 }) : null, t]
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function L({ lesson: e, index: n, done: r }) {
|
|
50
|
+
let i = F[e.kind] ?? F.ARTICLE, a = i.icon;
|
|
51
|
+
return /* @__PURE__ */ A("div", {
|
|
52
|
+
className: "group flex items-center gap-3 rounded-xl px-2 py-3 transition-colors hover:bg-bg-sunken/60",
|
|
26
53
|
children: [
|
|
27
|
-
/* @__PURE__ */
|
|
28
|
-
className:
|
|
29
|
-
children:
|
|
54
|
+
/* @__PURE__ */ k("span", {
|
|
55
|
+
className: "hidden w-6 shrink-0 text-center text-sm font-medium tabular-nums text-text-muted sm:block",
|
|
56
|
+
children: n + 1
|
|
57
|
+
}),
|
|
58
|
+
/* @__PURE__ */ k("span", {
|
|
59
|
+
className: t("flex h-9 w-9 shrink-0 items-center justify-center rounded-lg", r ? "bg-status-success-bg-subtle text-status-success-text" : i.chip),
|
|
60
|
+
children: k(r ? _ : a, { size: 17 })
|
|
30
61
|
}),
|
|
31
|
-
/* @__PURE__ */
|
|
62
|
+
/* @__PURE__ */ A("div", {
|
|
32
63
|
className: "min-w-0 flex-1",
|
|
33
|
-
children: [/* @__PURE__ */
|
|
34
|
-
className: "
|
|
35
|
-
children:
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
]
|
|
64
|
+
children: [/* @__PURE__ */ k("span", {
|
|
65
|
+
className: "block truncate text-sm font-medium text-text-primary",
|
|
66
|
+
children: e.title
|
|
67
|
+
}), /* @__PURE__ */ A("div", {
|
|
68
|
+
className: "mt-1 flex flex-wrap items-center gap-2",
|
|
69
|
+
children: [/* @__PURE__ */ k("span", {
|
|
70
|
+
className: t("inline-flex items-center rounded-full border px-2 py-0.5 text-[11px] font-medium", i.badge),
|
|
71
|
+
children: s(e.kind)
|
|
72
|
+
}), /* @__PURE__ */ A("span", {
|
|
73
|
+
className: "inline-flex items-center gap-1 text-xs text-text-muted",
|
|
74
|
+
children: [
|
|
75
|
+
/* @__PURE__ */ k(v, { size: 12 }),
|
|
76
|
+
" ",
|
|
77
|
+
o(e.estimatedMinutes)
|
|
78
|
+
]
|
|
79
|
+
})]
|
|
50
80
|
})]
|
|
51
81
|
}),
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
82
|
+
/* @__PURE__ */ k("div", {
|
|
83
|
+
className: "shrink-0",
|
|
84
|
+
children: r ? /* @__PURE__ */ k(_, {
|
|
85
|
+
size: 18,
|
|
86
|
+
className: "text-status-success-text"
|
|
87
|
+
}) : e.isPreview ? /* @__PURE__ */ A("span", {
|
|
88
|
+
className: "inline-flex items-center gap-1 rounded-full border border-accent-primary/30 bg-accent-primary/10 px-2 py-0.5 text-[11px] font-medium text-accent-primary",
|
|
89
|
+
children: [/* @__PURE__ */ k(y, { size: 12 }), " Preview"]
|
|
90
|
+
}) : /* @__PURE__ */ k(S, {
|
|
91
|
+
size: 15,
|
|
92
|
+
className: "text-text-muted"
|
|
93
|
+
})
|
|
94
|
+
})
|
|
56
95
|
]
|
|
57
96
|
});
|
|
58
97
|
}
|
|
59
|
-
function
|
|
60
|
-
let [
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
98
|
+
function R() {
|
|
99
|
+
let [t] = O(), y = t.get("id") ?? "", { currentUser: S } = e(), { data: C, loading: w, error: F, refetch: R } = f(y), [z, { loading: B }] = p(), [V, { loading: H }] = ee(), { data: U, refetch: W } = d(S?.id && y ? {
|
|
100
|
+
learnerId: S.id,
|
|
101
|
+
courseId: y
|
|
102
|
+
} : {}), [G, { loading: K }] = m(), q = C?.tecCourse ?? null, J = [...q?.lessons ?? []].sort((e, t) => e.position - t.position), Y = (q?.enrollments ?? []).find((e) => e.learnerId === S?.id), X = (U?.tecListCertificates ?? []).find((e) => e.status === "ISSUED"), Z = !!Y && Y.progressPct >= 100, ne = async () => {
|
|
103
|
+
!y || !S?.id || (await G({ variables: {
|
|
104
|
+
courseId: y,
|
|
105
|
+
learnerId: S.id,
|
|
106
|
+
score: 100
|
|
107
|
+
} })).data?.tecIssueCertificate && (u.success("Certificate issued", "Your verifiable certificate is ready in My Learning."), await W());
|
|
108
|
+
}, re = async () => {
|
|
109
|
+
y && (await z({ variables: { input: { courseId: y } } })).data?.tecEnroll && (u.success("Enrolled", `You're enrolled in "${q?.title}".`), await R());
|
|
110
|
+
}, ie = async () => {
|
|
111
|
+
if (!Y) return;
|
|
112
|
+
let e = Math.min(J.length, Y.lessonsCompleted + 1), t = J.length ? Math.round(e / J.length * 100) : 100;
|
|
113
|
+
(await V({ variables: { input: {
|
|
114
|
+
enrollmentId: Y.id,
|
|
67
115
|
lessonsCompleted: e,
|
|
68
116
|
progressPct: t
|
|
69
|
-
} } })).data?.tecUpdateProgress && (
|
|
117
|
+
} } })).data?.tecUpdateProgress && (u.success("Progress saved", `${t}% complete`), await R());
|
|
70
118
|
};
|
|
71
|
-
if (
|
|
119
|
+
if (w && !q) return /* @__PURE__ */ A(n, {
|
|
72
120
|
title: "Course",
|
|
73
121
|
backPath: "/courses/courses-page",
|
|
74
|
-
children: [/* @__PURE__ */
|
|
122
|
+
children: [/* @__PURE__ */ k(P, { className: "mb-4 h-40 w-full rounded-2xl" }), /* @__PURE__ */ k(P, { className: "h-64 w-full rounded-xl" })]
|
|
75
123
|
});
|
|
76
|
-
if (
|
|
124
|
+
if (F || !q) return /* @__PURE__ */ k(n, {
|
|
77
125
|
title: "Course",
|
|
78
126
|
backPath: "/courses/courses-page",
|
|
79
|
-
children:
|
|
127
|
+
children: F ? /* @__PURE__ */ k(l, {
|
|
80
128
|
title: "Couldn't load course",
|
|
81
|
-
error:
|
|
82
|
-
}) : /* @__PURE__ */
|
|
129
|
+
error: F
|
|
130
|
+
}) : /* @__PURE__ */ k(c, {
|
|
83
131
|
title: "Course not found",
|
|
84
132
|
description: "This course may have been unpublished."
|
|
85
133
|
})
|
|
86
134
|
});
|
|
87
|
-
let
|
|
88
|
-
return /* @__PURE__ */
|
|
89
|
-
hero: !0,
|
|
135
|
+
let Q = Y?.lessonsCompleted ?? 0, $ = Math.max(0, J.length - Q), ae = Y && Y.progressPct >= 100 ? "Course complete — claim your certificate below." : $ === 1 ? "1 lesson to go — almost there!" : `${$} lessons to go — keep it up!`, oe = `${q.topic ?? ""} ${q.title}`;
|
|
136
|
+
return /* @__PURE__ */ A(n, {
|
|
90
137
|
backPath: "/courses/courses-page",
|
|
91
|
-
title:
|
|
92
|
-
description:
|
|
93
|
-
actions:
|
|
94
|
-
onClick:
|
|
95
|
-
disabled:
|
|
96
|
-
children: [/* @__PURE__ */
|
|
97
|
-
}) : /* @__PURE__ */
|
|
98
|
-
onClick:
|
|
99
|
-
disabled:
|
|
138
|
+
title: q.title,
|
|
139
|
+
description: q.summary ?? void 0,
|
|
140
|
+
actions: Y ? /* @__PURE__ */ A(M, {
|
|
141
|
+
onClick: ie,
|
|
142
|
+
disabled: H || Q >= J.length,
|
|
143
|
+
children: [/* @__PURE__ */ k(_, { size: 16 }), Q >= J.length ? "Completed" : "Complete next lesson"]
|
|
144
|
+
}) : /* @__PURE__ */ A(M, {
|
|
145
|
+
onClick: re,
|
|
146
|
+
disabled: B,
|
|
100
147
|
children: [
|
|
101
|
-
/* @__PURE__ */
|
|
148
|
+
/* @__PURE__ */ k(g, { size: 16 }),
|
|
102
149
|
" ",
|
|
103
|
-
|
|
150
|
+
B ? "Enrolling…" : "Enroll"
|
|
104
151
|
]
|
|
105
152
|
}),
|
|
106
153
|
children: [
|
|
107
|
-
/* @__PURE__ */
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
" ",
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
154
|
+
/* @__PURE__ */ k(i, {
|
|
155
|
+
height: "h-40",
|
|
156
|
+
seed: oe,
|
|
157
|
+
icon: a(q.topic ?? q.title),
|
|
158
|
+
className: "mb-5 rounded-2xl",
|
|
159
|
+
children: /* @__PURE__ */ A("div", {
|
|
160
|
+
className: "flex w-full flex-col gap-2",
|
|
161
|
+
children: [
|
|
162
|
+
/* @__PURE__ */ k("span", {
|
|
163
|
+
className: "text-xs font-medium uppercase tracking-wide text-white/80",
|
|
164
|
+
children: q.topic ?? "Course"
|
|
165
|
+
}),
|
|
166
|
+
/* @__PURE__ */ k("h2", {
|
|
167
|
+
className: "text-2xl font-bold leading-tight text-white drop-shadow-sm sm:text-3xl",
|
|
168
|
+
children: q.title
|
|
169
|
+
}),
|
|
170
|
+
/* @__PURE__ */ A("div", {
|
|
171
|
+
className: "flex flex-wrap items-center gap-2",
|
|
172
|
+
children: [
|
|
173
|
+
/* @__PURE__ */ k(I, {
|
|
174
|
+
icon: T,
|
|
175
|
+
children: s(q.level)
|
|
176
|
+
}),
|
|
177
|
+
/* @__PURE__ */ k(I, {
|
|
178
|
+
icon: _,
|
|
179
|
+
children: s(q.status)
|
|
180
|
+
}),
|
|
181
|
+
q.topic ? /* @__PURE__ */ k(I, {
|
|
182
|
+
icon: x,
|
|
183
|
+
children: q.topic
|
|
184
|
+
}) : null
|
|
185
|
+
]
|
|
186
|
+
})
|
|
187
|
+
]
|
|
188
|
+
})
|
|
129
189
|
}),
|
|
130
|
-
/* @__PURE__ */
|
|
190
|
+
q.tags.length ? /* @__PURE__ */ k("div", {
|
|
191
|
+
className: "mb-5 flex flex-wrap items-center gap-2",
|
|
192
|
+
children: q.tags.map((e) => /* @__PURE__ */ k(j, {
|
|
193
|
+
variant: "secondary",
|
|
194
|
+
children: e
|
|
195
|
+
}, e))
|
|
196
|
+
}) : null,
|
|
197
|
+
/* @__PURE__ */ A("div", {
|
|
131
198
|
className: "mb-6 grid grid-cols-2 gap-3 sm:grid-cols-4",
|
|
132
199
|
children: [
|
|
133
|
-
/* @__PURE__ */
|
|
200
|
+
/* @__PURE__ */ k(r, {
|
|
134
201
|
label: "Lessons",
|
|
135
|
-
value:
|
|
136
|
-
icon:
|
|
202
|
+
value: J.length,
|
|
203
|
+
icon: b,
|
|
204
|
+
tone: "blue"
|
|
137
205
|
}),
|
|
138
|
-
/* @__PURE__ */
|
|
206
|
+
/* @__PURE__ */ k(r, {
|
|
139
207
|
label: "Duration",
|
|
140
|
-
value:
|
|
141
|
-
icon:
|
|
208
|
+
value: o(q.estimatedMinutes),
|
|
209
|
+
icon: v,
|
|
210
|
+
tone: "teal"
|
|
142
211
|
}),
|
|
143
|
-
/* @__PURE__ */
|
|
212
|
+
/* @__PURE__ */ k(r, {
|
|
144
213
|
label: "Enrolled",
|
|
145
|
-
value:
|
|
146
|
-
icon:
|
|
214
|
+
value: q.enrollments?.length ?? 0,
|
|
215
|
+
icon: D,
|
|
216
|
+
tone: "indigo"
|
|
147
217
|
}),
|
|
148
|
-
/* @__PURE__ */
|
|
218
|
+
/* @__PURE__ */ k(r, {
|
|
149
219
|
label: "Your progress",
|
|
150
|
-
value:
|
|
151
|
-
icon:
|
|
220
|
+
value: Y ? `${Y.progressPct}%` : "Not started",
|
|
221
|
+
icon: T,
|
|
222
|
+
tone: "purple"
|
|
152
223
|
})
|
|
153
224
|
]
|
|
154
225
|
}),
|
|
155
|
-
|
|
156
|
-
className: "mb-6 p-
|
|
157
|
-
children: [
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
className: "font-medium text-text-primary",
|
|
161
|
-
children: "Your progress"
|
|
162
|
-
}), /* @__PURE__ */ S("span", {
|
|
163
|
-
className: "text-text-secondary",
|
|
226
|
+
Y ? /* @__PURE__ */ A(N, {
|
|
227
|
+
className: "mb-6 p-5",
|
|
228
|
+
children: [
|
|
229
|
+
/* @__PURE__ */ A("div", {
|
|
230
|
+
className: "mb-3 flex items-center gap-2",
|
|
164
231
|
children: [
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
232
|
+
/* @__PURE__ */ k("span", {
|
|
233
|
+
className: "flex h-8 w-8 items-center justify-center rounded-lg bg-accent-primary/15 text-accent-primary",
|
|
234
|
+
children: /* @__PURE__ */ k(T, { size: 16 })
|
|
235
|
+
}),
|
|
236
|
+
/* @__PURE__ */ k("span", {
|
|
237
|
+
className: "text-sm font-semibold text-text-primary",
|
|
238
|
+
children: "Your progress"
|
|
239
|
+
}),
|
|
240
|
+
/* @__PURE__ */ A("span", {
|
|
241
|
+
className: "ml-auto text-sm text-text-secondary",
|
|
242
|
+
children: [
|
|
243
|
+
Q,
|
|
244
|
+
"/",
|
|
245
|
+
J.length,
|
|
246
|
+
" lessons · ",
|
|
247
|
+
Y.progressPct,
|
|
248
|
+
"%"
|
|
249
|
+
]
|
|
250
|
+
})
|
|
171
251
|
]
|
|
172
|
-
})
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
252
|
+
}),
|
|
253
|
+
/* @__PURE__ */ k("div", {
|
|
254
|
+
className: "h-2.5 w-full overflow-hidden rounded-full bg-bg-sunken",
|
|
255
|
+
children: /* @__PURE__ */ k("div", {
|
|
256
|
+
className: "h-full rounded-full bg-gradient-to-r from-accent-primary via-accent-indigo to-accent-purple transition-all duration-500",
|
|
257
|
+
style: { width: `${Y.progressPct}%` }
|
|
258
|
+
})
|
|
259
|
+
}),
|
|
260
|
+
/* @__PURE__ */ k("p", {
|
|
261
|
+
className: "mt-2 text-xs text-text-muted",
|
|
262
|
+
children: ae
|
|
263
|
+
})
|
|
264
|
+
]
|
|
265
|
+
}) : null,
|
|
266
|
+
X ? /* @__PURE__ */ A(N, {
|
|
267
|
+
className: "mb-6 flex flex-wrap items-center gap-3 border border-status-success-border/60 bg-status-success-bg-subtle/30 p-4",
|
|
268
|
+
children: [
|
|
269
|
+
/* @__PURE__ */ k("span", {
|
|
270
|
+
className: "flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-status-warning-bg-subtle text-status-warning-text ring-1 ring-status-warning-border/30",
|
|
271
|
+
children: /* @__PURE__ */ k(h, { size: 22 })
|
|
272
|
+
}),
|
|
273
|
+
/* @__PURE__ */ A("div", {
|
|
274
|
+
className: "min-w-0 flex-1",
|
|
275
|
+
children: [/* @__PURE__ */ k("div", {
|
|
276
|
+
className: "text-sm font-semibold text-text-primary",
|
|
277
|
+
children: "Certificate earned"
|
|
278
|
+
}), /* @__PURE__ */ A("div", {
|
|
279
|
+
className: "text-xs text-text-muted",
|
|
280
|
+
children: [
|
|
281
|
+
"Serial ",
|
|
282
|
+
X.serial,
|
|
283
|
+
" · Score ",
|
|
284
|
+
X.score
|
|
285
|
+
]
|
|
286
|
+
})]
|
|
287
|
+
}),
|
|
288
|
+
/* @__PURE__ */ A("span", {
|
|
289
|
+
className: "inline-flex items-center gap-1 rounded-full border border-status-success-border bg-status-success-bg-subtle px-2.5 py-1 text-xs font-medium text-status-success-text",
|
|
290
|
+
children: [/* @__PURE__ */ k(te, { size: 13 }), " Verified"]
|
|
178
291
|
})
|
|
292
|
+
]
|
|
293
|
+
}) : Z ? /* @__PURE__ */ A(N, {
|
|
294
|
+
className: "mb-6 flex flex-col items-start gap-4 border border-status-success-border/60 bg-status-success-bg-subtle/40 p-5 sm:flex-row sm:items-center sm:justify-between",
|
|
295
|
+
children: [/* @__PURE__ */ A("div", {
|
|
296
|
+
className: "flex items-center gap-4",
|
|
297
|
+
children: [/* @__PURE__ */ k("span", {
|
|
298
|
+
className: "flex h-14 w-14 shrink-0 items-center justify-center rounded-2xl bg-status-warning-bg-subtle text-status-warning-text ring-1 ring-status-warning-border/40",
|
|
299
|
+
children: /* @__PURE__ */ k(E, { size: 26 })
|
|
300
|
+
}), /* @__PURE__ */ A("div", { children: [/* @__PURE__ */ k("div", {
|
|
301
|
+
className: "text-base font-semibold text-text-primary",
|
|
302
|
+
children: "You finished this course"
|
|
303
|
+
}), /* @__PURE__ */ k("div", {
|
|
304
|
+
className: "text-sm text-text-muted",
|
|
305
|
+
children: "Claim your verifiable certificate of completion."
|
|
306
|
+
})] })]
|
|
307
|
+
}), /* @__PURE__ */ A(M, {
|
|
308
|
+
onClick: ne,
|
|
309
|
+
disabled: K,
|
|
310
|
+
children: [
|
|
311
|
+
/* @__PURE__ */ k(h, { size: 16 }),
|
|
312
|
+
" ",
|
|
313
|
+
K ? "Issuing…" : "Claim certificate"
|
|
314
|
+
]
|
|
179
315
|
})]
|
|
180
316
|
}) : null,
|
|
181
|
-
/* @__PURE__ */
|
|
317
|
+
/* @__PURE__ */ A(N, {
|
|
182
318
|
className: "p-5",
|
|
183
|
-
children: [/* @__PURE__ */
|
|
184
|
-
className: "mb-
|
|
185
|
-
children: "
|
|
186
|
-
|
|
319
|
+
children: [/* @__PURE__ */ A("div", {
|
|
320
|
+
className: "mb-3 flex items-center justify-between gap-2",
|
|
321
|
+
children: [/* @__PURE__ */ A("h2", {
|
|
322
|
+
className: "flex items-center gap-2 text-base font-semibold text-text-primary",
|
|
323
|
+
children: [/* @__PURE__ */ k(g, {
|
|
324
|
+
size: 18,
|
|
325
|
+
className: "text-accent-primary"
|
|
326
|
+
}), " Curriculum"]
|
|
327
|
+
}), J.length ? /* @__PURE__ */ A("span", {
|
|
328
|
+
className: "rounded-full bg-status-success-bg-subtle px-2.5 py-1 text-xs font-medium text-status-success-text",
|
|
329
|
+
children: [
|
|
330
|
+
Q,
|
|
331
|
+
"/",
|
|
332
|
+
J.length,
|
|
333
|
+
" completed"
|
|
334
|
+
]
|
|
335
|
+
}) : null]
|
|
336
|
+
}), J.length === 0 ? /* @__PURE__ */ k("p", {
|
|
187
337
|
className: "py-6 text-center text-sm text-text-muted",
|
|
188
338
|
children: "Lessons for this course are being prepared."
|
|
189
|
-
}) : /* @__PURE__ */
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
339
|
+
}) : /* @__PURE__ */ k("div", {
|
|
340
|
+
className: "flex flex-col",
|
|
341
|
+
children: J.map((e, t) => /* @__PURE__ */ k(L, {
|
|
342
|
+
lesson: e,
|
|
343
|
+
index: t,
|
|
344
|
+
done: t < Q
|
|
345
|
+
}, e.id))
|
|
346
|
+
})]
|
|
193
347
|
})
|
|
194
348
|
]
|
|
195
349
|
});
|
|
196
350
|
}
|
|
197
351
|
//#endregion
|
|
198
|
-
export {
|
|
352
|
+
export { R as CoursesCourseDetailsPagePage };
|
|
199
353
|
|
|
200
354
|
//# sourceMappingURL=CoursesCourseDetailsPagePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CoursesCourseDetailsPagePage.js","names":[],"sources":["../../../src/technospam/pages/CoursesCourseDetailsPagePage.tsx"],"sourcesContent":["import { useSearchParams } from 'react-router-dom';\nimport {\n BookOpen,\n CheckCircle2,\n Clock,\n FileText,\n Lock,\n PlayCircle,\n Layers,\n Sparkles,\n} from 'lucide-react';\nimport { Badge, Button, GlassCard, Skeleton } from '@burdenoff/fe-libs/ui';\nimport { EmptyView, ErrorView, PageLayout, StatCard, StatusBadge } from '../components';\nimport { useTechnoSpam } from '../providers/TechnoSpamProvider';\nimport {\n useCourseQuery,\n useEnroll,\n useUpdateProgress,\n type TecLesson,\n type TecEnrollment,\n} from '../api/technospamApi';\nimport { formatDuration, humanizeEnum } from '../utils/format';\nimport { notify } from '../lib/toast';\n\nconst LESSON_ICON: Record<string, typeof PlayCircle> = {\n VIDEO: PlayCircle,\n ARTICLE: FileText,\n INTERACTIVE: Sparkles,\n QUIZ: CheckCircle2,\n PROJECT: Layers,\n};\n\nfunction LessonRow({ lesson, done }: { lesson: TecLesson; done: boolean }) {\n const Icon = LESSON_ICON[lesson.kind] ?? FileText;\n return (\n <div className=\"flex items-center gap-3 border-b border-border-seam py-3 last:border-0\">\n <span\n className={`flex h-8 w-8 shrink-0 items-center justify-center rounded-lg ${\n done\n ? 'bg-status-success-bg-subtle text-status-success-text'\n : 'bg-bg-sunken text-text-secondary'\n }`}\n >\n {done ? <CheckCircle2 size={16} /> : <Icon size={16} />}\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex items-center gap-2\">\n <span className=\"truncate text-sm font-medium text-text-primary\">{lesson.title}</span>\n {lesson.isPreview ? (\n <Badge variant=\"secondary\" className=\"text-[11px]\">\n Preview\n </Badge>\n ) : null}\n </div>\n <div className=\"text-xs text-text-muted\">\n {humanizeEnum(lesson.kind)} · {formatDuration(lesson.estimatedMinutes)}\n </div>\n </div>\n {!lesson.isPreview && !done ? <Lock size={14} className=\"text-text-muted\" /> : null}\n </div>\n );\n}\n\nexport function CoursesCourseDetailsPagePage() {\n const [params] = useSearchParams();\n const id = params.get('id') ?? '';\n const { currentUser } = useTechnoSpam();\n const { data, loading, error, refetch } = useCourseQuery(id);\n const [enroll, { loading: enrolling }] = useEnroll();\n const [updateProgress, { loading: savingProgress }] = useUpdateProgress();\n\n const course = data?.tecCourse ?? null;\n const lessons = [...(course?.lessons ?? [])].sort((a, b) => a.position - b.position);\n const myEnrollment: TecEnrollment | undefined = (course?.enrollments ?? []).find(\n (e) => e.learnerId === currentUser?.id\n );\n\n const handleEnroll = async () => {\n if (!id) return;\n const res = await enroll({ variables: { input: { courseId: id } } });\n if (res.data?.tecEnroll) {\n notify.success('Enrolled', `You're enrolled in \"${course?.title}\".`);\n await refetch();\n }\n };\n\n const handleProgress = async () => {\n if (!myEnrollment) return;\n const nextCompleted = Math.min(lessons.length, myEnrollment.lessonsCompleted + 1);\n const pct = lessons.length ? Math.round((nextCompleted / lessons.length) * 100) : 100;\n const res = await updateProgress({\n variables: {\n input: { enrollmentId: myEnrollment.id, lessonsCompleted: nextCompleted, progressPct: pct },\n },\n });\n if (res.data?.tecUpdateProgress) {\n notify.success('Progress saved', `${pct}% complete`);\n await refetch();\n }\n };\n\n if (loading && !course) {\n return (\n <PageLayout title=\"Course\" backPath=\"/courses/courses-page\">\n <Skeleton className=\"mb-4 h-40 w-full rounded-xl\" />\n <Skeleton className=\"h-64 w-full rounded-xl\" />\n </PageLayout>\n );\n }\n\n if (error || !course) {\n return (\n <PageLayout title=\"Course\" backPath=\"/courses/courses-page\">\n {error ? (\n <ErrorView title=\"Couldn't load course\" error={error} />\n ) : (\n <EmptyView\n title=\"Course not found\"\n description=\"This course may have been unpublished.\"\n />\n )}\n </PageLayout>\n );\n }\n\n const completedLessons = myEnrollment?.lessonsCompleted ?? 0;\n\n return (\n <PageLayout\n hero\n backPath=\"/courses/courses-page\"\n title={course.title}\n description={course.summary ?? undefined}\n actions={\n myEnrollment ? (\n <Button\n onClick={handleProgress}\n disabled={savingProgress || completedLessons >= lessons.length}\n >\n <CheckCircle2 size={16} />\n {completedLessons >= lessons.length ? 'Completed' : 'Complete next lesson'}\n </Button>\n ) : (\n <Button onClick={handleEnroll} disabled={enrolling}>\n <BookOpen size={16} /> {enrolling ? 'Enrolling…' : 'Enroll'}\n </Button>\n )\n }\n >\n <div className=\"mb-5 flex flex-wrap items-center gap-2\">\n <StatusBadge status={course.level} kind=\"level\" />\n <StatusBadge status={course.status} />\n {course.topic ? (\n <Badge variant=\"outline\" className=\"gap-1\">\n <Layers size={12} /> {course.topic}\n </Badge>\n ) : null}\n {course.tags.map((t) => (\n <Badge key={t} variant=\"secondary\">\n {t}\n </Badge>\n ))}\n </div>\n\n <div className=\"mb-6 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <StatCard label=\"Lessons\" value={lessons.length} icon={FileText} />\n <StatCard label=\"Duration\" value={formatDuration(course.estimatedMinutes)} icon={Clock} />\n <StatCard label=\"Enrolled\" value={course.enrollments?.length ?? 0} icon={BookOpen} />\n <StatCard\n label=\"Your progress\"\n value={myEnrollment ? `${myEnrollment.progressPct}%` : 'Not started'}\n icon={CheckCircle2}\n />\n </div>\n\n {myEnrollment ? (\n <GlassCard className=\"mb-6 p-4\">\n <div className=\"mb-2 flex items-center justify-between text-sm\">\n <span className=\"font-medium text-text-primary\">Your progress</span>\n <span className=\"text-text-secondary\">\n {completedLessons}/{lessons.length} lessons · {myEnrollment.progressPct}%\n </span>\n </div>\n <div className=\"h-2 w-full overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-full rounded-full bg-accent-primary transition-all\"\n style={{ width: `${myEnrollment.progressPct}%` }}\n />\n </div>\n </GlassCard>\n ) : null}\n\n <GlassCard className=\"p-5\">\n <h2 className=\"mb-2 text-base font-semibold text-text-primary\">Curriculum</h2>\n {lessons.length === 0 ? (\n <p className=\"py-6 text-center text-sm text-text-muted\">\n Lessons for this course are being prepared.\n </p>\n ) : (\n <div>\n {lessons.map((lesson, i) => (\n <LessonRow key={lesson.id} lesson={lesson} done={i < completedLessons} />\n ))}\n </div>\n )}\n </GlassCard>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CoursesCourseDetailsPagePage.js","names":[],"sources":["../../../src/technospam/pages/CoursesCourseDetailsPagePage.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { useSearchParams } from 'react-router-dom';\nimport {\n Award,\n BookOpen,\n CheckCircle2,\n Clock,\n Eye,\n FileText,\n Lock,\n PlayCircle,\n Layers,\n ShieldCheck,\n Sparkles,\n Trophy,\n TrendingUp,\n Users,\n type LucideIcon,\n} from 'lucide-react';\nimport { Badge, Button, GlassCard, Skeleton } from '@burdenoff/fe-libs/ui';\nimport {\n EmptyView,\n ErrorView,\n MediaCover,\n PageLayout,\n StatCard,\n StatusBadge,\n topicIcon,\n} from '../components';\nimport { useTechnoSpam } from '../providers/TechnoSpamProvider';\nimport {\n useCertificatesQuery,\n useCourseQuery,\n useEnroll,\n useIssueCertificate,\n useUpdateProgress,\n type TecLesson,\n type TecEnrollment,\n} from '../api/technospamApi';\nimport { formatDuration, humanizeEnum } from '../utils/format';\nimport { notify } from '../lib/toast';\nimport { cn } from '../utils/cn';\n\n/** Per-lesson-kind icon + on-theme tint (chip) and pill (badge) classes. */\nconst KIND_META: Record<string, { icon: LucideIcon; chip: string; badge: string }> = {\n VIDEO: {\n icon: PlayCircle,\n chip: 'bg-accent-primary/15 text-accent-primary',\n badge: 'border-accent-primary/25 bg-accent-primary/10 text-accent-primary',\n },\n ARTICLE: {\n icon: FileText,\n chip: 'bg-accent-indigo/15 text-accent-indigo',\n badge: 'border-accent-indigo/25 bg-accent-indigo/10 text-accent-indigo',\n },\n INTERACTIVE: {\n icon: Sparkles,\n chip: 'bg-accent-teal/15 text-accent-teal',\n badge: 'border-accent-teal/25 bg-accent-teal/10 text-accent-teal',\n },\n QUIZ: {\n icon: CheckCircle2,\n chip: 'bg-accent-purple/15 text-accent-purple',\n badge: 'border-accent-purple/25 bg-accent-purple/10 text-accent-purple',\n },\n PROJECT: {\n icon: Layers,\n chip: 'bg-status-warning-bg-subtle text-status-warning-text',\n badge: 'border-status-warning-border bg-status-warning-bg-subtle text-status-warning-text',\n },\n};\n\nfunction HeroBadge({ icon: Icon, children }: { icon?: LucideIcon; children: ReactNode }) {\n return (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-white/15 px-2.5 py-1 text-xs font-medium text-white ring-1 ring-white/25 backdrop-blur-sm\">\n {Icon ? <Icon size={12} /> : null}\n {children}\n </span>\n );\n}\n\nfunction LessonRow({ lesson, index, done }: { lesson: TecLesson; index: number; done: boolean }) {\n const meta = KIND_META[lesson.kind] ?? KIND_META.ARTICLE!;\n const Icon = meta.icon;\n return (\n <div className=\"group flex items-center gap-3 rounded-xl px-2 py-3 transition-colors hover:bg-bg-sunken/60\">\n <span className=\"hidden w-6 shrink-0 text-center text-sm font-medium tabular-nums text-text-muted sm:block\">\n {index + 1}\n </span>\n <span\n className={cn(\n 'flex h-9 w-9 shrink-0 items-center justify-center rounded-lg',\n done ? 'bg-status-success-bg-subtle text-status-success-text' : meta.chip\n )}\n >\n {done ? <CheckCircle2 size={17} /> : <Icon size={17} />}\n </span>\n <div className=\"min-w-0 flex-1\">\n <span className=\"block truncate text-sm font-medium text-text-primary\">{lesson.title}</span>\n <div className=\"mt-1 flex flex-wrap items-center gap-2\">\n <span\n className={cn(\n 'inline-flex items-center rounded-full border px-2 py-0.5 text-[11px] font-medium',\n meta.badge\n )}\n >\n {humanizeEnum(lesson.kind)}\n </span>\n <span className=\"inline-flex items-center gap-1 text-xs text-text-muted\">\n <Clock size={12} /> {formatDuration(lesson.estimatedMinutes)}\n </span>\n </div>\n </div>\n <div className=\"shrink-0\">\n {done ? (\n <CheckCircle2 size={18} className=\"text-status-success-text\" />\n ) : lesson.isPreview ? (\n <span className=\"inline-flex items-center gap-1 rounded-full border border-accent-primary/30 bg-accent-primary/10 px-2 py-0.5 text-[11px] font-medium text-accent-primary\">\n <Eye size={12} /> Preview\n </span>\n ) : (\n <Lock size={15} className=\"text-text-muted\" />\n )}\n </div>\n </div>\n );\n}\n\nexport function CoursesCourseDetailsPagePage() {\n const [params] = useSearchParams();\n const id = params.get('id') ?? '';\n const { currentUser } = useTechnoSpam();\n const { data, loading, error, refetch } = useCourseQuery(id);\n const [enroll, { loading: enrolling }] = useEnroll();\n const [updateProgress, { loading: savingProgress }] = useUpdateProgress();\n const { data: certData, refetch: refetchCerts } = useCertificatesQuery(\n currentUser?.id && id ? { learnerId: currentUser.id, courseId: id } : {}\n );\n const [issueCertificate, { loading: claiming }] = useIssueCertificate();\n\n const course = data?.tecCourse ?? null;\n const lessons = [...(course?.lessons ?? [])].sort((a, b) => a.position - b.position);\n const myEnrollment: TecEnrollment | undefined = (course?.enrollments ?? []).find(\n (e) => e.learnerId === currentUser?.id\n );\n const myCertificate = (certData?.tecListCertificates ?? []).find((c) => c.status === 'ISSUED');\n const isComplete = !!myEnrollment && myEnrollment.progressPct >= 100;\n\n const handleClaimCertificate = async () => {\n if (!id || !currentUser?.id) return;\n const res = await issueCertificate({\n variables: { courseId: id, learnerId: currentUser.id, score: 100 },\n });\n if (res.data?.tecIssueCertificate) {\n notify.success('Certificate issued', 'Your verifiable certificate is ready in My Learning.');\n await refetchCerts();\n }\n };\n\n const handleEnroll = async () => {\n if (!id) return;\n const res = await enroll({ variables: { input: { courseId: id } } });\n if (res.data?.tecEnroll) {\n notify.success('Enrolled', `You're enrolled in \"${course?.title}\".`);\n await refetch();\n }\n };\n\n const handleProgress = async () => {\n if (!myEnrollment) return;\n const nextCompleted = Math.min(lessons.length, myEnrollment.lessonsCompleted + 1);\n const pct = lessons.length ? Math.round((nextCompleted / lessons.length) * 100) : 100;\n const res = await updateProgress({\n variables: {\n input: { enrollmentId: myEnrollment.id, lessonsCompleted: nextCompleted, progressPct: pct },\n },\n });\n if (res.data?.tecUpdateProgress) {\n notify.success('Progress saved', `${pct}% complete`);\n await refetch();\n }\n };\n\n if (loading && !course) {\n return (\n <PageLayout title=\"Course\" backPath=\"/courses/courses-page\">\n <Skeleton className=\"mb-4 h-40 w-full rounded-2xl\" />\n <Skeleton className=\"h-64 w-full rounded-xl\" />\n </PageLayout>\n );\n }\n\n if (error || !course) {\n return (\n <PageLayout title=\"Course\" backPath=\"/courses/courses-page\">\n {error ? (\n <ErrorView title=\"Couldn't load course\" error={error} />\n ) : (\n <EmptyView\n title=\"Course not found\"\n description=\"This course may have been unpublished.\"\n />\n )}\n </PageLayout>\n );\n }\n\n const completedLessons = myEnrollment?.lessonsCompleted ?? 0;\n const remaining = Math.max(0, lessons.length - completedLessons);\n const milestone =\n myEnrollment && myEnrollment.progressPct >= 100\n ? 'Course complete — claim your certificate below.'\n : remaining === 1\n ? '1 lesson to go — almost there!'\n : `${remaining} lessons to go — keep it up!`;\n const heroSeed = `${course.topic ?? ''} ${course.title}`;\n\n return (\n <PageLayout\n backPath=\"/courses/courses-page\"\n title={course.title}\n description={course.summary ?? undefined}\n actions={\n myEnrollment ? (\n <Button\n onClick={handleProgress}\n disabled={savingProgress || completedLessons >= lessons.length}\n >\n <CheckCircle2 size={16} />\n {completedLessons >= lessons.length ? 'Completed' : 'Complete next lesson'}\n </Button>\n ) : (\n <Button onClick={handleEnroll} disabled={enrolling}>\n <BookOpen size={16} /> {enrolling ? 'Enrolling…' : 'Enroll'}\n </Button>\n )\n }\n >\n {/* Bold hero banner */}\n <MediaCover\n height=\"h-40\"\n seed={heroSeed}\n icon={topicIcon(course.topic ?? course.title)}\n className=\"mb-5 rounded-2xl\"\n >\n <div className=\"flex w-full flex-col gap-2\">\n <span className=\"text-xs font-medium uppercase tracking-wide text-white/80\">\n {course.topic ?? 'Course'}\n </span>\n <h2 className=\"text-2xl font-bold leading-tight text-white drop-shadow-sm sm:text-3xl\">\n {course.title}\n </h2>\n <div className=\"flex flex-wrap items-center gap-2\">\n <HeroBadge icon={TrendingUp}>{humanizeEnum(course.level)}</HeroBadge>\n <HeroBadge icon={CheckCircle2}>{humanizeEnum(course.status)}</HeroBadge>\n {course.topic ? <HeroBadge icon={Layers}>{course.topic}</HeroBadge> : null}\n </div>\n </div>\n </MediaCover>\n\n {course.tags.length ? (\n <div className=\"mb-5 flex flex-wrap items-center gap-2\">\n {course.tags.map((t) => (\n <Badge key={t} variant=\"secondary\">\n {t}\n </Badge>\n ))}\n </div>\n ) : null}\n\n <div className=\"mb-6 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <StatCard label=\"Lessons\" value={lessons.length} icon={FileText} tone=\"blue\" />\n <StatCard\n label=\"Duration\"\n value={formatDuration(course.estimatedMinutes)}\n icon={Clock}\n tone=\"teal\"\n />\n <StatCard\n label=\"Enrolled\"\n value={course.enrollments?.length ?? 0}\n icon={Users}\n tone=\"indigo\"\n />\n <StatCard\n label=\"Your progress\"\n value={myEnrollment ? `${myEnrollment.progressPct}%` : 'Not started'}\n icon={TrendingUp}\n tone=\"purple\"\n />\n </div>\n\n {myEnrollment ? (\n <GlassCard className=\"mb-6 p-5\">\n <div className=\"mb-3 flex items-center gap-2\">\n <span className=\"flex h-8 w-8 items-center justify-center rounded-lg bg-accent-primary/15 text-accent-primary\">\n <TrendingUp size={16} />\n </span>\n <span className=\"text-sm font-semibold text-text-primary\">Your progress</span>\n <span className=\"ml-auto text-sm text-text-secondary\">\n {completedLessons}/{lessons.length} lessons · {myEnrollment.progressPct}%\n </span>\n </div>\n <div className=\"h-2.5 w-full overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-full rounded-full bg-gradient-to-r from-accent-primary via-accent-indigo to-accent-purple transition-all duration-500\"\n style={{ width: `${myEnrollment.progressPct}%` }}\n />\n </div>\n <p className=\"mt-2 text-xs text-text-muted\">{milestone}</p>\n </GlassCard>\n ) : null}\n\n {myCertificate ? (\n <GlassCard className=\"mb-6 flex flex-wrap items-center gap-3 border border-status-success-border/60 bg-status-success-bg-subtle/30 p-4\">\n <span className=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-status-warning-bg-subtle text-status-warning-text ring-1 ring-status-warning-border/30\">\n <Award size={22} />\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"text-sm font-semibold text-text-primary\">Certificate earned</div>\n <div className=\"text-xs text-text-muted\">\n Serial {myCertificate.serial} · Score {myCertificate.score}\n </div>\n </div>\n <span className=\"inline-flex items-center gap-1 rounded-full border border-status-success-border bg-status-success-bg-subtle px-2.5 py-1 text-xs font-medium text-status-success-text\">\n <ShieldCheck size={13} /> Verified\n </span>\n </GlassCard>\n ) : isComplete ? (\n <GlassCard className=\"mb-6 flex flex-col items-start gap-4 border border-status-success-border/60 bg-status-success-bg-subtle/40 p-5 sm:flex-row sm:items-center sm:justify-between\">\n <div className=\"flex items-center gap-4\">\n <span className=\"flex h-14 w-14 shrink-0 items-center justify-center rounded-2xl bg-status-warning-bg-subtle text-status-warning-text ring-1 ring-status-warning-border/40\">\n <Trophy size={26} />\n </span>\n <div>\n <div className=\"text-base font-semibold text-text-primary\">\n You finished this course\n </div>\n <div className=\"text-sm text-text-muted\">\n Claim your verifiable certificate of completion.\n </div>\n </div>\n </div>\n <Button onClick={handleClaimCertificate} disabled={claiming}>\n <Award size={16} /> {claiming ? 'Issuing…' : 'Claim certificate'}\n </Button>\n </GlassCard>\n ) : null}\n\n <GlassCard className=\"p-5\">\n <div className=\"mb-3 flex items-center justify-between gap-2\">\n <h2 className=\"flex items-center gap-2 text-base font-semibold text-text-primary\">\n <BookOpen size={18} className=\"text-accent-primary\" /> Curriculum\n </h2>\n {lessons.length ? (\n <span className=\"rounded-full bg-status-success-bg-subtle px-2.5 py-1 text-xs font-medium text-status-success-text\">\n {completedLessons}/{lessons.length} completed\n </span>\n ) : null}\n </div>\n {lessons.length === 0 ? (\n <p className=\"py-6 text-center text-sm text-text-muted\">\n Lessons for this course are being prepared.\n </p>\n ) : (\n <div className=\"flex flex-col\">\n {lessons.map((lesson, i) => (\n <LessonRow key={lesson.id} lesson={lesson} index={i} done={i < completedLessons} />\n ))}\n </div>\n )}\n </GlassCard>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AA4CA,IAAM,IAA+E;CACnF,OAAO;EACL,MAAM;EACN,MAAM;EACN,OAAO;EACR;CACD,SAAS;EACP,MAAM;EACN,MAAM;EACN,OAAO;EACR;CACD,aAAa;EACX,MAAM;EACN,MAAM;EACN,OAAO;EACR;CACD,MAAM;EACJ,MAAM;EACN,MAAM;EACN,OAAO;EACR;CACD,SAAS;EACP,MAAM;EACN,MAAM;EACN,OAAO;EACR;CACF;AAED,SAAS,EAAU,EAAE,MAAM,GAAM,eAAwD;AACvF,QACE,kBAAC,QAAD;EAAM,WAAU;YAAhB,CACG,IAAO,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,GAAG,MAC5B,EACI;;;AAIX,SAAS,EAAU,EAAE,WAAQ,UAAO,WAA6D;CAC/F,IAAM,IAAO,EAAU,EAAO,SAAS,EAAU,SAC3C,IAAO,EAAK;AAClB,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf;GACE,kBAAC,QAAD;IAAM,WAAU;cACb,IAAQ;IACJ,CAAA;GACP,kBAAC,QAAD;IACE,WAAW,EACT,gEACA,IAAO,yDAAyD,EAAK,KACtE;cAEO,EAAP,IAAQ,IAA6B,GAA9B,EAAc,MAAM,IAAM,CAAqB;IAClD,CAAA;GACP,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,QAAD;KAAM,WAAU;eAAwD,EAAO;KAAa,CAAA,EAC5F,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,QAAD;MACE,WAAW,EACT,oFACA,EAAK,MACN;gBAEA,EAAa,EAAO,KAAK;MACrB,CAAA,EACP,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OACE,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;;OAAE,EAAe,EAAO,iBAAiB;OACvD;QACH;OACF;;GACN,kBAAC,OAAD;IAAK,WAAU;cACZ,IACC,kBAAC,GAAD;KAAc,MAAM;KAAI,WAAU;KAA6B,CAAA,GAC7D,EAAO,YACT,kBAAC,QAAD;KAAM,WAAU;eAAhB,CACE,kBAAC,GAAD,EAAK,MAAM,IAAM,CAAA,EAAA,WACZ;SAEP,kBAAC,GAAD;KAAM,MAAM;KAAI,WAAU;KAAoB,CAAA;IAE5C,CAAA;GACF;;;AAIV,SAAgB,IAA+B;CAC7C,IAAM,CAAC,KAAU,GAAiB,EAC5B,IAAK,EAAO,IAAI,KAAK,IAAI,IACzB,EAAE,mBAAgB,GAAe,EACjC,EAAE,SAAM,YAAS,UAAO,eAAY,EAAe,EAAG,EACtD,CAAC,GAAQ,EAAE,SAAS,OAAe,GAAW,EAC9C,CAAC,GAAgB,EAAE,SAAS,OAAoB,IAAmB,EACnE,EAAE,MAAM,GAAU,SAAS,MAAiB,EAChD,GAAa,MAAM,IAAK;EAAE,WAAW,EAAY;EAAI,UAAU;EAAI,GAAG,EAAE,CACzE,EACK,CAAC,GAAkB,EAAE,SAAS,OAAc,GAAqB,EAEjE,IAAS,GAAM,aAAa,MAC5B,IAAU,CAAC,GAAI,GAAQ,WAAW,EAAE,CAAE,CAAC,MAAM,GAAG,MAAM,EAAE,WAAW,EAAE,SAAS,EAC9E,KAA2C,GAAQ,eAAe,EAAE,EAAE,MACzE,MAAM,EAAE,cAAc,GAAa,GACrC,EACK,KAAiB,GAAU,uBAAuB,EAAE,EAAE,MAAM,MAAM,EAAE,WAAW,SAAS,EACxF,IAAa,CAAC,CAAC,KAAgB,EAAa,eAAe,KAE3D,KAAyB,YAAY;AACrC,GAAC,KAAM,CAAC,GAAa,OACb,MAAM,EAAiB,EACjC,WAAW;GAAE,UAAU;GAAI,WAAW,EAAY;GAAI,OAAO;GAAK,EACnE,CAAC,EACM,MAAM,wBACZ,EAAO,QAAQ,sBAAsB,uDAAuD,EAC5F,MAAM,GAAc;IAIlB,KAAe,YAAY;AAC1B,QACO,MAAM,EAAO,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,GAAI,EAAE,EAAE,CAAC,EAC5D,MAAM,cACZ,EAAO,QAAQ,YAAY,uBAAuB,GAAQ,MAAM,IAAI,EACpE,MAAM,GAAS;IAIb,KAAiB,YAAY;AACjC,MAAI,CAAC,EAAc;EACnB,IAAM,IAAgB,KAAK,IAAI,EAAQ,QAAQ,EAAa,mBAAmB,EAAE,EAC3E,IAAM,EAAQ,SAAS,KAAK,MAAO,IAAgB,EAAQ,SAAU,IAAI,GAAG;AAMlF,GALY,MAAM,EAAe,EAC/B,WAAW,EACT,OAAO;GAAE,cAAc,EAAa;GAAI,kBAAkB;GAAe,aAAa;GAAK,EAC5F,EACF,CAAC,EACM,MAAM,sBACZ,EAAO,QAAQ,kBAAkB,GAAG,EAAI,YAAY,EACpD,MAAM,GAAS;;AAInB,KAAI,KAAW,CAAC,EACd,QACE,kBAAC,GAAD;EAAY,OAAM;EAAS,UAAS;YAApC,CACE,kBAAC,GAAD,EAAU,WAAU,gCAAiC,CAAA,EACrD,kBAAC,GAAD,EAAU,WAAU,0BAA2B,CAAA,CACpC;;AAIjB,KAAI,KAAS,CAAC,EACZ,QACE,kBAAC,GAAD;EAAY,OAAM;EAAS,UAAS;YACjC,IACC,kBAAC,GAAD;GAAW,OAAM;GAA8B;GAAS,CAAA,GAExD,kBAAC,GAAD;GACE,OAAM;GACN,aAAY;GACZ,CAAA;EAEO,CAAA;CAIjB,IAAM,IAAmB,GAAc,oBAAoB,GACrD,IAAY,KAAK,IAAI,GAAG,EAAQ,SAAS,EAAiB,EAC1D,KACJ,KAAgB,EAAa,eAAe,MACxC,oDACA,MAAc,IACZ,mCACA,GAAG,EAAU,+BACf,KAAW,GAAG,EAAO,SAAS,GAAG,GAAG,EAAO;AAEjD,QACE,kBAAC,GAAD;EACE,UAAS;EACT,OAAO,EAAO;EACd,aAAa,EAAO,WAAW,KAAA;EAC/B,SACE,IACE,kBAAC,GAAD;GACE,SAAS;GACT,UAAU,KAAkB,KAAoB,EAAQ;aAF1D,CAIE,kBAAC,GAAD,EAAc,MAAM,IAAM,CAAA,EACzB,KAAoB,EAAQ,SAAS,cAAc,uBAC7C;OAET,kBAAC,GAAD;GAAQ,SAAS;GAAc,UAAU;aAAzC;IACE,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA;;IAAE,IAAY,eAAe;IAC5C;;YAhBf;GAqBE,kBAAC,GAAD;IACE,QAAO;IACP,MAAM;IACN,MAAM,EAAU,EAAO,SAAS,EAAO,MAAM;IAC7C,WAAU;cAEV,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,QAAD;OAAM,WAAU;iBACb,EAAO,SAAS;OACZ,CAAA;MACP,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAO;OACL,CAAA;MACL,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,GAAD;SAAW,MAAM;mBAAa,EAAa,EAAO,MAAM;SAAa,CAAA;QACrE,kBAAC,GAAD;SAAW,MAAM;mBAAe,EAAa,EAAO,OAAO;SAAa,CAAA;QACvE,EAAO,QAAQ,kBAAC,GAAD;SAAW,MAAM;mBAAS,EAAO;SAAkB,CAAA,GAAG;QAClE;;MACF;;IACK,CAAA;GAEZ,EAAO,KAAK,SACX,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAO,KAAK,KAAK,MAChB,kBAAC,GAAD;KAAe,SAAQ;eACpB;KACK,EAFI,EAEJ,CACR;IACE,CAAA,GACJ;GAEJ,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MAAU,OAAM;MAAU,OAAO,EAAQ;MAAQ,MAAM;MAAU,MAAK;MAAS,CAAA;KAC/E,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,EAAe,EAAO,iBAAiB;MAC9C,MAAM;MACN,MAAK;MACL,CAAA;KACF,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,EAAO,aAAa,UAAU;MACrC,MAAM;MACN,MAAK;MACL,CAAA;KACF,kBAAC,GAAD;MACE,OAAM;MACN,OAAO,IAAe,GAAG,EAAa,YAAY,KAAK;MACvD,MAAM;MACN,MAAK;MACL,CAAA;KACE;;GAEL,IACC,kBAAC,GAAD;IAAW,WAAU;cAArB;KACE,kBAAC,OAAD;MAAK,WAAU;gBAAf;OACE,kBAAC,QAAD;QAAM,WAAU;kBACd,kBAAC,GAAD,EAAY,MAAM,IAAM,CAAA;QACnB,CAAA;OACP,kBAAC,QAAD;QAAM,WAAU;kBAA0C;QAAoB,CAAA;OAC9E,kBAAC,QAAD;QAAM,WAAU;kBAAhB;SACG;SAAiB;SAAE,EAAQ;SAAO;SAAY,EAAa;SAAY;SACnE;;OACH;;KACN,kBAAC,OAAD;MAAK,WAAU;gBACb,kBAAC,OAAD;OACE,WAAU;OACV,OAAO,EAAE,OAAO,GAAG,EAAa,YAAY,IAAI;OAChD,CAAA;MACE,CAAA;KACN,kBAAC,KAAD;MAAG,WAAU;gBAAgC;MAAc,CAAA;KACjD;QACV;GAEH,IACC,kBAAC,GAAD;IAAW,WAAU;cAArB;KACE,kBAAC,QAAD;MAAM,WAAU;gBACd,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;MACd,CAAA;KACP,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAA0C;OAAwB,CAAA,EACjF,kBAAC,OAAD;OAAK,WAAU;iBAAf;QAAyC;QAC/B,EAAc;QAAO;QAAU,EAAc;QACjD;SACF;;KACN,kBAAC,QAAD;MAAM,WAAU;gBAAhB,CACE,kBAAC,IAAD,EAAa,MAAM,IAAM,CAAA,EAAA,YACpB;;KACG;QACV,IACF,kBAAC,GAAD;IAAW,WAAU;cAArB,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,QAAD;MAAM,WAAU;gBACd,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA;MACf,CAAA,EACP,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,OAAD;MAAK,WAAU;gBAA4C;MAErD,CAAA,EACN,kBAAC,OAAD;MAAK,WAAU;gBAA0B;MAEnC,CAAA,CACF,EAAA,CAAA,CACF;QACN,kBAAC,GAAD;KAAQ,SAAS;KAAwB,UAAU;eAAnD;MACE,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;;MAAE,IAAW,aAAa;MACtC;OACC;QACV;GAEJ,kBAAC,GAAD;IAAW,WAAU;cAArB,CACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAAd,CACE,kBAAC,GAAD;OAAU,MAAM;OAAI,WAAU;OAAwB,CAAA,EAAA,cACnD;SACJ,EAAQ,SACP,kBAAC,QAAD;MAAM,WAAU;gBAAhB;OACG;OAAiB;OAAE,EAAQ;OAAO;OAC9B;UACL,KACA;QACL,EAAQ,WAAW,IAClB,kBAAC,KAAD;KAAG,WAAU;eAA2C;KAEpD,CAAA,GAEJ,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAQ,KAAK,GAAQ,MACpB,kBAAC,GAAD;MAAmC;MAAQ,OAAO;MAAG,MAAM,IAAI;MAAoB,EAAnE,EAAO,GAA4D,CACnF;KACE,CAAA,CAEE;;GACD"}
|