@burdenoff/microfe-technospam 2026.704.2 → 2026.705.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/technospam/api/technospamApi.js +233 -150
  2. package/dist/technospam/api/technospamApi.js.map +1 -1
  3. package/dist/technospam/components/LoadMore.js +18 -2
  4. package/dist/technospam/components/LoadMore.js.map +1 -0
  5. package/dist/technospam/components/MediaCover.js +57 -0
  6. package/dist/technospam/components/MediaCover.js.map +1 -0
  7. package/dist/technospam/components/PageLayout.js +1 -1
  8. package/dist/technospam/components/PageLayout.js.map +1 -1
  9. package/dist/technospam/components/StatCard.js +19 -11
  10. package/dist/technospam/components/StatCard.js.map +1 -1
  11. package/dist/technospam/components/StatusBadge.js +1 -1
  12. package/dist/technospam/components/StatusBadge.js.map +1 -1
  13. package/dist/technospam/components/index.js +1 -0
  14. package/dist/technospam/pages/AdminAdminDashboardPagePage.js +748 -352
  15. package/dist/technospam/pages/AdminAdminDashboardPagePage.js.map +1 -1
  16. package/dist/technospam/pages/AnalyticsAnalyticsPagePage.js +167 -127
  17. package/dist/technospam/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
  18. package/dist/technospam/pages/ApplicationsProblemDetailsPagePage.js +178 -125
  19. package/dist/technospam/pages/ApplicationsProblemDetailsPagePage.js.map +1 -1
  20. package/dist/technospam/pages/ApplicationsProblemsPagePage.js +89 -93
  21. package/dist/technospam/pages/ApplicationsProblemsPagePage.js.map +1 -1
  22. package/dist/technospam/pages/CoursesCourseDetailsPagePage.js +295 -141
  23. package/dist/technospam/pages/CoursesCourseDetailsPagePage.js.map +1 -1
  24. package/dist/technospam/pages/CoursesCoursesPagePage.js +86 -86
  25. package/dist/technospam/pages/CoursesCoursesPagePage.js.map +1 -1
  26. package/dist/technospam/pages/CoursesMyLearningPagePage.js +122 -88
  27. package/dist/technospam/pages/CoursesMyLearningPagePage.js.map +1 -1
  28. package/dist/technospam/pages/DashboardDashboardPagePage.js +232 -199
  29. package/dist/technospam/pages/DashboardDashboardPagePage.js.map +1 -1
  30. package/dist/technospam/pages/HackathonsHackathonDetailsPagePage.js +128 -106
  31. package/dist/technospam/pages/HackathonsHackathonDetailsPagePage.js.map +1 -1
  32. package/dist/technospam/pages/HackathonsHackathonsPagePage.js +89 -73
  33. package/dist/technospam/pages/HackathonsHackathonsPagePage.js.map +1 -1
  34. package/dist/technospam/pages/MentorshipMentorProfilePagePage.js +171 -115
  35. package/dist/technospam/pages/MentorshipMentorProfilePagePage.js.map +1 -1
  36. package/dist/technospam/pages/MentorshipMentorsPagePage.js +214 -151
  37. package/dist/technospam/pages/MentorshipMentorsPagePage.js.map +1 -1
  38. package/dist/technospam/pages/MentorshipSessionsPagePage.js +241 -116
  39. package/dist/technospam/pages/MentorshipSessionsPagePage.js.map +1 -1
  40. package/package.json +1 -1
@@ -1,17 +1,19 @@
1
1
  import { useTechnoSpamNavigate as e } from "../providers/TechnoSpamProvider.js";
2
2
  import { PageLayout as t } from "../components/PageLayout.js";
3
3
  import { StatCard as n } from "../components/StatCard.js";
4
- import { formatDuration as r } from "../utils/format.js";
5
- import { StatusBadge as i } from "../components/StatusBadge.js";
6
- import { EmptyView as a, ErrorView as o, LoadingGrid as s } from "../components/StateViews.js";
4
+ import { MediaCover as r, topicIcon as i } from "../components/MediaCover.js";
5
+ import { formatDuration as a } from "../utils/format.js";
6
+ import { StatusBadge as o } from "../components/StatusBadge.js";
7
+ import { LoadMore as s } from "../components/LoadMore.js";
8
+ import { EmptyView as c, ErrorView as l, LoadingGrid as u } from "../components/StateViews.js";
7
9
  import "../components/index.js";
