@burdenoff/microfe-technospam 2026.704.1 → 2026.705.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. package/dist/technospam/api/technospamApi.js +232 -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/pages/AdminAdminDashboardPagePage.js +664 -334
  6. package/dist/technospam/pages/AdminAdminDashboardPagePage.js.map +1 -1
  7. package/dist/technospam/pages/ApplicationsProblemsPagePage.js +79 -72
  8. package/dist/technospam/pages/ApplicationsProblemsPagePage.js.map +1 -1
  9. package/dist/technospam/pages/CoursesCourseDetailsPagePage.js +140 -87
  10. package/dist/technospam/pages/CoursesCourseDetailsPagePage.js.map +1 -1
  11. package/dist/technospam/pages/CoursesCoursesPagePage.js +75 -68
  12. package/dist/technospam/pages/CoursesCoursesPagePage.js.map +1 -1
  13. package/dist/technospam/pages/CoursesMyLearningPagePage.js +3 -0
  14. package/dist/technospam/pages/CoursesMyLearningPagePage.js.map +1 -1
  15. package/dist/technospam/pages/HackathonsHackathonsPagePage.js +76 -53
  16. package/dist/technospam/pages/HackathonsHackathonsPagePage.js.map +1 -1
  17. package/dist/technospam/pages/MentorshipMentorProfilePagePage.js +36 -35
  18. package/dist/technospam/pages/MentorshipMentorProfilePagePage.js.map +1 -1
  19. package/dist/technospam/pages/MentorshipMentorsPagePage.js +112 -103
  20. package/dist/technospam/pages/MentorshipMentorsPagePage.js.map +1 -1
  21. package/dist/technospam/pages/MentorshipSessionsPagePage.js +219 -110
  22. package/dist/technospam/pages/MentorshipSessionsPagePage.js.map +1 -1
  23. package/package.json +1 -1
@@ -3,15 +3,16 @@ import { PageLayout as t } from "../components/PageLayout.js";
3
3
  import { StatCard as n } from "../components/StatCard.js";
4
4
  import { formatDuration as r } from "../utils/format.js";
5
5
  import { StatusBadge as i } from "../components/StatusBadge.js";
6
- import { EmptyView as a, ErrorView as o, LoadingGrid as s } from "../components/StateViews.js";
6
+ import { LoadMore as a } from "../components/LoadMore.js";
7
+ import { EmptyView as o, ErrorView as s, LoadingGrid as c } from "../components/StateViews.js";
7
8
  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";
9
+ import { pageArg as l, useCoursesQuery as u, usePageLimit as d } from "../api/technospamApi.js";
10
+ import { useMemo as f, useState as p } from "react";
11
+ import { BookOpen as m, Clock as h, GraduationCap as g, Layers as _, Search as v, Sparkles as y } from "lucide-react";
12
+ import { jsx as b, jsxs as x } from "react/jsx-runtime";
13
+ import { Badge as S, Button as C, GlassCard as w, Input as T, Tabs as E, TabsList as D, TabsTrigger as O } from "@burdenoff/fe-libs/ui";
13
14
  //#region src/technospam/pages/CoursesCoursesPagePage.tsx
