@burdenoff/microfe-technospam 2026.1101.4 → 2026.1102.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/index.js +5 -3
  2. package/dist/technospam/TechnoSpamRoutes.d.ts.map +1 -1
  3. package/dist/technospam/TechnoSpamRoutes.js +91 -90
  4. package/dist/technospam/TechnoSpamRoutes.js.map +1 -1
  5. package/dist/technospam/api/technospamApi.d.ts +21 -7
  6. package/dist/technospam/api/technospamApi.d.ts.map +1 -1
  7. package/dist/technospam/api/technospamApi.js +317 -284
  8. package/dist/technospam/api/technospamApi.js.map +1 -1
  9. package/dist/technospam/components/CertificateVerifyLink.d.ts +11 -0
  10. package/dist/technospam/components/CertificateVerifyLink.d.ts.map +1 -0
  11. package/dist/technospam/components/CertificateVerifyLink.js +63 -0
  12. package/dist/technospam/components/CertificateVerifyLink.js.map +1 -0
  13. package/dist/technospam/components/index.d.ts +1 -0
  14. package/dist/technospam/components/index.d.ts.map +1 -1
  15. package/dist/technospam/components/index.js +1 -0
  16. package/dist/technospam/components/lesson/OfflineProgressSync.d.ts +9 -0
  17. package/dist/technospam/components/lesson/OfflineProgressSync.d.ts.map +1 -0
  18. package/dist/technospam/components/lesson/OfflineProgressSync.js +20 -0
  19. package/dist/technospam/components/lesson/OfflineProgressSync.js.map +1 -0
  20. package/dist/technospam/components/lesson/SaveCourseOffline.d.ts +17 -0
  21. package/dist/technospam/components/lesson/SaveCourseOffline.d.ts.map +1 -0
  22. package/dist/technospam/components/lesson/SaveCourseOffline.js +73 -0
  23. package/dist/technospam/components/lesson/SaveCourseOffline.js.map +1 -0
  24. package/dist/technospam/components/lesson/SavedCourseOutline.d.ts +17 -0
  25. package/dist/technospam/components/lesson/SavedCourseOutline.d.ts.map +1 -0
  26. package/dist/technospam/components/lesson/SavedCourseOutline.js +77 -0
  27. package/dist/technospam/components/lesson/SavedCourseOutline.js.map +1 -0
  28. package/dist/technospam/hooks/useOfflineLessons.d.ts +26 -0
  29. package/dist/technospam/hooks/useOfflineLessons.d.ts.map +1 -0
  30. package/dist/technospam/hooks/useOfflineLessons.js +70 -0
  31. package/dist/technospam/hooks/useOfflineLessons.js.map +1 -0
  32. package/dist/technospam/index.d.ts +2 -0
  33. package/dist/technospam/index.d.ts.map +1 -1
  34. package/dist/technospam/index.js +2 -0
  35. package/dist/technospam/lib/certificateVerify.d.ts +71 -0
  36. package/dist/technospam/lib/certificateVerify.d.ts.map +1 -0
  37. package/dist/technospam/lib/certificateVerify.js +55 -0
  38. package/dist/technospam/lib/certificateVerify.js.map +1 -0
  39. package/dist/technospam/lib/offlineLessons.d.ts +97 -0
  40. package/dist/technospam/lib/offlineLessons.d.ts.map +1 -0
  41. package/dist/technospam/lib/offlineLessons.js +168 -0
  42. package/dist/technospam/lib/offlineLessons.js.map +1 -0
  43. package/dist/technospam/pages/CoursesCourseDetailsPagePage.d.ts.map +1 -1
  44. package/dist/technospam/pages/CoursesCourseDetailsPagePage.js +241 -198
  45. package/dist/technospam/pages/CoursesCourseDetailsPagePage.js.map +1 -1
  46. package/dist/technospam/pages/CoursesLessonPlayerPagePage.d.ts.map +1 -1
  47. package/dist/technospam/pages/CoursesLessonPlayerPagePage.js +216 -138
  48. package/dist/technospam/pages/CoursesLessonPlayerPagePage.js.map +1 -1
  49. package/dist/technospam/pages/CoursesMyLearningPagePage.d.ts.map +1 -1
  50. package/dist/technospam/pages/CoursesMyLearningPagePage.js +104 -96
  51. package/dist/technospam/pages/CoursesMyLearningPagePage.js.map +1 -1
  52. package/dist/technospam/pages/public/CertificateVerifyPage.d.ts +4 -0
  53. package/dist/technospam/pages/public/CertificateVerifyPage.d.ts.map +1 -0
  54. package/dist/technospam/pages/public/CertificateVerifyPage.js +257 -0
  55. package/dist/technospam/pages/public/CertificateVerifyPage.js.map +1 -0
  56. package/package.json +1 -1
@@ -10,17 +10,22 @@ import { EmptyView as ee, ErrorView as te } from "../components/StateViews.js";
10
10
  import { TagStrip as ne } from "../components/TagStrip.js";
11
11
  import { notify as p } from "../lib/toast.js";
12
12
  import { useCourseQuery as re, useEnroll as ie, useIssueCertificate as ae, useMyCertificatesQuery as oe } from "../api/technospamApi.js";
13
+ import { CertificateVerifyLink as m } from "../components/CertificateVerifyLink.js";
13
14
  import "../components/index.js";