8
- import { useCoursesQuery as c } from "../api/technospamApi.js";
9
- import { useMemo as l, useState as u } from "react";
10
- import { BookOpen as d, Clock as f, GraduationCap as p, Layers as m, Search as h, Sparkles as g } from "lucide-react";
11
- import { jsx as _, jsxs as v } from "react/jsx-runtime";
12
- import { Badge as y, Button as b, GlassCard as x, Input as S, Tabs as C, TabsList as w, TabsTrigger as T } from "@burdenoff/fe-libs/ui";
10
+ import { pageArg as d, useCoursesQuery as f, usePageLimit as p } from "../api/technospamApi.js";
11
+ import { useMemo as m, useState as h } from "react";
12
+ import { BookOpen as g, Clock as _, GraduationCap as v, Layers as y, Search as b, Sparkles as x } from "lucide-react";
13
+ import { jsx as S, jsxs as C } from "react/jsx-runtime";
14
+ import { Badge as w, Button as T, GlassCard as E, Input as D, Tabs as O, TabsList as k, TabsTrigger as A } from "@burdenoff/fe-libs/ui";
13
15
  //#region src/technospam/pages/CoursesCoursesPagePage.tsx
14
- var E = [
16
+ var j = [
15
17
  {
16
18
  value: "ALL",
17
19
  label: "All levels"
@@ -33,63 +35,51 @@ var E = [
33
35
  label: "Expert"
34
36
  }
35
37
  ];
36
- function D(e) {
37
- let t = [
38
- "from-accent-primary/25 to-accent-teal/20",
39
- "from-accent-teal/25 to-accent-primary/15",
40
- "from-status-success-bg-subtle to-accent-wash",
41
- "from-accent-wash to-bg-sunken"
42
- ], n = 0;
43
- for (let t = 0; t < e.length; t += 1) n = n * 31 + e.charCodeAt(t) >>> 0;
44
- return t[n % t.length];
45
- }
46
- function O({ course: e, onOpen: t }) {
47
- return /* @__PURE__ */ v(x, {
48
- className: "group flex cursor-pointer flex-col overflow-hidden transition-shadow hover:shadow-md",
38
+ function M({ course: e, onOpen: t }) {
39
+ return /* @__PURE__ */ C(E, {
40
+ className: "group flex cursor-pointer flex-col overflow-hidden transition-all duration-200 hover:-translate-y-1 hover:shadow-lg",
49
41
  onClick: t,
50
- children: [/* @__PURE__ */ v("div", {
51
- className: `relative flex h-28 items-end bg-gradient-to-br ${D(e.topic ?? e.level)} p-4`,
52
- children: [/* @__PURE__ */ _(d, {
53
- className: "absolute right-3 top-3 text-text-muted/60",
54
- size: 20
55
- }), /* @__PURE__ */ _(i, {
42
+ children: [/* @__PURE__ */ S(r, {
43
+ seed: `${e.topic ?? ""} ${e.title}`,
44
+ icon: i(e.topic ?? e.title),
45
+ children: /* @__PURE__ */ S(o, {
56
46
  status: e.level,
57
47
  kind: "level"
58
- })]
59
- }), /* @__PURE__ */ v("div", {
48
+ })
49
+ }), /* @__PURE__ */ C("div", {
60
50
  className: "flex flex-1 flex-col gap-3 p-4",
61
51
  children: [
62
- /* @__PURE__ */ v("div", {
52
+ /* @__PURE__ */ C("div", {
63
53
  className: "min-w-0",
64
- children: [/* @__PURE__ */ _("h3", {
54
+ children: [/* @__PURE__ */ S("h3", {
65
55
  className: "truncate text-base font-semibold text-text-primary group-hover:text-accent-primary",
66
56
  children: e.title
67
- }), e.summary ? /* @__PURE__ */ _("p", {
57
+ }), e.summary ? /* @__PURE__ */ S("p", {
68
58
  className: "mt-1 line-clamp-2 text-sm text-text-secondary",
69
59
  children: e.summary
70
60
  }) : null]
71
61
  }),
72
- /* @__PURE__ */ v("div", {
62
+ /* @__PURE__ */ C("div", {
73
63
  className: "mt-auto flex flex-wrap items-center gap-3 text-xs text-text-muted",
74
- children: [e.topic ? /* @__PURE__ */ v("span", {
64
+ children: [e.topic ? /* @__PURE__ */ C("span", {
75
65
  className: "inline-flex items-center gap-1",
76
66
  children: [
77
- /* @__PURE__ */ _(m, { size: 13 }),
67
+ /* @__PURE__ */ S(y, { size: 13 }),
78
68
  " ",
79
69
  e.topic
80
70
  ]
81
- }) : null, /* @__PURE__ */ v("span", {
71
+ }) : null, /* @__PURE__ */ C("span", {
82
72
  className: "inline-flex items-center gap-1",
83
73
  children: [
84
- /* @__PURE__ */ _(f, { size: 13 }),
74
+ /* @__PURE__ */ S(_, { size: 13 }),
85
75
  " ",
86
- r(e.estimatedMinutes)
76
+ a(e.estimatedMinutes)
87
77
  ]
88
78
  })]
89
79
  }),
90
- e.tags.length ? /* @__PURE__ */ _("div", {
80
+ e.tags.length ? /* @__PURE__ */ S("div", {
91
81
  className: "flex flex-wrap gap-1.5",
92
- children: e.tags.slice(0, 3).map((e) => /* @__PURE__ */ _(y, {
82
+ children: e.tags.slice(0, 3).map((e) => /* @__PURE__ */ S(w, {
93
83
  variant: "secondary",
94
84
  className: "text-[11px]",
95
85
  children: e
@@ -99,89 +89,99 @@ function O({ course: e, onOpen: t }) {
99
89
  })]
100
90
  });
101
91
  }
102
- function k() {
103
- let i = e(), [m, y] = u("ALL"), [x, D] = u(""), { data: k, loading: A, error: j } = c({
92
+ function N() {
93
+ let r = e(), [i, o] = h("ALL"), [y, w] = h(""), { limit: E, more: N } = p(), { data: P, loading: F, error: I } = f({
104
94
  status: "PUBLISHED",
105
- ...m === "ALL" ? {} : { level: m }
106
- }), M = k?.tecListCourses.items ?? [], N = l(() => {
107
- let e = x.trim().toLowerCase();
108
- return e ? M.filter((t) => t.title.toLowerCase().includes(e) || (t.topic ?? "").toLowerCase().includes(e) || t.tags.some((t) => t.toLowerCase().includes(e))) : M;
109
- }, [M, x]);
110
- return /* @__PURE__ */ v(t, {
95
+ pagination: d(E),
96
+ ...i === "ALL" ? {} : { level: i }
97
+ }), L = P?.tecListCourses.items ?? [], R = P?.tecListCourses.pageInfo.hasMore ?? !1, z = m(() => {
98
+ let e = y.trim().toLowerCase();
99
+ return e ? L.filter((t) => t.title.toLowerCase().includes(e) || (t.topic ?? "").toLowerCase().includes(e) || t.tags.some((t) => t.toLowerCase().includes(e))) : L;
100
+ }, [L, y]);
101
+ return /* @__PURE__ */ C(t, {
111
102
  hero: !0,
112
103
  title: "Course Catalog",
113
- description: "Learn technology by doing — runnable, project-based courses across the stack.",
114
- actions: /* @__PURE__ */ v(b, {
104
+ description: "Learn technology by doing — practical, project-based courses across the stack.",
105
+ actions: /* @__PURE__ */ C(T, {
115
106
  variant: "outline",
116
- onClick: () => i("/courses/my-learning-page"),
117
- children: [/* @__PURE__ */ _(p, { size: 16 }), " My Learning"]
107
+ onClick: () => r("/courses/my-learning-page"),
108
+ children: [/* @__PURE__ */ S(v, { size: 16 }), " My Learning"]
118
109
  }),
119
110
  children: [
120
- /* @__PURE__ */ v("div", {
111
+ /* @__PURE__ */ C("div", {
121
112
  className: "mb-5 grid grid-cols-2 gap-3 sm:grid-cols-4",
122
113
  children: [
123
- /* @__PURE__ */ _(n, {
114
+ /* @__PURE__ */ S(n, {
124
115
  label: "Courses",
125
- value: M.length,
126
- icon: d
116
+ value: L.length,
117
+ icon: g,
118
+ tone: "blue"
127
119
  }),
128
- /* @__PURE__ */ _(n, {
120
+ /* @__PURE__ */ S(n, {
129
121
  label: "Beginner-friendly",
130
- value: M.filter((e) => e.level === "BEGINNER").length,
131
- icon: g
122
+ value: L.filter((e) => e.level === "BEGINNER").length,
123
+ icon: x,
124
+ tone: "green"
132
125
  }),
133
- /* @__PURE__ */ _(n, {
126
+ /* @__PURE__ */ S(n, {
134
127
  label: "Advanced+",
135
- value: M.filter((e) => e.level === "ADVANCED" || e.level === "EXPERT").length,
136
- icon: p
128
+ value: L.filter((e) => e.level === "ADVANCED" || e.level === "EXPERT").length,
129
+ icon: v,
130
+ tone: "purple"
137
131
  }),
138
- /* @__PURE__ */ _(n, {
132
+ /* @__PURE__ */ S(n, {
139
133
  label: "Total content",
140
- value: r(M.reduce((e, t) => e + t.estimatedMinutes, 0)),
141
- icon: f
134
+ value: a(L.reduce((e, t) => e + t.estimatedMinutes, 0)),
135
+ icon: _,
136
+ tone: "teal"
142
137
  })
143
138
  ]
144
139
  }),
145
- /* @__PURE__ */ v("div", {
140
+ /* @__PURE__ */ C("div", {
146
141
  className: "mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",
147
- children: [/* @__PURE__ */ _(C, {
148
- value: m,
149
- onValueChange: (e) => y(e),
150
- children: /* @__PURE__ */ _(w, { children: E.map((e) => /* @__PURE__ */ _(T, {
142
+ children: [/* @__PURE__ */ S(O, {
143
+ value: i,
144
+ onValueChange: (e) => o(e),
145
+ children: /* @__PURE__ */ S(k, { children: j.map((e) => /* @__PURE__ */ S(A, {
151
146
  value: e.value,
152
147
  children: e.label
153
148
  }, e.value)) })
154
- }), /* @__PURE__ */ v("div", {
149
+ }), /* @__PURE__ */ C("div", {
155
150
  className: "relative w-full sm:w-72",
156
- children: [/* @__PURE__ */ _(h, {
151
+ children: [/* @__PURE__ */ S(b, {
157
152
  className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-text-muted",
158
153
  size: 16
159
- }), /* @__PURE__ */ _(S, {
154
+ }), /* @__PURE__ */ S(D, {
160
155
  className: "pl-9",
161
156
  placeholder: "Search courses…",
162
- value: x,
163
- onChange: (e) => D(e.target.value)
157
+ value: y,
158
+ onChange: (e) => w(e.target.value)
164
159
  })]
165
160
  })]
166
161
  }),
167
- A && !M.length ? /* @__PURE__ */ _(s, {}) : j ? /* @__PURE__ */ _(o, {
162
+ F && !L.length ? /* @__PURE__ */ S(u, {}) : I ? /* @__PURE__ */ S(l, {
168
163
  title: "Couldn't load courses",
169
- error: j
170
- }) : N.length === 0 ? /* @__PURE__ */ _(a, {
171
- icon: /* @__PURE__ */ _(d, { size: 26 }),
172
- title: x || m !== "ALL" ? "No matching courses" : "No courses published yet",
173
- description: x || m !== "ALL" ? "Try a different search or level filter." : "Published courses will appear here. Check back soon — TechnoSpam is pre-launch."
174
- }) : /* @__PURE__ */ _("div", {
164
+ error: I
165
+ }) : z.length === 0 ? /* @__PURE__ */ S(c, {
166
+ icon: /* @__PURE__ */ S(g, { size: 26 }),
167
+ title: y || i !== "ALL" ? "No matching courses" : "No courses published yet",
168
+ description: y || i !== "ALL" ? "Try a different search or level filter." : "Published courses will appear here. Check back soon — TechnoSpam is pre-launch."
169
+ }) : /* @__PURE__ */ S("div", {
175
170
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
176
- children: N.map((e) => /* @__PURE__ */ _(O, {
171
+ children: z.map((e) => /* @__PURE__ */ S(M, {
177
172
  course: e,
178
- onOpen: () => i(`/courses/course-details-page?id=${e.id}`)
173
+ onOpen: () => r(`/courses/course-details-page?id=${e.id}`)
179
174
  }, e.id))
175
+ }),
176
+ /* @__PURE__ */ S(s, {
177
+ hasMore: R,
178
+ loading: F,
179
+ onClick: N
180
180
  })
181
181
  ]
182
182
  });
183
183
  }
184
184
  //#endregion
185
- export { k as CoursesCoursesPagePage };
185
+ export { N as CoursesCoursesPagePage };
186
186
 
187
187
  //# sourceMappingURL=CoursesCoursesPagePage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"CoursesCoursesPagePage.js","names":[],"sources":["../../../src/technospam/pages/CoursesCoursesPagePage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { BookOpen, Clock, GraduationCap, Layers, Search, Sparkles } from 'lucide-react';\nimport {\n Badge,\n Button,\n GlassCard,\n Input,\n Tabs,\n TabsList,\n TabsTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport {\n EmptyView,\n ErrorView,\n LoadingGrid,\n PageLayout,\n StatCard,\n StatusBadge,\n} from '../components';\nimport { useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport { useCoursesQuery, type TecCourse, type TecCourseLevel } from '../api/technospamApi';\nimport { formatDuration } from '../utils/format';\n\nconst LEVELS: Array<{ value: 'ALL' | TecCourseLevel; label: string }> = [\n { value: 'ALL', label: 'All levels' },\n { value: 'BEGINNER', label: 'Beginner' },\n { value: 'INTERMEDIATE', label: 'Intermediate' },\n { value: 'ADVANCED', label: 'Advanced' },\n { value: 'EXPERT', label: 'Expert' },\n];\n\n/** Deterministic token-based cover gradient keyed off the course topic/level. */\nfunction coverGradient(seed: string): string {\n const gradients = [\n 'from-accent-primary/25 to-accent-teal/20',\n 'from-accent-teal/25 to-accent-primary/15',\n 'from-status-success-bg-subtle to-accent-wash',\n 'from-accent-wash to-bg-sunken',\n ];\n let hash = 0;\n for (let i = 0; i < seed.length; i += 1) hash = (hash * 31 + seed.charCodeAt(i)) >>> 0;\n return gradients[hash % gradients.length]!;\n}\n\nfunction CourseCard({ course, onOpen }: { course: TecCourse; onOpen: () => void }) {\n return (\n <GlassCard\n className=\"group flex cursor-pointer flex-col overflow-hidden transition-shadow hover:shadow-md\"\n onClick={onOpen}\n >\n <div\n className={`relative flex h-28 items-end bg-gradient-to-br ${coverGradient(\n course.topic ?? course.level\n )} p-4`}\n >\n <BookOpen className=\"absolute right-3 top-3 text-text-muted/60\" size={20} />\n <StatusBadge status={course.level} kind=\"level\" />\n </div>\n <div className=\"flex flex-1 flex-col gap-3 p-4\">\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-base font-semibold text-text-primary group-hover:text-accent-primary\">\n {course.title}\n </h3>\n {course.summary ? (\n <p className=\"mt-1 line-clamp-2 text-sm text-text-secondary\">{course.summary}</p>\n ) : null}\n </div>\n <div className=\"mt-auto flex flex-wrap items-center gap-3 text-xs text-text-muted\">\n {course.topic ? (\n <span className=\"inline-flex items-center gap-1\">\n <Layers size={13} /> {course.topic}\n </span>\n ) : null}\n <span className=\"inline-flex items-center gap-1\">\n <Clock size={13} /> {formatDuration(course.estimatedMinutes)}\n </span>\n </div>\n {course.tags.length ? (\n <div className=\"flex flex-wrap gap-1.5\">\n {course.tags.slice(0, 3).map((t) => (\n <Badge key={t} variant=\"secondary\" className=\"text-[11px]\">\n {t}\n </Badge>\n ))}\n </div>\n ) : null}\n </div>\n </GlassCard>\n );\n}\n\nexport function CoursesCoursesPagePage() {\n const navigate = useTechnoSpamNavigate();\n const [level, setLevel] = useState<'ALL' | TecCourseLevel>('ALL');\n const [search, setSearch] = useState('');\n\n const { data, loading, error } = useCoursesQuery({\n status: 'PUBLISHED',\n ...(level !== 'ALL' ? { level } : {}),\n });\n\n const courses = data?.tecListCourses.items ?? [];\n const filtered = useMemo(() => {\n const q = search.trim().toLowerCase();\n if (!q) return courses;\n return courses.filter(\n (c) =>\n c.title.toLowerCase().includes(q) ||\n (c.topic ?? '').toLowerCase().includes(q) ||\n c.tags.some((t) => t.toLowerCase().includes(q))\n );\n }, [courses, search]);\n\n return (\n <PageLayout\n hero\n title=\"Course Catalog\"\n description=\"Learn technology by doing — runnable, project-based courses across the stack.\"\n actions={\n <Button variant=\"outline\" onClick={() => navigate('/courses/my-learning-page')}>\n <GraduationCap size={16} /> My Learning\n </Button>\n }\n >\n <div className=\"mb-5 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <StatCard label=\"Courses\" value={courses.length} icon={BookOpen} />\n <StatCard\n label=\"Beginner-friendly\"\n value={courses.filter((c) => c.level === 'BEGINNER').length}\n icon={Sparkles}\n />\n <StatCard\n label=\"Advanced+\"\n value={courses.filter((c) => c.level === 'ADVANCED' || c.level === 'EXPERT').length}\n icon={GraduationCap}\n />\n <StatCard\n label=\"Total content\"\n value={formatDuration(courses.reduce((s, c) => s + c.estimatedMinutes, 0))}\n icon={Clock}\n />\n </div>\n\n <div className=\"mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <Tabs value={level} onValueChange={(v) => setLevel(v as 'ALL' | TecCourseLevel)}>\n <TabsList>\n {LEVELS.map((l) => (\n <TabsTrigger key={l.value} value={l.value}>\n {l.label}\n </TabsTrigger>\n ))}\n </TabsList>\n </Tabs>\n <div className=\"relative w-full sm:w-72\">\n <Search\n className=\"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-text-muted\"\n size={16}\n />\n <Input\n className=\"pl-9\"\n placeholder=\"Search courses…\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n />\n </div>\n </div>\n\n {loading && !courses.length ? (\n <LoadingGrid />\n ) : error ? (\n <ErrorView title=\"Couldn't load courses\" error={error} />\n ) : filtered.length === 0 ? (\n <EmptyView\n icon={<BookOpen size={26} />}\n title={search || level !== 'ALL' ? 'No matching courses' : 'No courses published yet'}\n description={\n search || level !== 'ALL'\n ? 'Try a different search or level filter.'\n : 'Published courses will appear here. Check back soon — TechnoSpam is pre-launch.'\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {filtered.map((course) => (\n <CourseCard\n key={course.id}\n course={course}\n onOpen={() => navigate(`/courses/course-details-page?id=${course.id}`)}\n />\n ))}\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"CoursesCoursesPagePage.js","names":[],"sources":["../../../src/technospam/pages/CoursesCoursesPagePage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { BookOpen, Clock, GraduationCap, Layers, Search, Sparkles } from 'lucide-react';\nimport {\n Badge,\n Button,\n GlassCard,\n Input,\n Tabs,\n TabsList,\n TabsTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport {\n EmptyView,\n ErrorView,\n LoadMore,\n LoadingGrid,\n MediaCover,\n PageLayout,\n StatCard,\n StatusBadge,\n topicIcon,\n} from '../components';\nimport { useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n pageArg,\n useCoursesQuery,\n usePageLimit,\n type TecCourse,\n type TecCourseLevel,\n} from '../api/technospamApi';\nimport { formatDuration } from '../utils/format';\n\nconst LEVELS: Array<{ value: 'ALL' | TecCourseLevel; label: string }> = [\n { value: 'ALL', label: 'All levels' },\n { value: 'BEGINNER', label: 'Beginner' },\n { value: 'INTERMEDIATE', label: 'Intermediate' },\n { value: 'ADVANCED', label: 'Advanced' },\n { value: 'EXPERT', label: 'Expert' },\n];\n\nfunction CourseCard({ course, onOpen }: { course: TecCourse; onOpen: () => void }) {\n return (\n <GlassCard\n className=\"group flex cursor-pointer flex-col overflow-hidden transition-all duration-200 hover:-translate-y-1 hover:shadow-lg\"\n onClick={onOpen}\n >\n <MediaCover\n seed={`${course.topic ?? ''} ${course.title}`}\n icon={topicIcon(course.topic ?? course.title)}\n >\n <StatusBadge status={course.level} kind=\"level\" />\n </MediaCover>\n <div className=\"flex flex-1 flex-col gap-3 p-4\">\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-base font-semibold text-text-primary group-hover:text-accent-primary\">\n {course.title}\n </h3>\n {course.summary ? (\n <p className=\"mt-1 line-clamp-2 text-sm text-text-secondary\">{course.summary}</p>\n ) : null}\n </div>\n <div className=\"mt-auto flex flex-wrap items-center gap-3 text-xs text-text-muted\">\n {course.topic ? (\n <span className=\"inline-flex items-center gap-1\">\n <Layers size={13} /> {course.topic}\n </span>\n ) : null}\n <span className=\"inline-flex items-center gap-1\">\n <Clock size={13} /> {formatDuration(course.estimatedMinutes)}\n </span>\n </div>\n {course.tags.length ? (\n <div className=\"flex flex-wrap gap-1.5\">\n {course.tags.slice(0, 3).map((t) => (\n <Badge key={t} variant=\"secondary\" className=\"text-[11px]\">\n {t}\n </Badge>\n ))}\n </div>\n ) : null}\n </div>\n </GlassCard>\n );\n}\n\nexport function CoursesCoursesPagePage() {\n const navigate = useTechnoSpamNavigate();\n const [level, setLevel] = useState<'ALL' | TecCourseLevel>('ALL');\n const [search, setSearch] = useState('');\n\n const { limit, more } = usePageLimit();\n const { data, loading, error } = useCoursesQuery({\n status: 'PUBLISHED',\n pagination: pageArg(limit),\n ...(level !== 'ALL' ? { level } : {}),\n });\n\n const courses = data?.tecListCourses.items ?? [];\n const hasMore = data?.tecListCourses.pageInfo.hasMore ?? false;\n const filtered = useMemo(() => {\n const q = search.trim().toLowerCase();\n if (!q) return courses;\n return courses.filter(\n (c) =>\n c.title.toLowerCase().includes(q) ||\n (c.topic ?? '').toLowerCase().includes(q) ||\n c.tags.some((t) => t.toLowerCase().includes(q))\n );\n }, [courses, search]);\n\n return (\n <PageLayout\n hero\n title=\"Course Catalog\"\n description=\"Learn technology by doing — practical, project-based courses across the stack.\"\n actions={\n <Button variant=\"outline\" onClick={() => navigate('/courses/my-learning-page')}>\n <GraduationCap size={16} /> My Learning\n </Button>\n }\n >\n <div className=\"mb-5 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <StatCard label=\"Courses\" value={courses.length} icon={BookOpen} tone=\"blue\" />\n <StatCard\n label=\"Beginner-friendly\"\n value={courses.filter((c) => c.level === 'BEGINNER').length}\n icon={Sparkles}\n tone=\"green\"\n />\n <StatCard\n label=\"Advanced+\"\n value={courses.filter((c) => c.level === 'ADVANCED' || c.level === 'EXPERT').length}\n icon={GraduationCap}\n tone=\"purple\"\n />\n <StatCard\n label=\"Total content\"\n value={formatDuration(courses.reduce((s, c) => s + c.estimatedMinutes, 0))}\n icon={Clock}\n tone=\"teal\"\n />\n </div>\n\n <div className=\"mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <Tabs value={level} onValueChange={(v) => setLevel(v as 'ALL' | TecCourseLevel)}>\n <TabsList>\n {LEVELS.map((l) => (\n <TabsTrigger key={l.value} value={l.value}>\n {l.label}\n </TabsTrigger>\n ))}\n </TabsList>\n </Tabs>\n <div className=\"relative w-full sm:w-72\">\n <Search\n className=\"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-text-muted\"\n size={16}\n />\n <Input\n className=\"pl-9\"\n placeholder=\"Search courses…\"\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n />\n </div>\n </div>\n\n {loading && !courses.length ? (\n <LoadingGrid />\n ) : error ? (\n <ErrorView title=\"Couldn't load courses\" error={error} />\n ) : filtered.length === 0 ? (\n <EmptyView\n icon={<BookOpen size={26} />}\n title={search || level !== 'ALL' ? 'No matching courses' : 'No courses published yet'}\n description={\n search || level !== 'ALL'\n ? 'Try a different search or level filter.'\n : 'Published courses will appear here. Check back soon — TechnoSpam is pre-launch.'\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {filtered.map((course) => (\n <CourseCard\n key={course.id}\n course={course}\n onOpen={() => navigate(`/courses/course-details-page?id=${course.id}`)}\n />\n ))}\n </div>\n )}\n <LoadMore hasMore={hasMore} loading={loading} onClick={more} />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1,149 +1,183 @@
1
1
  import { useTechnoSpam as e, useTechnoSpamNavigate as t } from "../providers/TechnoSpamProvider.js";
2
- import { PageLayout as n } from "../components/PageLayout.js";
3
- import { StatCard as r } from "../components/StatCard.js";
4
- import { formatDate as i, relativeTime as a } from "../utils/format.js";
5
- import { StatusBadge as o } from "../components/StatusBadge.js";
6
- import { EmptyView as s, ErrorView as c, LoadingRows as l } from "../components/StateViews.js";
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 u, useEnrollmentsQuery as d } from "../api/technospamApi.js";
9
- import { Award as f, BookOpen as p, GraduationCap as m, PlayCircle as h } from "lucide-react";
10
- import { jsx as g, jsxs as _ } from "react/jsx-runtime";
11
- import { Button as v, GlassCard as y } from "@burdenoff/fe-libs/ui";
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 b({ e, onOpen: t }) {
14
- return /* @__PURE__ */ _(y, {
15
- className: "flex cursor-pointer items-center gap-4 p-4 transition-shadow hover:shadow-md",
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__ */ g("span", {
19
- className: "flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-accent-wash text-accent-primary",
20
- children: /* @__PURE__ */ g(p, { size: 20 })
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__ */ _("div", {
26
+ /* @__PURE__ */ S("div", {
23
27
  className: "min-w-0 flex-1",
24
28
  children: [
25
- /* @__PURE__ */ _("div", {
26
- className: "flex items-center gap-2",
27
- children: [/* @__PURE__ */ g("span", {
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__ */ g(o, { status: e.status })]
34
+ }), /* @__PURE__ */ x(l, { status: e.status })]
31
35
  }),
32
- /* @__PURE__ */ _("div", {
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__ */ _("span", { children: [e.lessonsCompleted, " lessons done"] }),
36
- /* @__PURE__ */ g("span", { children: "·" }),
37
- /* @__PURE__ */ _("span", { children: ["Active ", a(e.lastActiveAt ?? e.startedAt)] })
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__ */ g("div", {
47
+ /* @__PURE__ */ x("div", {
41
48
  className: "mt-2 h-1.5 w-full overflow-hidden rounded-full bg-bg-sunken",
42
- children: /* @__PURE__ */ g("div", {
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__ */ _("div", {
50
- className: "shrink-0 text-right",
51
- children: [/* @__PURE__ */ _("div", {
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__ */ _(v, {
61
+ }), /* @__PURE__ */ S(C, {
55
62
  variant: "ghost",
56
63
  size: "sm",
57
- children: [/* @__PURE__ */ g(h, { size: 15 }), " Resume"]
64
+ onClick: (e) => {
65
+ e.stopPropagation(), t();
66
+ },
67
+ children: [/* @__PURE__ */ x(y, { size: 15 }), " Resume"]
58
68
  })]
59
69
  })
60
70
  ]
61
71
  });
62
72
  }
63
- function x() {
64
- let a = t(), { currentUser: o } = e(), x = o?.id, { data: S, loading: C, error: w } = d(x ? { learnerId: x } : {}), { data: T } = u(x ? { learnerId: x } : {}), E = S?.tecListEnrollments.items ?? [], D = T?.tecListCertificates ?? [], O = E.filter((e) => e.status === "COMPLETED").length, k = E.filter((e) => e.status === "IN_PROGRESS" || e.status === "ENROLLED").length;
65
- return /* @__PURE__ */ _(n, {
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, {
66
76
  hero: !0,
67
77
  title: "My Learning",
68
78
  description: "Track your enrolled courses, progress, and earned certificates.",
69
- actions: /* @__PURE__ */ _(v, {
79
+ actions: /* @__PURE__ */ S(C, {
70
80
  variant: "outline",
71
- onClick: () => a("/courses/courses-page"),
72
- children: [/* @__PURE__ */ g(p, { size: 16 }), " Browse catalog"]
81
+ onClick: () => n("/courses/courses-page"),
82
+ children: [/* @__PURE__ */ x(_, { size: 16 }), " Browse catalog"]
73
83
  }),
74
84
  children: [
75
- /* @__PURE__ */ _("div", {
85
+ /* @__PURE__ */ S("div", {
76
86
  className: "mb-6 grid grid-cols-2 gap-3 sm:grid-cols-4",
77
87
  children: [
78
- /* @__PURE__ */ g(r, {
88
+ /* @__PURE__ */ x(i, {
79
89
  label: "Enrolled",
80
- value: E.length,
81
- icon: p
90
+ value: O.length,
91
+ hint: "Courses you've enrolled in",
92
+ icon: _,
93
+ tone: "blue"
82
94
  }),
83
- /* @__PURE__ */ g(r, {
95
+ /* @__PURE__ */ x(i, {
84
96
  label: "In progress",
85
- value: k,
86
- icon: h
97
+ value: j,
98
+ hint: "Courses in progress",
99
+ icon: y,
100
+ tone: "indigo"
87
101
  }),
88
- /* @__PURE__ */ g(r, {
102
+ /* @__PURE__ */ x(i, {
89
103
  label: "Completed",
90
- value: O,
91
- icon: m
104
+ value: A,
105
+ hint: "Courses completed",
106
+ icon: v,
107
+ tone: "green"
92
108
  }),
93
- /* @__PURE__ */ g(r, {
109
+ /* @__PURE__ */ x(i, {
94
110
  label: "Certificates",
95
- value: D.length,
96
- icon: f
111
+ value: k.length,
112
+ hint: "Certificates earned",
113
+ icon: g,
114
+ tone: "purple"
97
115
  })
98
116
  ]
99
117
  }),
100
- C && !E.length ? /* @__PURE__ */ g(l, {
118
+ h && !O.length ? /* @__PURE__ */ x(f, {
101
119
  count: 3,
102
120
  height: "h-24"
103
- }) : w ? /* @__PURE__ */ g(c, {
121
+ }) : E ? /* @__PURE__ */ x(d, {
104
122
  title: "Couldn't load your learning",
105
- error: w
106
- }) : E.length === 0 ? /* @__PURE__ */ g(s, {
107
- icon: /* @__PURE__ */ g(p, { size: 26 }),
123
+ error: E
124
+ }) : O.length === 0 ? /* @__PURE__ */ x(u, {
125
+ icon: /* @__PURE__ */ x(_, { size: 26 }),
108
126
  title: "You haven't enrolled yet",
109
127
  description: "Browse the catalog and enroll in a course to start building skill by doing.",
110
- action: /* @__PURE__ */ g(v, {
111
- onClick: () => a("/courses/courses-page"),
128
+ action: /* @__PURE__ */ x(C, {
129
+ onClick: () => n("/courses/courses-page"),
112
130
  children: "Browse courses"
113
131
  })
114
- }) : /* @__PURE__ */ g("div", {
132
+ }) : /* @__PURE__ */ x("div", {
115
133
  className: "space-y-3",
116
- children: E.map((e) => /* @__PURE__ */ g(b, {
134
+ children: O.map((e) => /* @__PURE__ */ x(T, {
117
135
  e,
118
- onOpen: () => a(`/courses/course-details-page?id=${e.courseId}`)
136
+ onOpen: () => n(`/courses/course-details-page?id=${e.courseId}`)
119
137
  }, e.id))
120
138
  }),
121
- D.length > 0 ? /* @__PURE__ */ _("div", {
139
+ k.length > 0 ? /* @__PURE__ */ S("div", {
122
140
  className: "mt-8",
123
- children: [/* @__PURE__ */ g("h2", {
124
- className: "mb-3 text-base font-semibold text-text-primary",
125
- children: "Certificates"
126
- }), /* @__PURE__ */ g("div", {
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", {
127
148
  className: "grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3",
128
- children: D.map((e) => /* @__PURE__ */ _(y, {
129
- className: "flex items-center gap-3 p-4",
130
- children: [/* @__PURE__ */ g(f, {
131
- className: "text-accent-teal",
132
- size: 22
133
- }), /* @__PURE__ */ _("div", {
134
- className: "min-w-0",
135
- children: [/* @__PURE__ */ g("div", {
136
- className: "truncate text-sm font-medium text-text-primary",
137
- children: e.course?.title ?? "Certificate"
138
- }), /* @__PURE__ */ _("div", {
139
- className: "text-xs text-text-muted",
140
- children: [
141
- "Score ",
142
- e.score,
143
- " · Issued ",
144
- i(e.issuedAt)
145
- ]
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
+ })]
146
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 })]
147
181
  })]
148
182
  }, e.id))
149
183
  })]
@@ -152,6 +186,6 @@ function x() {
152
186
  });
153
187
  }
154
188
  //#endregion
155
- export { x as CoursesMyLearningPagePage };
189
+ export { E as CoursesMyLearningPagePage };
156
190
 
157
191
  //# sourceMappingURL=CoursesMyLearningPagePage.js.map