14
- var E = [
15
+ var k = [
15
16
  {
16
17
  value: "ALL",
17
18
  label: "All levels"
@@ -33,7 +34,7 @@ var E = [
33
34
  label: "Expert"
34
35
  }
35
36
  ];
36
- function D(e) {
37
+ function A(e) {
37
38
  let t = [
38
39
  "from-accent-primary/25 to-accent-teal/20",
39
40
  "from-accent-teal/25 to-accent-primary/15",
@@ -43,53 +44,53 @@ function D(e) {
43
44
  for (let t = 0; t < e.length; t += 1) n = n * 31 + e.charCodeAt(t) >>> 0;
44
45
  return t[n % t.length];
45
46
  }
46
- function O({ course: e, onOpen: t }) {
47
- return /* @__PURE__ */ v(x, {
47
+ function j({ course: e, onOpen: t }) {
48
+ return /* @__PURE__ */ x(w, {
48
49
  className: "group flex cursor-pointer flex-col overflow-hidden transition-shadow hover:shadow-md",
49
50
  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, {
51
+ children: [/* @__PURE__ */ x("div", {
52
+ className: `relative flex h-28 items-end bg-gradient-to-br ${A(e.topic ?? e.level)} p-4`,
53
+ children: [/* @__PURE__ */ b(m, {
53
54
  className: "absolute right-3 top-3 text-text-muted/60",
54
55
  size: 20
55
- }), /* @__PURE__ */ _(i, {
56
+ }), /* @__PURE__ */ b(i, {
56
57
  status: e.level,
57
58
  kind: "level"
58
59
  })]
59
- }), /* @__PURE__ */ v("div", {
60
+ }), /* @__PURE__ */ x("div", {
60
61
  className: "flex flex-1 flex-col gap-3 p-4",
61
62
  children: [
62
- /* @__PURE__ */ v("div", {
63
+ /* @__PURE__ */ x("div", {
63
64
  className: "min-w-0",
64
- children: [/* @__PURE__ */ _("h3", {
65
+ children: [/* @__PURE__ */ b("h3", {
65
66
  className: "truncate text-base font-semibold text-text-primary group-hover:text-accent-primary",
66
67
  children: e.title
67
- }), e.summary ? /* @__PURE__ */ _("p", {
68
+ }), e.summary ? /* @__PURE__ */ b("p", {
68
69
  className: "mt-1 line-clamp-2 text-sm text-text-secondary",
69
70
  children: e.summary
70
71
  }) : null]
71
72
  }),
72
- /* @__PURE__ */ v("div", {
73
+ /* @__PURE__ */ x("div", {
73
74
  className: "mt-auto flex flex-wrap items-center gap-3 text-xs text-text-muted",
74
- children: [e.topic ? /* @__PURE__ */ v("span", {
75
+ children: [e.topic ? /* @__PURE__ */ x("span", {
75
76
  className: "inline-flex items-center gap-1",
76
77
  children: [
77
- /* @__PURE__ */ _(m, { size: 13 }),
78
+ /* @__PURE__ */ b(_, { size: 13 }),
78
79
  " ",
79
80
  e.topic
80
81
  ]
81
- }) : null, /* @__PURE__ */ v("span", {
82
+ }) : null, /* @__PURE__ */ x("span", {
82
83
  className: "inline-flex items-center gap-1",
83
84
  children: [
84
- /* @__PURE__ */ _(f, { size: 13 }),
85
+ /* @__PURE__ */ b(h, { size: 13 }),
85
86
  " ",
86
87
  r(e.estimatedMinutes)
87
88
  ]
88
89
  })]
89
90
  }),
90
- e.tags.length ? /* @__PURE__ */ _("div", {
91
+ e.tags.length ? /* @__PURE__ */ b("div", {
91
92
  className: "flex flex-wrap gap-1.5",
92
- children: e.tags.slice(0, 3).map((e) => /* @__PURE__ */ _(y, {
93
+ children: e.tags.slice(0, 3).map((e) => /* @__PURE__ */ b(S, {
93
94
  variant: "secondary",
94
95
  className: "text-[11px]",
95
96
  children: e
@@ -99,89 +100,95 @@ function O({ course: e, onOpen: t }) {
99
100
  })]
100
101
  });
101
102
  }
102
- function k() {
103
- let i = e(), [m, y] = u("ALL"), [x, D] = u(""), { data: k, loading: A, error: j } = c({
103
+ function M() {
104
+ let i = e(), [_, S] = p("ALL"), [w, A] = p(""), { limit: M, more: N } = d(), { data: P, loading: F, error: I } = u({
104
105
  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, {
106
+ pagination: l(M),
107
+ ..._ === "ALL" ? {} : { level: _ }
108
+ }), L = P?.tecListCourses.items ?? [], R = P?.tecListCourses.pageInfo.hasMore ?? !1, z = f(() => {
109
+ let e = w.trim().toLowerCase();
110
+ return e ? L.filter((t) => t.title.toLowerCase().includes(e) || (t.topic ?? "").toLowerCase().includes(e) || t.tags.some((t) => t.toLowerCase().includes(e))) : L;
111
+ }, [L, w]);
112
+ return /* @__PURE__ */ x(t, {
111
113
  hero: !0,
112
114
  title: "Course Catalog",
113
- description: "Learn technology by doing — runnable, project-based courses across the stack.",
114
- actions: /* @__PURE__ */ v(b, {
115
+ description: "Learn technology by doing — practical, project-based courses across the stack.",
116
+ actions: /* @__PURE__ */ x(C, {
115
117
  variant: "outline",
116
118
  onClick: () => i("/courses/my-learning-page"),
117
- children: [/* @__PURE__ */ _(p, { size: 16 }), " My Learning"]
119
+ children: [/* @__PURE__ */ b(g, { size: 16 }), " My Learning"]
118
120
  }),
119
121
  children: [
120
- /* @__PURE__ */ v("div", {
122
+ /* @__PURE__ */ x("div", {
121
123
  className: "mb-5 grid grid-cols-2 gap-3 sm:grid-cols-4",
122
124
  children: [
123
- /* @__PURE__ */ _(n, {
125
+ /* @__PURE__ */ b(n, {
124
126
  label: "Courses",
125
- value: M.length,
126
- icon: d
127
+ value: L.length,
128
+ icon: m
127
129
  }),
128
- /* @__PURE__ */ _(n, {
130
+ /* @__PURE__ */ b(n, {
129
131
  label: "Beginner-friendly",
130
- value: M.filter((e) => e.level === "BEGINNER").length,
131
- icon: g
132
+ value: L.filter((e) => e.level === "BEGINNER").length,
133
+ icon: y
132
134
  }),
133
- /* @__PURE__ */ _(n, {
135
+ /* @__PURE__ */ b(n, {
134
136
  label: "Advanced+",
135
- value: M.filter((e) => e.level === "ADVANCED" || e.level === "EXPERT").length,
136
- icon: p
137
+ value: L.filter((e) => e.level === "ADVANCED" || e.level === "EXPERT").length,
138
+ icon: g
137
139
  }),
138
- /* @__PURE__ */ _(n, {
140
+ /* @__PURE__ */ b(n, {
139
141
  label: "Total content",
140
- value: r(M.reduce((e, t) => e + t.estimatedMinutes, 0)),
141
- icon: f
142
+ value: r(L.reduce((e, t) => e + t.estimatedMinutes, 0)),
143
+ icon: h
142
144
  })
143
145
  ]
144
146
  }),
145
- /* @__PURE__ */ v("div", {
147
+ /* @__PURE__ */ x("div", {
146
148
  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, {
149
+ children: [/* @__PURE__ */ b(E, {
150
+ value: _,
151
+ onValueChange: (e) => S(e),
152
+ children: /* @__PURE__ */ b(D, { children: k.map((e) => /* @__PURE__ */ b(O, {
151
153
  value: e.value,
152
154
  children: e.label
153
155
  }, e.value)) })
154
- }), /* @__PURE__ */ v("div", {
156
+ }), /* @__PURE__ */ x("div", {
155
157
  className: "relative w-full sm:w-72",
156
- children: [/* @__PURE__ */ _(h, {
158
+ children: [/* @__PURE__ */ b(v, {
157
159
  className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-text-muted",
158
160
  size: 16
159
- }), /* @__PURE__ */ _(S, {
161
+ }), /* @__PURE__ */ b(T, {
160
162
  className: "pl-9",
161
163
  placeholder: "Search courses…",
162
- value: x,
163
- onChange: (e) => D(e.target.value)
164
+ value: w,
165
+ onChange: (e) => A(e.target.value)
164
166
  })]
165
167
  })]
166
168
  }),
167
- A && !M.length ? /* @__PURE__ */ _(s, {}) : j ? /* @__PURE__ */ _(o, {
169
+ F && !L.length ? /* @__PURE__ */ b(c, {}) : I ? /* @__PURE__ */ b(s, {
168
170
  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", {
171
+ error: I
172
+ }) : z.length === 0 ? /* @__PURE__ */ b(o, {
173
+ icon: /* @__PURE__ */ b(m, { size: 26 }),
174
+ title: w || _ !== "ALL" ? "No matching courses" : "No courses published yet",
175
+ description: w || _ !== "ALL" ? "Try a different search or level filter." : "Published courses will appear here. Check back soon — TechnoSpam is pre-launch."
176
+ }) : /* @__PURE__ */ b("div", {
175
177
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
176
- children: N.map((e) => /* @__PURE__ */ _(O, {
178
+ children: z.map((e) => /* @__PURE__ */ b(j, {
177
179
  course: e,
178
180
  onOpen: () => i(`/courses/course-details-page?id=${e.id}`)
179
181
  }, e.id))
182
+ }),
183
+ /* @__PURE__ */ b(a, {
184
+ hasMore: R,
185
+ loading: F,
186
+ onClick: N
180
187
  })
181
188
  ]
182
189
  });
183
190
  }
184
191
  //#endregion
185
- export { k as CoursesCoursesPagePage };
192
+ export { M as CoursesCoursesPagePage };
186
193
 
187
194
  //# 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 PageLayout,\n StatCard,\n StatusBadge,\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\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 { 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} />\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 <LoadMore hasMore={hasMore} loading={loading} onClick={more} />\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -54,6 +54,9 @@ function b({ e, onOpen: t }) {
54
54
  }), /* @__PURE__ */ _(v, {
55
55
  variant: "ghost",
56
56
  size: "sm",
57
+ onClick: (e) => {
58
+ e.stopPropagation(), t();
59
+ },
57
60
  children: [/* @__PURE__ */ g(h, { size: 15 }), " Resume"]
58
61
  })]
59
62
  })
@@ -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 variant=\"ghost\" size=\"sm\">\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 { 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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"}
@@ -3,15 +3,16 @@ import { PageLayout as t } from "../components/PageLayout.js";
3
3
  import { StatCard as n } from "../components/StatCard.js";
4
4
  import { formatDate as r, formatMoney as i } from "../utils/format.js";
5
5
  import { StatusBadge as a } from "../components/StatusBadge.js";
6
- import { EmptyView as o, ErrorView as s, LoadingGrid as c } from "../components/StateViews.js";
6
+ import { LoadMore as o } from "../components/LoadMore.js";
7
+ import { EmptyView as s, ErrorView as c, LoadingGrid as l } from "../components/StateViews.js";
7
8
  import "../components/index.js";
8
- import { useHackathonsQuery as l } from "../api/technospamApi.js";
9
- import { useState as u } from "react";
10
- import { Award as d, CalendarDays as f, Sparkles as p, Trophy as m, Users as h } from "lucide-react";
11
- import { jsx as g, jsxs as _ } from "react/jsx-runtime";
12
- import { Badge as v, GlassCard as y, Tabs as b, TabsList as x, TabsTrigger as S } from "@burdenoff/fe-libs/ui";
9
+ import { pageArg as u, useHackathonsQuery as d, usePageLimit as f } from "../api/technospamApi.js";
10
+ import { useMemo as p, useState as m } from "react";
11
+ import { Award as h, CalendarDays as g, Search as _, Sparkles as v, Trophy as y, Users as b } from "lucide-react";
12
+ import { jsx as x, jsxs as S } from "react/jsx-runtime";
13
+ import { Badge as C, GlassCard as w, Input as T, Tabs as E, TabsList as D, TabsTrigger as O } from "@burdenoff/fe-libs/ui";
13
14
  //#region src/technospam/pages/HackathonsHackathonsPagePage.tsx
14
- var C = [
15
+ var k = [
15
16
  {
16
17
  value: "ALL",
17
18
  label: "All"
@@ -33,7 +34,7 @@ var C = [
33
34
  label: "Completed"
34
35
  }
35
36
  ];
36
- function w(e) {
37
+ function A(e) {
37
38
  let t = [
38
39
  "from-accent-primary/25 to-accent-teal/20",
39
40
  "from-accent-teal/25 to-accent-primary/15",
@@ -43,62 +44,62 @@ function w(e) {
43
44
  for (let t = 0; t < e.length; t += 1) n = n * 31 + e.charCodeAt(t) >>> 0;
44
45
  return t[n % t.length];
45
46
  }
46
- function T({ hackathon: e, onOpen: t }) {
47
- return /* @__PURE__ */ _(y, {
47
+ function j({ hackathon: e, onOpen: t }) {
48
+ return /* @__PURE__ */ S(w, {
48
49
  className: "group flex cursor-pointer flex-col overflow-hidden transition-shadow hover:shadow-md",
49
50
  onClick: t,
50
- children: [/* @__PURE__ */ _("div", {
51
- className: `relative flex h-24 items-end bg-gradient-to-br ${w(e.theme ?? e.id)} p-4`,
52
- children: [/* @__PURE__ */ g(m, {
51
+ children: [/* @__PURE__ */ S("div", {
52
+ className: `relative flex h-24 items-end bg-gradient-to-br ${A(e.theme ?? e.id)} p-4`,
53
+ children: [/* @__PURE__ */ x(y, {
53
54
  className: "absolute right-3 top-3 text-text-muted/60",
54
55
  size: 20
55
- }), /* @__PURE__ */ g(a, { status: e.status })]
56
- }), /* @__PURE__ */ _("div", {
56
+ }), /* @__PURE__ */ x(a, { status: e.status })]
57
+ }), /* @__PURE__ */ S("div", {
57
58
  className: "flex flex-1 flex-col gap-3 p-4",
58
59
  children: [
59
- /* @__PURE__ */ _("div", {
60
+ /* @__PURE__ */ S("div", {
60
61
  className: "min-w-0",
61
- children: [/* @__PURE__ */ g("h3", {
62
+ children: [/* @__PURE__ */ x("h3", {
62
63
  className: "truncate text-base font-semibold text-text-primary group-hover:text-accent-primary",
63
64
  children: e.title
64
- }), e.theme ? /* @__PURE__ */ g("p", {
65
+ }), e.theme ? /* @__PURE__ */ x("p", {
65
66
  className: "mt-1 line-clamp-2 text-sm text-text-secondary",
66
67
  children: e.theme
67
68
  }) : null]
68
69
  }),
69
- /* @__PURE__ */ _("div", {
70
+ /* @__PURE__ */ S("div", {
70
71
  className: "mt-auto flex flex-wrap items-center gap-x-4 gap-y-1.5 text-xs text-text-muted",
71
- children: [/* @__PURE__ */ _("span", {
72
+ children: [/* @__PURE__ */ S("span", {
72
73
  className: "inline-flex items-center gap-1",
73
74
  children: [
74
- /* @__PURE__ */ g(f, { size: 13 }),
75
+ /* @__PURE__ */ x(g, { size: 13 }),
75
76
  " ",
76
77
  r(e.startAt),
77
78
  " –",
78
79
  " ",
79
80
  r(e.endAt)
80
81
  ]
81
- }), /* @__PURE__ */ _("span", {
82
+ }), /* @__PURE__ */ S("span", {
82
83
  className: "inline-flex items-center gap-1",
83
84
  children: [
84
- /* @__PURE__ */ g(h, { size: 13 }),
85
+ /* @__PURE__ */ x(b, { size: 13 }),
85
86
  " Team of ",
86
87
  e.maxTeamSize
87
88
  ]
88
89
  })]
89
90
  }),
90
- /* @__PURE__ */ _("div", {
91
+ /* @__PURE__ */ S("div", {
91
92
  className: "flex items-center gap-1.5 text-sm font-medium text-accent-teal",
92
93
  children: [
93
- /* @__PURE__ */ g(d, { size: 15 }),
94
+ /* @__PURE__ */ x(h, { size: 15 }),
94
95
  " ",
95
96
  i(e.prizePool, e.currency),
96
97
  " prize pool"
97
98
  ]
98
99
  }),
99
- e.tags.length ? /* @__PURE__ */ g("div", {
100
+ e.tags.length ? /* @__PURE__ */ x("div", {
100
101
  className: "flex flex-wrap gap-1.5",
101
- children: e.tags.slice(0, 4).map((e) => /* @__PURE__ */ g(v, {
102
+ children: e.tags.slice(0, 4).map((e) => /* @__PURE__ */ x(C, {
102
103
  variant: "secondary",
103
104
  className: "text-[11px]",
104
105
  children: e
@@ -108,62 +109,84 @@ function T({ hackathon: e, onOpen: t }) {
108
109
  })]
109
110
  });
110
111
  }
111
- function E() {
112
- let r = e(), [a, f] = u("ALL"), { data: h, loading: v, error: y } = l(a === "ALL" ? {} : { status: a }), w = h?.tecListHackathons.items ?? [], E = w.filter((e) => e.status === "REGISTRATION_OPEN").length, D = w.reduce((e, t) => e + t.prizePool, 0), O = w[0]?.currency ?? "USD";
113
- return /* @__PURE__ */ _(t, {
112
+ function M() {
113
+ let r = e(), [a, g] = m("ALL"), [b, C] = m(""), { limit: w, more: A } = f(), { data: M, loading: N, error: P } = d({
114
+ pagination: u(w),
115
+ ...a === "ALL" ? {} : { status: a }
116
+ }), F = M?.tecListHackathons.items ?? [], I = M?.tecListHackathons.pageInfo.hasMore ?? !1, L = p(() => {
117
+ let e = b.trim().toLowerCase();
118
+ return e ? F.filter((t) => t.title.toLowerCase().includes(e) || (t.theme ?? "").toLowerCase().includes(e) || t.tags.some((t) => t.toLowerCase().includes(e))) : F;
119
+ }, [F, b]), R = L.filter((e) => e.status === "REGISTRATION_OPEN").length, z = L.reduce((e, t) => e + t.prizePool, 0), B = L[0]?.currency ?? "USD";
120
+ return /* @__PURE__ */ S(t, {
114
121
  hero: !0,
115
122
  title: "Hackathons",
116
123
  description: "Compete, collaborate, and ship real projects with a team in a fixed window.",
117
124
  children: [
118
- /* @__PURE__ */ _("div", {
125
+ /* @__PURE__ */ S("div", {
119
126
  className: "mb-5 grid grid-cols-1 gap-3 sm:grid-cols-3",
120
127
  children: [
121
- /* @__PURE__ */ g(n, {
128
+ /* @__PURE__ */ x(n, {
122
129
  label: "Hackathons",
123
- value: w.length,
124
- icon: m
130
+ value: L.length,
131
+ icon: y
125
132
  }),
126
- /* @__PURE__ */ g(n, {
133
+ /* @__PURE__ */ x(n, {
127
134
  label: "Registration open",
128
- value: E,
129
- icon: p
135
+ value: R,
136
+ icon: v
130
137
  }),
131
- /* @__PURE__ */ g(n, {
138
+ /* @__PURE__ */ x(n, {
132
139
  label: "Total prize pool",
133
- value: i(D, O),
134
- icon: d
140
+ value: i(z, B),
141
+ icon: h
135
142
  })
136
143
  ]
137
144
  }),
138
- /* @__PURE__ */ g("div", {
139
- className: "mb-4",
140
- children: /* @__PURE__ */ g(b, {
145
+ /* @__PURE__ */ S("div", {
146
+ className: "mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",
147
+ children: [/* @__PURE__ */ x(E, {
141
148
  value: a,
142
- onValueChange: (e) => f(e),
143
- children: /* @__PURE__ */ g(x, { children: C.map((e) => /* @__PURE__ */ g(S, {
149
+ onValueChange: (e) => g(e),
150
+ children: /* @__PURE__ */ x(D, { children: k.map((e) => /* @__PURE__ */ x(O, {
144
151
  value: e.value,
145
152
  children: e.label
146
153
  }, e.value)) })
147
- })
154
+ }), /* @__PURE__ */ S("div", {
155
+ className: "relative w-full sm:w-72",
156
+ children: [/* @__PURE__ */ x(_, {
157
+ className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-text-muted",
158
+ size: 16
159
+ }), /* @__PURE__ */ x(T, {
160
+ className: "pl-9",
161
+ placeholder: "Search hackathons…",
162
+ value: b,
163
+ onChange: (e) => C(e.target.value)
164
+ })]
165
+ })]
148
166
  }),
149
- v && !w.length ? /* @__PURE__ */ g(c, {}) : y ? /* @__PURE__ */ g(s, {
167
+ N && !L.length ? /* @__PURE__ */ x(l, {}) : P ? /* @__PURE__ */ x(c, {
150
168
  title: "Couldn't load hackathons",
151
- error: y
152
- }) : w.length === 0 ? /* @__PURE__ */ g(o, {
153
- icon: /* @__PURE__ */ g(m, { size: 26 }),
169
+ error: P
170
+ }) : L.length === 0 ? /* @__PURE__ */ x(s, {
171
+ icon: /* @__PURE__ */ x(y, { size: 26 }),
154
172
  title: a === "ALL" ? "No hackathons yet" : "No hackathons in this stage",
155
173
  description: a === "ALL" ? "Upcoming hackathons will appear here. Check back soon — TechnoSpam is pre-launch." : "Try a different stage filter."
156
- }) : /* @__PURE__ */ g("div", {
174
+ }) : /* @__PURE__ */ x("div", {
157
175
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
158
- children: w.map((e) => /* @__PURE__ */ g(T, {
176
+ children: L.map((e) => /* @__PURE__ */ x(j, {
159
177
  hackathon: e,
160
178
  onOpen: () => r(`/hackathons/hackathon-details-page?id=${e.id}`)
161
179
  }, e.id))
180
+ }),
181
+ /* @__PURE__ */ x(o, {
182
+ hasMore: I,
183
+ loading: N,
184
+ onClick: A
162
185
  })
163
186
  ]
164
187
  });
165
188
  }
166
189
  //#endregion
167
- export { E as HackathonsHackathonsPagePage };
190
+ export { M as HackathonsHackathonsPagePage };
168
191
 
169
192
  //# sourceMappingURL=HackathonsHackathonsPagePage.js.map