14
- import { FOCUS_PARAMS as se } from "../utils/pageContext.js";
15
- import { lessonPlayerPath as ce } from "../utils/lessonPath.js";
16
- import { useFocusedRecordParam as m } from "../hooks/useFocusedRecordParam.js";
17
- import { useEffect as le } from "react";
18
- import { ArrowRight as h, Award as g, BookOpen as _, CheckCircle2 as v, ChevronDown as y, Clock as b, Eye as x, FileText as S, Layers as C, Lock as w, PlayCircle as T, ShieldCheck as E, Sparkles as D, TrendingUp as O, Trophy as k, Users as A } from "lucide-react";
19
- import { useSearchParams as ue } from "react-router";
20
- import { jsx as j, jsxs as M } from "react/jsx-runtime";
21
- import { Button as N, GlassCard as P, Skeleton as F } from "@burdenoff/fe-libs/ui";
15
+ import { offlineScope as se, readSavedCourseOutline as ce } from "../lib/offlineLessons.js";
16
+ import { useOnlineStatus as le, usePurgeOtherUsersLessons as ue } from "../hooks/useOfflineLessons.js";
17
+ import { FOCUS_PARAMS as de } from "../utils/pageContext.js";
18
+ import { lessonPlayerPath as h } from "../utils/lessonPath.js";
19
+ import { useFocusedRecordParam as fe } from "../hooks/useFocusedRecordParam.js";
20
+ import { SaveCourseOffline as pe } from "../components/lesson/SaveCourseOffline.js";
21
+ import { SavedCourseOutline as me } from "../components/lesson/SavedCourseOutline.js";
22
+ import { useEffect as g, useMemo as he, useRef as ge } from "react";
23
+ import { ArrowRight as _e, Award as _, BookOpen as ve, CheckCircle2 as v, ChevronDown as y, Clock as b, Eye as x, FileText as S, Layers as C, Lock as w, PlayCircle as T, ShieldCheck as E, Sparkles as D, TrendingUp as O, Trophy as k, Users as ye } from "lucide-react";
24
+ import { useSearchParams as be } from "react-router";
25
+ import { jsx as A, jsxs as j } from "react/jsx-runtime";
26
+ import { Button as M, GlassCard as N, Skeleton as P } from "@burdenoff/fe-libs/ui";
22
27
  //#region src/technospam/pages/CoursesCourseDetailsPagePage.tsx
