@burdenoff/microfe-technospam 2026.1102.2 → 2026.1102.3
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/TechnoSpamRoot.d.ts.map +1 -1
- package/dist/technospam/TechnoSpamRoot.js +187 -178
- package/dist/technospam/TechnoSpamRoot.js.map +1 -1
- package/dist/technospam/api/technospamApi.js +141 -141
- package/dist/technospam/api/technospamApi.js.map +1 -1
- package/dist/technospam/components/MediaCover.js +2 -2
- package/dist/technospam/components/MediaCover.js.map +1 -1
- package/dist/technospam/components/MobileSectionTabs.d.ts +9 -0
- package/dist/technospam/components/MobileSectionTabs.d.ts.map +1 -0
- package/dist/technospam/components/MobileSectionTabs.js +43 -0
- package/dist/technospam/components/MobileSectionTabs.js.map +1 -0
- package/dist/technospam/components/PageLayout.d.ts +7 -1
- package/dist/technospam/components/PageLayout.d.ts.map +1 -1
- package/dist/technospam/components/PageLayout.js +40 -28
- package/dist/technospam/components/PageLayout.js.map +1 -1
- package/dist/technospam/components/lesson/SaveCourseOffline.js +6 -6
- package/dist/technospam/hooks/usePhoneLayout.d.ts +18 -0
- package/dist/technospam/hooks/usePhoneLayout.d.ts.map +1 -0
- package/dist/technospam/hooks/usePhoneLayout.js +14 -0
- package/dist/technospam/hooks/usePhoneLayout.js.map +1 -0
- package/dist/technospam/pages/AdminAdminDashboardPagePage.js +1 -1
- 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 +89 -87
- package/dist/technospam/pages/ApplicationsProblemsPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesCourseDetailsPagePage.js +3 -3
- package/dist/technospam/pages/CoursesCourseDetailsPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesCoursesPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/CoursesCoursesPagePage.js +117 -81
- package/dist/technospam/pages/CoursesCoursesPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesMyLearningPagePage.js +1 -1
- package/dist/technospam/pages/CoursesMyLearningPagePage.js.map +1 -1
- package/dist/technospam/pages/DashboardDashboardPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/DashboardDashboardPagePage.js +21 -17
- package/dist/technospam/pages/DashboardDashboardPagePage.js.map +1 -1
- package/dist/technospam/pages/HackathonsHackathonDetailsPagePage.js +3 -3
- package/dist/technospam/pages/HackathonsHackathonDetailsPagePage.js.map +1 -1
- package/dist/technospam/pages/MentorshipMentorProfilePagePage.js +4 -4
- package/dist/technospam/pages/MentorshipMentorProfilePagePage.js.map +1 -1
- package/dist/technospam/pages/MentorshipMentorsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/MentorshipMentorsPagePage.js +6 -4
- package/dist/technospam/pages/MentorshipMentorsPagePage.js.map +1 -1
- package/dist/technospam/pages/MentorshipSessionsPagePage.js +1 -1
- package/dist/technospam/pages/MentorshipSessionsPagePage.js.map +1 -1
- package/dist/technospam/types/index.d.ts +7 -0
- package/dist/technospam/types/index.d.ts.map +1 -1
- package/dist/technospam/utils/mobileTabs.d.ts +45 -0
- package/dist/technospam/utils/mobileTabs.d.ts.map +1 -0
- package/dist/technospam/utils/mobileTabs.js +70 -0
- package/dist/technospam/utils/mobileTabs.js.map +1 -0
- package/dist/test/matchMedia.d.ts +6 -0
- package/dist/test/matchMedia.d.ts.map +1 -0
- package/package.json +3 -3
|
@@ -1,26 +1,27 @@
|
|
|
1
1
|
import { useTechnoSpam as e, useTechnoSpamNavigate as t } from "../providers/TechnoSpamProvider.js";
|
|
2
2
|
import { useTechnoSpamAnalytics as n } from "../hooks/useTechnoSpamAnalytics.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { usePhoneLayout as r } from "../hooks/usePhoneLayout.js";
|
|
4
|
+
import { useTr as i } from "../hooks/useTr.js";
|
|
5
|
+
import { PageLayout as ee } from "../components/PageLayout.js";
|
|
5
6
|
import { StatCard as a } from "../components/StatCard.js";
|
|
6
7
|
import { MediaCover as o, topicIcon as s } from "../components/MediaCover.js";
|
|
7
8
|
import { StatusBadge as c } from "../components/StatusBadge.js";
|
|
8
|
-
import { readProblemCatalogView as l, safeLocalStorage as u, writeProblemCatalogView as
|
|
9
|
-
import { PremiumBadge as
|
|
10
|
-
import { ProblemsTable as
|
|
11
|
-
import { LoadMore as
|
|
12
|
-
import { EmptyView as
|
|
13
|
-
import { pageArg as
|
|
14
|
-
import { CardOpenButton as
|
|
9
|
+
import { readProblemCatalogView as l, safeLocalStorage as u, writeProblemCatalogView as te } from "../utils/problemCatalog.js";
|
|
10
|
+
import { PremiumBadge as d } from "../components/UpgradeGate.js";
|
|
11
|
+
import { ProblemsTable as ne } from "../components/ProblemsTable.js";
|
|
12
|
+
import { LoadMore as re } from "../components/LoadMore.js";
|
|
13
|
+
import { EmptyView as f, ErrorView as ie, LoadingGrid as ae } from "../components/StateViews.js";
|
|
14
|
+
import { pageArg as oe, usePageLimit as se, useProblemsQuery as p, useSolvedProblemIds as m } from "../api/technospamApi.js";
|
|
15
|
+
import { CardOpenButton as h } from "../components/CardOpenButton.js";
|
|
15
16
|
import "../components/index.js";
|
|
16
17
|
import { TAB_PANEL_FOCUS_CLASS as ce } from "../lib/a11y.js";
|
|
17
|
-
import { useCallback as
|
|
18
|
-
import { Building2 as
|
|
18
|
+
import { useCallback as g, useMemo as _, useState as v } from "react";
|
|
19
|
+
import { Building2 as y, CheckCircle2 as b, Code2 as x, Flame as S, Gauge as C, Layers as w, LayoutGrid as le, List as ue, Search as de, Tag as T, Trophy as E } from "lucide-react";
|
|
19
20
|
import { jsx as D, jsxs as O } from "react/jsx-runtime";
|
|
20
|
-
import { Badge as k, Button as A, GlassCard as j, Input as
|
|
21
|
+
import { Badge as k, Button as A, GlassCard as j, Input as fe, Select as M, SelectContent as N, SelectItem as P, SelectTrigger as F, SelectValue as I, Tabs as L, TabsContent as R, TabsList as z, TabsTrigger as B } from "@burdenoff/fe-libs/ui";
|
|
21
22
|
//#region src/technospam/pages/ApplicationsProblemsPagePage.tsx
|
|
22
23
|
function V({ problem: e, solved: t, onOpen: n }) {
|
|
23
|
-
let
|
|
24
|
+
let r = i();
|
|
24
25
|
return /* @__PURE__ */ O(j, {
|
|
25
26
|
className: "group flex cursor-pointer flex-col overflow-hidden transition-all duration-200 hover:-translate-y-1 hover:shadow-lg",
|
|
26
27
|
children: [/* @__PURE__ */ O(o, {
|
|
@@ -29,16 +30,16 @@ function V({ problem: e, solved: t, onOpen: n }) {
|
|
|
29
30
|
children: [t ? /* @__PURE__ */ O("span", {
|
|
30
31
|
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",
|
|
31
32
|
children: [
|
|
32
|
-
/* @__PURE__ */ D(
|
|
33
|
+
/* @__PURE__ */ D(b, { size: 12 }),
|
|
33
34
|
" ",
|
|
34
|
-
|
|
35
|
+
r("technospam.problems.solved", "Solved")
|
|
35
36
|
]
|
|
36
37
|
}) : /* @__PURE__ */ D("span", {}), /* @__PURE__ */ O("div", {
|
|
37
38
|
className: "flex flex-wrap items-center gap-1.5",
|
|
38
39
|
children: [/* @__PURE__ */ D(c, {
|
|
39
40
|
status: e.difficulty,
|
|
40
41
|
kind: "level"
|
|
41
|
-
}), e.isPremiumOnly ? /* @__PURE__ */ D(
|
|
42
|
+
}), e.isPremiumOnly ? /* @__PURE__ */ D(d, { locked: e.isLocked }) : null]
|
|
42
43
|
})]
|
|
43
44
|
}), /* @__PURE__ */ O("div", {
|
|
44
45
|
className: "flex flex-1 flex-col gap-3 p-4",
|
|
@@ -47,7 +48,7 @@ function V({ problem: e, solved: t, onOpen: n }) {
|
|
|
47
48
|
className: "min-w-0",
|
|
48
49
|
children: [/* @__PURE__ */ D("h3", {
|
|
49
50
|
className: "truncate text-base font-semibold text-text-primary group-hover:text-accent-primary",
|
|
50
|
-
children: /* @__PURE__ */ D(
|
|
51
|
+
children: /* @__PURE__ */ D(h, {
|
|
51
52
|
onOpen: n,
|
|
52
53
|
children: e.title
|
|
53
54
|
})
|
|
@@ -62,7 +63,7 @@ function V({ problem: e, solved: t, onOpen: n }) {
|
|
|
62
63
|
e.topic ? /* @__PURE__ */ O("span", {
|
|
63
64
|
className: "inline-flex items-center gap-1",
|
|
64
65
|
children: [
|
|
65
|
-
/* @__PURE__ */ D(
|
|
66
|
+
/* @__PURE__ */ D(w, { size: 13 }),
|
|
66
67
|
" ",
|
|
67
68
|
e.topic
|
|
68
69
|
]
|
|
@@ -72,15 +73,15 @@ function V({ problem: e, solved: t, onOpen: n }) {
|
|
|
72
73
|
children: [
|
|
73
74
|
/* @__PURE__ */ D(E, { size: 13 }),
|
|
74
75
|
" ",
|
|
75
|
-
|
|
76
|
+
r("technospam.problems.points", "{{count}} pts", { count: e.points })
|
|
76
77
|
]
|
|
77
78
|
}),
|
|
78
79
|
e.languages.length ? /* @__PURE__ */ O("span", {
|
|
79
80
|
className: "inline-flex items-center gap-1",
|
|
80
81
|
children: [
|
|
81
|
-
/* @__PURE__ */ D(
|
|
82
|
+
/* @__PURE__ */ D(x, { size: 13 }),
|
|
82
83
|
" ",
|
|
83
|
-
e.languages.length === 1 ?
|
|
84
|
+
e.languages.length === 1 ? r("technospam.problems.languages", "{{count}} lang", { count: e.languages.length }) : r("technospam.problems.languagesPlural", "{{count}} langs", { count: e.languages.length })
|
|
84
85
|
]
|
|
85
86
|
}) : null
|
|
86
87
|
]
|
|
@@ -90,17 +91,17 @@ function V({ problem: e, solved: t, onOpen: n }) {
|
|
|
90
91
|
children: [e.companies.slice(0, 3).map((e) => /* @__PURE__ */ O(k, {
|
|
91
92
|
variant: "outline",
|
|
92
93
|
className: "text-[11px] capitalize",
|
|
93
|
-
children: [/* @__PURE__ */ D(
|
|
94
|
+
children: [/* @__PURE__ */ D(y, {
|
|
94
95
|
size: 10,
|
|
95
96
|
className: "mr-1"
|
|
96
97
|
}), e.replace(/-/g, " ")]
|
|
97
98
|
}, e)), e.interviewFrequency === "HIGH" ? /* @__PURE__ */ O(k, {
|
|
98
99
|
variant: "secondary",
|
|
99
100
|
className: "text-[11px] text-status-warning-text",
|
|
100
|
-
children: [/* @__PURE__ */ D(
|
|
101
|
+
children: [/* @__PURE__ */ D(S, {
|
|
101
102
|
size: 10,
|
|
102
103
|
className: "mr-1"
|
|
103
|
-
}),
|
|
104
|
+
}), r("technospam.problems.frequentlyAsked", "Frequently asked")]
|
|
104
105
|
}) : null]
|
|
105
106
|
}) : null,
|
|
106
107
|
e.tags.length ? /* @__PURE__ */ D("div", {
|
|
@@ -119,26 +120,26 @@ function V({ problem: e, solved: t, onOpen: n }) {
|
|
|
119
120
|
});
|
|
120
121
|
}
|
|
121
122
|
function H() {
|
|
122
|
-
let o = t(), { currentUser: s, workspaceId: c } = e(), { emit:
|
|
123
|
-
|
|
124
|
-
}, []),
|
|
123
|
+
let o = t(), { currentUser: s, workspaceId: c } = e(), { emit: d } = n(), h = i(), y = s?.id, [w, T] = v("ALL"), [E, k] = v("ALL"), [j, H] = v("ALL"), [U, pe] = v(""), [W, me] = v(() => l(u())), { isPhone: he } = r(), ge = he ? "cards" : W, G = g((e) => {
|
|
124
|
+
me(e), te(u(), e);
|
|
125
|
+
}, []), _e = _(() => [
|
|
125
126
|
{
|
|
126
127
|
value: "ALL",
|
|
127
|
-
label:
|
|
128
|
+
label: h("technospam.problems.filterAll", "All")
|
|
128
129
|
},
|
|
129
130
|
{
|
|
130
131
|
value: "EASY",
|
|
131
|
-
label:
|
|
132
|
+
label: h("technospam.problems.filterEasy", "Easy")
|
|
132
133
|
},
|
|
133
134
|
{
|
|
134
135
|
value: "MEDIUM",
|
|
135
|
-
label:
|
|
136
|
+
label: h("technospam.problems.filterMedium", "Medium")
|
|
136
137
|
},
|
|
137
138
|
{
|
|
138
139
|
value: "HARD",
|
|
139
|
-
label:
|
|
140
|
+
label: h("technospam.problems.filterHard", "Hard")
|
|
140
141
|
}
|
|
141
|
-
], [
|
|
142
|
+
], [h]), ve = _(() => [
|
|
142
143
|
{
|
|
143
144
|
value: "google",
|
|
144
145
|
label: "Google"
|
|
@@ -163,7 +164,7 @@ function H() {
|
|
|
163
164
|
value: "netflix",
|
|
164
165
|
label: "Netflix"
|
|
165
166
|
}
|
|
166
|
-
], []),
|
|
167
|
+
], []), ye = _(() => [
|
|
167
168
|
{
|
|
168
169
|
value: "frontend",
|
|
169
170
|
label: "Frontend"
|
|
@@ -188,82 +189,83 @@ function H() {
|
|
|
188
189
|
value: "sre",
|
|
189
190
|
label: "SRE / Infra"
|
|
190
191
|
}
|
|
191
|
-
], []), { limit:
|
|
192
|
+
], []), { limit: be, more: xe } = se(), { data: K, loading: q, error: J } = p({
|
|
192
193
|
status: "PUBLISHED",
|
|
193
|
-
pagination:
|
|
194
|
-
...
|
|
194
|
+
pagination: oe(be),
|
|
195
|
+
...w === "ALL" ? {} : { difficulty: w },
|
|
195
196
|
...E === "ALL" ? {} : { company: E },
|
|
196
197
|
...j === "ALL" ? {} : { role: j }
|
|
197
|
-
}), Y =
|
|
198
|
+
}), Y = m(y), X = _(() => K?.tecListProblems?.items ?? [], [K]), Z = K?.tecListProblems?.pageInfo?.hasMore ?? !1, Se = X.filter((e) => Y.has(e.id)).length, Ce = X.filter((e) => e.difficulty === "EASY").length, we = X.filter((e) => e.difficulty === "HARD").length, Q = _(() => {
|
|
198
199
|
let e = U.trim().toLowerCase();
|
|
199
200
|
return e ? X.filter((t) => t.title.toLowerCase().includes(e) || (t.topic ?? "").toLowerCase().includes(e) || t.tags.some((t) => t.toLowerCase().includes(e))) : X;
|
|
200
|
-
}, [X, U]), $ =
|
|
201
|
-
|
|
201
|
+
}, [X, U]), $ = g((e) => {
|
|
202
|
+
d("technospam.problem.viewed", {
|
|
202
203
|
workspaceId: c,
|
|
203
204
|
entityId: e.id,
|
|
204
205
|
entityType: "TEC_PROBLEM"
|
|
205
206
|
}), o(`/applications/problem-details-page?id=${e.id}`);
|
|
206
207
|
}, [
|
|
207
|
-
|
|
208
|
+
d,
|
|
208
209
|
o,
|
|
209
210
|
c
|
|
210
211
|
]);
|
|
211
|
-
return /* @__PURE__ */ O(
|
|
212
|
+
return /* @__PURE__ */ O(ee, {
|
|
212
213
|
hero: !0,
|
|
213
|
-
|
|
214
|
-
|
|
214
|
+
mobileSection: !0,
|
|
215
|
+
title: h("technospam.problems.title", "Coding Problems"),
|
|
216
|
+
description: h("technospam.problems.description", "Sharpen your problem-solving with hands-on, mentor-judged challenges across topics and difficulty."),
|
|
215
217
|
children: [/* @__PURE__ */ O("div", {
|
|
216
|
-
className: "mb-5
|
|
218
|
+
className: "mb-5 hidden grid-cols-2 gap-3 sm:grid sm:grid-cols-4",
|
|
217
219
|
children: [
|
|
218
220
|
/* @__PURE__ */ D(a, {
|
|
219
|
-
label:
|
|
221
|
+
label: h("technospam.problems.statProblems", "Problems"),
|
|
220
222
|
value: X.length,
|
|
221
|
-
icon:
|
|
223
|
+
icon: x,
|
|
222
224
|
tone: "blue"
|
|
223
225
|
}),
|
|
224
226
|
/* @__PURE__ */ D(a, {
|
|
225
|
-
label:
|
|
226
|
-
value:
|
|
227
|
-
icon:
|
|
227
|
+
label: h("technospam.problems.statSolved", "Solved"),
|
|
228
|
+
value: Se,
|
|
229
|
+
icon: b,
|
|
228
230
|
tone: "green"
|
|
229
231
|
}),
|
|
230
232
|
/* @__PURE__ */ D(a, {
|
|
231
|
-
label:
|
|
232
|
-
value:
|
|
233
|
-
icon:
|
|
233
|
+
label: h("technospam.problems.statEasy", "Easy"),
|
|
234
|
+
value: Ce,
|
|
235
|
+
icon: C,
|
|
234
236
|
tone: "teal"
|
|
235
237
|
}),
|
|
236
238
|
/* @__PURE__ */ D(a, {
|
|
237
|
-
label:
|
|
238
|
-
value:
|
|
239
|
-
icon:
|
|
239
|
+
label: h("technospam.problems.statHard", "Hard"),
|
|
240
|
+
value: we,
|
|
241
|
+
icon: S,
|
|
240
242
|
tone: "amber"
|
|
241
243
|
})
|
|
242
244
|
]
|
|
243
245
|
}), /* @__PURE__ */ O(L, {
|
|
244
246
|
className: "gap-0",
|
|
245
|
-
value:
|
|
247
|
+
value: w,
|
|
246
248
|
onValueChange: (e) => T(e),
|
|
247
249
|
children: [
|
|
248
250
|
/* @__PURE__ */ O("div", {
|
|
249
251
|
className: "mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",
|
|
250
|
-
children: [/* @__PURE__ */ D(z, { children:
|
|
252
|
+
children: [/* @__PURE__ */ D(z, { children: _e.map((e) => /* @__PURE__ */ D(B, {
|
|
251
253
|
value: e.value,
|
|
252
254
|
children: e.label
|
|
253
255
|
}, e.value)) }), /* @__PURE__ */ O("div", {
|
|
254
256
|
className: "relative w-full sm:w-72",
|
|
255
|
-
children: [/* @__PURE__ */ D(
|
|
257
|
+
children: [/* @__PURE__ */ D(de, {
|
|
256
258
|
className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-text-muted",
|
|
257
259
|
size: 16
|
|
258
|
-
}), /* @__PURE__ */ D(
|
|
260
|
+
}), /* @__PURE__ */ D(fe, {
|
|
259
261
|
className: "pl-9",
|
|
260
|
-
"aria-label":
|
|
261
|
-
placeholder:
|
|
262
|
+
"aria-label": h("technospam.problems.searchLabel", "Search problems"),
|
|
263
|
+
placeholder: h("technospam.problems.searchPlaceholder", "Search problems…"),
|
|
262
264
|
value: U,
|
|
263
|
-
onChange: (e) =>
|
|
265
|
+
onChange: (e) => pe(e.target.value),
|
|
264
266
|
onBlur: () => {
|
|
265
267
|
let e = U.trim();
|
|
266
|
-
e &&
|
|
268
|
+
e && d("technospam.search.performed", {
|
|
267
269
|
workspaceId: c,
|
|
268
270
|
entityType: "problem",
|
|
269
271
|
queryLength: e.length
|
|
@@ -280,12 +282,12 @@ function H() {
|
|
|
280
282
|
onValueChange: k,
|
|
281
283
|
children: [/* @__PURE__ */ D(F, {
|
|
282
284
|
className: "w-full sm:w-48",
|
|
283
|
-
"aria-label":
|
|
284
|
-
children: /* @__PURE__ */ D(I, { placeholder:
|
|
285
|
+
"aria-label": h("technospam.problems.companyFilterLabel", "Filter by company"),
|
|
286
|
+
children: /* @__PURE__ */ D(I, { placeholder: h("technospam.problems.companyPlaceholder", "Company") })
|
|
285
287
|
}), /* @__PURE__ */ O(N, { children: [/* @__PURE__ */ D(P, {
|
|
286
288
|
value: "ALL",
|
|
287
|
-
children:
|
|
288
|
-
}),
|
|
289
|
+
children: h("technospam.problems.companyAll", "All companies")
|
|
290
|
+
}), ve.map((e) => /* @__PURE__ */ D(P, {
|
|
289
291
|
value: e.value,
|
|
290
292
|
children: e.label
|
|
291
293
|
}, e.value))] })]
|
|
@@ -295,20 +297,20 @@ function H() {
|
|
|
295
297
|
onValueChange: H,
|
|
296
298
|
children: [/* @__PURE__ */ D(F, {
|
|
297
299
|
className: "w-full sm:w-48",
|
|
298
|
-
"aria-label":
|
|
299
|
-
children: /* @__PURE__ */ D(I, { placeholder:
|
|
300
|
+
"aria-label": h("technospam.problems.roleFilterLabel", "Filter by role"),
|
|
301
|
+
children: /* @__PURE__ */ D(I, { placeholder: h("technospam.problems.rolePlaceholder", "Role") })
|
|
300
302
|
}), /* @__PURE__ */ O(N, { children: [/* @__PURE__ */ D(P, {
|
|
301
303
|
value: "ALL",
|
|
302
|
-
children:
|
|
303
|
-
}),
|
|
304
|
+
children: h("technospam.problems.roleAll", "All roles")
|
|
305
|
+
}), ye.map((e) => /* @__PURE__ */ D(P, {
|
|
304
306
|
value: e.value,
|
|
305
307
|
children: e.label
|
|
306
308
|
}, e.value))] })]
|
|
307
309
|
}),
|
|
308
310
|
/* @__PURE__ */ O("div", {
|
|
309
311
|
role: "group",
|
|
310
|
-
"aria-label":
|
|
311
|
-
className: "
|
|
312
|
+
"aria-label": h("technospam.problems.viewLabel", "Catalog layout"),
|
|
313
|
+
className: "hidden items-center gap-1 self-start rounded-md border border-border-subtle bg-bg-surface p-1 sm:ml-auto sm:inline-flex sm:self-auto",
|
|
312
314
|
children: [/* @__PURE__ */ O(A, {
|
|
313
315
|
type: "button",
|
|
314
316
|
size: "sm",
|
|
@@ -319,7 +321,7 @@ function H() {
|
|
|
319
321
|
size: 14,
|
|
320
322
|
className: "mr-1.5",
|
|
321
323
|
"aria-hidden": "true"
|
|
322
|
-
}),
|
|
324
|
+
}), h("technospam.problems.viewTable", "Table")]
|
|
323
325
|
}), /* @__PURE__ */ O(A, {
|
|
324
326
|
type: "button",
|
|
325
327
|
size: "sm",
|
|
@@ -330,23 +332,23 @@ function H() {
|
|
|
330
332
|
size: 14,
|
|
331
333
|
className: "mr-1.5",
|
|
332
334
|
"aria-hidden": "true"
|
|
333
|
-
}),
|
|
335
|
+
}), h("technospam.problems.viewCards", "Cards")]
|
|
334
336
|
})]
|
|
335
337
|
})
|
|
336
338
|
]
|
|
337
339
|
}),
|
|
338
340
|
/* @__PURE__ */ O(R, {
|
|
339
|
-
value:
|
|
341
|
+
value: w,
|
|
340
342
|
className: ce,
|
|
341
343
|
children: [
|
|
342
|
-
q && !X.length ? /* @__PURE__ */ D(
|
|
343
|
-
title:
|
|
344
|
+
q && !X.length ? /* @__PURE__ */ D(ae, {}) : J ? /* @__PURE__ */ D(ie, {
|
|
345
|
+
title: h("technospam.common.errorLoadProblems", "Couldn't load problems"),
|
|
344
346
|
error: J
|
|
345
|
-
}) : Q.length === 0 ? /* @__PURE__ */ D(
|
|
346
|
-
icon: /* @__PURE__ */ D(
|
|
347
|
-
title: U ||
|
|
348
|
-
description: U ||
|
|
349
|
-
}) :
|
|
347
|
+
}) : Q.length === 0 ? /* @__PURE__ */ D(f, {
|
|
348
|
+
icon: /* @__PURE__ */ D(x, { size: 26 }),
|
|
349
|
+
title: U || w !== "ALL" || E !== "ALL" || j !== "ALL" ? h("technospam.problems.emptySearchTitle", "No matching problems") : h("technospam.problems.emptyDefaultTitle", "No problems published yet"),
|
|
350
|
+
description: U || w !== "ALL" || E !== "ALL" || j !== "ALL" ? h("technospam.problems.emptySearchDesc", "Try a different search, difficulty, company, or role filter.") : h("technospam.problems.emptyDefaultDesc", "Published problems will appear here. Check back soon — TechnoSpam is pre-launch.")
|
|
351
|
+
}) : ge === "table" ? /* @__PURE__ */ D(ne, {
|
|
350
352
|
problems: Q,
|
|
351
353
|
solvedIds: Y,
|
|
352
354
|
onOpen: $
|
|
@@ -360,15 +362,15 @@ function H() {
|
|
|
360
362
|
}),
|
|
361
363
|
Q.length > 0 && !J ? /* @__PURE__ */ D("p", {
|
|
362
364
|
className: "mt-4 text-sm text-text-muted",
|
|
363
|
-
children: Q.length < X.length ?
|
|
365
|
+
children: Q.length < X.length ? h("technospam.problems.showingFiltered", "Showing {{shown}} of {{total}} loaded problems", {
|
|
364
366
|
shown: Q.length,
|
|
365
367
|
total: X.length
|
|
366
|
-
}) : Z ?
|
|
368
|
+
}) : Z ? h("technospam.problems.showingMore", "Showing 1 to {{count}} problems — load more to see the rest", { count: Q.length }) : h("technospam.problems.showingAll", "Showing all {{count}} problems", { count: Q.length })
|
|
367
369
|
}) : null,
|
|
368
|
-
/* @__PURE__ */ D(
|
|
370
|
+
/* @__PURE__ */ D(re, {
|
|
369
371
|
hasMore: Z,
|
|
370
372
|
loading: q,
|
|
371
|
-
onClick:
|
|
373
|
+
onClick: xe
|
|
372
374
|
})
|
|
373
375
|
]
|
|
374
376
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApplicationsProblemsPagePage.js","names":[],"sources":["../../../src/technospam/pages/ApplicationsProblemsPagePage.tsx"],"sourcesContent":["import { useCallback, useMemo, useState } from 'react';\nimport {\n Building2,\n CheckCircle2,\n Code2,\n Flame,\n Gauge,\n LayoutGrid,\n Layers,\n List,\n Search,\n Tag,\n Trophy,\n} from 'lucide-react';\nimport {\n Badge,\n Button,\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 CardOpenButton,\n EmptyView,\n ErrorView,\n LoadMore,\n LoadingGrid,\n MediaCover,\n PageLayout,\n PremiumBadge,\n ProblemsTable,\n StatCard,\n StatusBadge,\n topicIcon,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n pageArg,\n usePageLimit,\n useProblemsQuery,\n useSolvedProblemIds,\n type TecProblem,\n type TecProblemDifficulty,\n} from '../api/technospamApi';\nimport { useTechnoSpamAnalytics } from '../hooks/useTechnoSpamAnalytics';\nimport { useTr } from '../hooks/useTr';\nimport {\n readProblemCatalogView,\n safeLocalStorage,\n writeProblemCatalogView,\n type ProblemCatalogView,\n} from '../utils/problemCatalog';\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 className=\"group flex cursor-pointer flex-col overflow-hidden transition-all duration-200 hover:-translate-y-1 hover:shadow-lg\">\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 <CardOpenButton onOpen={onOpen}>{problem.title}</CardOpenButton>\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 // The mock's data table is the default layout; the card grid stays available\n // as a compact visual view and the choice is remembered per viewer (BOFF-4077).\n const [view, setView] = useState<ProblemCatalogView>(() =>\n readProblemCatalogView(safeLocalStorage())\n );\n const changeView = useCallback((next: ProblemCatalogView) => {\n setView(next);\n writeProblemCatalogView(safeLocalStorage(), next);\n }, []);\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 solvedSet = useSolvedProblemIds(learnerId);\n\n const problems = useMemo(() => data?.tecListProblems?.items ?? [], [data]);\n const hasMore = data?.tecListProblems?.pageInfo?.hasMore ?? false;\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 const openProblem = useCallback(\n (problem: TecProblem) => {\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 [emit, navigate, workspaceId]\n );\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 role=\"group\"\n aria-label={tr('technospam.problems.viewLabel', 'Catalog layout')}\n className=\"inline-flex items-center gap-1 self-start rounded-md border border-border-subtle bg-bg-surface p-1 sm:ml-auto sm:self-auto\"\n >\n <Button\n type=\"button\"\n size=\"sm\"\n variant={view === 'table' ? 'secondary' : 'ghost'}\n aria-pressed={view === 'table'}\n onClick={() => changeView('table')}\n >\n <List size={14} className=\"mr-1.5\" aria-hidden=\"true\" />\n {tr('technospam.problems.viewTable', 'Table')}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n variant={view === 'cards' ? 'secondary' : 'ghost'}\n aria-pressed={view === 'cards'}\n onClick={() => changeView('cards')}\n >\n <LayoutGrid size={14} className=\"mr-1.5\" aria-hidden=\"true\" />\n {tr('technospam.problems.viewCards', 'Cards')}\n </Button>\n </div>\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 ) : view === 'table' ? (\n <ProblemsTable problems={filtered} solvedIds={solvedSet} onOpen={openProblem} />\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={() => openProblem(problem)}\n />\n ))}\n </div>\n )}\n {filtered.length > 0 && !error ? (\n <p className=\"mt-4 text-sm text-text-muted\">\n {filtered.length < problems.length\n ? tr(\n 'technospam.problems.showingFiltered',\n 'Showing {{shown}} of {{total}} loaded problems',\n { shown: filtered.length, total: problems.length }\n )\n : hasMore\n ? tr(\n 'technospam.problems.showingMore',\n 'Showing 1 to {{count}} problems — load more to see the rest',\n { count: filtered.length }\n )\n : tr('technospam.problems.showingAll', 'Showing all {{count}} problems', {\n count: filtered.length,\n })}\n </p>\n ) : null}\n <LoadMore hasMore={hasMore} loading={loading} onClick={more} />\n </TabsContent>\n </Tabs>\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AA8DA,SAAS,EAAY,EACnB,YACA,WACA,aAKC;CACD,IAAM,IAAK,GAAO;AAClB,QACE,kBAAC,GAAD;EAAW,WAAU;YAArB,CACE,kBAAC,GAAD;GACE,MAAM,GAAG,EAAQ,SAAS,GAAG,GAAG,EAAQ,WAAW,GAAG,EAAQ;GAC9D,MAAM,EAAU,EAAQ,SAAS,EAAQ,MAAM;aAFjD,CAIG,IACC,kBAAC,QAAD;IAAM,WAAU;cAAhB;KACE,kBAAC,GAAD,EAAc,MAAM,IAAM,CAAA;;KAAE,EAAG,8BAA8B,SAAS;KACjE;QAEP,kBAAC,QAAD,EAAQ,CAAA,EAEV,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KAAa,QAAQ,EAAQ;KAAY,MAAK;KAAU,CAAA,EACvD,EAAQ,gBAAgB,kBAAC,GAAD,EAAc,QAAQ,EAAQ,UAAY,CAAA,GAAG,KAClE;MACK;MACb,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBACZ,kBAAC,GAAD;OAAwB;iBAAS,EAAQ;OAAuB,CAAA;MAC7D,CAAA,EACJ,EAAQ,YACP,kBAAC,KAAD;MAAG,WAAU;gBAAiD,EAAQ;MAAc,CAAA,GAClF,KACA;;IACN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACG,EAAQ,QACP,kBAAC,QAAD;OAAM,WAAU;iBAAhB;QACE,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA;;QAAE,EAAQ;QACzB;WACL;MACJ,kBAAC,QAAD;OAAM,WAAU;iBAAhB;QACE,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA;QAAC;QACpB,EAAG,8BAA8B,iBAAiB,EAAE,OAAO,EAAQ,QAAQ,CAAC;QACxE;;MACN,EAAQ,UAAU,SACjB,kBAAC,QAAD;OAAM,WAAU;iBAAhB;QACE,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;QAAC;QACnB,EAAQ,UAAU,WAAW,IAC1B,EAAG,iCAAiC,kBAAkB,EACpD,OAAO,EAAQ,UAAU,QAC1B,CAAC,GACF,EAAG,uCAAuC,mBAAmB,EAC3D,OAAO,EAAQ,UAAU,QAC1B,CAAC;QACD;WACL;MACA;;IACL,EAAQ,UAAU,SACjB,kBAAC,OAAD;KAAK,WAAU;eAAf,CACG,EAAQ,UAAU,MAAM,GAAG,EAAE,CAAC,KAAK,MAClC,kBAAC,GAAD;MAAe,SAAQ;MAAU,WAAU;gBAA3C,CACE,kBAAC,GAAD;OAAW,MAAM;OAAI,WAAU;OAAS,CAAA,EACvC,EAAE,QAAQ,MAAM,IAAI,CACf;QAHI,EAGJ,CACR,EACD,EAAQ,uBAAuB,SAC9B,kBAAC,GAAD;MAAO,SAAQ;MAAY,WAAU;gBAArC,CACE,kBAAC,GAAD;OAAO,MAAM;OAAI,WAAU;OAAS,CAAA,EACnC,EAAG,uCAAuC,mBAAmB,CACxD;UACN,KACA;SACJ;IACH,EAAQ,KAAK,SACZ,kBAAC,OAAD;KAAK,WAAU;eACZ,EAAQ,KAAK,MAAM,GAAG,EAAE,CAAC,KAAK,MAC7B,kBAAC,GAAD;MAAe,SAAQ;MAAY,WAAU;gBAA7C,CACE,kBAAC,GAAD;OAAK,MAAM;OAAI,WAAU;OAAS,CAAA,EACjC,EACK;QAHI,EAGJ,CACR;KACE,CAAA,GACJ;IACA;KACI;;;AAIhB,SAAgB,IAA+B;CAC7C,IAAM,IAAW,GAAuB,EAClC,EAAE,gBAAa,mBAAgB,GAAe,EAC9C,EAAE,YAAS,GAAwB,EACnC,IAAK,GAAO,EACZ,IAAY,GAAa,IACzB,CAAC,GAAY,KAAiB,EAAuC,MAAM,EAC3E,CAAC,GAAS,KAAc,EAAiB,MAAM,EAC/C,CAAC,GAAM,KAAW,EAAiB,MAAM,EACzC,CAAC,GAAQ,MAAa,EAAS,GAAG,EAGlC,CAAC,GAAM,MAAW,QACtB,EAAuB,GAAkB,CAAC,CAC3C,EACK,IAAa,GAAa,MAA6B;AAE3D,EADA,GAAQ,EAAK,EACb,EAAwB,GAAkB,EAAE,EAAK;IAChD,EAAE,CAAC,EAEA,KAAe,QACb;EACJ;GAAE,OAAO;GAAgB,OAAO,EAAG,iCAAiC,MAAM;GAAE;EAC5E;GACE,OAAO;GACP,OAAO,EAAG,kCAAkC,OAAO;GACpD;EACD;GACE,OAAO;GACP,OAAO,EAAG,oCAAoC,SAAS;GACxD;EACD;GACE,OAAO;GACP,OAAO,EAAG,kCAAkC,OAAO;GACpD;EACF,EACD,CAAC,EAAG,CACL,EAIK,KAAY,QACV;EACJ;GAAE,OAAO;GAAU,OAAO;GAAU;EACpC;GAAE,OAAO;GAAU,OAAO;GAAU;EACpC;GAAE,OAAO;GAAQ,OAAO;GAAQ;EAChC;GAAE,OAAO;GAAa,OAAO;GAAa;EAC1C;GAAE,OAAO;GAAS,OAAO;GAAS;EAClC;GAAE,OAAO;GAAW,OAAO;GAAW;EACvC,EACD,EAAE,CACH,EAEK,KAAQ,QACN;EACJ;GAAE,OAAO;GAAY,OAAO;GAAY;EACxC;GAAE,OAAO;GAAW,OAAO;GAAW;EACtC;GAAE,OAAO;GAAa,OAAO;GAAc;EAC3C;GAAE,OAAO;GAAgB,OAAO;GAAgB;EAChD;GAAE,OAAO;GAAU,OAAO;GAAU;EACpC;GAAE,OAAO;GAAO,OAAO;GAAe;EACvC,EACD,EAAE,CACH,EAEK,EAAE,WAAO,aAAS,IAAc,EAChC,EAAE,SAAM,YAAS,aAAU,GAAiB;EAChD,QAAQ;EACR,YAAY,GAAQ,GAAM;EAC1B,GAAI,MAAe,QAAyB,EAAE,GAAnB,EAAE,eAAY;EACzC,GAAI,MAAY,QAAsB,EAAE,GAAhB,EAAE,YAAS;EACnC,GAAI,MAAS,QAAmB,EAAE,GAAb,EAAE,SAAM;EAC9B,CAAC,EACI,IAAY,GAAoB,EAAU,EAE1C,IAAW,QAAc,GAAM,iBAAiB,SAAS,EAAE,EAAE,CAAC,EAAK,CAAC,EACpE,IAAU,GAAM,iBAAiB,UAAU,WAAW,IAEtD,KAAc,EAAS,QAAQ,MAAM,EAAU,IAAI,EAAE,GAAG,CAAC,CAAC,QAC1D,KAAY,EAAS,QAAQ,MAAM,EAAE,eAAe,OAAO,CAAC,QAC5D,KAAY,EAAS,QAAQ,MAAM,EAAE,eAAe,OAAO,CAAC,QAE5D,IAAW,QAAc;EAC7B,IAAM,IAAI,EAAO,MAAM,CAAC,aAAa;AAErC,SADK,IACE,EAAS,QACb,MACC,EAAE,MAAM,aAAa,CAAC,SAAS,EAAE,KAChC,EAAE,SAAS,IAAI,aAAa,CAAC,SAAS,EAAE,IACzC,EAAE,KAAK,MAAM,MAAM,EAAE,aAAa,CAAC,SAAS,EAAE,CAAC,CAClD,GANc;IAOd,CAAC,GAAU,EAAO,CAAC,EAEhB,IAAc,GACjB,MAAwB;AAMvB,EALA,EAAK,6BAA6B;GAChC;GACA,UAAU,EAAQ;GAClB,YAAY;GACb,CAAC,EACF,EAAS,yCAAyC,EAAQ,KAAK;IAEjE;EAAC;EAAM;EAAU;EAAY,CAC9B;AAED,QACE,kBAAC,GAAD;EACE,MAAA;EACA,OAAO,EAAG,6BAA6B,kBAAkB;EACzD,aAAa,EACX,mCACA,qGACD;YANH,CAQE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KACE,OAAO,EAAG,oCAAoC,WAAW;KACzD,OAAO,EAAS;KAChB,MAAM;KACN,MAAK;KACL,CAAA;IACF,kBAAC,GAAD;KACE,OAAO,EAAG,kCAAkC,SAAS;KACrD,OAAO;KACP,MAAM;KACN,MAAK;KACL,CAAA;IACF,kBAAC,GAAD;KACE,OAAO,EAAG,gCAAgC,OAAO;KACjD,OAAO;KACP,MAAM;KACN,MAAK;KACL,CAAA;IACF,kBAAC,GAAD;KACE,OAAO,EAAG,gCAAgC,OAAO;KACjD,OAAO;KACP,MAAM;KACN,MAAK;KACL,CAAA;IACE;MAKN,kBAAC,GAAD;GACE,WAAU;GACV,OAAO;GACP,gBAAgB,MAAM,EAAc,EAAkC;aAHxE;IAKE,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD,EAAA,UACG,GAAa,KAAK,MACjB,kBAAC,GAAD;MAA2B,OAAO,EAAE;gBACjC,EAAE;MACS,EAFI,EAAE,MAEN,CACd,EACO,CAAA,EACX,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OACE,WAAU;OACV,MAAM;OACN,CAAA,EACF,kBAAC,IAAD;OACE,WAAU;OACV,cAAY,EAAG,mCAAmC,kBAAkB;OACpE,aAAa,EAAG,yCAAyC,mBAAmB;OAC5E,OAAO;OACP,WAAW,MAAM,GAAU,EAAE,OAAO,MAAM;OAC1C,cAAc;QACZ,IAAM,IAAQ,EAAO,MAAM;AAC3B,QAAI,KACF,EAAK,+BAA+B;SAClC;SACA,YAAY;SACZ,aAAa,EAAM;SACpB,CAAC;;OAGN,CAAA,CACE;QACF;;IAEN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OAAQ,OAAO;OAAS,eAAe;iBAAvC,CACE,kBAAC,GAAD;QACE,WAAU;QACV,cAAY,EAAG,0CAA0C,oBAAoB;kBAE7E,kBAAC,GAAD,EAAa,aAAa,EAAG,0CAA0C,UAAU,EAAI,CAAA;QACvE,CAAA,EAChB,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;QAAY,OAAM;kBACf,EAAG,kCAAkC,gBAAgB;QAC3C,CAAA,EACZ,GAAU,KAAK,MACd,kBAAC,GAAD;QAA0B,OAAO,EAAE;kBAChC,EAAE;QACQ,EAFI,EAAE,MAEN,CACb,CACY,EAAA,CAAA,CACT;;MACT,kBAAC,GAAD;OAAQ,OAAO;OAAM,eAAe;iBAApC,CACE,kBAAC,GAAD;QACE,WAAU;QACV,cAAY,EAAG,uCAAuC,iBAAiB;kBAEvE,kBAAC,GAAD,EAAa,aAAa,EAAG,uCAAuC,OAAO,EAAI,CAAA;QACjE,CAAA,EAChB,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;QAAY,OAAM;kBAAO,EAAG,+BAA+B,YAAY;QAAc,CAAA,EACpF,GAAM,KAAK,MACV,kBAAC,GAAD;QAA0B,OAAO,EAAE;kBAChC,EAAE;QACQ,EAFI,EAAE,MAEN,CACb,CACY,EAAA,CAAA,CACT;;MACT,kBAAC,OAAD;OACE,MAAK;OACL,cAAY,EAAG,iCAAiC,iBAAiB;OACjE,WAAU;iBAHZ,CAKE,kBAAC,GAAD;QACE,MAAK;QACL,MAAK;QACL,SAAS,MAAS,UAAU,cAAc;QAC1C,gBAAc,MAAS;QACvB,eAAe,EAAW,QAAQ;kBALpC,CAOE,kBAAC,IAAD;SAAM,MAAM;SAAI,WAAU;SAAS,eAAY;SAAS,CAAA,EACvD,EAAG,iCAAiC,QAAQ,CACtC;WACT,kBAAC,GAAD;QACE,MAAK;QACL,MAAK;QACL,SAAS,MAAS,UAAU,cAAc;QAC1C,gBAAc,MAAS;QACvB,eAAe,EAAW,QAAQ;kBALpC,CAOE,kBAAC,IAAD;SAAY,MAAM;SAAI,WAAU;SAAS,eAAY;SAAS,CAAA,EAC7D,EAAG,iCAAiC,QAAQ,CACtC;UACL;;MACF;;IAEN,kBAAC,GAAD;KAAa,OAAO;KAAY,WAAW;eAA3C;MACG,KAAW,CAAC,EAAS,SACpB,kBAAC,GAAD,EAAe,CAAA,GACb,IACF,kBAAC,IAAD;OACE,OAAO,EAAG,uCAAuC,yBAAyB;OACnE;OACP,CAAA,GACA,EAAS,WAAW,IACtB,kBAAC,IAAD;OACE,MAAM,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;OACzB,OACE,KAAU,MAAe,SAAS,MAAY,SAAS,MAAS,QAC5D,EAAG,wCAAwC,uBAAuB,GAClE,EAAG,yCAAyC,4BAA4B;OAE9E,aACE,KAAU,MAAe,SAAS,MAAY,SAAS,MAAS,QAC5D,EACE,uCACA,+DACD,GACD,EACE,wCACA,mFACD;OAEP,CAAA,GACA,MAAS,UACX,kBAAC,IAAD;OAAe,UAAU;OAAU,WAAW;OAAW,QAAQ;OAAe,CAAA,GAEhF,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAS,KAAK,MACb,kBAAC,GAAD;QAEW;QACT,QAAQ,EAAU,IAAI,EAAQ,GAAG;QACjC,cAAc,EAAY,EAAQ;QAClC,EAJK,EAAQ,GAIb,CACF;OACE,CAAA;MAEP,EAAS,SAAS,KAAK,CAAC,IACvB,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAS,SAAS,EAAS,SACxB,EACE,uCACA,kDACA;QAAE,OAAO,EAAS;QAAQ,OAAO,EAAS;QAAQ,CACnD,GACD,IACE,EACE,mCACA,+DACA,EAAE,OAAO,EAAS,QAAQ,CAC3B,GACD,EAAG,kCAAkC,kCAAkC,EACrE,OAAO,EAAS,QACjB,CAAC;OACN,CAAA,GACF;MACJ,kBAAC,IAAD;OAAmB;OAAkB;OAAS,SAAS;OAAQ,CAAA;MACnD;;IACT;KACI"}
|
|
1
|
+
{"version":3,"file":"ApplicationsProblemsPagePage.js","names":[],"sources":["../../../src/technospam/pages/ApplicationsProblemsPagePage.tsx"],"sourcesContent":["import { useCallback, useMemo, useState } from 'react';\nimport {\n Building2,\n CheckCircle2,\n Code2,\n Flame,\n Gauge,\n LayoutGrid,\n Layers,\n List,\n Search,\n Tag,\n Trophy,\n} from 'lucide-react';\nimport {\n Badge,\n Button,\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 CardOpenButton,\n EmptyView,\n ErrorView,\n LoadMore,\n LoadingGrid,\n MediaCover,\n PageLayout,\n PremiumBadge,\n ProblemsTable,\n StatCard,\n StatusBadge,\n topicIcon,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n pageArg,\n usePageLimit,\n useProblemsQuery,\n useSolvedProblemIds,\n type TecProblem,\n type TecProblemDifficulty,\n} from '../api/technospamApi';\nimport { useTechnoSpamAnalytics } from '../hooks/useTechnoSpamAnalytics';\nimport { useTr } from '../hooks/useTr';\nimport { usePhoneLayout } from '../hooks/usePhoneLayout';\nimport {\n readProblemCatalogView,\n safeLocalStorage,\n writeProblemCatalogView,\n type ProblemCatalogView,\n} from '../utils/problemCatalog';\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 className=\"group flex cursor-pointer flex-col overflow-hidden transition-all duration-200 hover:-translate-y-1 hover:shadow-lg\">\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 <CardOpenButton onOpen={onOpen}>{problem.title}</CardOpenButton>\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 // The mock's data table is the default layout; the card grid stays available\n // as a compact visual view and the choice is remembered per viewer (BOFF-4077).\n const [view, setView] = useState<ProblemCatalogView>(() =>\n readProblemCatalogView(safeLocalStorage())\n );\n // Phones always get the card list: the multi-column table cannot fit a\n // 390px screen, so the remembered desktop choice is left untouched and\n // the layout toggle is hidden below `sm` (BOFF-4108).\n const { isPhone } = usePhoneLayout();\n const effectiveView: ProblemCatalogView = isPhone ? 'cards' : view;\n const changeView = useCallback((next: ProblemCatalogView) => {\n setView(next);\n writeProblemCatalogView(safeLocalStorage(), next);\n }, []);\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 solvedSet = useSolvedProblemIds(learnerId);\n\n const problems = useMemo(() => data?.tecListProblems?.items ?? [], [data]);\n const hasMore = data?.tecListProblems?.pageInfo?.hasMore ?? false;\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 const openProblem = useCallback(\n (problem: TecProblem) => {\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 [emit, navigate, workspaceId]\n );\n\n return (\n <PageLayout\n hero\n mobileSection\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 {/* Catalog stats are a desktop summary; phones go straight to the list. */}\n <div className=\"mb-5 hidden grid-cols-2 gap-3 sm:grid 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 role=\"group\"\n aria-label={tr('technospam.problems.viewLabel', 'Catalog layout')}\n className=\"hidden items-center gap-1 self-start rounded-md border border-border-subtle bg-bg-surface p-1 sm:ml-auto sm:inline-flex sm:self-auto\"\n >\n <Button\n type=\"button\"\n size=\"sm\"\n variant={view === 'table' ? 'secondary' : 'ghost'}\n aria-pressed={view === 'table'}\n onClick={() => changeView('table')}\n >\n <List size={14} className=\"mr-1.5\" aria-hidden=\"true\" />\n {tr('technospam.problems.viewTable', 'Table')}\n </Button>\n <Button\n type=\"button\"\n size=\"sm\"\n variant={view === 'cards' ? 'secondary' : 'ghost'}\n aria-pressed={view === 'cards'}\n onClick={() => changeView('cards')}\n >\n <LayoutGrid size={14} className=\"mr-1.5\" aria-hidden=\"true\" />\n {tr('technospam.problems.viewCards', 'Cards')}\n </Button>\n </div>\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 ) : effectiveView === 'table' ? (\n <ProblemsTable problems={filtered} solvedIds={solvedSet} onOpen={openProblem} />\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={() => openProblem(problem)}\n />\n ))}\n </div>\n )}\n {filtered.length > 0 && !error ? (\n <p className=\"mt-4 text-sm text-text-muted\">\n {filtered.length < problems.length\n ? tr(\n 'technospam.problems.showingFiltered',\n 'Showing {{shown}} of {{total}} loaded problems',\n { shown: filtered.length, total: problems.length }\n )\n : hasMore\n ? tr(\n 'technospam.problems.showingMore',\n 'Showing 1 to {{count}} problems — load more to see the rest',\n { count: filtered.length }\n )\n : tr('technospam.problems.showingAll', 'Showing all {{count}} problems', {\n count: filtered.length,\n })}\n </p>\n ) : null}\n <LoadMore hasMore={hasMore} loading={loading} onClick={more} />\n </TabsContent>\n </Tabs>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
@@ -54,7 +54,7 @@ var F = {
|
|
|
54
54
|
};
|
|
55
55
|
function I({ icon: e, children: t }) {
|
|
56
56
|
return /* @__PURE__ */ j("span", {
|
|
57
|
-
className: "inline-flex items-center gap-1 rounded-full bg-
|
|
57
|
+
className: "inline-flex items-center gap-1 rounded-full bg-action-primary-text/15 px-2.5 py-1 text-xs font-medium text-action-primary-text ring-1 ring-action-primary-text/25 backdrop-blur-sm",
|
|
58
58
|
children: [e ? /* @__PURE__ */ A(e, { size: 12 }) : null, t]
|
|
59
59
|
});
|
|
60
60
|
}
|
|
@@ -327,11 +327,11 @@ function L() {
|
|
|
327
327
|
className: "flex w-full flex-col gap-2",
|
|
328
328
|
children: [
|
|
329
329
|
/* @__PURE__ */ A("span", {
|
|
330
|
-
className: "text-xs font-medium uppercase tracking-wide text-
|
|
330
|
+
className: "text-xs font-medium uppercase tracking-wide text-action-primary-text/80",
|
|
331
331
|
children: W.topic ?? "Course"
|
|
332
332
|
}),
|
|
333
333
|
/* @__PURE__ */ A("h2", {
|
|
334
|
-
className: "text-2xl font-bold leading-tight text-
|
|
334
|
+
className: "text-2xl font-bold leading-tight text-action-primary-text drop-shadow-sm sm:text-3xl",
|
|
335
335
|
children: W.title
|
|
336
336
|
}),
|
|
337
337
|
/* @__PURE__ */ j("div", {
|