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