23
- var I = {
28
+ var F = {
24
29
  VIDEO: {
25
30
  icon: T,
26
31
  chip: "bg-accent-primary/15 text-accent-primary",
@@ -47,92 +52,92 @@ var I = {
47
52
  badge: "border-status-warning-border bg-status-warning-bg-subtle text-status-warning-text"
48
53
  }
49
54
  };
50
- function L({ icon: e, children: t }) {
51
- return /* @__PURE__ */ M("span", {
55
+ function I({ icon: e, children: t }) {
56
+ return /* @__PURE__ */ j("span", {
52
57
  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",
53
- children: [e ? /* @__PURE__ */ j(e, { size: 12 }) : null, t]
58
+ children: [e ? /* @__PURE__ */ A(e, { size: 12 }) : null, t]
54
59
  });
55
60
  }
56
- function de({ lesson: e, index: t, done: n, open: i, onToggle: a, onStart: o }) {
57
- let s = I[e.kind] ?? I.ARTICLE, c = s.icon, d = `tec-lesson-body-${e.id}`;
58
- return /* @__PURE__ */ M("div", {
61
+ function xe({ lesson: e, index: t, done: n, open: i, onToggle: a, onStart: o }) {
62
+ let s = F[e.kind] ?? F.ARTICLE, c = s.icon, d = `tec-lesson-body-${e.id}`;
63
+ return /* @__PURE__ */ j("div", {
59
64
  className: "border-b border-border-seam last:border-b-0",
60
- children: [/* @__PURE__ */ M("button", {
65
+ children: [/* @__PURE__ */ j("button", {
61
66
  type: "button",
62
67
  onClick: a,
63
68
  "aria-expanded": i,
64
69
  "aria-controls": d,
65
70
  className: "group flex w-full items-center gap-3 rounded-xl px-2 py-3 text-left transition-colors hover:bg-bg-sunken/60",
66
71
  children: [
67
- /* @__PURE__ */ j("span", {
72
+ /* @__PURE__ */ A("span", {
68
73
  className: "hidden w-6 shrink-0 text-center text-sm font-medium tabular-nums text-text-muted sm:block",
69
74
  children: t + 1
70
75
  }),
71
- /* @__PURE__ */ j("span", {
76
+ /* @__PURE__ */ A("span", {
72
77
  className: r("flex h-9 w-9 shrink-0 items-center justify-center rounded-lg", n ? "bg-status-success-bg-subtle text-status-success-text" : s.chip),
73
- children: j(n ? v : c, { size: 17 })
78
+ children: A(n ? v : c, { size: 17 })
74
79
  }),
75
- /* @__PURE__ */ M("div", {
80
+ /* @__PURE__ */ j("div", {
76
81
  className: "min-w-0 flex-1",
77
- children: [/* @__PURE__ */ j("span", {
82
+ children: [/* @__PURE__ */ A("span", {
78
83
  className: "block truncate text-sm font-medium text-text-primary",
79
84
  children: e.title
80
- }), /* @__PURE__ */ M("div", {
85
+ }), /* @__PURE__ */ j("div", {
81
86
  className: "mt-1 flex flex-wrap items-center gap-2",
82
- children: [/* @__PURE__ */ j("span", {
87
+ children: [/* @__PURE__ */ A("span", {
83
88
  className: r("inline-flex items-center rounded-full border px-2 py-0.5 text-[11px] font-medium", s.badge),
84
89
  children: u(e.kind)
85
- }), /* @__PURE__ */ M("span", {
90
+ }), /* @__PURE__ */ j("span", {
86
91
  className: "inline-flex items-center gap-1 text-xs text-text-muted",
87
92
  children: [
88
- /* @__PURE__ */ j(b, { size: 12 }),
93
+ /* @__PURE__ */ A(b, { size: 12 }),
89
94
  " ",
90
95
  l(e.estimatedMinutes)
91
96
  ]
92
97
  })]
93
98
  })]
94
99
  }),
95
- /* @__PURE__ */ M("div", {
100
+ /* @__PURE__ */ j("div", {
96
101
  className: "flex shrink-0 items-center gap-2",
97
- children: [n ? /* @__PURE__ */ j(v, {
102
+ children: [n ? /* @__PURE__ */ A(v, {
98
103
  size: 18,
99
104
  className: "text-status-success-text"
100
- }) : e.isPreview ? /* @__PURE__ */ M("span", {
105
+ }) : e.isPreview ? /* @__PURE__ */ j("span", {
101
106
  className: "inline-flex items-center gap-1 rounded-full border border-accent-primary/30 bg-accent-primary/6 px-2 py-0.5 text-[11px] font-medium text-accent-primary",
102
- children: [/* @__PURE__ */ j(x, { size: 12 }), " Preview"]
103
- }) : e.isLocked ? /* @__PURE__ */ M("span", {
107
+ children: [/* @__PURE__ */ A(x, { size: 12 }), " Preview"]
108
+ }) : e.isLocked ? /* @__PURE__ */ j("span", {
104
109
  className: "inline-flex items-center gap-1 rounded-full border border-status-warning-border/60 bg-status-warning-bg-subtle px-2 py-0.5 text-[11px] font-medium text-status-warning-text",
105
- children: [/* @__PURE__ */ j(w, { size: 12 }), " Upgrade to unlock"]
106
- }) : /* @__PURE__ */ j(w, {
110
+ children: [/* @__PURE__ */ A(w, { size: 12 }), " Upgrade to unlock"]
111
+ }) : /* @__PURE__ */ A(w, {
107
112
  size: 15,
108
113
  className: "text-text-muted"
109
- }), /* @__PURE__ */ j(y, {
114
+ }), /* @__PURE__ */ A(y, {
110
115
  size: 16,
111
116
  "aria-hidden": !0,
112
117
  className: r("text-text-muted transition-transform duration-200", i ? "rotate-180" : "")
113
118
  })]
114
119
  })
115
120
  ]
116
- }), i ? /* @__PURE__ */ j("div", {
121
+ }), i ? /* @__PURE__ */ A("div", {
117
122
  id: d,
118
123
  className: "px-2 pb-4 sm:pl-11",
119
- children: e.isLocked ? /* @__PURE__ */ j(f, {
124
+ children: e.isLocked ? /* @__PURE__ */ A(f, {
120
125
  compact: !0,
121
126
  description: "This lesson is part of a paid TechnoSpam plan — its content unlocks once your workspace upgrades."
122
- }) : /* @__PURE__ */ M("div", {
127
+ }) : /* @__PURE__ */ j("div", {
123
128
  className: "space-y-3",
124
- children: [e.content ? /* @__PURE__ */ j("div", {
129
+ children: [e.content ? /* @__PURE__ */ A("div", {
125
130
  className: "rounded-xl border border-border-seam bg-bg-sunken p-4",
126
- children: /* @__PURE__ */ j("p", {
131
+ children: /* @__PURE__ */ A("p", {
127
132
  className: "line-clamp-6 whitespace-pre-wrap text-sm leading-relaxed text-text-secondary",
128
133
  children: e.content
129
134
  })
130
- }) : null, /* @__PURE__ */ M(N, {
135
+ }) : null, /* @__PURE__ */ j(M, {
131
136
  size: "sm",
132
137
  onClick: o,
133
138
  "data-testid": `open-lesson-${e.id}`,
134
139
  children: [
135
- /* @__PURE__ */ j(T, { size: 15 }),
140
+ /* @__PURE__ */ A(T, { size: 15 }),
136
141
  " ",
137
142
  n ? "Review lesson" : "Start lesson"
138
143
  ]
@@ -141,183 +146,210 @@ function de({ lesson: e, index: t, done: n, open: i, onToggle: a, onStart: o })
141
146
  }) : null]
142
147
  });
143
148
  }
144
- function fe({ certificate: e, learnerName: t, courseTitle: n }) {
149
+ function Se({ certificate: e, learnerName: t, courseTitle: n }) {
145
150
  let r = e.status === "REVOKED";
146
- return /* @__PURE__ */ M(P, {
151
+ return /* @__PURE__ */ j(N, {
147
152
  className: "overflow-hidden p-0",
148
- children: [/* @__PURE__ */ j("div", {
153
+ children: [/* @__PURE__ */ A("div", {
149
154
  className: "border-b border-border-seam px-5 py-3",
150
- children: /* @__PURE__ */ j("span", {
155
+ children: /* @__PURE__ */ A("span", {
151
156
  className: "text-xs font-semibold uppercase tracking-wide text-text-muted",
152
157
  children: "Certificate preview"
153
158
  })
154
- }), /* @__PURE__ */ M("div", {
159
+ }), /* @__PURE__ */ j("div", {
155
160
  className: "space-y-4 bg-bg-sunken/50 px-5 py-6 text-center",
156
161
  children: [
157
- /* @__PURE__ */ M("div", {
162
+ /* @__PURE__ */ j("div", {
158
163
  className: "flex items-center justify-center gap-2 text-xs font-semibold uppercase tracking-[0.2em] text-text-muted",
159
- children: [/* @__PURE__ */ j(g, {
164
+ children: [/* @__PURE__ */ A(_, {
160
165
  size: 14,
161
166
  className: "text-status-warning-text"
162
167
  }), " Certificate of completion"]
163
168
  }),
164
- /* @__PURE__ */ j("h3", {
169
+ /* @__PURE__ */ A("h3", {
165
170
  className: "text-lg font-bold text-text-primary",
166
171
  children: n
167
172
  }),
168
- t ? /* @__PURE__ */ M("p", {
173
+ t ? /* @__PURE__ */ j("p", {
169
174
  className: "text-sm text-text-secondary",
170
- children: ["Awarded to ", /* @__PURE__ */ j("span", {
175
+ children: ["Awarded to ", /* @__PURE__ */ A("span", {
171
176
  className: "font-semibold text-text-primary",
172
177
  children: t
173
178
  })]
174
179
  }) : null,
175
- /* @__PURE__ */ M("div", {
180
+ /* @__PURE__ */ j("div", {
176
181
  className: "grid grid-cols-1 gap-3 border-t border-border-seam pt-4 text-left sm:grid-cols-3",
177
182
  children: [
178
- /* @__PURE__ */ M("div", { children: [/* @__PURE__ */ j("div", {
183
+ /* @__PURE__ */ j("div", { children: [/* @__PURE__ */ A("div", {
179
184
  className: "text-[11px] font-medium uppercase tracking-wide text-text-muted",
180
185
  children: "Serial number"
181
- }), /* @__PURE__ */ j("div", {
186
+ }), /* @__PURE__ */ A("div", {
182
187
  className: "truncate font-mono text-sm text-text-primary",
183
188
  children: e.serial
184
189
  })] }),
185
- /* @__PURE__ */ M("div", { children: [/* @__PURE__ */ j("div", {
190
+ /* @__PURE__ */ j("div", { children: [/* @__PURE__ */ A("div", {
186
191
  className: "text-[11px] font-medium uppercase tracking-wide text-text-muted",
187
192
  children: "Score"
188
- }), /* @__PURE__ */ j("div", {
193
+ }), /* @__PURE__ */ A("div", {
189
194
  className: "text-sm font-semibold text-text-primary",
190
195
  children: e.score
191
196
  })] }),
192
- /* @__PURE__ */ M("div", { children: [/* @__PURE__ */ j("div", {
197
+ /* @__PURE__ */ j("div", { children: [/* @__PURE__ */ A("div", {
193
198
  className: "text-[11px] font-medium uppercase tracking-wide text-text-muted",
194
199
  children: "Issued on"
195
- }), /* @__PURE__ */ j("div", {
200
+ }), /* @__PURE__ */ A("div", {
196
201
  className: "text-sm text-text-primary",
197
202
  children: c(e.issuedAt)
198
203
  })] })
199
204
  ]
200
205
  }),
201
- r ? /* @__PURE__ */ M("div", {
206
+ r ? /* @__PURE__ */ j("div", {
202
207
  className: "rounded-lg border border-status-error-border bg-status-error-bg-subtle px-3 py-2 text-left text-xs text-status-error-text",
203
208
  children: [
204
209
  "This certificate was revoked",
205
210
  e.revokedAt ? ` on ${c(e.revokedAt)}` : "",
206
211
  e.revokeReason ? ` — ${e.revokeReason}` : "."
207
212
  ]
208
- }) : /* @__PURE__ */ M("span", {
213
+ }) : /* @__PURE__ */ j("span", {
209
214
  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",
210
- children: [/* @__PURE__ */ j(E, { size: 13 }), " Verified"]
215
+ children: [/* @__PURE__ */ A(E, { size: 13 }), " Verified"]
216
+ }),
217
+ /* @__PURE__ */ A(m, {
218
+ serial: e.serial,
219
+ className: "border-t border-border-seam pt-4 text-left"
211
220
  })
212
221
  ]
213
222
  })]
214
223
  });
215
224
  }
216
- function R() {
217
- let [c] = ue(), y = c.get("id") ?? "", [x, T] = m(se.lesson), { currentUser: E, workspaceId: D } = e(), I = t(), { emit: R } = n(), { data: z, loading: B, error: V, refetch: pe } = re(y);
218
- le(() => {
219
- y && R("technospam.course.viewed", {
220
- workspaceId: D,
221
- entityId: y,
225
+ function L() {
226
+ let [c] = be(), m = c.get("id") ?? "", [y, x] = fe(de.lesson), { currentUser: T, workspaceId: E } = e(), D = t();
227
+ ue(T?.id);
228
+ let { emit: F } = n(), { data: L, loading: R, error: z, refetch: B } = re(m), V = le(), H = se(E, T?.id);
229
+ g(() => {
230
+ m && F("technospam.course.viewed", {
231
+ workspaceId: E,
232
+ entityId: m,
222
233
  entityType: "TEC_COURSE"
223
234
  });
224
235
  }, [
225
- y,
226
- D,
227
- R
236
+ m,
237
+ E,
238
+ F
228
239
  ]);
229
- let [me, { loading: H }] = ie(), U = z?.tecCourse ?? null, { certificates: W, refetch: he } = oe({
230
- buiCourseId: U?.buiCourseId,
231
- technospamCourseId: y,
232
- skip: !y
233
- }), [ge, { loading: G }] = ae(), K = [...U?.lessons ?? []].sort((e, t) => e.position - t.position), q = (U?.enrollments ?? []).find((e) => e.learnerId === E?.id), J = W.find((e) => e.status === "ISSUED") ?? W[0], _e = !!q && q.progressPct >= 100, ve = async () => {
234
- !y || !E?.id || (R("technospam.course.certificate_claimed", {
235
- workspaceId: D,
236
- entityId: y,
240
+ let [Ce, { loading: U }] = ie(), W = L?.tecCourse ?? null, { certificates: G, refetch: we } = oe({
241
+ buiCourseId: W?.buiCourseId,
242
+ technospamCourseId: m,
243
+ skip: !m
244
+ }), [Te, { loading: K }] = ae(), q = [...W?.lessons ?? []].sort((e, t) => e.position - t.position), J = (W?.enrollments ?? []).find((e) => e.learnerId === T?.id), Y = G.find((e) => e.status === "ISSUED") ?? G[0], Ee = !!J && J.progressPct >= 100, De = async () => {
245
+ !m || !T?.id || (F("technospam.course.certificate_claimed", {
246
+ workspaceId: E,
247
+ entityId: m,
237
248
  entityType: "TEC_COURSE"
238
- }), (await ge({ variables: {
239
- courseId: y,
240
- learnerId: E.id,
249
+ }), (await Te({ variables: {
250
+ courseId: m,
251
+ learnerId: T.id,
241
252
  score: 100
242
- } })).data?.tecIssueCertificate && (p.success("Certificate issued", "Your verifiable certificate is ready in My Learning."), await he()));
243
- }, ye = async () => {
244
- y && (R("technospam.course.enroll_clicked", {
245
- workspaceId: D,
246
- entityId: y,
253
+ } })).data?.tecIssueCertificate && (p.success("Certificate issued", "Your verifiable certificate is ready in My Learning."), await we()));
254
+ }, Oe = async () => {
255
+ m && (F("technospam.course.enroll_clicked", {
256
+ workspaceId: E,
257
+ entityId: m,
247
258
  entityType: "TEC_COURSE"
248
- }), (await me({ variables: { input: { courseId: y } } })).data?.tecEnroll && (R("technospam.course.enrolled", {
249
- workspaceId: D,
250
- entityId: y,
259
+ }), (await Ce({ variables: { input: { courseId: m } } })).data?.tecEnroll && (F("technospam.course.enrolled", {
260
+ workspaceId: E,
261
+ entityId: m,
251
262
  entityType: "TEC_COURSE"
252
- }), p.success("Enrolled", `You're enrolled in "${U?.title}".`), await pe()));
253
- };
254
- if (B && !U) return /* @__PURE__ */ M(i, {
263
+ }), p.success("Enrolled", `You're enrolled in "${W?.title}".`), await B()));
264
+ }, X = he(() => W || R ? null : ce(H, m), [
265
+ W,
266
+ R,
267
+ H,
268
+ m
269
+ ]), Z = ge(V);
270
+ if (g(() => {
271
+ let e = V && !Z.current;
272
+ Z.current = V, e && !W && B();
273
+ }, [
274
+ V,
275
+ W,
276
+ B
277
+ ]), R && !W) return /* @__PURE__ */ j(i, {
255
278
  title: "Course",
256
279
  backPath: "/courses/courses-page",
257
- children: [/* @__PURE__ */ j(F, { className: "mb-4 h-40 w-full rounded-2xl" }), /* @__PURE__ */ j(F, { className: "h-64 w-full rounded-xl" })]
280
+ children: [/* @__PURE__ */ A(P, { className: "mb-4 h-40 w-full rounded-2xl" }), /* @__PURE__ */ A(P, { className: "h-64 w-full rounded-xl" })]
258
281
  });
259
- if (V || !U) return /* @__PURE__ */ j(i, {
282
+ if (!W && X) return /* @__PURE__ */ A(i, {
283
+ title: X.course.title,
284
+ backPath: "/courses/courses-page",
285
+ children: /* @__PURE__ */ A(me, {
286
+ outline: X,
287
+ online: V,
288
+ onOpenLesson: (e) => D(h(m, e))
289
+ })
290
+ });
291
+ if (z || !W) return /* @__PURE__ */ A(i, {
260
292
  title: "Course",
261
293
  backPath: "/courses/courses-page",
262
- children: V ? /* @__PURE__ */ j(te, {
294
+ children: z ? /* @__PURE__ */ A(te, {
263
295
  title: "Couldn't load course",
264
- error: V
265
- }) : /* @__PURE__ */ j(ee, {
296
+ error: z
297
+ }) : /* @__PURE__ */ A(ee, {
266
298
  title: "Course not found",
267
299
  description: "This course may have been unpublished."
268
300
  })
269
301
  });
270
- let Y = q?.lessonsCompleted ?? 0, X = K.find((e) => !e.completedByMe) ?? K[0], Z = K.length > 0 && K.every((e) => e.completedByMe), Q = (e) => I(ce(y, e)), $ = Math.max(0, K.length - Y), be = q && q.progressPct >= 100 ? "Course complete — claim your certificate below." : $ === 1 ? "1 lesson to go — almost there!" : `${$} lessons to go — keep it up!`, xe = `${U.topic ?? ""} ${U.title}`;
271
- return /* @__PURE__ */ M(i, {
302
+ let Q = J?.lessonsCompleted ?? 0, ke = q.find((e) => !e.completedByMe) ?? q[0], Ae = q.length > 0 && q.every((e) => e.completedByMe), je = (e) => D(h(m, e)), $ = Math.max(0, q.length - Q), Me = J && J.progressPct >= 100 ? "Course complete — claim your certificate below." : $ === 1 ? "1 lesson to go — almost there!" : `${$} lessons to go — keep it up!`, Ne = `${W.topic ?? ""} ${W.title}`;
303
+ return /* @__PURE__ */ j(i, {
272
304
  backPath: "/courses/courses-page",
273
- title: U.title,
274
- description: U.summary ?? void 0,
275
- actions: q ? X ? /* @__PURE__ */ M(N, {
276
- onClick: () => Q(X.id),
305
+ title: W.title,
306
+ description: W.summary ?? void 0,
307
+ actions: J ? ke ? /* @__PURE__ */ j(M, {
308
+ onClick: () => je(ke.id),
277
309
  "data-testid": "continue-learning",
278
- children: [j(Z ? v : h, { size: 16 }), Z ? "Review course" : Y > 0 ? "Continue learning" : "Start course"]
279
- }) : null : /* @__PURE__ */ M(N, {
280
- onClick: ye,
281
- disabled: H,
310
+ children: [A(Ae ? v : _e, { size: 16 }), Ae ? "Review course" : Q > 0 ? "Continue learning" : "Start course"]
311
+ }) : null : /* @__PURE__ */ j(M, {
312
+ onClick: Oe,
313
+ disabled: U,
282
314
  children: [
283
- /* @__PURE__ */ j(_, { size: 16 }),
315
+ /* @__PURE__ */ A(ve, { size: 16 }),
284
316
  " ",
285
- H ? "Enrolling…" : "Enroll"
317
+ U ? "Enrolling…" : "Enroll"
286
318
  ]
287
319
  }),
288
320
  children: [
289
- /* @__PURE__ */ j(o, {
321
+ /* @__PURE__ */ A(o, {
290
322
  height: "h-40",
291
- seed: xe,
292
- icon: s(U.topic ?? U.title),
323
+ seed: Ne,
324
+ icon: s(W.topic ?? W.title),
293
325
  className: "mb-5 rounded-2xl",
294
- children: /* @__PURE__ */ M("div", {
326
+ children: /* @__PURE__ */ j("div", {
295
327
  className: "flex w-full flex-col gap-2",
296
328
  children: [
297
- /* @__PURE__ */ j("span", {
329
+ /* @__PURE__ */ A("span", {
298
330
  className: "text-xs font-medium uppercase tracking-wide text-white/80",
299
- children: U.topic ?? "Course"
331
+ children: W.topic ?? "Course"
300
332
  }),
301
- /* @__PURE__ */ j("h2", {
333
+ /* @__PURE__ */ A("h2", {
302
334
  className: "text-2xl font-bold leading-tight text-white drop-shadow-sm sm:text-3xl",
303
- children: U.title
335
+ children: W.title
304
336
  }),
305
- /* @__PURE__ */ M("div", {
337
+ /* @__PURE__ */ j("div", {
306
338
  className: "flex flex-wrap items-center gap-2",
307
339
  children: [
308
- /* @__PURE__ */ j(L, {
340
+ /* @__PURE__ */ A(I, {
309
341
  icon: O,
310
- children: u(U.level)
342
+ children: u(W.level)
311
343
  }),
312
- /* @__PURE__ */ j(L, {
344
+ /* @__PURE__ */ A(I, {
313
345
  icon: v,
314
- children: u(U.status)
346
+ children: u(W.status)
315
347
  }),
316
- U.topic ? /* @__PURE__ */ j(L, {
348
+ W.topic ? /* @__PURE__ */ A(I, {
317
349
  icon: C,
318
- children: U.topic
350
+ children: W.topic
319
351
  }) : null,
320
- U.isPremiumOnly ? /* @__PURE__ */ j(L, {
352
+ W.isPremiumOnly ? /* @__PURE__ */ A(I, {
321
353
  icon: w,
322
354
  children: "Premium"
323
355
  }) : null
@@ -326,169 +358,180 @@ function R() {
326
358
  ]
327
359
  })
328
360
  }),
329
- /* @__PURE__ */ j(ne, {
330
- tags: U.tags,
361
+ /* @__PURE__ */ A(ne, {
362
+ tags: W.tags,
331
363
  className: "mb-5"
332
364
  }),
333
- /* @__PURE__ */ M("div", {
365
+ /* @__PURE__ */ j("div", {
334
366
  className: "mb-6 grid grid-cols-2 gap-3 sm:grid-cols-4",
335
367
  children: [
336
- /* @__PURE__ */ j(a, {
368
+ /* @__PURE__ */ A(a, {
337
369
  label: "Lessons",
338
- value: K.length,
370
+ value: q.length,
339
371
  icon: S,
340
372
  tone: "blue"
341
373
  }),
342
- /* @__PURE__ */ j(a, {
374
+ /* @__PURE__ */ A(a, {
343
375
  label: "Duration",
344
- value: l(U.estimatedMinutes),
376
+ value: l(W.estimatedMinutes),
345
377
  icon: b,
346
378
  tone: "teal"
347
379
  }),
348
- /* @__PURE__ */ j(a, {
380
+ /* @__PURE__ */ A(a, {
349
381
  label: "Enrolled",
350
- value: U.enrollments?.length ?? 0,
351
- icon: A,
382
+ value: W.enrollments?.length ?? 0,
383
+ icon: ye,
352
384
  tone: "indigo"
353
385
  }),
354
- /* @__PURE__ */ j(a, {
386
+ /* @__PURE__ */ A(a, {
355
387
  label: "Your progress",
356
- value: q ? `${q.progressPct}%` : "Not started",
388
+ value: J ? `${J.progressPct}%` : "Not started",
357
389
  icon: O,
358
390
  tone: "purple"
359
391
  })
360
392
  ]
361
393
  }),
362
- q ? /* @__PURE__ */ M(P, {
394
+ J ? /* @__PURE__ */ j(N, {
363
395
  className: "mb-6 p-5",
364
396
  children: [
365
- /* @__PURE__ */ M("div", {
397
+ /* @__PURE__ */ j("div", {
366
398
  className: "mb-3 flex items-center gap-2",
367
399
  children: [
368
- /* @__PURE__ */ j("span", {
400
+ /* @__PURE__ */ A("span", {
369
401
  className: "flex h-8 w-8 items-center justify-center rounded-lg bg-accent-primary/15 text-accent-primary",
370
- children: /* @__PURE__ */ j(O, { size: 16 })
402
+ children: /* @__PURE__ */ A(O, { size: 16 })
371
403
  }),
372
- /* @__PURE__ */ j("span", {
404
+ /* @__PURE__ */ A("span", {
373
405
  className: "text-sm font-semibold text-text-primary",
374
406
  children: "Your progress"
375
407
  }),
376
- /* @__PURE__ */ M("span", {
408
+ /* @__PURE__ */ j("span", {
377
409
  className: "ml-auto text-sm text-text-secondary",
378
410
  children: [
379
- Y,
411
+ Q,
380
412
  "/",
381
- K.length,
413
+ q.length,
382
414
  " lessons · ",
383
- q.progressPct,
415
+ J.progressPct,
384
416
  "%"
385
417
  ]
386
418
  })
387
419
  ]
388
420
  }),
389
- /* @__PURE__ */ j("div", {
421
+ /* @__PURE__ */ A("div", {
390
422
  className: "h-2.5 w-full overflow-hidden rounded-full bg-bg-sunken",
391
- children: /* @__PURE__ */ j("div", {
423
+ children: /* @__PURE__ */ A("div", {
392
424
  className: "h-full rounded-full bg-gradient-to-r from-accent-primary via-accent-indigo to-accent-purple transition-all duration-500",
393
- style: { width: `${q.progressPct}%` }
425
+ style: { width: `${J.progressPct}%` }
394
426
  })
395
427
  }),
396
- /* @__PURE__ */ j("p", {
428
+ /* @__PURE__ */ A("p", {
397
429
  className: "mt-2 text-xs text-text-muted",
398
- children: be
430
+ children: Me
399
431
  })
400
432
  ]
401
433
  }) : null,
402
- J ? /* @__PURE__ */ M("div", {
434
+ Y ? /* @__PURE__ */ j("div", {
403
435
  className: "mb-6 grid grid-cols-1 gap-4 lg:grid-cols-2",
404
- children: [/* @__PURE__ */ M(P, {
405
- className: r("flex items-center gap-4 border p-5", J.status === "REVOKED" ? "border-status-error-border/60 bg-status-error-bg-subtle/20" : "border-status-success-border/60 bg-status-success-bg-subtle/30"),
406
- children: [/* @__PURE__ */ j("span", {
436
+ children: [/* @__PURE__ */ j(N, {
437
+ className: r("flex items-center gap-4 border p-5", Y.status === "REVOKED" ? "border-status-error-border/60 bg-status-error-bg-subtle/20" : "border-status-success-border/60 bg-status-success-bg-subtle/30"),
438
+ children: [/* @__PURE__ */ A("span", {
407
439
  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",
408
- children: /* @__PURE__ */ j(k, { size: 26 })
409
- }), /* @__PURE__ */ M("div", {
440
+ children: /* @__PURE__ */ A(k, { size: 26 })
441
+ }), /* @__PURE__ */ j("div", {
410
442
  className: "min-w-0",
411
- children: [/* @__PURE__ */ j("div", {
443
+ children: [/* @__PURE__ */ A("div", {
412
444
  className: "text-base font-semibold text-text-primary",
413
- children: J.status === "REVOKED" ? "Your certificate was revoked" : "You finished this course"
414
- }), /* @__PURE__ */ j("div", {
445
+ children: Y.status === "REVOKED" ? "Your certificate was revoked" : "You finished this course"
446
+ }), /* @__PURE__ */ A("div", {
415
447
  className: "text-sm text-text-muted",
416
- children: J.status === "REVOKED" ? "Contact a workspace admin if you believe this was a mistake." : "Your verifiable certificate of completion is issued — it also appears in My Learning."
448
+ children: Y.status === "REVOKED" ? "Contact a workspace admin if you believe this was a mistake." : "Your verifiable certificate of completion is issued — it also appears in My Learning."
417
449
  })]
418
450
  })]
419
- }), /* @__PURE__ */ j(fe, {
420
- certificate: J,
421
- learnerName: E?.name ?? void 0,
422
- courseTitle: J.course?.title ?? U.title
451
+ }), /* @__PURE__ */ A(Se, {
452
+ certificate: Y,
453
+ learnerName: T?.name ?? void 0,
454
+ courseTitle: Y.course?.title ?? W.title
423
455
  })]
424
- }) : _e ? /* @__PURE__ */ M(P, {
456
+ }) : Ee ? /* @__PURE__ */ j(N, {
425
457
  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",
426
- children: [/* @__PURE__ */ M("div", {
458
+ children: [/* @__PURE__ */ j("div", {
427
459
  className: "flex items-center gap-4",
428
- children: [/* @__PURE__ */ j("span", {
460
+ children: [/* @__PURE__ */ A("span", {
429
461
  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",
430
- children: /* @__PURE__ */ j(k, { size: 26 })
431
- }), /* @__PURE__ */ M("div", { children: [/* @__PURE__ */ j("div", {
462
+ children: /* @__PURE__ */ A(k, { size: 26 })
463
+ }), /* @__PURE__ */ j("div", { children: [/* @__PURE__ */ A("div", {
432
464
  className: "text-base font-semibold text-text-primary",
433
465
  children: "You finished this course"
434
- }), /* @__PURE__ */ j("div", {
466
+ }), /* @__PURE__ */ A("div", {
435
467
  className: "text-sm text-text-muted",
436
468
  children: "Claim your verifiable certificate of completion."
437
469
  })] })]
438
- }), /* @__PURE__ */ M(N, {
439
- onClick: ve,
440
- disabled: G,
470
+ }), /* @__PURE__ */ j(M, {
471
+ onClick: De,
472
+ disabled: K,
441
473
  children: [
442
- /* @__PURE__ */ j(g, { size: 16 }),
474
+ /* @__PURE__ */ A(_, { size: 16 }),
443
475
  " ",
444
- G ? "Issuing…" : "Claim certificate"
476
+ K ? "Issuing…" : "Claim certificate"
445
477
  ]
446
478
  })]
447
479
  }) : null,
448
- /* @__PURE__ */ M(P, {
480
+ /* @__PURE__ */ j(N, {
449
481
  className: "p-5",
450
482
  children: [
451
- /* @__PURE__ */ M("div", {
483
+ /* @__PURE__ */ j("div", {
452
484
  className: "mb-3 flex items-center justify-between gap-2",
453
- children: [/* @__PURE__ */ M("h2", {
485
+ children: [/* @__PURE__ */ j("h2", {
454
486
  className: "flex items-center gap-2 text-base font-semibold text-text-primary",
455
487
  children: [
456
- /* @__PURE__ */ j(_, {
488
+ /* @__PURE__ */ A(ve, {
457
489
  size: 18,
458
490
  className: "text-accent-primary"
459
491
  }),
460
492
  " Curriculum",
461
- U.isPremiumOnly ? /* @__PURE__ */ j(d, { locked: U.isLocked }) : null
462
- ]
463
- }), K.length ? /* @__PURE__ */ M("span", {
464
- className: "rounded-full bg-status-success-bg-subtle px-2.5 py-1 text-xs font-medium text-status-success-text",
465
- children: [
466
- Y,
467
- "/",
468
- K.length,
469
- " completed"
493
+ W.isPremiumOnly ? /* @__PURE__ */ A(d, { locked: W.isLocked }) : null
470
494
  ]
471
- }) : null]
495
+ }), /* @__PURE__ */ j("div", {
496
+ className: "flex flex-wrap items-center justify-end gap-2",
497
+ children: [J ? /* @__PURE__ */ A(pe, {
498
+ scope: H,
499
+ courseId: W.id,
500
+ lessons: q,
501
+ enrollment: {
502
+ id: J.id,
503
+ progressPct: J.progressPct
504
+ }
505
+ }) : null, q.length ? /* @__PURE__ */ j("span", {
506
+ className: "rounded-full bg-status-success-bg-subtle px-2.5 py-1 text-xs font-medium text-status-success-text",
507
+ children: [
508
+ Q,
509
+ "/",
510
+ q.length,
511
+ " completed"
512
+ ]
513
+ }) : null]
514
+ })]
472
515
  }),
473
- U.isLocked ? /* @__PURE__ */ j("div", {
516
+ W.isLocked ? /* @__PURE__ */ A("div", {
474
517
  className: "mb-4",
475
- children: /* @__PURE__ */ j(f, {
518
+ children: /* @__PURE__ */ A(f, {
476
519
  compact: !0,
477
520
  description: "This course is on a paid TechnoSpam plan. Preview lessons below stay free — the rest unlock once your workspace upgrades."
478
521
  })
479
522
  }) : null,
480
- K.length === 0 ? /* @__PURE__ */ j("p", {
523
+ q.length === 0 ? /* @__PURE__ */ A("p", {
481
524
  className: "py-6 text-center text-sm text-text-muted",
482
525
  children: "Lessons for this course are being prepared."
483
- }) : /* @__PURE__ */ j("div", {
526
+ }) : /* @__PURE__ */ A("div", {
484
527
  className: "flex flex-col",
485
- children: K.map((e, t) => /* @__PURE__ */ j(de, {
528
+ children: q.map((e, t) => /* @__PURE__ */ A(xe, {
486
529
  lesson: e,
487
530
  index: t,
488
531
  done: !!e.completedByMe,
489
- open: x === e.id,
490
- onToggle: () => T(x === e.id ? null : e.id),
491
- onStart: () => Q(e.id)
532
+ open: y === e.id,
533
+ onToggle: () => x(y === e.id ? null : e.id),
534
+ onStart: () => je(e.id)
492
535
  }, e.id))
493
536
  })
494
537
  ]
@@ -497,6 +540,6 @@ function R() {
497
540
  });
498
541
  }
499
542
  //#endregion
500
- export { R as CoursesCourseDetailsPagePage };
543
+ export { L as CoursesCourseDetailsPagePage };
501
544
 
502
545
  //# sourceMappingURL=CoursesCourseDetailsPagePage.js.map