@burdenoff/microfe-technospam 2026.705.1 → 2026.709.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.
- package/dist/generated/wspace-operations.js +67 -0
- package/dist/generated/wspace-operations.js.map +1 -0
- package/dist/index.js +3 -1
- 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/hooks/useTechnoSpamAnalytics.js +13 -0
- package/dist/technospam/hooks/useTechnoSpamAnalytics.js.map +1 -0
- package/dist/technospam/index.js +2 -0
- package/dist/technospam/pages/AdminAdminDashboardPagePage.js +525 -459
- 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 -101
- package/dist/technospam/pages/ApplicationsProblemDetailsPagePage.js.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemsPagePage.js +103 -99
- package/dist/technospam/pages/ApplicationsProblemsPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesCourseDetailsPagePage.js +316 -184
- package/dist/technospam/pages/CoursesCourseDetailsPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesCoursesPagePage.js +101 -93
- package/dist/technospam/pages/CoursesCoursesPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesMyLearningPagePage.js +119 -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 +99 -91
- package/dist/technospam/pages/HackathonsHackathonsPagePage.js.map +1 -1
- package/dist/technospam/pages/MentorshipMentorProfilePagePage.js +185 -116
- package/dist/technospam/pages/MentorshipMentorProfilePagePage.js.map +1 -1
- package/dist/technospam/pages/MentorshipMentorsPagePage.js +233 -165
- package/dist/technospam/pages/MentorshipMentorsPagePage.js.map +1 -1
- package/dist/technospam/pages/MentorshipSessionsPagePage.js +146 -113
- package/dist/technospam/pages/MentorshipSessionsPagePage.js.map +1 -1
- package/dist/technospam/services/assistantApi.js +222 -0
- package/dist/technospam/services/assistantApi.js.map +1 -0
- package/dist/technospam/services/createSandboxAssistantTransport.js +120 -0
- package/dist/technospam/services/createSandboxAssistantTransport.js.map +1 -0
- package/dist/technospam/utils/pageContext.js +46 -0
- package/dist/technospam/utils/pageContext.js.map +1 -0
- package/package.json +5 -1
|
@@ -1,152 +1,183 @@
|
|
|
1
1
|
import { useTechnoSpam as e, useTechnoSpamNavigate as t } from "../providers/TechnoSpamProvider.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
2
|
+
import { cn as n } from "../utils/cn.js";
|
|
3
|
+
import { PageLayout as r } from "../components/PageLayout.js";
|
|
4
|
+
import { StatCard as i } from "../components/StatCard.js";
|
|
5
|
+
import { coverGradient as a, topicIcon as o } from "../components/MediaCover.js";
|
|
6
|
+
import { formatDate as s, relativeTime as c } from "../utils/format.js";
|
|
7
|
+
import { StatusBadge as l } from "../components/StatusBadge.js";
|
|
8
|
+
import { EmptyView as u, ErrorView as d, LoadingRows as f } from "../components/StateViews.js";
|
|
7
9
|
import "../components/index.js";
|
|
8
|
-
import { useCertificatesQuery as
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
10
|
+
import { useCertificatesQuery as p, useEnrollmentsQuery as m } from "../api/technospamApi.js";
|
|
11
|
+
import { createElement as h } from "react";
|
|
12
|
+
import { Award as g, BookOpen as _, GraduationCap as v, PlayCircle as y, ShieldCheck as b } from "lucide-react";
|
|
13
|
+
import { jsx as x, jsxs as S } from "react/jsx-runtime";
|
|
14
|
+
import { Button as C, GlassCard as w } from "@burdenoff/fe-libs/ui";
|
|
12
15
|
//#region src/technospam/pages/CoursesMyLearningPagePage.tsx
|
|
13
|
-
function
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
function T({ e, onOpen: t }) {
|
|
17
|
+
let r = `${e.course?.topic ?? ""} ${e.course?.title ?? "Course"}`;
|
|
18
|
+
return /* @__PURE__ */ S(w, {
|
|
19
|
+
className: "group flex cursor-pointer flex-col gap-3 p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md sm:flex-row sm:items-center sm:gap-4",
|
|
16
20
|
onClick: t,
|
|
17
21
|
children: [
|
|
18
|
-
/* @__PURE__ */
|
|
19
|
-
className: "flex h-
|
|
20
|
-
children:
|
|
22
|
+
/* @__PURE__ */ x("span", {
|
|
23
|
+
className: n("flex h-12 w-12 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br text-white shadow-sm transition-transform duration-200 group-hover:scale-105", a(r)),
|
|
24
|
+
children: h(o(r), { size: 20 })
|
|
21
25
|
}),
|
|
22
|
-
/* @__PURE__ */
|
|
26
|
+
/* @__PURE__ */ S("div", {
|
|
23
27
|
className: "min-w-0 flex-1",
|
|
24
28
|
children: [
|
|
25
|
-
/* @__PURE__ */
|
|
26
|
-
className: "flex items-center gap-2",
|
|
27
|
-
children: [/* @__PURE__ */
|
|
28
|
-
className: "truncate text-sm font-semibold text-text-primary",
|
|
29
|
+
/* @__PURE__ */ S("div", {
|
|
30
|
+
className: "flex flex-wrap items-center gap-2",
|
|
31
|
+
children: [/* @__PURE__ */ x("span", {
|
|
32
|
+
className: "truncate text-sm font-semibold text-text-primary group-hover:text-accent-primary",
|
|
29
33
|
children: e.course?.title ?? "Course"
|
|
30
|
-
}), /* @__PURE__ */
|
|
34
|
+
}), /* @__PURE__ */ x(l, { status: e.status })]
|
|
31
35
|
}),
|
|
32
|
-
/* @__PURE__ */
|
|
33
|
-
className: "mt-1 flex items-center gap-2 text-xs text-text-muted",
|
|
36
|
+
/* @__PURE__ */ S("div", {
|
|
37
|
+
className: "mt-1 flex flex-wrap items-center gap-2 text-xs text-text-muted",
|
|
34
38
|
children: [
|
|
35
|
-
/* @__PURE__ */
|
|
36
|
-
/* @__PURE__ */
|
|
37
|
-
|
|
39
|
+
/* @__PURE__ */ S("span", { children: [e.lessonsCompleted, " lessons done"] }),
|
|
40
|
+
/* @__PURE__ */ x("span", {
|
|
41
|
+
"aria-hidden": !0,
|
|
42
|
+
children: "·"
|
|
43
|
+
}),
|
|
44
|
+
/* @__PURE__ */ S("span", { children: ["Active ", c(e.lastActiveAt ?? e.startedAt)] })
|
|
38
45
|
]
|
|
39
46
|
}),
|
|
40
|
-
/* @__PURE__ */
|
|
47
|
+
/* @__PURE__ */ x("div", {
|
|
41
48
|
className: "mt-2 h-1.5 w-full overflow-hidden rounded-full bg-bg-sunken",
|
|
42
|
-
children: /* @__PURE__ */
|
|
43
|
-
className: "h-full rounded-full bg-accent-primary",
|
|
49
|
+
children: /* @__PURE__ */ x("div", {
|
|
50
|
+
className: "h-full rounded-full bg-gradient-to-r from-accent-primary via-accent-indigo to-accent-purple transition-all duration-500",
|
|
44
51
|
style: { width: `${e.progressPct}%` }
|
|
45
52
|
})
|
|
46
53
|
})
|
|
47
54
|
]
|
|
48
55
|
}),
|
|
49
|
-
/* @__PURE__ */
|
|
50
|
-
className: "
|
|
51
|
-
children: [/* @__PURE__ */
|
|
52
|
-
className: "text-lg font-semibold text-text-primary",
|
|
56
|
+
/* @__PURE__ */ S("div", {
|
|
57
|
+
className: "flex items-center justify-between gap-3 sm:flex-col sm:items-end sm:justify-center",
|
|
58
|
+
children: [/* @__PURE__ */ S("div", {
|
|
59
|
+
className: "text-lg font-semibold tabular-nums text-text-primary",
|
|
53
60
|
children: [e.progressPct, "%"]
|
|
54
|
-
}), /* @__PURE__ */
|
|
61
|
+
}), /* @__PURE__ */ S(C, {
|
|
55
62
|
variant: "ghost",
|
|
56
63
|
size: "sm",
|
|
57
64
|
onClick: (e) => {
|
|
58
65
|
e.stopPropagation(), t();
|
|
59
66
|
},
|
|
60
|
-
children: [/* @__PURE__ */
|
|
67
|
+
children: [/* @__PURE__ */ x(y, { size: 15 }), " Resume"]
|
|
61
68
|
})]
|
|
62
69
|
})
|
|
63
70
|
]
|
|
64
71
|
});
|
|
65
72
|
}
|
|
66
|
-
function
|
|
67
|
-
let
|
|
68
|
-
return /* @__PURE__ */
|
|
73
|
+
function E() {
|
|
74
|
+
let n = t(), { currentUser: a } = e(), o = a?.id, { data: c, loading: h, error: E } = m(o ? { learnerId: o } : {}), { data: D } = p(o ? { learnerId: o } : {}), O = c?.tecListEnrollments.items ?? [], k = D?.tecListCertificates ?? [], A = O.filter((e) => e.status === "COMPLETED").length, j = O.filter((e) => e.status === "IN_PROGRESS" || e.status === "ENROLLED").length;
|
|
75
|
+
return /* @__PURE__ */ S(r, {
|
|
69
76
|
hero: !0,
|
|
70
77
|
title: "My Learning",
|
|
71
78
|
description: "Track your enrolled courses, progress, and earned certificates.",
|
|
72
|
-
actions: /* @__PURE__ */
|
|
79
|
+
actions: /* @__PURE__ */ S(C, {
|
|
73
80
|
variant: "outline",
|
|
74
|
-
onClick: () =>
|
|
75
|
-
children: [/* @__PURE__ */
|
|
81
|
+
onClick: () => n("/courses/courses-page"),
|
|
82
|
+
children: [/* @__PURE__ */ x(_, { size: 16 }), " Browse catalog"]
|
|
76
83
|
}),
|
|
77
84
|
children: [
|
|
78
|
-
/* @__PURE__ */
|
|
85
|
+
/* @__PURE__ */ S("div", {
|
|
79
86
|
className: "mb-6 grid grid-cols-2 gap-3 sm:grid-cols-4",
|
|
80
87
|
children: [
|
|
81
|
-
/* @__PURE__ */
|
|
88
|
+
/* @__PURE__ */ x(i, {
|
|
82
89
|
label: "Enrolled",
|
|
83
|
-
value:
|
|
84
|
-
|
|
90
|
+
value: O.length,
|
|
91
|
+
hint: "Courses you've enrolled in",
|
|
92
|
+
icon: _,
|
|
93
|
+
tone: "blue"
|
|
85
94
|
}),
|
|
86
|
-
/* @__PURE__ */
|
|
95
|
+
/* @__PURE__ */ x(i, {
|
|
87
96
|
label: "In progress",
|
|
88
|
-
value:
|
|
89
|
-
|
|
97
|
+
value: j,
|
|
98
|
+
hint: "Courses in progress",
|
|
99
|
+
icon: y,
|
|
100
|
+
tone: "indigo"
|
|
90
101
|
}),
|
|
91
|
-
/* @__PURE__ */
|
|
102
|
+
/* @__PURE__ */ x(i, {
|
|
92
103
|
label: "Completed",
|
|
93
|
-
value:
|
|
94
|
-
|
|
104
|
+
value: A,
|
|
105
|
+
hint: "Courses completed",
|
|
106
|
+
icon: v,
|
|
107
|
+
tone: "green"
|
|
95
108
|
}),
|
|
96
|
-
/* @__PURE__ */
|
|
109
|
+
/* @__PURE__ */ x(i, {
|
|
97
110
|
label: "Certificates",
|
|
98
|
-
value:
|
|
99
|
-
|
|
111
|
+
value: k.length,
|
|
112
|
+
hint: "Certificates earned",
|
|
113
|
+
icon: g,
|
|
114
|
+
tone: "purple"
|
|
100
115
|
})
|
|
101
116
|
]
|
|
102
117
|
}),
|
|
103
|
-
|
|
118
|
+
h && !O.length ? /* @__PURE__ */ x(f, {
|
|
104
119
|
count: 3,
|
|
105
120
|
height: "h-24"
|
|
106
|
-
}) :
|
|
121
|
+
}) : E ? /* @__PURE__ */ x(d, {
|
|
107
122
|
title: "Couldn't load your learning",
|
|
108
|
-
error:
|
|
109
|
-
}) :
|
|
110
|
-
icon: /* @__PURE__ */
|
|
123
|
+
error: E
|
|
124
|
+
}) : O.length === 0 ? /* @__PURE__ */ x(u, {
|
|
125
|
+
icon: /* @__PURE__ */ x(_, { size: 26 }),
|
|
111
126
|
title: "You haven't enrolled yet",
|
|
112
127
|
description: "Browse the catalog and enroll in a course to start building skill by doing.",
|
|
113
|
-
action: /* @__PURE__ */
|
|
114
|
-
onClick: () =>
|
|
128
|
+
action: /* @__PURE__ */ x(C, {
|
|
129
|
+
onClick: () => n("/courses/courses-page"),
|
|
115
130
|
children: "Browse courses"
|
|
116
131
|
})
|
|
117
|
-
}) : /* @__PURE__ */
|
|
132
|
+
}) : /* @__PURE__ */ x("div", {
|
|
118
133
|
className: "space-y-3",
|
|
119
|
-
children:
|
|
134
|
+
children: O.map((e) => /* @__PURE__ */ x(T, {
|
|
120
135
|
e,
|
|
121
|
-
onOpen: () =>
|
|
136
|
+
onOpen: () => n(`/courses/course-details-page?id=${e.courseId}`)
|
|
122
137
|
}, e.id))
|
|
123
138
|
}),
|
|
124
|
-
|
|
139
|
+
k.length > 0 ? /* @__PURE__ */ S("div", {
|
|
125
140
|
className: "mt-8",
|
|
126
|
-
children: [/* @__PURE__ */
|
|
127
|
-
className: "mb-3 text-base font-semibold text-text-primary",
|
|
128
|
-
children:
|
|
129
|
-
|
|
141
|
+
children: [/* @__PURE__ */ S("h2", {
|
|
142
|
+
className: "mb-3 flex items-center gap-2 text-base font-semibold text-text-primary",
|
|
143
|
+
children: [/* @__PURE__ */ x(g, {
|
|
144
|
+
size: 18,
|
|
145
|
+
className: "text-accent-teal"
|
|
146
|
+
}), " Certificates"]
|
|
147
|
+
}), /* @__PURE__ */ x("div", {
|
|
130
148
|
className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3",
|
|
131
|
-
children:
|
|
132
|
-
className: "flex
|
|
133
|
-
children: [/* @__PURE__ */
|
|
134
|
-
className: "
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
className: "
|
|
140
|
-
children:
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
"
|
|
145
|
-
e.
|
|
146
|
-
|
|
147
|
-
i(e.issuedAt)
|
|
148
|
-
]
|
|
149
|
+
children: k.map((e) => /* @__PURE__ */ S(w, {
|
|
150
|
+
className: "group flex flex-col gap-3 p-4 transition-all duration-200 hover:-translate-y-1 hover:shadow-lg",
|
|
151
|
+
children: [/* @__PURE__ */ S("div", {
|
|
152
|
+
className: "flex items-center gap-3",
|
|
153
|
+
children: [/* @__PURE__ */ x("span", {
|
|
154
|
+
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 transition-transform duration-200 group-hover:scale-105",
|
|
155
|
+
children: /* @__PURE__ */ x(g, { size: 22 })
|
|
156
|
+
}), /* @__PURE__ */ S("div", {
|
|
157
|
+
className: "min-w-0",
|
|
158
|
+
children: [/* @__PURE__ */ x("div", {
|
|
159
|
+
className: "truncate text-sm font-medium text-text-primary",
|
|
160
|
+
children: e.course?.title ?? "Certificate"
|
|
161
|
+
}), /* @__PURE__ */ S("div", {
|
|
162
|
+
className: "truncate text-xs text-text-muted",
|
|
163
|
+
children: ["Serial ", e.serial]
|
|
164
|
+
})]
|
|
149
165
|
})]
|
|
166
|
+
}), /* @__PURE__ */ S("div", {
|
|
167
|
+
className: "mt-auto flex items-center justify-between border-t border-border-seam pt-3 text-xs text-text-muted",
|
|
168
|
+
children: [/* @__PURE__ */ S("span", { children: [
|
|
169
|
+
"Score ",
|
|
170
|
+
/* @__PURE__ */ x("span", {
|
|
171
|
+
className: "font-semibold text-text-primary",
|
|
172
|
+
children: e.score
|
|
173
|
+
}),
|
|
174
|
+
" ·",
|
|
175
|
+
" ",
|
|
176
|
+
s(e.issuedAt)
|
|
177
|
+
] }), e.status === "ISSUED" ? /* @__PURE__ */ S("span", {
|
|
178
|
+
className: "inline-flex items-center gap-1 rounded-full border border-status-success-border bg-status-success-bg-subtle px-2 py-0.5 font-medium text-status-success-text",
|
|
179
|
+
children: [/* @__PURE__ */ x(b, { size: 12 }), " Verified"]
|
|
180
|
+
}) : /* @__PURE__ */ x(l, { status: e.status })]
|
|
150
181
|
})]
|
|
151
182
|
}, e.id))
|
|
152
183
|
})]
|
|
@@ -155,6 +186,6 @@ function x() {
|
|
|
155
186
|
});
|
|
156
187
|
}
|
|
157
188
|
//#endregion
|
|
158
|
-
export {
|
|
189
|
+
export { E as CoursesMyLearningPagePage };
|
|
159
190
|
|
|
160
191
|
//# sourceMappingURL=CoursesMyLearningPagePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CoursesMyLearningPagePage.js","names":[],"sources":["../../../src/technospam/pages/CoursesMyLearningPagePage.tsx"],"sourcesContent":["import { Award, BookOpen, Clock, GraduationCap, PlayCircle } from 'lucide-react';\nimport { Button, GlassCard } from '@burdenoff/fe-libs/ui';\nimport {\n EmptyView,\n ErrorView,\n LoadingRows,\n PageLayout,\n StatCard,\n StatusBadge,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n useEnrollmentsQuery,\n useCertificatesQuery,\n type TecEnrollment,\n} from '../api/technospamApi';\nimport { formatDate, relativeTime } from '../utils/format';\n\nfunction EnrollmentRow({ e, onOpen }: { e: TecEnrollment; onOpen: () => void }) {\n return (\n <GlassCard\n className=\"flex cursor-pointer items-center gap-4 p-4 transition-shadow hover:shadow-md\"\n onClick={onOpen}\n >\n <span className=\"flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-accent-wash text-accent-primary\">\n <BookOpen size={20} />\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-semibold text-text-primary\">\n {e.course?.title ?? 'Course'}\n </span>\n <StatusBadge status={e.status} />\n </div>\n <div className=\"mt-1 flex items-center gap-2 text-xs text-text-muted\">\n <span>{e.lessonsCompleted} lessons done</span>\n <span>·</span>\n <span>Active {relativeTime(e.lastActiveAt ?? e.startedAt)}</span>\n </div>\n <div className=\"mt-2 h-1.5 w-full overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-full rounded-full bg-accent-primary\"\n style={{ width: `${e.progressPct}%` }}\n />\n </div>\n </div>\n <div className=\"shrink-0 text-right\">\n <div className=\"text-lg font-semibold text-text-primary\">{e.progressPct}%</div>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={(ev) => {\n ev.stopPropagation();\n onOpen();\n }}\n >\n <PlayCircle size={15} /> Resume\n </Button>\n </div>\n </GlassCard>\n );\n}\n\nexport function CoursesMyLearningPagePage() {\n const navigate = useTechnoSpamNavigate();\n const { currentUser } = useTechnoSpam();\n const learnerId = currentUser?.id;\n\n const { data, loading, error } = useEnrollmentsQuery(learnerId ? { learnerId } : {});\n const { data: certData } = useCertificatesQuery(learnerId ? { learnerId } : {});\n\n const enrollments = data?.tecListEnrollments.items ?? [];\n const certificates = certData?.tecListCertificates ?? [];\n const completed = enrollments.filter((e) => e.status === 'COMPLETED').length;\n const inProgress = enrollments.filter(\n (e) => e.status === 'IN_PROGRESS' || e.status === 'ENROLLED'\n ).length;\n\n return (\n <PageLayout\n hero\n title=\"My Learning\"\n description=\"Track your enrolled courses, progress, and earned certificates.\"\n actions={\n <Button variant=\"outline\" onClick={() => navigate('/courses/courses-page')}>\n <BookOpen size={16} /> Browse catalog\n </Button>\n }\n >\n <div className=\"mb-6 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <StatCard label=\"Enrolled\" value={enrollments.length} icon={BookOpen} />\n <StatCard label=\"In progress\" value={inProgress} icon={PlayCircle} />\n <StatCard label=\"Completed\" value={completed} icon={GraduationCap} />\n <StatCard label=\"Certificates\" value={certificates.length} icon={Award} />\n </div>\n\n {loading && !enrollments.length ? (\n <LoadingRows count={3} height=\"h-24\" />\n ) : error ? (\n <ErrorView title=\"Couldn't load your learning\" error={error} />\n ) : enrollments.length === 0 ? (\n <EmptyView\n icon={<BookOpen size={26} />}\n title=\"You haven't enrolled yet\"\n description=\"Browse the catalog and enroll in a course to start building skill by doing.\"\n action={<Button onClick={() => navigate('/courses/courses-page')}>Browse courses</Button>}\n />\n ) : (\n <div className=\"space-y-3\">\n {enrollments.map((e) => (\n <EnrollmentRow\n key={e.id}\n e={e}\n onOpen={() => navigate(`/courses/course-details-page?id=${e.courseId}`)}\n />\n ))}\n </div>\n )}\n\n {certificates.length > 0 ? (\n <div className=\"mt-8\">\n <h2 className=\"mb-3 text-base font-semibold text-text-primary\">Certificates</h2>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3\">\n {certificates.map((c) => (\n <GlassCard key={c.id} className=\"flex items-center gap-3 p-4\">\n <Award className=\"text-accent-teal\" size={22} />\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-medium text-text-primary\">\n {c.course?.title ?? 'Certificate'}\n </div>\n <div className=\"text-xs text-text-muted\">\n Score {c.score} · Issued {formatDate(c.issuedAt)}\n </div>\n </div>\n </GlassCard>\n ))}\n </div>\n </div>\n ) : null}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"CoursesMyLearningPagePage.js","names":[],"sources":["../../../src/technospam/pages/CoursesMyLearningPagePage.tsx"],"sourcesContent":["import { createElement } from 'react';\nimport { Award, BookOpen, GraduationCap, PlayCircle, ShieldCheck } from 'lucide-react';\nimport { Button, GlassCard } from '@burdenoff/fe-libs/ui';\nimport {\n EmptyView,\n ErrorView,\n LoadingRows,\n PageLayout,\n StatCard,\n StatusBadge,\n coverGradient,\n topicIcon,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n useEnrollmentsQuery,\n useCertificatesQuery,\n type TecEnrollment,\n} from '../api/technospamApi';\nimport { formatDate, relativeTime } from '../utils/format';\nimport { cn } from '../utils/cn';\n\nfunction EnrollmentRow({ e, onOpen }: { e: TecEnrollment; onOpen: () => void }) {\n const seed = `${e.course?.topic ?? ''} ${e.course?.title ?? 'Course'}`;\n return (\n <GlassCard\n className=\"group flex cursor-pointer flex-col gap-3 p-4 transition-all duration-200 hover:-translate-y-0.5 hover:shadow-md sm:flex-row sm:items-center sm:gap-4\"\n onClick={onOpen}\n >\n <span\n className={cn(\n 'flex h-12 w-12 shrink-0 items-center justify-center rounded-xl bg-gradient-to-br text-white shadow-sm transition-transform duration-200 group-hover:scale-105',\n coverGradient(seed)\n )}\n >\n {createElement(topicIcon(seed), { size: 20 })}\n </span>\n <div className=\"min-w-0 flex-1\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <span className=\"truncate text-sm font-semibold text-text-primary group-hover:text-accent-primary\">\n {e.course?.title ?? 'Course'}\n </span>\n <StatusBadge status={e.status} />\n </div>\n <div className=\"mt-1 flex flex-wrap items-center gap-2 text-xs text-text-muted\">\n <span>{e.lessonsCompleted} lessons done</span>\n <span aria-hidden>·</span>\n <span>Active {relativeTime(e.lastActiveAt ?? e.startedAt)}</span>\n </div>\n <div className=\"mt-2 h-1.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: `${e.progressPct}%` }}\n />\n </div>\n </div>\n <div className=\"flex items-center justify-between gap-3 sm:flex-col sm:items-end sm:justify-center\">\n <div className=\"text-lg font-semibold tabular-nums text-text-primary\">{e.progressPct}%</div>\n <Button\n variant=\"ghost\"\n size=\"sm\"\n onClick={(ev) => {\n ev.stopPropagation();\n onOpen();\n }}\n >\n <PlayCircle size={15} /> Resume\n </Button>\n </div>\n </GlassCard>\n );\n}\n\nexport function CoursesMyLearningPagePage() {\n const navigate = useTechnoSpamNavigate();\n const { currentUser } = useTechnoSpam();\n const learnerId = currentUser?.id;\n\n const { data, loading, error } = useEnrollmentsQuery(learnerId ? { learnerId } : {});\n const { data: certData } = useCertificatesQuery(learnerId ? { learnerId } : {});\n\n const enrollments = data?.tecListEnrollments.items ?? [];\n const certificates = certData?.tecListCertificates ?? [];\n const completed = enrollments.filter((e) => e.status === 'COMPLETED').length;\n const inProgress = enrollments.filter(\n (e) => e.status === 'IN_PROGRESS' || e.status === 'ENROLLED'\n ).length;\n\n return (\n <PageLayout\n hero\n title=\"My Learning\"\n description=\"Track your enrolled courses, progress, and earned certificates.\"\n actions={\n <Button variant=\"outline\" onClick={() => navigate('/courses/courses-page')}>\n <BookOpen size={16} /> Browse catalog\n </Button>\n }\n >\n <div className=\"mb-6 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <StatCard\n label=\"Enrolled\"\n value={enrollments.length}\n hint=\"Courses you've enrolled in\"\n icon={BookOpen}\n tone=\"blue\"\n />\n <StatCard\n label=\"In progress\"\n value={inProgress}\n hint=\"Courses in progress\"\n icon={PlayCircle}\n tone=\"indigo\"\n />\n <StatCard\n label=\"Completed\"\n value={completed}\n hint=\"Courses completed\"\n icon={GraduationCap}\n tone=\"green\"\n />\n <StatCard\n label=\"Certificates\"\n value={certificates.length}\n hint=\"Certificates earned\"\n icon={Award}\n tone=\"purple\"\n />\n </div>\n\n {loading && !enrollments.length ? (\n <LoadingRows count={3} height=\"h-24\" />\n ) : error ? (\n <ErrorView title=\"Couldn't load your learning\" error={error} />\n ) : enrollments.length === 0 ? (\n <EmptyView\n icon={<BookOpen size={26} />}\n title=\"You haven't enrolled yet\"\n description=\"Browse the catalog and enroll in a course to start building skill by doing.\"\n action={<Button onClick={() => navigate('/courses/courses-page')}>Browse courses</Button>}\n />\n ) : (\n <div className=\"space-y-3\">\n {enrollments.map((e) => (\n <EnrollmentRow\n key={e.id}\n e={e}\n onOpen={() => navigate(`/courses/course-details-page?id=${e.courseId}`)}\n />\n ))}\n </div>\n )}\n\n {certificates.length > 0 ? (\n <div className=\"mt-8\">\n <h2 className=\"mb-3 flex items-center gap-2 text-base font-semibold text-text-primary\">\n <Award size={18} className=\"text-accent-teal\" /> Certificates\n </h2>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3\">\n {certificates.map((c) => (\n <GlassCard\n key={c.id}\n className=\"group flex flex-col gap-3 p-4 transition-all duration-200 hover:-translate-y-1 hover:shadow-lg\"\n >\n <div className=\"flex items-center gap-3\">\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 transition-transform duration-200 group-hover:scale-105\">\n <Award size={22} />\n </span>\n <div className=\"min-w-0\">\n <div className=\"truncate text-sm font-medium text-text-primary\">\n {c.course?.title ?? 'Certificate'}\n </div>\n <div className=\"truncate text-xs text-text-muted\">Serial {c.serial}</div>\n </div>\n </div>\n <div className=\"mt-auto flex items-center justify-between border-t border-border-seam pt-3 text-xs text-text-muted\">\n <span>\n Score <span className=\"font-semibold text-text-primary\">{c.score}</span> ·{' '}\n {formatDate(c.issuedAt)}\n </span>\n {c.status === 'ISSUED' ? (\n <span className=\"inline-flex items-center gap-1 rounded-full border border-status-success-border bg-status-success-bg-subtle px-2 py-0.5 font-medium text-status-success-text\">\n <ShieldCheck size={12} /> Verified\n </span>\n ) : (\n <StatusBadge status={c.status} />\n )}\n </div>\n </GlassCard>\n ))}\n </div>\n </div>\n ) : null}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|