@burdenoff/microfe-technospam 2026.1031.4 → 2026.1031.6
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/technospam/api/technospamApi.d.ts +56 -0
- package/dist/technospam/api/technospamApi.d.ts.map +1 -1
- package/dist/technospam/api/technospamApi.js +298 -231
- package/dist/technospam/api/technospamApi.js.map +1 -1
- package/dist/technospam/components/StatusBadge.d.ts.map +1 -1
- package/dist/technospam/components/StatusBadge.js +2 -0
- package/dist/technospam/components/StatusBadge.js.map +1 -1
- package/dist/technospam/components/admin/EnterpriseIntegrationsTab.d.ts +6 -0
- package/dist/technospam/components/admin/EnterpriseIntegrationsTab.d.ts.map +1 -0
- package/dist/technospam/components/admin/EnterpriseIntegrationsTab.js +256 -0
- package/dist/technospam/components/admin/EnterpriseIntegrationsTab.js.map +1 -0
- package/dist/technospam/lib/a11y.d.ts +11 -0
- package/dist/technospam/lib/a11y.d.ts.map +1 -0
- package/dist/technospam/lib/a11y.js +6 -0
- package/dist/technospam/lib/a11y.js.map +1 -0
- package/dist/technospam/lib/enterprise.d.ts +21 -0
- package/dist/technospam/lib/enterprise.d.ts.map +1 -0
- package/dist/technospam/lib/enterprise.js +36 -0
- package/dist/technospam/lib/enterprise.js.map +1 -0
- package/dist/technospam/pages/AdminAdminDashboardPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/AdminAdminDashboardPagePage.js +206 -199
- package/dist/technospam/pages/AdminAdminDashboardPagePage.js.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemsPagePage.js +47 -45
- package/dist/technospam/pages/ApplicationsProblemsPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesCoursesPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/CoursesCoursesPagePage.js +74 -72
- package/dist/technospam/pages/CoursesCoursesPagePage.js.map +1 -1
- package/dist/technospam/pages/HackathonsHackathonsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/HackathonsHackathonsPagePage.js +83 -81
- package/dist/technospam/pages/HackathonsHackathonsPagePage.js.map +1 -1
- package/dist/technospam/pages/MentorshipSessionsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/MentorshipSessionsPagePage.js +99 -97
- package/dist/technospam/pages/MentorshipSessionsPagePage.js.map +1 -1
- package/package.json +1 -1
|
@@ -8,12 +8,13 @@ import { StatusBadge as c } from "../components/StatusBadge.js";
|
|
|
8
8
|
import { LoadMore as l } from "../components/LoadMore.js";
|
|
9
9
|
import { EmptyView as ee, ErrorView as te, LoadingGrid as u } from "../components/StateViews.js";
|
|
10
10
|
import { PremiumBadge as d } from "../components/UpgradeGate.js";
|
|
11
|
-
import { pageArg as ne, usePageLimit as
|
|
11
|
+
import { pageArg as ne, usePageLimit as f, useProblemsQuery as p, useSubmissionsQuery as m } from "../api/technospamApi.js";
|
|
12
12
|
import "../components/index.js";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
13
|
+
import { TAB_PANEL_FOCUS_CLASS as re } from "../lib/a11y.js";
|
|
14
|
+
import { useMemo as h, useState as g } from "react";
|
|
15
|
+
import { Building2 as _, CheckCircle2 as v, Code2 as y, Flame as b, Gauge as ie, Layers as x, Search as ae, Tag as S, Trophy as C } from "lucide-react";
|
|
15
16
|
import { jsx as w, jsxs as T } from "react/jsx-runtime";
|
|
16
|
-
import { Badge as E, GlassCard as D, Input as
|
|
17
|
+
import { Badge as E, GlassCard as D, Input as oe, Select as O, SelectContent as k, SelectItem as A, SelectTrigger as j, SelectValue as M, Tabs as N, TabsContent as P, TabsList as F, TabsTrigger as I } from "@burdenoff/fe-libs/ui";
|
|
17
18
|
//#region src/technospam/pages/ApplicationsProblemsPagePage.tsx
|
|
18
19
|
function L({ problem: e, solved: t, onOpen: n }) {
|
|
19
20
|
let i = r();
|
|
@@ -26,7 +27,7 @@ function L({ problem: e, solved: t, onOpen: n }) {
|
|
|
26
27
|
children: [t ? /* @__PURE__ */ T("span", {
|
|
27
28
|
className: "inline-flex items-center gap-1 rounded-full bg-status-success-bg-subtle px-2 py-0.5 text-[11px] font-medium text-status-success-text ring-1 ring-status-success-border/30",
|
|
28
29
|
children: [
|
|
29
|
-
/* @__PURE__ */ w(
|
|
30
|
+
/* @__PURE__ */ w(v, { size: 12 }),
|
|
30
31
|
" ",
|
|
31
32
|
i("technospam.problems.solved", "Solved")
|
|
32
33
|
]
|
|
@@ -72,7 +73,7 @@ function L({ problem: e, solved: t, onOpen: n }) {
|
|
|
72
73
|
e.languages.length ? /* @__PURE__ */ T("span", {
|
|
73
74
|
className: "inline-flex items-center gap-1",
|
|
74
75
|
children: [
|
|
75
|
-
/* @__PURE__ */ w(
|
|
76
|
+
/* @__PURE__ */ w(y, { size: 13 }),
|
|
76
77
|
" ",
|
|
77
78
|
e.languages.length === 1 ? i("technospam.problems.languages", "{{count}} lang", { count: e.languages.length }) : i("technospam.problems.languagesPlural", "{{count}} langs", { count: e.languages.length })
|
|
78
79
|
]
|
|
@@ -84,14 +85,14 @@ function L({ problem: e, solved: t, onOpen: n }) {
|
|
|
84
85
|
children: [e.companies.slice(0, 3).map((e) => /* @__PURE__ */ T(E, {
|
|
85
86
|
variant: "outline",
|
|
86
87
|
className: "text-[11px] capitalize",
|
|
87
|
-
children: [/* @__PURE__ */ w(
|
|
88
|
+
children: [/* @__PURE__ */ w(_, {
|
|
88
89
|
size: 10,
|
|
89
90
|
className: "mr-1"
|
|
90
91
|
}), e.replace(/-/g, " ")]
|
|
91
92
|
}, e)), e.interviewFrequency === "HIGH" ? /* @__PURE__ */ T(E, {
|
|
92
93
|
variant: "secondary",
|
|
93
94
|
className: "text-[11px] text-status-warning-text",
|
|
94
|
-
children: [/* @__PURE__ */ w(
|
|
95
|
+
children: [/* @__PURE__ */ w(b, {
|
|
95
96
|
size: 10,
|
|
96
97
|
className: "mr-1"
|
|
97
98
|
}), i("technospam.problems.frequentlyAsked", "Frequently asked")]
|
|
@@ -113,24 +114,24 @@ function L({ problem: e, solved: t, onOpen: n }) {
|
|
|
113
114
|
});
|
|
114
115
|
}
|
|
115
116
|
function R() {
|
|
116
|
-
let o = t(), { currentUser: s, workspaceId: c } = e(), { emit: d } = n(),
|
|
117
|
+
let o = t(), { currentUser: s, workspaceId: c } = e(), { emit: d } = n(), _ = r(), x = s?.id, [S, C] = g("ALL"), [E, D] = g("ALL"), [R, z] = g("ALL"), [B, V] = g(""), H = h(() => [
|
|
117
118
|
{
|
|
118
119
|
value: "ALL",
|
|
119
|
-
label:
|
|
120
|
+
label: _("technospam.problems.filterAll", "All")
|
|
120
121
|
},
|
|
121
122
|
{
|
|
122
123
|
value: "EASY",
|
|
123
|
-
label:
|
|
124
|
+
label: _("technospam.problems.filterEasy", "Easy")
|
|
124
125
|
},
|
|
125
126
|
{
|
|
126
127
|
value: "MEDIUM",
|
|
127
|
-
label:
|
|
128
|
+
label: _("technospam.problems.filterMedium", "Medium")
|
|
128
129
|
},
|
|
129
130
|
{
|
|
130
131
|
value: "HARD",
|
|
131
|
-
label:
|
|
132
|
+
label: _("technospam.problems.filterHard", "Hard")
|
|
132
133
|
}
|
|
133
|
-
], [
|
|
134
|
+
], [_]), U = h(() => [
|
|
134
135
|
{
|
|
135
136
|
value: "google",
|
|
136
137
|
label: "Google"
|
|
@@ -155,7 +156,7 @@ function R() {
|
|
|
155
156
|
value: "netflix",
|
|
156
157
|
label: "Netflix"
|
|
157
158
|
}
|
|
158
|
-
], []), W =
|
|
159
|
+
], []), W = h(() => [
|
|
159
160
|
{
|
|
160
161
|
value: "frontend",
|
|
161
162
|
label: "Frontend"
|
|
@@ -180,45 +181,45 @@ function R() {
|
|
|
180
181
|
value: "sre",
|
|
181
182
|
label: "SRE / Infra"
|
|
182
183
|
}
|
|
183
|
-
], []), { limit: G, more: K } =
|
|
184
|
+
], []), { limit: G, more: K } = f(), { data: q, loading: J, error: Y } = p({
|
|
184
185
|
status: "PUBLISHED",
|
|
185
186
|
pagination: ne(G),
|
|
186
187
|
...S === "ALL" ? {} : { difficulty: S },
|
|
187
188
|
...E === "ALL" ? {} : { company: E },
|
|
188
189
|
...R === "ALL" ? {} : { role: R }
|
|
189
|
-
}), { data: X } =
|
|
190
|
+
}), { data: X } = m(x ? { learnerId: x } : {}), Z = q?.tecListProblems?.items ?? [], se = q?.tecListProblems?.pageInfo?.hasMore ?? !1, Q = h(() => new Set((X?.tecListSubmissions?.items ?? []).filter((e) => e.verdict === "ACCEPTED").map((e) => e.problemId)), [X]), ce = Z.filter((e) => Q.has(e.id)).length, le = Z.filter((e) => e.difficulty === "EASY").length, ue = Z.filter((e) => e.difficulty === "HARD").length, $ = h(() => {
|
|
190
191
|
let e = B.trim().toLowerCase();
|
|
191
192
|
return e ? Z.filter((t) => t.title.toLowerCase().includes(e) || (t.topic ?? "").toLowerCase().includes(e) || t.tags.some((t) => t.toLowerCase().includes(e))) : Z;
|
|
192
193
|
}, [Z, B]);
|
|
193
194
|
return /* @__PURE__ */ T(i, {
|
|
194
195
|
hero: !0,
|
|
195
|
-
title:
|
|
196
|
-
description:
|
|
196
|
+
title: _("technospam.problems.title", "Coding Problems"),
|
|
197
|
+
description: _("technospam.problems.description", "Sharpen your problem-solving with hands-on, mentor-judged challenges across topics and difficulty."),
|
|
197
198
|
children: [/* @__PURE__ */ T("div", {
|
|
198
199
|
className: "mb-5 grid grid-cols-2 gap-3 sm:grid-cols-4",
|
|
199
200
|
children: [
|
|
200
201
|
/* @__PURE__ */ w(a, {
|
|
201
|
-
label:
|
|
202
|
+
label: _("technospam.problems.statProblems", "Problems"),
|
|
202
203
|
value: Z.length,
|
|
203
|
-
icon:
|
|
204
|
+
icon: y,
|
|
204
205
|
tone: "blue"
|
|
205
206
|
}),
|
|
206
207
|
/* @__PURE__ */ w(a, {
|
|
207
|
-
label:
|
|
208
|
-
value:
|
|
209
|
-
icon:
|
|
208
|
+
label: _("technospam.problems.statSolved", "Solved"),
|
|
209
|
+
value: ce,
|
|
210
|
+
icon: v,
|
|
210
211
|
tone: "green"
|
|
211
212
|
}),
|
|
212
213
|
/* @__PURE__ */ w(a, {
|
|
213
|
-
label:
|
|
214
|
-
value:
|
|
215
|
-
icon:
|
|
214
|
+
label: _("technospam.problems.statEasy", "Easy"),
|
|
215
|
+
value: le,
|
|
216
|
+
icon: ie,
|
|
216
217
|
tone: "teal"
|
|
217
218
|
}),
|
|
218
219
|
/* @__PURE__ */ w(a, {
|
|
219
|
-
label:
|
|
220
|
-
value:
|
|
221
|
-
icon:
|
|
220
|
+
label: _("technospam.problems.statHard", "Hard"),
|
|
221
|
+
value: ue,
|
|
222
|
+
icon: b,
|
|
222
223
|
tone: "amber"
|
|
223
224
|
})
|
|
224
225
|
]
|
|
@@ -234,13 +235,13 @@ function R() {
|
|
|
234
235
|
children: e.label
|
|
235
236
|
}, e.value)) }), /* @__PURE__ */ T("div", {
|
|
236
237
|
className: "relative w-full sm:w-72",
|
|
237
|
-
children: [/* @__PURE__ */ w(
|
|
238
|
+
children: [/* @__PURE__ */ w(ae, {
|
|
238
239
|
className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-text-muted",
|
|
239
240
|
size: 16
|
|
240
|
-
}), /* @__PURE__ */ w(
|
|
241
|
+
}), /* @__PURE__ */ w(oe, {
|
|
241
242
|
className: "pl-9",
|
|
242
|
-
"aria-label":
|
|
243
|
-
placeholder:
|
|
243
|
+
"aria-label": _("technospam.problems.searchLabel", "Search problems"),
|
|
244
|
+
placeholder: _("technospam.problems.searchPlaceholder", "Search problems…"),
|
|
244
245
|
value: B,
|
|
245
246
|
onChange: (e) => V(e.target.value),
|
|
246
247
|
onBlur: () => {
|
|
@@ -261,11 +262,11 @@ function R() {
|
|
|
261
262
|
onValueChange: D,
|
|
262
263
|
children: [/* @__PURE__ */ w(j, {
|
|
263
264
|
className: "w-full sm:w-48",
|
|
264
|
-
"aria-label":
|
|
265
|
-
children: /* @__PURE__ */ w(M, { placeholder:
|
|
265
|
+
"aria-label": _("technospam.problems.companyFilterLabel", "Filter by company"),
|
|
266
|
+
children: /* @__PURE__ */ w(M, { placeholder: _("technospam.problems.companyPlaceholder", "Company") })
|
|
266
267
|
}), /* @__PURE__ */ T(k, { children: [/* @__PURE__ */ w(A, {
|
|
267
268
|
value: "ALL",
|
|
268
|
-
children:
|
|
269
|
+
children: _("technospam.problems.companyAll", "All companies")
|
|
269
270
|
}), U.map((e) => /* @__PURE__ */ w(A, {
|
|
270
271
|
value: e.value,
|
|
271
272
|
children: e.label
|
|
@@ -275,11 +276,11 @@ function R() {
|
|
|
275
276
|
onValueChange: z,
|
|
276
277
|
children: [/* @__PURE__ */ w(j, {
|
|
277
278
|
className: "w-full sm:w-48",
|
|
278
|
-
"aria-label":
|
|
279
|
-
children: /* @__PURE__ */ w(M, { placeholder:
|
|
279
|
+
"aria-label": _("technospam.problems.roleFilterLabel", "Filter by role"),
|
|
280
|
+
children: /* @__PURE__ */ w(M, { placeholder: _("technospam.problems.rolePlaceholder", "Role") })
|
|
280
281
|
}), /* @__PURE__ */ T(k, { children: [/* @__PURE__ */ w(A, {
|
|
281
282
|
value: "ALL",
|
|
282
|
-
children:
|
|
283
|
+
children: _("technospam.problems.roleAll", "All roles")
|
|
283
284
|
}), W.map((e) => /* @__PURE__ */ w(A, {
|
|
284
285
|
value: e.value,
|
|
285
286
|
children: e.label
|
|
@@ -288,13 +289,14 @@ function R() {
|
|
|
288
289
|
}),
|
|
289
290
|
/* @__PURE__ */ T(P, {
|
|
290
291
|
value: S,
|
|
292
|
+
className: re,
|
|
291
293
|
children: [J && !Z.length ? /* @__PURE__ */ w(u, {}) : Y ? /* @__PURE__ */ w(te, {
|
|
292
|
-
title:
|
|
294
|
+
title: _("technospam.common.errorLoadProblems", "Couldn't load problems"),
|
|
293
295
|
error: Y
|
|
294
296
|
}) : $.length === 0 ? /* @__PURE__ */ w(ee, {
|
|
295
|
-
icon: /* @__PURE__ */ w(
|
|
296
|
-
title: B || S !== "ALL" || E !== "ALL" || R !== "ALL" ?
|
|
297
|
-
description: B || S !== "ALL" || E !== "ALL" || R !== "ALL" ?
|
|
297
|
+
icon: /* @__PURE__ */ w(y, { size: 26 }),
|
|
298
|
+
title: B || S !== "ALL" || E !== "ALL" || R !== "ALL" ? _("technospam.problems.emptySearchTitle", "No matching problems") : _("technospam.problems.emptyDefaultTitle", "No problems published yet"),
|
|
299
|
+
description: B || S !== "ALL" || E !== "ALL" || R !== "ALL" ? _("technospam.problems.emptySearchDesc", "Try a different search, difficulty, company, or role filter.") : _("technospam.problems.emptyDefaultDesc", "Published problems will appear here. Check back soon — TechnoSpam is pre-launch.")
|
|
298
300
|
}) : /* @__PURE__ */ w("div", {
|
|
299
301
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
300
302
|
children: $.map((e) => /* @__PURE__ */ w(L, {
|
|
@@ -309,7 +311,7 @@ function R() {
|
|
|
309
311
|
}
|
|
310
312
|
}, e.id))
|
|
311
313
|
}), /* @__PURE__ */ w(l, {
|
|
312
|
-
hasMore:
|
|
314
|
+
hasMore: se,
|
|
313
315
|
loading: J,
|
|
314
316
|
onClick: K
|
|
315
317
|
})]
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApplicationsProblemsPagePage.js","names":[],"sources":["../../../src/technospam/pages/ApplicationsProblemsPagePage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n Building2,\n CheckCircle2,\n Code2,\n Flame,\n Gauge,\n Layers,\n Search,\n Tag,\n Trophy,\n} from 'lucide-react';\nimport {\n Badge,\n GlassCard,\n Input,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport {\n EmptyView,\n ErrorView,\n LoadMore,\n LoadingGrid,\n MediaCover,\n PageLayout,\n PremiumBadge,\n StatCard,\n StatusBadge,\n topicIcon,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n pageArg,\n usePageLimit,\n useProblemsQuery,\n useSubmissionsQuery,\n type TecProblem,\n type TecProblemDifficulty,\n} from '../api/technospamApi';\nimport { useTechnoSpamAnalytics } from '../hooks/useTechnoSpamAnalytics';\nimport { useTr } from '../hooks/useTr';\n\nfunction ProblemCard({\n problem,\n solved,\n onOpen,\n}: {\n problem: TecProblem;\n solved: boolean;\n onOpen: () => void;\n}) {\n const tr = useTr();\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={`${problem.topic ?? ''} ${problem.difficulty} ${problem.title}`}\n icon={topicIcon(problem.topic ?? problem.title)}\n >\n {solved ? (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-status-success-bg-subtle px-2 py-0.5 text-[11px] font-medium text-status-success-text ring-1 ring-status-success-border/30\">\n <CheckCircle2 size={12} /> {tr('technospam.problems.solved', 'Solved')}\n </span>\n ) : (\n <span />\n )}\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <StatusBadge status={problem.difficulty} kind=\"level\" />\n {problem.isPremiumOnly ? <PremiumBadge locked={problem.isLocked} /> : null}\n </div>\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 {problem.title}\n </h3>\n {problem.statement ? (\n <p className=\"mt-1 line-clamp-2 text-sm text-text-secondary\">{problem.statement}</p>\n ) : null}\n </div>\n <div className=\"mt-auto flex flex-wrap items-center gap-3 text-xs text-text-muted\">\n {problem.topic ? (\n <span className=\"inline-flex items-center gap-1\">\n <Layers size={13} /> {problem.topic}\n </span>\n ) : null}\n <span className=\"inline-flex items-center gap-1\">\n <Trophy size={13} />{' '}\n {tr('technospam.problems.points', '{{count}} pts', { count: problem.points })}\n </span>\n {problem.languages.length ? (\n <span className=\"inline-flex items-center gap-1\">\n <Code2 size={13} />{' '}\n {problem.languages.length === 1\n ? tr('technospam.problems.languages', '{{count}} lang', {\n count: problem.languages.length,\n })\n : tr('technospam.problems.languagesPlural', '{{count}} langs', {\n count: problem.languages.length,\n })}\n </span>\n ) : null}\n </div>\n {problem.companies.length ? (\n <div className=\"flex flex-wrap gap-1.5\">\n {problem.companies.slice(0, 3).map((c) => (\n <Badge key={c} variant=\"outline\" className=\"text-[11px] capitalize\">\n <Building2 size={10} className=\"mr-1\" />\n {c.replace(/-/g, ' ')}\n </Badge>\n ))}\n {problem.interviewFrequency === 'HIGH' ? (\n <Badge variant=\"secondary\" className=\"text-[11px] text-status-warning-text\">\n <Flame size={10} className=\"mr-1\" />\n {tr('technospam.problems.frequentlyAsked', 'Frequently asked')}\n </Badge>\n ) : null}\n </div>\n ) : null}\n {problem.tags.length ? (\n <div className=\"flex flex-wrap gap-1.5\">\n {problem.tags.slice(0, 3).map((t) => (\n <Badge key={t} variant=\"secondary\" className=\"text-[11px]\">\n <Tag size={10} className=\"mr-1\" />\n {t}\n </Badge>\n ))}\n </div>\n ) : null}\n </div>\n </GlassCard>\n );\n}\n\nexport function ApplicationsProblemsPagePage() {\n const navigate = useTechnoSpamNavigate();\n const { currentUser, workspaceId } = useTechnoSpam();\n const { emit } = useTechnoSpamAnalytics();\n const tr = useTr();\n const learnerId = currentUser?.id;\n const [difficulty, setDifficulty] = useState<'ALL' | TecProblemDifficulty>('ALL');\n const [company, setCompany] = useState<string>('ALL');\n const [role, setRole] = useState<string>('ALL');\n const [search, setSearch] = useState('');\n\n const difficulties = useMemo(\n () => [\n { value: 'ALL' as const, label: tr('technospam.problems.filterAll', 'All') },\n {\n value: 'EASY' as TecProblemDifficulty,\n label: tr('technospam.problems.filterEasy', 'Easy'),\n },\n {\n value: 'MEDIUM' as TecProblemDifficulty,\n label: tr('technospam.problems.filterMedium', 'Medium'),\n },\n {\n value: 'HARD' as TecProblemDifficulty,\n label: tr('technospam.problems.filterHard', 'Hard'),\n },\n ],\n [tr]\n );\n\n // Curated interview-target lists for the catalog filters. Values follow the\n // backend's lowercase-slug convention (see practice.service.ts normalizeSlug).\n const companies = useMemo(\n () => [\n { value: 'google', label: 'Google' },\n { value: 'amazon', label: 'Amazon' },\n { value: 'meta', label: 'Meta' },\n { value: 'microsoft', label: 'Microsoft' },\n { value: 'apple', label: 'Apple' },\n { value: 'netflix', label: 'Netflix' },\n ],\n []\n );\n\n const roles = useMemo(\n () => [\n { value: 'frontend', label: 'Frontend' },\n { value: 'backend', label: 'Backend' },\n { value: 'fullstack', label: 'Full-Stack' },\n { value: 'data-science', label: 'Data Science' },\n { value: 'mobile', label: 'Mobile' },\n { value: 'sre', label: 'SRE / Infra' },\n ],\n []\n );\n\n const { limit, more } = usePageLimit();\n const { data, loading, error } = useProblemsQuery({\n status: 'PUBLISHED',\n pagination: pageArg(limit),\n ...(difficulty !== 'ALL' ? { difficulty } : {}),\n ...(company !== 'ALL' ? { company } : {}),\n ...(role !== 'ALL' ? { role } : {}),\n });\n const { data: subsData } = useSubmissionsQuery(learnerId ? { learnerId } : {});\n\n const problems = data?.tecListProblems?.items ?? [];\n const hasMore = data?.tecListProblems?.pageInfo?.hasMore ?? false;\n\n const solvedSet = useMemo(\n () =>\n new Set(\n (subsData?.tecListSubmissions?.items ?? [])\n .filter((s) => s.verdict === 'ACCEPTED')\n .map((s) => s.problemId)\n ),\n [subsData]\n );\n\n const solvedCount = problems.filter((p) => solvedSet.has(p.id)).length;\n const easyCount = problems.filter((p) => p.difficulty === 'EASY').length;\n const hardCount = problems.filter((p) => p.difficulty === 'HARD').length;\n\n const filtered = useMemo(() => {\n const q = search.trim().toLowerCase();\n if (!q) return problems;\n return problems.filter(\n (p) =>\n p.title.toLowerCase().includes(q) ||\n (p.topic ?? '').toLowerCase().includes(q) ||\n p.tags.some((t) => t.toLowerCase().includes(q))\n );\n }, [problems, search]);\n\n return (\n <PageLayout\n hero\n title={tr('technospam.problems.title', 'Coding Problems')}\n description={tr(\n 'technospam.problems.description',\n 'Sharpen your problem-solving with hands-on, mentor-judged challenges across topics and difficulty.'\n )}\n >\n <div className=\"mb-5 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <StatCard\n label={tr('technospam.problems.statProblems', 'Problems')}\n value={problems.length}\n icon={Code2}\n tone=\"blue\"\n />\n <StatCard\n label={tr('technospam.problems.statSolved', 'Solved')}\n value={solvedCount}\n icon={CheckCircle2}\n tone=\"green\"\n />\n <StatCard\n label={tr('technospam.problems.statEasy', 'Easy')}\n value={easyCount}\n icon={Gauge}\n tone=\"teal\"\n />\n <StatCard\n label={tr('technospam.problems.statHard', 'Hard')}\n value={hardCount}\n icon={Flame}\n tone=\"amber\"\n />\n </div>\n\n {/* The tabs filter one list, so that list is their tab panel: Radix points every\n trigger's aria-controls at a panel id, and without a TabsContent the id\n resolved to nothing (axe aria-valid-attr-value, critical; BOFF-3157). */}\n <Tabs\n className=\"gap-0\"\n value={difficulty}\n onValueChange={(v) => setDifficulty(v as 'ALL' | TecProblemDifficulty)}\n >\n <div className=\"mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <TabsList>\n {difficulties.map((d) => (\n <TabsTrigger key={d.value} value={d.value}>\n {d.label}\n </TabsTrigger>\n ))}\n </TabsList>\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 aria-label={tr('technospam.problems.searchLabel', 'Search problems')}\n placeholder={tr('technospam.problems.searchPlaceholder', 'Search problems…')}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n onBlur={() => {\n const value = search.trim();\n if (value) {\n emit('technospam.search.performed', {\n workspaceId,\n entityType: 'problem',\n queryLength: value.length,\n });\n }\n }}\n />\n </div>\n </div>\n\n <div className=\"mb-4 flex flex-col gap-3 sm:flex-row sm:items-center\">\n <Select value={company} onValueChange={setCompany}>\n <SelectTrigger\n className=\"w-full sm:w-48\"\n aria-label={tr('technospam.problems.companyFilterLabel', 'Filter by company')}\n >\n <SelectValue placeholder={tr('technospam.problems.companyPlaceholder', 'Company')} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">\n {tr('technospam.problems.companyAll', 'All companies')}\n </SelectItem>\n {companies.map((c) => (\n <SelectItem key={c.value} value={c.value}>\n {c.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Select value={role} onValueChange={setRole}>\n <SelectTrigger\n className=\"w-full sm:w-48\"\n aria-label={tr('technospam.problems.roleFilterLabel', 'Filter by role')}\n >\n <SelectValue placeholder={tr('technospam.problems.rolePlaceholder', 'Role')} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">{tr('technospam.problems.roleAll', 'All roles')}</SelectItem>\n {roles.map((r) => (\n <SelectItem key={r.value} value={r.value}>\n {r.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <TabsContent value={difficulty}>\n {loading && !problems.length ? (\n <LoadingGrid />\n ) : error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadProblems', \"Couldn't load problems\")}\n error={error}\n />\n ) : filtered.length === 0 ? (\n <EmptyView\n icon={<Code2 size={26} />}\n title={\n search || difficulty !== 'ALL' || company !== 'ALL' || role !== 'ALL'\n ? tr('technospam.problems.emptySearchTitle', 'No matching problems')\n : tr('technospam.problems.emptyDefaultTitle', 'No problems published yet')\n }\n description={\n search || difficulty !== 'ALL' || company !== 'ALL' || role !== 'ALL'\n ? tr(\n 'technospam.problems.emptySearchDesc',\n 'Try a different search, difficulty, company, or role filter.'\n )\n : tr(\n 'technospam.problems.emptyDefaultDesc',\n 'Published problems will appear here. Check back soon — TechnoSpam is pre-launch.'\n )\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {filtered.map((problem) => (\n <ProblemCard\n key={problem.id}\n problem={problem}\n solved={solvedSet.has(problem.id)}\n onOpen={() => {\n emit('technospam.problem.viewed', {\n workspaceId,\n entityId: problem.id,\n entityType: 'TEC_PROBLEM',\n });\n navigate(`/applications/problem-details-page?id=${problem.id}`);\n }}\n />\n ))}\n </div>\n )}\n <LoadMore hasMore={hasMore} loading={loading} onClick={more} />\n </TabsContent>\n </Tabs>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ApplicationsProblemsPagePage.js","names":[],"sources":["../../../src/technospam/pages/ApplicationsProblemsPagePage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport {\n Building2,\n CheckCircle2,\n Code2,\n Flame,\n Gauge,\n Layers,\n Search,\n Tag,\n Trophy,\n} from 'lucide-react';\nimport {\n Badge,\n GlassCard,\n Input,\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport { TAB_PANEL_FOCUS_CLASS } from '../lib/a11y';\nimport {\n EmptyView,\n ErrorView,\n LoadMore,\n LoadingGrid,\n MediaCover,\n PageLayout,\n PremiumBadge,\n StatCard,\n StatusBadge,\n topicIcon,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n pageArg,\n usePageLimit,\n useProblemsQuery,\n useSubmissionsQuery,\n type TecProblem,\n type TecProblemDifficulty,\n} from '../api/technospamApi';\nimport { useTechnoSpamAnalytics } from '../hooks/useTechnoSpamAnalytics';\nimport { useTr } from '../hooks/useTr';\n\nfunction ProblemCard({\n problem,\n solved,\n onOpen,\n}: {\n problem: TecProblem;\n solved: boolean;\n onOpen: () => void;\n}) {\n const tr = useTr();\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={`${problem.topic ?? ''} ${problem.difficulty} ${problem.title}`}\n icon={topicIcon(problem.topic ?? problem.title)}\n >\n {solved ? (\n <span className=\"inline-flex items-center gap-1 rounded-full bg-status-success-bg-subtle px-2 py-0.5 text-[11px] font-medium text-status-success-text ring-1 ring-status-success-border/30\">\n <CheckCircle2 size={12} /> {tr('technospam.problems.solved', 'Solved')}\n </span>\n ) : (\n <span />\n )}\n <div className=\"flex flex-wrap items-center gap-1.5\">\n <StatusBadge status={problem.difficulty} kind=\"level\" />\n {problem.isPremiumOnly ? <PremiumBadge locked={problem.isLocked} /> : null}\n </div>\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 {problem.title}\n </h3>\n {problem.statement ? (\n <p className=\"mt-1 line-clamp-2 text-sm text-text-secondary\">{problem.statement}</p>\n ) : null}\n </div>\n <div className=\"mt-auto flex flex-wrap items-center gap-3 text-xs text-text-muted\">\n {problem.topic ? (\n <span className=\"inline-flex items-center gap-1\">\n <Layers size={13} /> {problem.topic}\n </span>\n ) : null}\n <span className=\"inline-flex items-center gap-1\">\n <Trophy size={13} />{' '}\n {tr('technospam.problems.points', '{{count}} pts', { count: problem.points })}\n </span>\n {problem.languages.length ? (\n <span className=\"inline-flex items-center gap-1\">\n <Code2 size={13} />{' '}\n {problem.languages.length === 1\n ? tr('technospam.problems.languages', '{{count}} lang', {\n count: problem.languages.length,\n })\n : tr('technospam.problems.languagesPlural', '{{count}} langs', {\n count: problem.languages.length,\n })}\n </span>\n ) : null}\n </div>\n {problem.companies.length ? (\n <div className=\"flex flex-wrap gap-1.5\">\n {problem.companies.slice(0, 3).map((c) => (\n <Badge key={c} variant=\"outline\" className=\"text-[11px] capitalize\">\n <Building2 size={10} className=\"mr-1\" />\n {c.replace(/-/g, ' ')}\n </Badge>\n ))}\n {problem.interviewFrequency === 'HIGH' ? (\n <Badge variant=\"secondary\" className=\"text-[11px] text-status-warning-text\">\n <Flame size={10} className=\"mr-1\" />\n {tr('technospam.problems.frequentlyAsked', 'Frequently asked')}\n </Badge>\n ) : null}\n </div>\n ) : null}\n {problem.tags.length ? (\n <div className=\"flex flex-wrap gap-1.5\">\n {problem.tags.slice(0, 3).map((t) => (\n <Badge key={t} variant=\"secondary\" className=\"text-[11px]\">\n <Tag size={10} className=\"mr-1\" />\n {t}\n </Badge>\n ))}\n </div>\n ) : null}\n </div>\n </GlassCard>\n );\n}\n\nexport function ApplicationsProblemsPagePage() {\n const navigate = useTechnoSpamNavigate();\n const { currentUser, workspaceId } = useTechnoSpam();\n const { emit } = useTechnoSpamAnalytics();\n const tr = useTr();\n const learnerId = currentUser?.id;\n const [difficulty, setDifficulty] = useState<'ALL' | TecProblemDifficulty>('ALL');\n const [company, setCompany] = useState<string>('ALL');\n const [role, setRole] = useState<string>('ALL');\n const [search, setSearch] = useState('');\n\n const difficulties = useMemo(\n () => [\n { value: 'ALL' as const, label: tr('technospam.problems.filterAll', 'All') },\n {\n value: 'EASY' as TecProblemDifficulty,\n label: tr('technospam.problems.filterEasy', 'Easy'),\n },\n {\n value: 'MEDIUM' as TecProblemDifficulty,\n label: tr('technospam.problems.filterMedium', 'Medium'),\n },\n {\n value: 'HARD' as TecProblemDifficulty,\n label: tr('technospam.problems.filterHard', 'Hard'),\n },\n ],\n [tr]\n );\n\n // Curated interview-target lists for the catalog filters. Values follow the\n // backend's lowercase-slug convention (see practice.service.ts normalizeSlug).\n const companies = useMemo(\n () => [\n { value: 'google', label: 'Google' },\n { value: 'amazon', label: 'Amazon' },\n { value: 'meta', label: 'Meta' },\n { value: 'microsoft', label: 'Microsoft' },\n { value: 'apple', label: 'Apple' },\n { value: 'netflix', label: 'Netflix' },\n ],\n []\n );\n\n const roles = useMemo(\n () => [\n { value: 'frontend', label: 'Frontend' },\n { value: 'backend', label: 'Backend' },\n { value: 'fullstack', label: 'Full-Stack' },\n { value: 'data-science', label: 'Data Science' },\n { value: 'mobile', label: 'Mobile' },\n { value: 'sre', label: 'SRE / Infra' },\n ],\n []\n );\n\n const { limit, more } = usePageLimit();\n const { data, loading, error } = useProblemsQuery({\n status: 'PUBLISHED',\n pagination: pageArg(limit),\n ...(difficulty !== 'ALL' ? { difficulty } : {}),\n ...(company !== 'ALL' ? { company } : {}),\n ...(role !== 'ALL' ? { role } : {}),\n });\n const { data: subsData } = useSubmissionsQuery(learnerId ? { learnerId } : {});\n\n const problems = data?.tecListProblems?.items ?? [];\n const hasMore = data?.tecListProblems?.pageInfo?.hasMore ?? false;\n\n const solvedSet = useMemo(\n () =>\n new Set(\n (subsData?.tecListSubmissions?.items ?? [])\n .filter((s) => s.verdict === 'ACCEPTED')\n .map((s) => s.problemId)\n ),\n [subsData]\n );\n\n const solvedCount = problems.filter((p) => solvedSet.has(p.id)).length;\n const easyCount = problems.filter((p) => p.difficulty === 'EASY').length;\n const hardCount = problems.filter((p) => p.difficulty === 'HARD').length;\n\n const filtered = useMemo(() => {\n const q = search.trim().toLowerCase();\n if (!q) return problems;\n return problems.filter(\n (p) =>\n p.title.toLowerCase().includes(q) ||\n (p.topic ?? '').toLowerCase().includes(q) ||\n p.tags.some((t) => t.toLowerCase().includes(q))\n );\n }, [problems, search]);\n\n return (\n <PageLayout\n hero\n title={tr('technospam.problems.title', 'Coding Problems')}\n description={tr(\n 'technospam.problems.description',\n 'Sharpen your problem-solving with hands-on, mentor-judged challenges across topics and difficulty.'\n )}\n >\n <div className=\"mb-5 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <StatCard\n label={tr('technospam.problems.statProblems', 'Problems')}\n value={problems.length}\n icon={Code2}\n tone=\"blue\"\n />\n <StatCard\n label={tr('technospam.problems.statSolved', 'Solved')}\n value={solvedCount}\n icon={CheckCircle2}\n tone=\"green\"\n />\n <StatCard\n label={tr('technospam.problems.statEasy', 'Easy')}\n value={easyCount}\n icon={Gauge}\n tone=\"teal\"\n />\n <StatCard\n label={tr('technospam.problems.statHard', 'Hard')}\n value={hardCount}\n icon={Flame}\n tone=\"amber\"\n />\n </div>\n\n {/* The tabs filter one list, so that list is their tab panel: Radix points every\n trigger's aria-controls at a panel id, and without a TabsContent the id\n resolved to nothing (axe aria-valid-attr-value, critical; BOFF-3157). */}\n <Tabs\n className=\"gap-0\"\n value={difficulty}\n onValueChange={(v) => setDifficulty(v as 'ALL' | TecProblemDifficulty)}\n >\n <div className=\"mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <TabsList>\n {difficulties.map((d) => (\n <TabsTrigger key={d.value} value={d.value}>\n {d.label}\n </TabsTrigger>\n ))}\n </TabsList>\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 aria-label={tr('technospam.problems.searchLabel', 'Search problems')}\n placeholder={tr('technospam.problems.searchPlaceholder', 'Search problems…')}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n onBlur={() => {\n const value = search.trim();\n if (value) {\n emit('technospam.search.performed', {\n workspaceId,\n entityType: 'problem',\n queryLength: value.length,\n });\n }\n }}\n />\n </div>\n </div>\n\n <div className=\"mb-4 flex flex-col gap-3 sm:flex-row sm:items-center\">\n <Select value={company} onValueChange={setCompany}>\n <SelectTrigger\n className=\"w-full sm:w-48\"\n aria-label={tr('technospam.problems.companyFilterLabel', 'Filter by company')}\n >\n <SelectValue placeholder={tr('technospam.problems.companyPlaceholder', 'Company')} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">\n {tr('technospam.problems.companyAll', 'All companies')}\n </SelectItem>\n {companies.map((c) => (\n <SelectItem key={c.value} value={c.value}>\n {c.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n <Select value={role} onValueChange={setRole}>\n <SelectTrigger\n className=\"w-full sm:w-48\"\n aria-label={tr('technospam.problems.roleFilterLabel', 'Filter by role')}\n >\n <SelectValue placeholder={tr('technospam.problems.rolePlaceholder', 'Role')} />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">{tr('technospam.problems.roleAll', 'All roles')}</SelectItem>\n {roles.map((r) => (\n <SelectItem key={r.value} value={r.value}>\n {r.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </div>\n\n <TabsContent value={difficulty} className={TAB_PANEL_FOCUS_CLASS}>\n {loading && !problems.length ? (\n <LoadingGrid />\n ) : error ? (\n <ErrorView\n title={tr('technospam.common.errorLoadProblems', \"Couldn't load problems\")}\n error={error}\n />\n ) : filtered.length === 0 ? (\n <EmptyView\n icon={<Code2 size={26} />}\n title={\n search || difficulty !== 'ALL' || company !== 'ALL' || role !== 'ALL'\n ? tr('technospam.problems.emptySearchTitle', 'No matching problems')\n : tr('technospam.problems.emptyDefaultTitle', 'No problems published yet')\n }\n description={\n search || difficulty !== 'ALL' || company !== 'ALL' || role !== 'ALL'\n ? tr(\n 'technospam.problems.emptySearchDesc',\n 'Try a different search, difficulty, company, or role filter.'\n )\n : tr(\n 'technospam.problems.emptyDefaultDesc',\n 'Published problems will appear here. Check back soon — TechnoSpam is pre-launch.'\n )\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {filtered.map((problem) => (\n <ProblemCard\n key={problem.id}\n problem={problem}\n solved={solvedSet.has(problem.id)}\n onOpen={() => {\n emit('technospam.problem.viewed', {\n workspaceId,\n entityId: problem.id,\n entityType: 'TEC_PROBLEM',\n });\n navigate(`/applications/problem-details-page?id=${problem.id}`);\n }}\n />\n ))}\n </div>\n )}\n <LoadMore hasMore={hasMore} loading={loading} onClick={more} />\n </TabsContent>\n </Tabs>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CoursesCoursesPagePage.d.ts","sourceRoot":"","sources":["../../../src/technospam/pages/CoursesCoursesPagePage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"CoursesCoursesPagePage.d.ts","sourceRoot":"","sources":["../../../src/technospam/pages/CoursesCoursesPagePage.tsx"],"names":[],"mappings":"AAqFA,wBAAgB,sBAAsB,gCAqLrC"}
|
|
@@ -11,59 +11,60 @@ import { EmptyView as d, ErrorView as f, LoadingGrid as p } from "../components/
|
|
|
11
11
|
import { PremiumBadge as m } from "../components/UpgradeGate.js";
|
|
12
12
|
import { pageArg as h, useCoursesQuery as g, usePageLimit as _ } from "../api/technospamApi.js";
|
|
13
13
|
import "../components/index.js";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
16
|
-
import {
|
|
17
|
-
import {
|
|
14
|
+
import { TAB_PANEL_FOCUS_CLASS as v } from "../lib/a11y.js";
|
|
15
|
+
import { useMemo as y, useState as b } from "react";
|
|
16
|
+
import { BookOpen as x, Clock as S, GraduationCap as C, Layers as w, Search as T, Sparkles as E } from "lucide-react";
|
|
17
|
+
import { jsx as D, jsxs as O } from "react/jsx-runtime";
|
|
18
|
+
import { Badge as k, Button as A, GlassCard as j, Input as M, Tabs as N, TabsContent as P, TabsList as F, TabsTrigger as I } from "@burdenoff/fe-libs/ui";
|
|
18
19
|
//#region src/technospam/pages/CoursesCoursesPagePage.tsx
|
|
19
|
-
function
|
|
20
|
-
return /* @__PURE__ */
|
|
20
|
+
function L({ course: e, onOpen: t }) {
|
|
21
|
+
return /* @__PURE__ */ O(j, {
|
|
21
22
|
className: "group flex cursor-pointer flex-col overflow-hidden transition-all duration-200 hover:-translate-y-1 hover:shadow-lg",
|
|
22
23
|
onClick: t,
|
|
23
|
-
children: [/* @__PURE__ */
|
|
24
|
+
children: [/* @__PURE__ */ D(o, {
|
|
24
25
|
seed: `${e.topic ?? ""} ${e.title}`,
|
|
25
26
|
icon: s(e.topic ?? e.title),
|
|
26
|
-
children: /* @__PURE__ */
|
|
27
|
+
children: /* @__PURE__ */ O("div", {
|
|
27
28
|
className: "flex flex-wrap items-center gap-1.5",
|
|
28
|
-
children: [/* @__PURE__ */
|
|
29
|
+
children: [/* @__PURE__ */ D(l, {
|
|
29
30
|
status: e.level,
|
|
30
31
|
kind: "level"
|
|
31
|
-
}), e.isPremiumOnly ? /* @__PURE__ */
|
|
32
|
+
}), e.isPremiumOnly ? /* @__PURE__ */ D(m, { locked: e.isLocked }) : null]
|
|
32
33
|
})
|
|
33
|
-
}), /* @__PURE__ */
|
|
34
|
+
}), /* @__PURE__ */ O("div", {
|
|
34
35
|
className: "flex flex-1 flex-col gap-3 p-4",
|
|
35
36
|
children: [
|
|
36
|
-
/* @__PURE__ */
|
|
37
|
+
/* @__PURE__ */ O("div", {
|
|
37
38
|
className: "min-w-0",
|
|
38
|
-
children: [/* @__PURE__ */
|
|
39
|
+
children: [/* @__PURE__ */ D("h3", {
|
|
39
40
|
className: "truncate text-base font-semibold text-text-primary group-hover:text-accent-primary",
|
|
40
41
|
children: e.title
|
|
41
|
-
}), e.summary ? /* @__PURE__ */
|
|
42
|
+
}), e.summary ? /* @__PURE__ */ D("p", {
|
|
42
43
|
className: "mt-1 line-clamp-2 text-sm text-text-secondary",
|
|
43
44
|
children: e.summary
|
|
44
45
|
}) : null]
|
|
45
46
|
}),
|
|
46
|
-
/* @__PURE__ */
|
|
47
|
+
/* @__PURE__ */ O("div", {
|
|
47
48
|
className: "mt-auto flex flex-wrap items-center gap-3 text-xs text-text-muted",
|
|
48
|
-
children: [e.topic ? /* @__PURE__ */
|
|
49
|
+
children: [e.topic ? /* @__PURE__ */ O("span", {
|
|
49
50
|
className: "inline-flex items-center gap-1",
|
|
50
51
|
children: [
|
|
51
|
-
/* @__PURE__ */
|
|
52
|
+
/* @__PURE__ */ D(w, { size: 13 }),
|
|
52
53
|
" ",
|
|
53
54
|
e.topic
|
|
54
55
|
]
|
|
55
|
-
}) : null, /* @__PURE__ */
|
|
56
|
+
}) : null, /* @__PURE__ */ O("span", {
|
|
56
57
|
className: "inline-flex items-center gap-1",
|
|
57
58
|
children: [
|
|
58
|
-
/* @__PURE__ */
|
|
59
|
+
/* @__PURE__ */ D(S, { size: 13 }),
|
|
59
60
|
" ",
|
|
60
61
|
c(e.estimatedMinutes)
|
|
61
62
|
]
|
|
62
63
|
})]
|
|
63
64
|
}),
|
|
64
|
-
e.tags.length ? /* @__PURE__ */
|
|
65
|
+
e.tags.length ? /* @__PURE__ */ D("div", {
|
|
65
66
|
className: "flex flex-wrap gap-1.5",
|
|
66
|
-
children: e.tags.slice(0, 3).map((e) => /* @__PURE__ */
|
|
67
|
+
children: e.tags.slice(0, 3).map((e) => /* @__PURE__ */ D(k, {
|
|
67
68
|
variant: "secondary",
|
|
68
69
|
className: "text-[11px]",
|
|
69
70
|
children: e
|
|
@@ -73,8 +74,8 @@ function I({ course: e, onOpen: t }) {
|
|
|
73
74
|
})]
|
|
74
75
|
});
|
|
75
76
|
}
|
|
76
|
-
function
|
|
77
|
-
let o = t(), { workspaceId: s } = e(), { emit: l } = n(), m = r(), [
|
|
77
|
+
function R() {
|
|
78
|
+
let o = t(), { workspaceId: s } = e(), { emit: l } = n(), m = r(), [w, k] = b("ALL"), [j, R] = b(""), z = y(() => [
|
|
78
79
|
{
|
|
79
80
|
value: "ALL",
|
|
80
81
|
label: m("technospam.courses.filterAllLevels", "All levels")
|
|
@@ -95,77 +96,77 @@ function L() {
|
|
|
95
96
|
value: "EXPERT",
|
|
96
97
|
label: m("technospam.courses.filterExpert", "Expert")
|
|
97
98
|
}
|
|
98
|
-
], [m]), { limit:
|
|
99
|
+
], [m]), { limit: B, more: V } = _(), { data: H, loading: U, error: W } = g({
|
|
99
100
|
status: "PUBLISHED",
|
|
100
|
-
pagination: h(
|
|
101
|
-
...
|
|
102
|
-
}),
|
|
103
|
-
let e =
|
|
104
|
-
return e ?
|
|
105
|
-
}, [
|
|
106
|
-
return /* @__PURE__ */
|
|
101
|
+
pagination: h(B),
|
|
102
|
+
...w === "ALL" ? {} : { level: w }
|
|
103
|
+
}), G = H?.tecListCourses?.items ?? [], K = H?.tecListCourses?.pageInfo?.hasMore ?? !1, q = y(() => {
|
|
104
|
+
let e = j.trim().toLowerCase();
|
|
105
|
+
return e ? G.filter((t) => t.title.toLowerCase().includes(e) || (t.topic ?? "").toLowerCase().includes(e) || t.tags.some((t) => t.toLowerCase().includes(e))) : G;
|
|
106
|
+
}, [G, j]);
|
|
107
|
+
return /* @__PURE__ */ O(i, {
|
|
107
108
|
hero: !0,
|
|
108
109
|
title: m("technospam.courses.title", "Course Catalog"),
|
|
109
110
|
description: m("technospam.courses.description", "Learn technology by doing — practical, project-based courses across the stack."),
|
|
110
|
-
actions: /* @__PURE__ */
|
|
111
|
+
actions: /* @__PURE__ */ O(A, {
|
|
111
112
|
variant: "outline",
|
|
112
113
|
onClick: () => o("/courses/my-learning-page"),
|
|
113
114
|
children: [
|
|
114
|
-
/* @__PURE__ */
|
|
115
|
+
/* @__PURE__ */ D(C, { size: 16 }),
|
|
115
116
|
" ",
|
|
116
117
|
m("technospam.common.myLearning", "My Learning")
|
|
117
118
|
]
|
|
118
119
|
}),
|
|
119
|
-
children: [/* @__PURE__ */
|
|
120
|
+
children: [/* @__PURE__ */ O("div", {
|
|
120
121
|
className: "mb-5 grid grid-cols-2 gap-3 sm:grid-cols-4",
|
|
121
122
|
children: [
|
|
122
|
-
/* @__PURE__ */
|
|
123
|
+
/* @__PURE__ */ D(a, {
|
|
123
124
|
label: m("technospam.courses.statCourses", "Courses"),
|
|
124
|
-
value:
|
|
125
|
-
icon:
|
|
125
|
+
value: G.length,
|
|
126
|
+
icon: x,
|
|
126
127
|
tone: "blue"
|
|
127
128
|
}),
|
|
128
|
-
/* @__PURE__ */
|
|
129
|
+
/* @__PURE__ */ D(a, {
|
|
129
130
|
label: m("technospam.courses.statBeginnerFriendly", "Beginner-friendly"),
|
|
130
|
-
value:
|
|
131
|
-
icon:
|
|
131
|
+
value: G.filter((e) => e.level === "BEGINNER").length,
|
|
132
|
+
icon: E,
|
|
132
133
|
tone: "green"
|
|
133
134
|
}),
|
|
134
|
-
/* @__PURE__ */
|
|
135
|
+
/* @__PURE__ */ D(a, {
|
|
135
136
|
label: m("technospam.courses.statAdvancedPlus", "Advanced+"),
|
|
136
|
-
value:
|
|
137
|
-
icon:
|
|
137
|
+
value: G.filter((e) => e.level === "ADVANCED" || e.level === "EXPERT").length,
|
|
138
|
+
icon: C,
|
|
138
139
|
tone: "purple"
|
|
139
140
|
}),
|
|
140
|
-
/* @__PURE__ */
|
|
141
|
+
/* @__PURE__ */ D(a, {
|
|
141
142
|
label: m("technospam.courses.statTotalContent", "Total content"),
|
|
142
|
-
value: c(
|
|
143
|
-
icon:
|
|
143
|
+
value: c(G.reduce((e, t) => e + t.estimatedMinutes, 0)),
|
|
144
|
+
icon: S,
|
|
144
145
|
tone: "teal"
|
|
145
146
|
})
|
|
146
147
|
]
|
|
147
|
-
}), /* @__PURE__ */
|
|
148
|
+
}), /* @__PURE__ */ O(N, {
|
|
148
149
|
className: "gap-0",
|
|
149
|
-
value:
|
|
150
|
-
onValueChange: (e) =>
|
|
151
|
-
children: [/* @__PURE__ */
|
|
150
|
+
value: w,
|
|
151
|
+
onValueChange: (e) => k(e),
|
|
152
|
+
children: [/* @__PURE__ */ O("div", {
|
|
152
153
|
className: "mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",
|
|
153
|
-
children: [/* @__PURE__ */
|
|
154
|
+
children: [/* @__PURE__ */ D(F, { children: z.map((e) => /* @__PURE__ */ D(I, {
|
|
154
155
|
value: e.value,
|
|
155
156
|
children: e.label
|
|
156
|
-
}, e.value)) }), /* @__PURE__ */
|
|
157
|
+
}, e.value)) }), /* @__PURE__ */ O("div", {
|
|
157
158
|
className: "relative w-full sm:w-72",
|
|
158
|
-
children: [/* @__PURE__ */
|
|
159
|
+
children: [/* @__PURE__ */ D(T, {
|
|
159
160
|
className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-text-muted",
|
|
160
161
|
size: 16
|
|
161
|
-
}), /* @__PURE__ */
|
|
162
|
+
}), /* @__PURE__ */ D(M, {
|
|
162
163
|
className: "pl-9",
|
|
163
164
|
"aria-label": m("technospam.courses.searchLabel", "Search courses"),
|
|
164
165
|
placeholder: m("technospam.courses.searchPlaceholder", "Search courses…"),
|
|
165
|
-
value:
|
|
166
|
-
onChange: (e) =>
|
|
166
|
+
value: j,
|
|
167
|
+
onChange: (e) => R(e.target.value),
|
|
167
168
|
onBlur: () => {
|
|
168
|
-
let e =
|
|
169
|
+
let e = j.trim();
|
|
169
170
|
e && l("technospam.search.performed", {
|
|
170
171
|
workspaceId: s,
|
|
171
172
|
entityType: "course",
|
|
@@ -174,18 +175,19 @@ function L() {
|
|
|
174
175
|
}
|
|
175
176
|
})]
|
|
176
177
|
})]
|
|
177
|
-
}), /* @__PURE__ */
|
|
178
|
-
value:
|
|
179
|
-
|
|
178
|
+
}), /* @__PURE__ */ O(P, {
|
|
179
|
+
value: w,
|
|
180
|
+
className: v,
|
|
181
|
+
children: [U && !G.length ? /* @__PURE__ */ D(p, {}) : W ? /* @__PURE__ */ D(f, {
|
|
180
182
|
title: m("technospam.common.errorLoadCourses", "Couldn't load courses"),
|
|
181
|
-
error:
|
|
182
|
-
}) :
|
|
183
|
-
icon: /* @__PURE__ */
|
|
184
|
-
title:
|
|
185
|
-
description:
|
|
186
|
-
}) : /* @__PURE__ */
|
|
183
|
+
error: W
|
|
184
|
+
}) : q.length === 0 ? /* @__PURE__ */ D(d, {
|
|
185
|
+
icon: /* @__PURE__ */ D(x, { size: 26 }),
|
|
186
|
+
title: j || w !== "ALL" ? m("technospam.courses.emptySearchTitle", "No matching courses") : m("technospam.courses.emptyDefaultTitle", "No courses published yet"),
|
|
187
|
+
description: j || w !== "ALL" ? m("technospam.courses.emptySearchDesc", "Try a different search or level filter.") : m("technospam.courses.emptyDefaultDesc", "Published courses will appear here. Check back soon — TechnoSpam is pre-launch.")
|
|
188
|
+
}) : /* @__PURE__ */ D("div", {
|
|
187
189
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
|
|
188
|
-
children:
|
|
190
|
+
children: q.map((e) => /* @__PURE__ */ D(L, {
|
|
189
191
|
course: e,
|
|
190
192
|
onOpen: () => {
|
|
191
193
|
l("technospam.course.viewed", {
|
|
@@ -195,16 +197,16 @@ function L() {
|
|
|
195
197
|
}), o(`/courses/course-details-page?id=${e.id}`);
|
|
196
198
|
}
|
|
197
199
|
}, e.id))
|
|
198
|
-
}), /* @__PURE__ */
|
|
199
|
-
hasMore:
|
|
200
|
-
loading:
|
|
201
|
-
onClick:
|
|
200
|
+
}), /* @__PURE__ */ D(u, {
|
|
201
|
+
hasMore: K,
|
|
202
|
+
loading: U,
|
|
203
|
+
onClick: V
|
|
202
204
|
})]
|
|
203
205
|
})]
|
|
204
206
|
})]
|
|
205
207
|
});
|
|
206
208
|
}
|
|
207
209
|
//#endregion
|
|
208
|
-
export {
|
|
210
|
+
export { R as CoursesCoursesPagePage };
|
|
209
211
|
|
|
210
212
|
//# sourceMappingURL=CoursesCoursesPagePage.js.map
|