@burdenoff/microfe-crewfoundry 2026.704.6 → 2026.707.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/crewfoundry/CrewFoundryRoutes.js +51 -43
- package/dist/crewfoundry/CrewFoundryRoutes.js.map +1 -1
- package/dist/crewfoundry/components/forms/formValues.js +5 -2
- package/dist/crewfoundry/components/forms/formValues.js.map +1 -1
- package/dist/crewfoundry/components/viz/AllocationBar.js +68 -0
- package/dist/crewfoundry/components/viz/AllocationBar.js.map +1 -0
- package/dist/crewfoundry/components/viz/AreaChart.js +102 -0
- package/dist/crewfoundry/components/viz/AreaChart.js.map +1 -0
- package/dist/crewfoundry/components/viz/AvatarStack.js +36 -0
- package/dist/crewfoundry/components/viz/AvatarStack.js.map +1 -0
- package/dist/crewfoundry/components/viz/BarChart.js +83 -0
- package/dist/crewfoundry/components/viz/BarChart.js.map +1 -0
- package/dist/crewfoundry/components/viz/CertificateCard.js +118 -0
- package/dist/crewfoundry/components/viz/CertificateCard.js.map +1 -0
- package/dist/crewfoundry/components/viz/DonutChart.js +109 -0
- package/dist/crewfoundry/components/viz/DonutChart.js.map +1 -0
- package/dist/crewfoundry/components/viz/HBarFunnel.js +54 -0
- package/dist/crewfoundry/components/viz/HBarFunnel.js.map +1 -0
- package/dist/crewfoundry/components/viz/Heatmap.js +91 -0
- package/dist/crewfoundry/components/viz/Heatmap.js.map +1 -0
- package/dist/crewfoundry/components/viz/KanbanBoard.js +50 -0
- package/dist/crewfoundry/components/viz/KanbanBoard.js.map +1 -0
- package/dist/crewfoundry/components/viz/LineChart.js +123 -0
- package/dist/crewfoundry/components/viz/LineChart.js.map +1 -0
- package/dist/crewfoundry/components/viz/OrgTree.js +83 -0
- package/dist/crewfoundry/components/viz/OrgTree.js.map +1 -0
- package/dist/crewfoundry/components/viz/PathSteps.js +68 -0
- package/dist/crewfoundry/components/viz/PathSteps.js.map +1 -0
- package/dist/crewfoundry/components/viz/ProgressRing.js +68 -0
- package/dist/crewfoundry/components/viz/ProgressRing.js.map +1 -0
- package/dist/crewfoundry/components/viz/RatingStars.js +51 -0
- package/dist/crewfoundry/components/viz/RatingStars.js.map +1 -0
- package/dist/crewfoundry/components/viz/SegmentedTabs.js +69 -0
- package/dist/crewfoundry/components/viz/SegmentedTabs.js.map +1 -0
- package/dist/crewfoundry/components/viz/SkillBar.js +3 -0
- package/dist/crewfoundry/components/viz/Sparkline.js +74 -0
- package/dist/crewfoundry/components/viz/Sparkline.js.map +1 -0
- package/dist/crewfoundry/components/viz/StatCard.js +60 -0
- package/dist/crewfoundry/components/viz/StatCard.js.map +1 -0
- package/dist/crewfoundry/components/viz/StepProgress.js +3 -0
- package/dist/crewfoundry/components/viz/Timeline.js +66 -0
- package/dist/crewfoundry/components/viz/Timeline.js.map +1 -0
- package/dist/crewfoundry/components/viz/index.js +23 -0
- package/dist/crewfoundry/components/viz/lib/Avatar.js +30 -0
- package/dist/crewfoundry/components/viz/lib/Avatar.js.map +1 -0
- package/dist/crewfoundry/components/viz/lib/CartesianFrame.js +119 -0
- package/dist/crewfoundry/components/viz/lib/CartesianFrame.js.map +1 -0
- package/dist/crewfoundry/components/viz/lib/ResponsiveSvg.js +35 -0
- package/dist/crewfoundry/components/viz/lib/ResponsiveSvg.js.map +1 -0
- package/dist/crewfoundry/components/viz/lib/format.js +21 -0
- package/dist/crewfoundry/components/viz/lib/format.js.map +1 -0
- package/dist/crewfoundry/components/viz/lib/geometry.js +69 -0
- package/dist/crewfoundry/components/viz/lib/geometry.js.map +1 -0
- package/dist/crewfoundry/components/viz/lib/hooks.js +39 -0
- package/dist/crewfoundry/components/viz/lib/hooks.js.map +1 -0
- package/dist/crewfoundry/components/viz/lib/palette.js +27 -0
- package/dist/crewfoundry/components/viz/lib/palette.js.map +1 -0
- package/dist/crewfoundry/lib/analytics.js +323 -0
- package/dist/crewfoundry/lib/analytics.js.map +1 -0
- package/dist/crewfoundry/lib/companyFinance.js +16 -0
- package/dist/crewfoundry/lib/companyFinance.js.map +1 -0
- package/dist/crewfoundry/pages/AnalyticsAnalyticsPagePage.js +226 -283
- package/dist/crewfoundry/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsEmployeeAnalyticsPagePage.js +107 -120
- package/dist/crewfoundry/pages/AnalyticsEmployeeAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.js +122 -162
- package/dist/crewfoundry/pages/AnalyticsFinancialAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.js +121 -158
- package/dist/crewfoundry/pages/AnalyticsHiringAnalyticsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js +316 -211
- package/dist/crewfoundry/pages/CommunicationsAnnouncementsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.js +75 -27
- package/dist/crewfoundry/pages/CommunicationsMessagesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js +167 -165
- package/dist/crewfoundry/pages/CompanyClientManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js +168 -109
- package/dist/crewfoundry/pages/CompanyVendorManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/DashboardDashboardPagePage.js +401 -290
- package/dist/crewfoundry/pages/DashboardDashboardPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HelpApiDocumentationPagePage.js +0 -41
- package/dist/crewfoundry/pages/HelpApiDocumentationPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringApplicationsPagePage.js +84 -75
- package/dist/crewfoundry/pages/HiringApplicationsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringCandidatesPagePage.js +368 -174
- package/dist/crewfoundry/pages/HiringCandidatesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringCreateJobPagePage.js +236 -201
- package/dist/crewfoundry/pages/HiringCreateJobPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringInterviewsPagePage.js +369 -196
- package/dist/crewfoundry/pages/HiringInterviewsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js +269 -270
- package/dist/crewfoundry/pages/HiringJobDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HiringJobsPagePage.js +210 -110
- package/dist/crewfoundry/pages/HiringJobsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrDepartmentsPagePage.js +216 -178
- package/dist/crewfoundry/pages/HrDepartmentsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrEmployeeProfilePagePage.js +385 -0
- package/dist/crewfoundry/pages/HrEmployeeProfilePagePage.js.map +1 -0
- package/dist/crewfoundry/pages/HrEmployeesPagePage.js +189 -130
- package/dist/crewfoundry/pages/HrEmployeesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js +397 -241
- package/dist/crewfoundry/pages/HrLeaveManagementPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrPayrollPagePage.js +262 -147
- package/dist/crewfoundry/pages/HrPayrollPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/HrTimeTrackingPagePage.js +345 -167
- package/dist/crewfoundry/pages/HrTimeTrackingPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCertificatesPagePage.js +98 -24
- package/dist/crewfoundry/pages/LearningCertificatesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js +1 -1
- package/dist/crewfoundry/pages/LearningCourseDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js +289 -108
- package/dist/crewfoundry/pages/LearningCourseLearningPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningCoursesPagePage.js +221 -114
- package/dist/crewfoundry/pages/LearningCoursesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js +134 -135
- package/dist/crewfoundry/pages/LearningLearningPathDetailPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js +148 -97
- package/dist/crewfoundry/pages/LearningLearningPathsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/LearningMyLearningPagePage.js +264 -157
- package/dist/crewfoundry/pages/LearningMyLearningPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.js +85 -93
- package/dist/crewfoundry/pages/MarketplaceFreelancerProfilePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js +211 -122
- package/dist/crewfoundry/pages/MarketplaceMarketplacePagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js +94 -54
- package/dist/crewfoundry/pages/MarketplaceMarketplaceServicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.js +289 -140
- package/dist/crewfoundry/pages/MarketplaceServiceDetailsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js +169 -79
- package/dist/crewfoundry/pages/PaymentsInvoicesPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js +296 -125
- package/dist/crewfoundry/pages/ProjectsProjectsPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/ProjectsResourcingPagePage.js +187 -0
- package/dist/crewfoundry/pages/ProjectsResourcingPagePage.js.map +1 -0
- package/dist/crewfoundry/pages/TeamsTeamDetailPagePage.js +140 -78
- package/dist/crewfoundry/pages/TeamsTeamDetailPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamDirectoryPagePage.js +93 -43
- package/dist/crewfoundry/pages/TeamsTeamDirectoryPagePage.js.map +1 -1
- package/dist/crewfoundry/pages/TeamsTeamsPagePage.js +250 -98
- package/dist/crewfoundry/pages/TeamsTeamsPagePage.js.map +1 -1
- package/dist/crewfoundry/store/crewfoundryStore.js +142 -184
- package/dist/crewfoundry/store/crewfoundryStore.js.map +1 -1
- package/dist/crewfoundry/utils/cn.js +10 -0
- package/dist/crewfoundry/utils/cn.js.map +1 -0
- package/dist/crewfoundry/utils/courseProgress.js +46 -0
- package/dist/crewfoundry/utils/courseProgress.js.map +1 -0
- package/dist/node_modules/clsx/dist/clsx.js +18 -0
- package/dist/node_modules/clsx/dist/clsx.js.map +1 -0
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +1716 -0
- package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,186 +1,293 @@
|
|
|
1
1
|
import { useCrewFoundryStore as e } from "../store/crewfoundryStore.js";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
2
|
+
import { chartPalette as t } from "../components/viz/lib/palette.js";
|
|
3
|
+
import { StatCard as n } from "../components/viz/StatCard.js";
|
|
4
|
+
import { RatingStars as r } from "../components/viz/RatingStars.js";
|
|
5
|
+
import { CertificateCard as i } from "../components/viz/CertificateCard.js";
|
|
6
|
+
import { SegmentedTabs as a } from "../components/viz/SegmentedTabs.js";
|
|
7
|
+
import "../components/viz/index.js";
|
|
8
|
+
import { readAllCourseProgress as o } from "../utils/courseProgress.js";
|
|
9
|
+
import { useMemo as s, useState as c } from "react";
|
|
10
|
+
import { Award as l, BookOpen as u, GraduationCap as d, Layers as f, Play as p } from "lucide-react";
|
|
11
|
+
import { useNavigate as m } from "react-router-dom";
|
|
12
|
+
import { jsx as h, jsxs as g } from "react/jsx-runtime";
|
|
13
|
+
import { Badge as _, Button as v, GlassCard as y, GradientText as b, IllustratedEmptyState as x, PagePurpose as S } from "@burdenoff/fe-libs/ui";
|
|
14
|
+
import { useAuth as C } from "@burdenoff/fe-libs/shared/providers/shell";
|
|
8
15
|
//#region src/crewfoundry/pages/LearningMyLearningPagePage.tsx
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
var w = "crewfoundry:course-enrolled:v1";
|
|
17
|
+
function T() {
|
|
18
|
+
if (typeof window > "u") return /* @__PURE__ */ new Set();
|
|
19
|
+
try {
|
|
20
|
+
let e = window.localStorage.getItem(w), t = e ? JSON.parse(e) : [];
|
|
21
|
+
return new Set(Array.isArray(t) ? t.filter((e) => typeof e == "string") : []);
|
|
22
|
+
} catch {
|
|
23
|
+
return /* @__PURE__ */ new Set();
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function E(e) {
|
|
27
|
+
let n = t(8), r = 0;
|
|
28
|
+
for (let t = 0; t < e.length; t += 1) r = r * 31 + e.charCodeAt(t) >>> 0;
|
|
29
|
+
return n[r % n.length];
|
|
30
|
+
}
|
|
31
|
+
function D(e) {
|
|
32
|
+
if (!e) return "—";
|
|
33
|
+
let t = new Date(e);
|
|
34
|
+
return Number.isNaN(t.getTime()) ? "—" : t.toLocaleDateString(void 0, {
|
|
35
|
+
year: "numeric",
|
|
36
|
+
month: "short",
|
|
37
|
+
day: "numeric"
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function O(e, t, n, r) {
|
|
41
|
+
if (typeof document > "u") return;
|
|
42
|
+
let i = (e) => e.replace(/[&<>"]/g, (e) => ({
|
|
43
|
+
"&": "&",
|
|
44
|
+
"<": "<",
|
|
45
|
+
">": ">",
|
|
46
|
+
"\"": """
|
|
47
|
+
})[e] ?? e), a = `<!doctype html><html><head><meta charset="utf-8"><title>Certificate — ${i(e)}</title>
|
|
48
|
+
<style>
|
|
49
|
+
body{font-family:Georgia,'Times New Roman',serif;margin:0;padding:48px;text-align:center}
|
|
50
|
+
.frame{border:6px double currentColor;padding:56px 40px;max-width:760px;margin:0 auto}
|
|
51
|
+
.kicker{letter-spacing:.3em;text-transform:uppercase;font-size:12px;opacity:.7}
|
|
52
|
+
h1{font-size:34px;margin:24px 0 8px}
|
|
53
|
+
.recipient{font-size:26px;margin:20px 0}
|
|
54
|
+
.rule{width:200px;border:0;border-top:1px solid currentColor;margin:24px auto;opacity:.4}
|
|
55
|
+
.meta{font-size:13px;opacity:.7;margin-top:32px}
|
|
56
|
+
</style></head><body>
|
|
57
|
+
<div class="frame">
|
|
58
|
+
<div class="kicker">CrewFoundry · Certificate of Completion</div>
|
|
59
|
+
<h1>${i(e)}</h1>
|
|
60
|
+
<div class="kicker">Awarded to</div>
|
|
61
|
+
<div class="recipient">${i(t)}</div>
|
|
62
|
+
<hr class="rule" />
|
|
63
|
+
<div class="meta">Issued ${i(n)} · Credential ID: ${i(r)}</div>
|
|
64
|
+
</div>
|
|
65
|
+
</body></html>`, o = new Blob([a], { type: "text/html" }), s = URL.createObjectURL(o), c = document.createElement("a");
|
|
66
|
+
c.href = s, c.download = `crewfoundry-certificate-${r}.html`, document.body.appendChild(c), c.click(), c.remove(), URL.revokeObjectURL(s);
|
|
67
|
+
}
|
|
68
|
+
var k = u;
|
|
69
|
+
function A() {
|
|
70
|
+
let { courses: t } = e(), w = C(), A = m(), [j, M] = c("in-progress"), N = w.user?.name?.trim() || "CrewFoundry Learner", { inProgress: P, completed: F, modulesDone: I } = s(() => {
|
|
71
|
+
let e = o(), n = T(), r = [], i = [], a = 0;
|
|
72
|
+
for (let o of t) {
|
|
73
|
+
let t = e[o.id], s = t?.completed.length ?? 0;
|
|
74
|
+
a += s;
|
|
75
|
+
let c = Math.max(o.modules, 1), l = {
|
|
76
|
+
course: o,
|
|
77
|
+
completedModules: s,
|
|
78
|
+
totalModules: c,
|
|
79
|
+
pct: Math.min(100, Math.round(s / c * 100)),
|
|
80
|
+
completedAt: t?.completedAt
|
|
81
|
+
};
|
|
82
|
+
s >= c && s > 0 ? i.push(l) : (s > 0 || n.has(o.id)) && r.push(l);
|
|
18
83
|
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
title: e.title,
|
|
24
|
-
progress: n,
|
|
25
|
-
modules: e.modules,
|
|
26
|
-
completedModules: t,
|
|
27
|
-
instructor: e.instructor
|
|
84
|
+
return r.sort((e, t) => t.pct - e.pct), i.sort((e, t) => (t.completedAt ?? "").localeCompare(e.completedAt ?? "")), {
|
|
85
|
+
inProgress: r,
|
|
86
|
+
completed: i,
|
|
87
|
+
modulesDone: a
|
|
28
88
|
};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
title: e.title,
|
|
32
|
-
rating: Number(e.rating.toFixed(1))
|
|
33
|
-
})), [C]), N = j.reduce((e, t) => e + t.completedModules * 1.25, 0), P = j.slice(O.offset, O.offset + O.pageSize), F = M.slice(k.offset, k.offset + k.pageSize);
|
|
34
|
-
return /* @__PURE__ */ l("div", {
|
|
89
|
+
}, [t]), L = (e) => A(`../learning/course-learning-page?id=${encodeURIComponent(e)}`);
|
|
90
|
+
return /* @__PURE__ */ g("div", {
|
|
35
91
|
className: "space-y-6 p-6",
|
|
36
92
|
children: [
|
|
37
|
-
/* @__PURE__ */
|
|
38
|
-
/* @__PURE__ */
|
|
93
|
+
/* @__PURE__ */ g("div", { children: [
|
|
94
|
+
/* @__PURE__ */ h("h1", {
|
|
39
95
|
className: "text-2xl font-semibold",
|
|
40
|
-
children: /* @__PURE__ */
|
|
96
|
+
children: /* @__PURE__ */ h(b, { children: "My Learning" })
|
|
41
97
|
}),
|
|
42
|
-
/* @__PURE__ */
|
|
98
|
+
/* @__PURE__ */ h("p", {
|
|
43
99
|
className: "text-sm text-text-secondary",
|
|
44
100
|
children: "Track your learning progress and achievements"
|
|
45
101
|
}),
|
|
46
|
-
/* @__PURE__ */
|
|
102
|
+
/* @__PURE__ */ h(S, {
|
|
47
103
|
className: "mt-1",
|
|
48
|
-
children: "Everything you're currently learning — track your enrolled courses and
|
|
104
|
+
children: "Everything you're currently learning — track your enrolled courses, completed training and the certificates you've earned, all in one place."
|
|
49
105
|
})
|
|
50
106
|
] }),
|
|
51
|
-
/* @__PURE__ */
|
|
52
|
-
className: "grid gap-4 sm:grid-cols-
|
|
107
|
+
/* @__PURE__ */ g("div", {
|
|
108
|
+
className: "grid gap-4 sm:grid-cols-2 lg:grid-cols-4",
|
|
53
109
|
children: [
|
|
54
|
-
/* @__PURE__ */
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
className: "
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
}), /* @__PURE__ */ c(i, { className: "h-4 w-4 text-status-info-text" })]
|
|
64
|
-
}), /* @__PURE__ */ c(f, { children: /* @__PURE__ */ c("div", {
|
|
65
|
-
className: "text-2xl font-bold tabular-nums",
|
|
66
|
-
children: j.length
|
|
67
|
-
}) })]
|
|
110
|
+
/* @__PURE__ */ h(n, {
|
|
111
|
+
label: "Courses in progress",
|
|
112
|
+
value: P.length,
|
|
113
|
+
icon: /* @__PURE__ */ h(u, { className: "h-4 w-4" })
|
|
114
|
+
}),
|
|
115
|
+
/* @__PURE__ */ h(n, {
|
|
116
|
+
label: "Completed",
|
|
117
|
+
value: F.length,
|
|
118
|
+
icon: /* @__PURE__ */ h(l, { className: "h-4 w-4" })
|
|
68
119
|
}),
|
|
69
|
-
/* @__PURE__ */
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
className: "flex flex-row items-center justify-between space-y-0 pb-2",
|
|
74
|
-
children: [/* @__PURE__ */ c(h, {
|
|
75
|
-
className: "text-sm font-medium",
|
|
76
|
-
children: "Completed"
|
|
77
|
-
}), /* @__PURE__ */ c(r, { className: "h-4 w-4 text-status-success-text" })]
|
|
78
|
-
}), /* @__PURE__ */ c(f, { children: /* @__PURE__ */ c("div", {
|
|
79
|
-
className: "text-2xl font-bold tabular-nums",
|
|
80
|
-
children: M.length
|
|
81
|
-
}) })]
|
|
120
|
+
/* @__PURE__ */ h(n, {
|
|
121
|
+
label: "Certificates",
|
|
122
|
+
value: F.length,
|
|
123
|
+
icon: /* @__PURE__ */ h(d, { className: "h-4 w-4" })
|
|
82
124
|
}),
|
|
83
|
-
/* @__PURE__ */
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
className: "flex flex-row items-center justify-between space-y-0 pb-2",
|
|
88
|
-
children: [/* @__PURE__ */ c(h, {
|
|
89
|
-
className: "text-sm font-medium",
|
|
90
|
-
children: "Hours Logged"
|
|
91
|
-
}), /* @__PURE__ */ c(a, { className: "h-4 w-4 text-text-secondary" })]
|
|
92
|
-
}), /* @__PURE__ */ c(f, { children: /* @__PURE__ */ c("div", {
|
|
93
|
-
className: "text-2xl font-bold tabular-nums",
|
|
94
|
-
children: N.toFixed(1)
|
|
95
|
-
}) })]
|
|
125
|
+
/* @__PURE__ */ h(n, {
|
|
126
|
+
label: "Modules done",
|
|
127
|
+
value: I,
|
|
128
|
+
icon: /* @__PURE__ */ h(f, { className: "h-4 w-4" })
|
|
96
129
|
})
|
|
97
130
|
]
|
|
98
131
|
}),
|
|
99
|
-
/* @__PURE__ */
|
|
100
|
-
className: "
|
|
101
|
-
children:
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
132
|
+
/* @__PURE__ */ h("div", {
|
|
133
|
+
className: "overflow-x-auto pb-1",
|
|
134
|
+
children: /* @__PURE__ */ h(a, {
|
|
135
|
+
"aria-label": "My learning",
|
|
136
|
+
value: j,
|
|
137
|
+
onChange: (e) => M(e),
|
|
138
|
+
className: "w-max",
|
|
139
|
+
tabs: [
|
|
140
|
+
{
|
|
141
|
+
id: "in-progress",
|
|
142
|
+
label: "In Progress",
|
|
143
|
+
count: P.length
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: "completed",
|
|
147
|
+
label: "Completed",
|
|
148
|
+
count: F.length
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
id: "certificates",
|
|
152
|
+
label: "Certificates",
|
|
153
|
+
count: F.length
|
|
154
|
+
}
|
|
155
|
+
]
|
|
156
|
+
})
|
|
157
|
+
}),
|
|
158
|
+
j === "in-progress" && (P.length === 0 ? /* @__PURE__ */ h(x, {
|
|
159
|
+
illustration: "empty-generic",
|
|
160
|
+
title: "Nothing in progress yet",
|
|
161
|
+
description: "Enroll in a course and it will show up here so you can pick up where you left off.",
|
|
162
|
+
action: /* @__PURE__ */ h(v, {
|
|
163
|
+
variant: "outline",
|
|
164
|
+
onClick: () => A("../learning/courses-page"),
|
|
165
|
+
children: "Browse courses"
|
|
166
|
+
})
|
|
167
|
+
}) : /* @__PURE__ */ h("div", {
|
|
168
|
+
className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
|
|
169
|
+
children: P.map(({ course: e, pct: t, completedModules: n, totalModules: r }) => {
|
|
170
|
+
let i = E(e.category);
|
|
171
|
+
return /* @__PURE__ */ g(y, {
|
|
172
|
+
treatment: "plain",
|
|
173
|
+
className: "flex flex-col overflow-hidden",
|
|
174
|
+
children: [/* @__PURE__ */ h("div", {
|
|
175
|
+
className: "flex h-20 items-center justify-center",
|
|
176
|
+
style: { background: `linear-gradient(135deg, color-mix(in srgb, ${i} 26%, var(--color-bg-surface)), color-mix(in srgb, ${i} 8%, var(--color-bg-surface)))` },
|
|
177
|
+
children: /* @__PURE__ */ h("span", {
|
|
178
|
+
className: "flex h-10 w-10 items-center justify-center rounded-xl bg-bg-surface/70 shadow-elevation-1",
|
|
179
|
+
style: { color: i },
|
|
180
|
+
children: /* @__PURE__ */ h(k, { className: "h-5 w-5" })
|
|
139
181
|
})
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
182
|
+
}), /* @__PURE__ */ g("div", {
|
|
183
|
+
className: "flex flex-1 flex-col gap-3 p-4",
|
|
184
|
+
children: [
|
|
185
|
+
/* @__PURE__ */ g("div", { children: [/* @__PURE__ */ h("p", {
|
|
186
|
+
className: "text-sm font-semibold",
|
|
187
|
+
children: e.title
|
|
188
|
+
}), /* @__PURE__ */ h("p", {
|
|
189
|
+
className: "text-xs text-text-secondary",
|
|
190
|
+
children: e.instructor
|
|
191
|
+
})] }),
|
|
192
|
+
/* @__PURE__ */ g("div", {
|
|
193
|
+
className: "mt-auto space-y-1",
|
|
194
|
+
children: [/* @__PURE__ */ g("div", {
|
|
195
|
+
className: "flex justify-between text-xs text-text-secondary",
|
|
196
|
+
children: [/* @__PURE__ */ g("span", {
|
|
197
|
+
className: "tabular-nums",
|
|
198
|
+
children: [t, "% complete"]
|
|
199
|
+
}), /* @__PURE__ */ g("span", {
|
|
200
|
+
className: "tabular-nums",
|
|
201
|
+
children: [
|
|
202
|
+
n,
|
|
203
|
+
"/",
|
|
204
|
+
r,
|
|
205
|
+
" modules"
|
|
206
|
+
]
|
|
207
|
+
})]
|
|
208
|
+
}), /* @__PURE__ */ h("div", {
|
|
209
|
+
className: "h-2 overflow-hidden rounded-full bg-bg-sunken",
|
|
210
|
+
children: /* @__PURE__ */ h("div", {
|
|
211
|
+
className: "h-full rounded-full bg-action-primary-bg",
|
|
212
|
+
style: { width: `${t}%` }
|
|
213
|
+
})
|
|
214
|
+
})]
|
|
215
|
+
}),
|
|
216
|
+
/* @__PURE__ */ g(v, {
|
|
217
|
+
className: "w-full",
|
|
218
|
+
onClick: () => L(e.id),
|
|
219
|
+
children: [/* @__PURE__ */ h(p, { className: "mr-1 h-4 w-4" }), "Continue"]
|
|
220
|
+
})
|
|
221
|
+
]
|
|
222
|
+
})]
|
|
223
|
+
}, e.id);
|
|
224
|
+
})
|
|
225
|
+
})),
|
|
226
|
+
j === "completed" && (F.length === 0 ? /* @__PURE__ */ h(x, {
|
|
227
|
+
illustration: "empty-generic",
|
|
228
|
+
title: "No completed courses yet",
|
|
229
|
+
description: "Finish a course from the player and it will be marked complete here."
|
|
230
|
+
}) : /* @__PURE__ */ h("div", {
|
|
155
231
|
className: "space-y-3",
|
|
156
|
-
children:
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
232
|
+
children: F.map(({ course: e, completedAt: t }) => /* @__PURE__ */ g("button", {
|
|
233
|
+
type: "button",
|
|
234
|
+
onClick: () => L(e.id),
|
|
235
|
+
className: "flex w-full items-center justify-between gap-3 rounded-lg border border-border-subtle p-4 text-left transition-colors hover:border-border-strong hover:bg-bg-sunken",
|
|
236
|
+
children: [/* @__PURE__ */ g("div", {
|
|
237
|
+
className: "min-w-0",
|
|
238
|
+
children: [/* @__PURE__ */ h("p", {
|
|
239
|
+
className: "truncate text-sm font-medium",
|
|
240
|
+
children: e.title
|
|
241
|
+
}), /* @__PURE__ */ g("p", {
|
|
242
|
+
className: "text-xs text-text-secondary",
|
|
243
|
+
children: [
|
|
244
|
+
"Completed ",
|
|
245
|
+
D(t),
|
|
246
|
+
" · ",
|
|
247
|
+
e.instructor
|
|
248
|
+
]
|
|
249
|
+
})]
|
|
250
|
+
}), /* @__PURE__ */ g("div", {
|
|
251
|
+
className: "flex shrink-0 items-center gap-3",
|
|
252
|
+
children: [/* @__PURE__ */ h(r, {
|
|
253
|
+
value: e.rating,
|
|
254
|
+
size: 13,
|
|
255
|
+
showValue: !0
|
|
256
|
+
}), /* @__PURE__ */ h(_, {
|
|
257
|
+
variant: "secondary",
|
|
258
|
+
className: "text-[10px]",
|
|
259
|
+
children: "Completed"
|
|
260
|
+
})]
|
|
172
261
|
})]
|
|
173
|
-
}, e.id))
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
262
|
+
}, e.id))
|
|
263
|
+
})),
|
|
264
|
+
j === "certificates" && (F.length === 0 ? /* @__PURE__ */ h(x, {
|
|
265
|
+
illustration: "empty-generic",
|
|
266
|
+
title: "No certificates yet",
|
|
267
|
+
description: "Certificates are issued when you complete a course. Finish one and it will appear here to download.",
|
|
268
|
+
action: /* @__PURE__ */ h(v, {
|
|
269
|
+
variant: "outline",
|
|
270
|
+
onClick: () => A("../learning/courses-page"),
|
|
271
|
+
children: "Browse courses"
|
|
272
|
+
})
|
|
273
|
+
}) : /* @__PURE__ */ h("div", {
|
|
274
|
+
className: "grid gap-4 md:grid-cols-2 lg:grid-cols-3",
|
|
275
|
+
children: F.map(({ course: e, completedAt: t }) => {
|
|
276
|
+
let n = `CF-${e.id.slice(-6).toUpperCase()}`, r = D(t);
|
|
277
|
+
return /* @__PURE__ */ h(i, {
|
|
278
|
+
title: e.title,
|
|
279
|
+
recipient: N,
|
|
280
|
+
date: r,
|
|
281
|
+
issuer: "CrewFoundry",
|
|
282
|
+
credentialId: n,
|
|
283
|
+
onDownload: () => O(e.title, N, r, n)
|
|
284
|
+
}, e.id);
|
|
285
|
+
})
|
|
286
|
+
}))
|
|
180
287
|
]
|
|
181
288
|
});
|
|
182
289
|
}
|
|
183
290
|
//#endregion
|
|
184
|
-
export {
|
|
291
|
+
export { A as LearningMyLearningPagePage };
|
|
185
292
|
|
|
186
293
|
//# sourceMappingURL=LearningMyLearningPagePage.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LearningMyLearningPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/LearningMyLearningPagePage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { Award, BookOpen, Clock, Play, Star } from 'lucide-react';\nimport {\n Button,\n Card,\n CardContent,\n CardDescription,\n CardHeader,\n CardTitle,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n Pagination,\n} from '@burdenoff/fe-libs/ui';\nimport { usePagination } from '@burdenoff/fe-libs/shared/hooks';\nimport { useGlobalUi } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useCrewFoundryStore } from '../store/crewfoundryStore';\n\ntype EnrolledCourse = {\n id: string;\n title: string;\n progress: number;\n modules: number;\n completedModules: number;\n instructor: string;\n};\n\ntype CompletedCourse = {\n id: string;\n title: string;\n rating: number;\n};\n\nexport function LearningMyLearningPagePage() {\n const { courses, enrollCourse } = useCrewFoundryStore();\n const { showToast } = useGlobalUi();\n const [enrollingId, setEnrollingId] = useState<string | null>(null);\n const inProgressPaging = usePagination({ initialPageSize: 6 });\n const completedPaging = usePagination({ initialPageSize: 6 });\n\n const handleContinue = async (id: string) => {\n setEnrollingId(id);\n try {\n await enrollCourse(id);\n showToast('Enrollment updated', 'success');\n } catch (error) {\n showToast(error instanceof Error ? error.message : 'Unable to update course', 'error');\n } finally {\n setEnrollingId(null);\n }\n };\n\n const myEnrolledCourses = useMemo<EnrolledCourse[]>(() => {\n return courses\n .filter((course) => course.status === 'active')\n .map((course) => {\n const completedModules = Math.min(\n course.modules,\n Math.round((course.completed / Math.max(course.enrolled, 1)) * course.modules)\n );\n const progress = Math.round((completedModules / Math.max(course.modules, 1)) * 100);\n return {\n id: course.id,\n title: course.title,\n progress,\n modules: course.modules,\n completedModules,\n instructor: course.instructor,\n };\n });\n }, [courses]);\n\n const completedCourses = useMemo<CompletedCourse[]>(() => {\n return courses\n .filter((course) => course.completed > 0)\n .map((course) => ({\n id: `completed-${course.id}`,\n title: course.title,\n rating: Number(course.rating.toFixed(1)),\n }));\n }, [courses]);\n\n const hoursLogged = myEnrolledCourses.reduce(\n (sum, course) => sum + course.completedModules * 1.25,\n 0\n );\n\n const pagedEnrolled = myEnrolledCourses.slice(\n inProgressPaging.offset,\n inProgressPaging.offset + inProgressPaging.pageSize\n );\n const pagedCompleted = completedCourses.slice(\n completedPaging.offset,\n completedPaging.offset + completedPaging.pageSize\n );\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>My Learning</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">Track your learning progress and achievements</p>\n <PagePurpose className=\"mt-1\">\n Everything you're currently learning — track your enrolled courses and progress all\n in one place.\n </PagePurpose>\n </div>\n\n <div className=\"grid gap-4 sm:grid-cols-3\">\n <GlassCard treatment=\"glass\" glow className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">In Progress</CardTitle>\n <BookOpen className=\"h-4 w-4 text-status-info-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{myEnrolledCourses.length}</div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Completed</CardTitle>\n <Award className=\"h-4 w-4 text-status-success-text\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{completedCourses.length}</div>\n </CardContent>\n </GlassCard>\n <GlassCard treatment=\"metric\" className=\"border-border-seam\">\n <CardHeader className=\"flex flex-row items-center justify-between space-y-0 pb-2\">\n <CardTitle className=\"text-sm font-medium\">Hours Logged</CardTitle>\n <Clock className=\"h-4 w-4 text-text-secondary\" />\n </CardHeader>\n <CardContent>\n <div className=\"text-2xl font-bold tabular-nums\">{hoursLogged.toFixed(1)}</div>\n </CardContent>\n </GlassCard>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>In Progress</CardTitle>\n <CardDescription>Continue where you left off</CardDescription>\n </CardHeader>\n <CardContent className=\"space-y-4\">\n {myEnrolledCourses.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"Nothing in progress yet\"\n description=\"Courses you enroll in will show up here so you can pick up where you left off.\"\n />\n ) : (\n pagedEnrolled.map((course) => (\n <div key={course.id} className=\"flex items-center gap-4 rounded border p-4\">\n <div className=\"flex-1\">\n <p className=\"text-sm font-medium\">{course.title}</p>\n <p className=\"text-xs text-text-secondary\">{course.instructor}</p>\n <div className=\"mt-2 flex items-center gap-2\">\n <div className=\"h-2 flex-1 overflow-hidden rounded bg-bg-sunken\">\n <div\n className=\"h-2 rounded bg-action-primary-bg\"\n style={{ width: `${course.progress}%` }}\n />\n </div>\n <span className=\"text-xs font-medium\">{course.progress}%</span>\n </div>\n <p className=\"mt-1 text-xs text-text-secondary\">\n {course.completedModules}/{course.modules} modules\n </p>\n </div>\n <Button\n size=\"sm\"\n onClick={() => handleContinue(course.id)}\n disabled={enrollingId === course.id}\n >\n <Play className=\"mr-1 h-3 w-3\" />\n {enrollingId === course.id ? 'Saving…' : 'Continue'}\n </Button>\n </div>\n ))\n )}\n {myEnrolledCourses.length > inProgressPaging.pageSize && (\n <Pagination\n page={inProgressPaging.page}\n pageSize={inProgressPaging.pageSize}\n totalItems={myEnrolledCourses.length}\n onPageChange={inProgressPaging.setPage}\n />\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>Completed</CardTitle>\n </CardHeader>\n <CardContent className=\"space-y-3\">\n {completedCourses.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No completed courses yet\"\n description=\"Courses your team finishes will be listed here.\"\n />\n ) : (\n pagedCompleted.map((course) => (\n <div key={course.id} className=\"flex items-center justify-between rounded border p-3\">\n <div>\n <p className=\"text-sm font-medium\">{course.title}</p>\n </div>\n <div className=\"flex items-center gap-1\">\n <Star className=\"h-3 w-3 text-status-warning-text\" />\n {course.rating}/5\n </div>\n </div>\n ))\n )}\n {completedCourses.length > completedPaging.pageSize && (\n <Pagination\n page={completedPaging.page}\n pageSize={completedPaging.pageSize}\n totalItems={completedCourses.length}\n onPageChange={completedPaging.setPage}\n />\n )}\n </CardContent>\n </Card>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"LearningMyLearningPagePage.js","names":[],"sources":["../../../src/crewfoundry/pages/LearningMyLearningPagePage.tsx"],"sourcesContent":["import { useMemo, useState, type ComponentType } from 'react';\nimport { Award, BookOpen, GraduationCap, Layers, Play } from 'lucide-react';\nimport {\n Badge,\n Button,\n GlassCard,\n GradientText,\n IllustratedEmptyState,\n PagePurpose,\n} from '@burdenoff/fe-libs/ui';\nimport { useNavigate } from 'react-router-dom';\nimport { useAuth } from '@burdenoff/fe-libs/shared/providers/shell';\nimport { useCrewFoundryStore, type LearningCourse } from '../store/crewfoundryStore';\nimport { CertificateCard, chartPalette, RatingStars, SegmentedTabs, StatCard } from '../components/viz';\nimport { readAllCourseProgress } from '../utils/courseProgress';\n\nconst ENROLLED_KEY = 'crewfoundry:course-enrolled:v1';\n\nfunction readEnrolledIds(): Set<string> {\n if (typeof window === 'undefined') return new Set();\n try {\n const raw = window.localStorage.getItem(ENROLLED_KEY);\n const ids: unknown = raw ? JSON.parse(raw) : [];\n return new Set(Array.isArray(ids) ? ids.filter((x): x is string => typeof x === 'string') : []);\n } catch {\n return new Set();\n }\n}\n\nfunction categoryAccent(category: string): string {\n const palette = chartPalette(8);\n let hash = 0;\n for (let i = 0; i < category.length; i += 1) {\n hash = (hash * 31 + category.charCodeAt(i)) >>> 0;\n }\n return palette[hash % palette.length];\n}\n\nfunction formatDate(iso?: string): string {\n if (!iso) return '—';\n const date = new Date(iso);\n if (Number.isNaN(date.getTime())) return '—';\n return date.toLocaleDateString(undefined, { year: 'numeric', month: 'short', day: 'numeric' });\n}\n\n/** Generate a self-contained, printable certificate document (no app tokens, no raw colors). */\nfunction downloadCertificate(course: string, recipient: string, dateLabel: string, id: string): void {\n if (typeof document === 'undefined') return;\n const esc = (s: string) =>\n s.replace(/[&<>\"]/g, (c) => ({ '&': '&', '<': '<', '>': '>', '\"': '"' })[c] ?? c);\n const html = `<!doctype html><html><head><meta charset=\"utf-8\"><title>Certificate — ${esc(course)}</title>\n<style>\n body{font-family:Georgia,'Times New Roman',serif;margin:0;padding:48px;text-align:center}\n .frame{border:6px double currentColor;padding:56px 40px;max-width:760px;margin:0 auto}\n .kicker{letter-spacing:.3em;text-transform:uppercase;font-size:12px;opacity:.7}\n h1{font-size:34px;margin:24px 0 8px}\n .recipient{font-size:26px;margin:20px 0}\n .rule{width:200px;border:0;border-top:1px solid currentColor;margin:24px auto;opacity:.4}\n .meta{font-size:13px;opacity:.7;margin-top:32px}\n</style></head><body>\n <div class=\"frame\">\n <div class=\"kicker\">CrewFoundry · Certificate of Completion</div>\n <h1>${esc(course)}</h1>\n <div class=\"kicker\">Awarded to</div>\n <div class=\"recipient\">${esc(recipient)}</div>\n <hr class=\"rule\" />\n <div class=\"meta\">Issued ${esc(dateLabel)} · Credential ID: ${esc(id)}</div>\n </div>\n</body></html>`;\n const blob = new Blob([html], { type: 'text/html' });\n const url = URL.createObjectURL(blob);\n const anchor = document.createElement('a');\n anchor.href = url;\n anchor.download = `crewfoundry-certificate-${id}.html`;\n document.body.appendChild(anchor);\n anchor.click();\n anchor.remove();\n URL.revokeObjectURL(url);\n}\n\ntype Tab = 'in-progress' | 'completed' | 'certificates';\n\ninterface LearnerCourse {\n course: LearningCourse;\n completedModules: number;\n totalModules: number;\n pct: number;\n completedAt?: string;\n}\n\nconst CATEGORY_ICON: ComponentType<{ className?: string }> = BookOpen;\n\nexport function LearningMyLearningPagePage() {\n const { courses } = useCrewFoundryStore();\n const auth = useAuth();\n const navigate = useNavigate();\n const [tab, setTab] = useState<Tab>('in-progress');\n\n const recipient = auth.user?.name?.trim() || 'CrewFoundry Learner';\n\n const { inProgress, completed, modulesDone } = useMemo(() => {\n const progressMap = readAllCourseProgress();\n const enrolled = readEnrolledIds();\n const inProg: LearnerCourse[] = [];\n const done: LearnerCourse[] = [];\n let totalModulesDone = 0;\n\n for (const course of courses) {\n const entry = progressMap[course.id];\n const completedModules = entry?.completed.length ?? 0;\n totalModulesDone += completedModules;\n const totalModules = Math.max(course.modules, 1);\n const pct = Math.min(100, Math.round((completedModules / totalModules) * 100));\n const record: LearnerCourse = {\n course,\n completedModules,\n totalModules,\n pct,\n completedAt: entry?.completedAt,\n };\n if (completedModules >= totalModules && completedModules > 0) {\n done.push(record);\n } else if (completedModules > 0 || enrolled.has(course.id)) {\n inProg.push(record);\n }\n }\n inProg.sort((a, b) => b.pct - a.pct);\n done.sort((a, b) => (b.completedAt ?? '').localeCompare(a.completedAt ?? ''));\n return { inProgress: inProg, completed: done, modulesDone: totalModulesDone };\n }, [courses]);\n\n const openPlayer = (id: string) =>\n navigate(`../learning/course-learning-page?id=${encodeURIComponent(id)}`);\n\n return (\n <div className=\"space-y-6 p-6\">\n <div>\n <h1 className=\"text-2xl font-semibold\">\n <GradientText>My Learning</GradientText>\n </h1>\n <p className=\"text-sm text-text-secondary\">Track your learning progress and achievements</p>\n <PagePurpose className=\"mt-1\">\n Everything you're currently learning — track your enrolled courses, completed\n training and the certificates you've earned, all in one place.\n </PagePurpose>\n </div>\n\n <div className=\"grid gap-4 sm:grid-cols-2 lg:grid-cols-4\">\n <StatCard\n label=\"Courses in progress\"\n value={inProgress.length}\n icon={<BookOpen className=\"h-4 w-4\" />}\n />\n <StatCard label=\"Completed\" value={completed.length} icon={<Award className=\"h-4 w-4\" />} />\n <StatCard\n label=\"Certificates\"\n value={completed.length}\n icon={<GraduationCap className=\"h-4 w-4\" />}\n />\n <StatCard label=\"Modules done\" value={modulesDone} icon={<Layers className=\"h-4 w-4\" />} />\n </div>\n\n <div className=\"overflow-x-auto pb-1\">\n <SegmentedTabs\n aria-label=\"My learning\"\n value={tab}\n onChange={(id) => setTab(id as Tab)}\n className=\"w-max\"\n tabs={[\n { id: 'in-progress', label: 'In Progress', count: inProgress.length },\n { id: 'completed', label: 'Completed', count: completed.length },\n { id: 'certificates', label: 'Certificates', count: completed.length },\n ]}\n />\n </div>\n\n {tab === 'in-progress' &&\n (inProgress.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"Nothing in progress yet\"\n description=\"Enroll in a course and it will show up here so you can pick up where you left off.\"\n action={\n <Button variant=\"outline\" onClick={() => navigate('../learning/courses-page')}>\n Browse courses\n </Button>\n }\n />\n ) : (\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {inProgress.map(({ course, pct, completedModules, totalModules }) => {\n const accent = categoryAccent(course.category);\n return (\n <GlassCard key={course.id} treatment=\"plain\" className=\"flex flex-col overflow-hidden\">\n <div\n className=\"flex h-20 items-center justify-center\"\n style={{\n background: `linear-gradient(135deg, color-mix(in srgb, ${accent} 26%, var(--color-bg-surface)), color-mix(in srgb, ${accent} 8%, var(--color-bg-surface)))`,\n }}\n >\n <span\n className=\"flex h-10 w-10 items-center justify-center rounded-xl bg-bg-surface/70 shadow-elevation-1\"\n style={{ color: accent }}\n >\n <CATEGORY_ICON className=\"h-5 w-5\" />\n </span>\n </div>\n <div className=\"flex flex-1 flex-col gap-3 p-4\">\n <div>\n <p className=\"text-sm font-semibold\">{course.title}</p>\n <p className=\"text-xs text-text-secondary\">{course.instructor}</p>\n </div>\n <div className=\"mt-auto space-y-1\">\n <div className=\"flex justify-between text-xs text-text-secondary\">\n <span className=\"tabular-nums\">{pct}% complete</span>\n <span className=\"tabular-nums\">\n {completedModules}/{totalModules} modules\n </span>\n </div>\n <div className=\"h-2 overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className=\"h-full rounded-full bg-action-primary-bg\"\n style={{ width: `${pct}%` }}\n />\n </div>\n </div>\n <Button className=\"w-full\" onClick={() => openPlayer(course.id)}>\n <Play className=\"mr-1 h-4 w-4\" />\n Continue\n </Button>\n </div>\n </GlassCard>\n );\n })}\n </div>\n ))}\n\n {tab === 'completed' &&\n (completed.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No completed courses yet\"\n description=\"Finish a course from the player and it will be marked complete here.\"\n />\n ) : (\n <div className=\"space-y-3\">\n {completed.map(({ course, completedAt }) => (\n <button\n key={course.id}\n type=\"button\"\n onClick={() => openPlayer(course.id)}\n className=\"flex w-full items-center justify-between gap-3 rounded-lg border border-border-subtle p-4 text-left transition-colors hover:border-border-strong hover:bg-bg-sunken\"\n >\n <div className=\"min-w-0\">\n <p className=\"truncate text-sm font-medium\">{course.title}</p>\n <p className=\"text-xs text-text-secondary\">\n Completed {formatDate(completedAt)} · {course.instructor}\n </p>\n </div>\n <div className=\"flex shrink-0 items-center gap-3\">\n <RatingStars value={course.rating} size={13} showValue />\n <Badge variant=\"secondary\" className=\"text-[10px]\">\n Completed\n </Badge>\n </div>\n </button>\n ))}\n </div>\n ))}\n\n {tab === 'certificates' &&\n (completed.length === 0 ? (\n <IllustratedEmptyState\n illustration=\"empty-generic\"\n title=\"No certificates yet\"\n description=\"Certificates are issued when you complete a course. Finish one and it will appear here to download.\"\n action={\n <Button variant=\"outline\" onClick={() => navigate('../learning/courses-page')}>\n Browse courses\n </Button>\n }\n />\n ) : (\n <div className=\"grid gap-4 md:grid-cols-2 lg:grid-cols-3\">\n {completed.map(({ course, completedAt }) => {\n const credentialId = `CF-${course.id.slice(-6).toUpperCase()}`;\n const dateLabel = formatDate(completedAt);\n return (\n <CertificateCard\n key={course.id}\n title={course.title}\n recipient={recipient}\n date={dateLabel}\n issuer=\"CrewFoundry\"\n credentialId={credentialId}\n onDownload={() =>\n downloadCertificate(course.title, recipient, dateLabel, credentialId)\n }\n />\n );\n })}\n </div>\n ))}\n </div>\n );\n}\n"],"mappings":"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"}
|