@burdenoff/microfe-technospam 2026.1101.3 → 2026.1101.4
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/stories/journeys/Problems.stories.d.ts +5 -0
- package/dist/stories/journeys/Problems.stories.d.ts.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemsPagePage.js +80 -77
- package/dist/technospam/pages/ApplicationsProblemsPagePage.js.map +1 -1
- package/package.json +1 -1
|
@@ -6,5 +6,10 @@ export default meta;
|
|
|
6
6
|
type Story = StoryObj<typeof meta>;
|
|
7
7
|
/** Pick a problem from the practice list, submit a solution, see the verdict. */
|
|
8
8
|
export declare const PracticeAndSubmit: Story;
|
|
9
|
+
/**
|
|
10
|
+
* BOFF-3157: in the card layout a keyboard-only learner Tabs to a problem card
|
|
11
|
+
* (its title is the card's button) and opens it with Enter.
|
|
12
|
+
*/
|
|
13
|
+
export declare const OpenProblemCardWithKeyboard: Story;
|
|
9
14
|
export declare const BackendUnavailable: Story;
|
|
10
15
|
//# sourceMappingURL=Problems.stories.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Problems.stories.d.ts","sourceRoot":"","sources":["../../../src/stories/journeys/Problems.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAGvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"Problems.stories.d.ts","sourceRoot":"","sources":["../../../src/stories/journeys/Problems.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAGvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AAIpE,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,kBAAkB,CAMzC,CAAC;AACF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,IAAI,CAAC,CAAC;AAEnC,iFAAiF;AACjF,eAAO,MAAM,iBAAiB,EAAE,KA8B/B,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,2BAA2B,EAAE,KAoBzC,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,KAMhC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ApplicationsProblemsPagePage.d.ts","sourceRoot":"","sources":["../../../src/technospam/pages/ApplicationsProblemsPagePage.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"ApplicationsProblemsPagePage.d.ts","sourceRoot":"","sources":["../../../src/technospam/pages/ApplicationsProblemsPagePage.tsx"],"names":[],"mappings":"AAyJA,wBAAgB,4BAA4B,gCAuT3C"}
|
|
@@ -5,31 +5,31 @@ import { PageLayout as i } from "../components/PageLayout.js";
|
|
|
5
5
|
import { StatCard as a } from "../components/StatCard.js";
|
|
6
6
|
import { MediaCover as o, topicIcon as s } from "../components/MediaCover.js";
|
|
7
7
|
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
|
|
8
|
+
import { readProblemCatalogView as l, safeLocalStorage as u, writeProblemCatalogView as d } from "../utils/problemCatalog.js";
|
|
9
|
+
import { PremiumBadge as f } from "../components/UpgradeGate.js";
|
|
10
|
+
import { ProblemsTable as ee } from "../components/ProblemsTable.js";
|
|
11
11
|
import { LoadMore as te } from "../components/LoadMore.js";
|
|
12
|
-
import { EmptyView as
|
|
13
|
-
import { pageArg as
|
|
12
|
+
import { EmptyView as ne, ErrorView as re, LoadingGrid as p } from "../components/StateViews.js";
|
|
13
|
+
import { pageArg as ie, usePageLimit as ae, useProblemsQuery as oe, useSolvedProblemIds as se } from "../api/technospamApi.js";
|
|
14
|
+
import { CardOpenButton as m } from "../components/CardOpenButton.js";
|
|
14
15
|
import "../components/index.js";
|
|
15
|
-
import { TAB_PANEL_FOCUS_CLASS as
|
|
16
|
-
import { useCallback as
|
|
17
|
-
import { Building2 as
|
|
16
|
+
import { TAB_PANEL_FOCUS_CLASS as ce } from "../lib/a11y.js";
|
|
17
|
+
import { useCallback as h, useMemo as g, useState as _ } from "react";
|
|
18
|
+
import { Building2 as v, CheckCircle2 as y, Code2 as b, Flame as x, Gauge as S, Layers as C, LayoutGrid as le, List as ue, Search as w, Tag as T, Trophy as E } from "lucide-react";
|
|
18
19
|
import { jsx as D, jsxs as O } from "react/jsx-runtime";
|
|
19
|
-
import { Badge as k, Button as A, GlassCard as j, Input as
|
|
20
|
+
import { Badge as k, Button as A, GlassCard as j, Input as de, 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";
|
|
20
21
|
//#region src/technospam/pages/ApplicationsProblemsPagePage.tsx
|
|
21
22
|
function V({ problem: e, solved: t, onOpen: n }) {
|
|
22
23
|
let i = r();
|
|
23
24
|
return /* @__PURE__ */ O(j, {
|
|
24
25
|
className: "group flex cursor-pointer flex-col overflow-hidden transition-all duration-200 hover:-translate-y-1 hover:shadow-lg",
|
|
25
|
-
onClick: n,
|
|
26
26
|
children: [/* @__PURE__ */ O(o, {
|
|
27
27
|
seed: `${e.topic ?? ""} ${e.difficulty} ${e.title}`,
|
|
28
28
|
icon: s(e.topic ?? e.title),
|
|
29
29
|
children: [t ? /* @__PURE__ */ O("span", {
|
|
30
30
|
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
31
|
children: [
|
|
32
|
-
/* @__PURE__ */ D(
|
|
32
|
+
/* @__PURE__ */ D(y, { size: 12 }),
|
|
33
33
|
" ",
|
|
34
34
|
i("technospam.problems.solved", "Solved")
|
|
35
35
|
]
|
|
@@ -38,7 +38,7 @@ function V({ problem: e, solved: t, onOpen: n }) {
|
|
|
38
38
|
children: [/* @__PURE__ */ D(c, {
|
|
39
39
|
status: e.difficulty,
|
|
40
40
|
kind: "level"
|
|
41
|
-
}), e.isPremiumOnly ? /* @__PURE__ */ D(
|
|
41
|
+
}), e.isPremiumOnly ? /* @__PURE__ */ D(f, { locked: e.isLocked }) : null]
|
|
42
42
|
})]
|
|
43
43
|
}), /* @__PURE__ */ O("div", {
|
|
44
44
|
className: "flex flex-1 flex-col gap-3 p-4",
|
|
@@ -47,7 +47,10 @@ function V({ problem: e, solved: t, onOpen: n }) {
|
|
|
47
47
|
className: "min-w-0",
|
|
48
48
|
children: [/* @__PURE__ */ D("h3", {
|
|
49
49
|
className: "truncate text-base font-semibold text-text-primary group-hover:text-accent-primary",
|
|
50
|
-
children:
|
|
50
|
+
children: /* @__PURE__ */ D(m, {
|
|
51
|
+
onOpen: n,
|
|
52
|
+
children: e.title
|
|
53
|
+
})
|
|
51
54
|
}), e.statement ? /* @__PURE__ */ D("p", {
|
|
52
55
|
className: "mt-1 line-clamp-2 text-sm text-text-secondary",
|
|
53
56
|
children: e.statement
|
|
@@ -75,7 +78,7 @@ function V({ problem: e, solved: t, onOpen: n }) {
|
|
|
75
78
|
e.languages.length ? /* @__PURE__ */ O("span", {
|
|
76
79
|
className: "inline-flex items-center gap-1",
|
|
77
80
|
children: [
|
|
78
|
-
/* @__PURE__ */ D(
|
|
81
|
+
/* @__PURE__ */ D(b, { size: 13 }),
|
|
79
82
|
" ",
|
|
80
83
|
e.languages.length === 1 ? i("technospam.problems.languages", "{{count}} lang", { count: e.languages.length }) : i("technospam.problems.languagesPlural", "{{count}} langs", { count: e.languages.length })
|
|
81
84
|
]
|
|
@@ -87,14 +90,14 @@ function V({ problem: e, solved: t, onOpen: n }) {
|
|
|
87
90
|
children: [e.companies.slice(0, 3).map((e) => /* @__PURE__ */ O(k, {
|
|
88
91
|
variant: "outline",
|
|
89
92
|
className: "text-[11px] capitalize",
|
|
90
|
-
children: [/* @__PURE__ */ D(
|
|
93
|
+
children: [/* @__PURE__ */ D(v, {
|
|
91
94
|
size: 10,
|
|
92
95
|
className: "mr-1"
|
|
93
96
|
}), e.replace(/-/g, " ")]
|
|
94
97
|
}, e)), e.interviewFrequency === "HIGH" ? /* @__PURE__ */ O(k, {
|
|
95
98
|
variant: "secondary",
|
|
96
99
|
className: "text-[11px] text-status-warning-text",
|
|
97
|
-
children: [/* @__PURE__ */ D(
|
|
100
|
+
children: [/* @__PURE__ */ D(x, {
|
|
98
101
|
size: 10,
|
|
99
102
|
className: "mr-1"
|
|
100
103
|
}), i("technospam.problems.frequentlyAsked", "Frequently asked")]
|
|
@@ -116,26 +119,26 @@ function V({ problem: e, solved: t, onOpen: n }) {
|
|
|
116
119
|
});
|
|
117
120
|
}
|
|
118
121
|
function H() {
|
|
119
|
-
let o = t(), { currentUser: s, workspaceId: c } = e(), { emit:
|
|
120
|
-
pe(e),
|
|
121
|
-
}, []), me =
|
|
122
|
+
let o = t(), { currentUser: s, workspaceId: c } = e(), { emit: f } = n(), m = r(), v = s?.id, [C, T] = _("ALL"), [E, k] = _("ALL"), [j, H] = _("ALL"), [U, fe] = _(""), [W, pe] = _(() => l(u())), G = h((e) => {
|
|
123
|
+
pe(e), d(u(), e);
|
|
124
|
+
}, []), me = g(() => [
|
|
122
125
|
{
|
|
123
126
|
value: "ALL",
|
|
124
|
-
label:
|
|
127
|
+
label: m("technospam.problems.filterAll", "All")
|
|
125
128
|
},
|
|
126
129
|
{
|
|
127
130
|
value: "EASY",
|
|
128
|
-
label:
|
|
131
|
+
label: m("technospam.problems.filterEasy", "Easy")
|
|
129
132
|
},
|
|
130
133
|
{
|
|
131
134
|
value: "MEDIUM",
|
|
132
|
-
label:
|
|
135
|
+
label: m("technospam.problems.filterMedium", "Medium")
|
|
133
136
|
},
|
|
134
137
|
{
|
|
135
138
|
value: "HARD",
|
|
136
|
-
label:
|
|
139
|
+
label: m("technospam.problems.filterHard", "Hard")
|
|
137
140
|
}
|
|
138
|
-
], [
|
|
141
|
+
], [m]), he = g(() => [
|
|
139
142
|
{
|
|
140
143
|
value: "google",
|
|
141
144
|
label: "Google"
|
|
@@ -160,7 +163,7 @@ function H() {
|
|
|
160
163
|
value: "netflix",
|
|
161
164
|
label: "Netflix"
|
|
162
165
|
}
|
|
163
|
-
], []), ge =
|
|
166
|
+
], []), ge = g(() => [
|
|
164
167
|
{
|
|
165
168
|
value: "frontend",
|
|
166
169
|
label: "Frontend"
|
|
@@ -185,62 +188,62 @@ function H() {
|
|
|
185
188
|
value: "sre",
|
|
186
189
|
label: "SRE / Infra"
|
|
187
190
|
}
|
|
188
|
-
], []), { limit: _e, more: ve } =
|
|
191
|
+
], []), { limit: _e, more: ve } = ae(), { data: K, loading: q, error: J } = oe({
|
|
189
192
|
status: "PUBLISHED",
|
|
190
|
-
pagination:
|
|
191
|
-
...
|
|
192
|
-
...
|
|
193
|
-
...
|
|
194
|
-
}), Y =
|
|
193
|
+
pagination: ie(_e),
|
|
194
|
+
...C === "ALL" ? {} : { difficulty: C },
|
|
195
|
+
...E === "ALL" ? {} : { company: E },
|
|
196
|
+
...j === "ALL" ? {} : { role: j }
|
|
197
|
+
}), Y = se(v), X = g(() => K?.tecListProblems?.items ?? [], [K]), Z = K?.tecListProblems?.pageInfo?.hasMore ?? !1, ye = X.filter((e) => Y.has(e.id)).length, be = X.filter((e) => e.difficulty === "EASY").length, xe = X.filter((e) => e.difficulty === "HARD").length, Q = g(() => {
|
|
195
198
|
let e = U.trim().toLowerCase();
|
|
196
199
|
return e ? X.filter((t) => t.title.toLowerCase().includes(e) || (t.topic ?? "").toLowerCase().includes(e) || t.tags.some((t) => t.toLowerCase().includes(e))) : X;
|
|
197
|
-
}, [X, U]), $ =
|
|
198
|
-
|
|
200
|
+
}, [X, U]), $ = h((e) => {
|
|
201
|
+
f("technospam.problem.viewed", {
|
|
199
202
|
workspaceId: c,
|
|
200
203
|
entityId: e.id,
|
|
201
204
|
entityType: "TEC_PROBLEM"
|
|
202
205
|
}), o(`/applications/problem-details-page?id=${e.id}`);
|
|
203
206
|
}, [
|
|
204
|
-
|
|
207
|
+
f,
|
|
205
208
|
o,
|
|
206
209
|
c
|
|
207
210
|
]);
|
|
208
211
|
return /* @__PURE__ */ O(i, {
|
|
209
212
|
hero: !0,
|
|
210
|
-
title:
|
|
211
|
-
description:
|
|
213
|
+
title: m("technospam.problems.title", "Coding Problems"),
|
|
214
|
+
description: m("technospam.problems.description", "Sharpen your problem-solving with hands-on, mentor-judged challenges across topics and difficulty."),
|
|
212
215
|
children: [/* @__PURE__ */ O("div", {
|
|
213
216
|
className: "mb-5 grid grid-cols-2 gap-3 sm:grid-cols-4",
|
|
214
217
|
children: [
|
|
215
218
|
/* @__PURE__ */ D(a, {
|
|
216
|
-
label:
|
|
219
|
+
label: m("technospam.problems.statProblems", "Problems"),
|
|
217
220
|
value: X.length,
|
|
218
|
-
icon:
|
|
221
|
+
icon: b,
|
|
219
222
|
tone: "blue"
|
|
220
223
|
}),
|
|
221
224
|
/* @__PURE__ */ D(a, {
|
|
222
|
-
label:
|
|
225
|
+
label: m("technospam.problems.statSolved", "Solved"),
|
|
223
226
|
value: ye,
|
|
224
|
-
icon:
|
|
227
|
+
icon: y,
|
|
225
228
|
tone: "green"
|
|
226
229
|
}),
|
|
227
230
|
/* @__PURE__ */ D(a, {
|
|
228
|
-
label:
|
|
231
|
+
label: m("technospam.problems.statEasy", "Easy"),
|
|
229
232
|
value: be,
|
|
230
|
-
icon:
|
|
233
|
+
icon: S,
|
|
231
234
|
tone: "teal"
|
|
232
235
|
}),
|
|
233
236
|
/* @__PURE__ */ D(a, {
|
|
234
|
-
label:
|
|
237
|
+
label: m("technospam.problems.statHard", "Hard"),
|
|
235
238
|
value: xe,
|
|
236
|
-
icon:
|
|
239
|
+
icon: x,
|
|
237
240
|
tone: "amber"
|
|
238
241
|
})
|
|
239
242
|
]
|
|
240
243
|
}), /* @__PURE__ */ O(L, {
|
|
241
244
|
className: "gap-0",
|
|
242
|
-
value:
|
|
243
|
-
onValueChange: (e) =>
|
|
245
|
+
value: C,
|
|
246
|
+
onValueChange: (e) => T(e),
|
|
244
247
|
children: [
|
|
245
248
|
/* @__PURE__ */ O("div", {
|
|
246
249
|
className: "mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",
|
|
@@ -249,18 +252,18 @@ function H() {
|
|
|
249
252
|
children: e.label
|
|
250
253
|
}, e.value)) }), /* @__PURE__ */ O("div", {
|
|
251
254
|
className: "relative w-full sm:w-72",
|
|
252
|
-
children: [/* @__PURE__ */ D(
|
|
255
|
+
children: [/* @__PURE__ */ D(w, {
|
|
253
256
|
className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-text-muted",
|
|
254
257
|
size: 16
|
|
255
|
-
}), /* @__PURE__ */ D(
|
|
258
|
+
}), /* @__PURE__ */ D(de, {
|
|
256
259
|
className: "pl-9",
|
|
257
|
-
"aria-label":
|
|
258
|
-
placeholder:
|
|
260
|
+
"aria-label": m("technospam.problems.searchLabel", "Search problems"),
|
|
261
|
+
placeholder: m("technospam.problems.searchPlaceholder", "Search problems…"),
|
|
259
262
|
value: U,
|
|
260
263
|
onChange: (e) => fe(e.target.value),
|
|
261
264
|
onBlur: () => {
|
|
262
265
|
let e = U.trim();
|
|
263
|
-
e &&
|
|
266
|
+
e && f("technospam.search.performed", {
|
|
264
267
|
workspaceId: c,
|
|
265
268
|
entityType: "problem",
|
|
266
269
|
queryLength: e.length
|
|
@@ -273,30 +276,30 @@ function H() {
|
|
|
273
276
|
className: "mb-4 flex flex-col gap-3 sm:flex-row sm:items-center",
|
|
274
277
|
children: [
|
|
275
278
|
/* @__PURE__ */ O(M, {
|
|
276
|
-
value:
|
|
277
|
-
onValueChange:
|
|
279
|
+
value: E,
|
|
280
|
+
onValueChange: k,
|
|
278
281
|
children: [/* @__PURE__ */ D(F, {
|
|
279
282
|
className: "w-full sm:w-48",
|
|
280
|
-
"aria-label":
|
|
281
|
-
children: /* @__PURE__ */ D(I, { placeholder:
|
|
283
|
+
"aria-label": m("technospam.problems.companyFilterLabel", "Filter by company"),
|
|
284
|
+
children: /* @__PURE__ */ D(I, { placeholder: m("technospam.problems.companyPlaceholder", "Company") })
|
|
282
285
|
}), /* @__PURE__ */ O(N, { children: [/* @__PURE__ */ D(P, {
|
|
283
286
|
value: "ALL",
|
|
284
|
-
children:
|
|
287
|
+
children: m("technospam.problems.companyAll", "All companies")
|
|
285
288
|
}), he.map((e) => /* @__PURE__ */ D(P, {
|
|
286
289
|
value: e.value,
|
|
287
290
|
children: e.label
|
|
288
291
|
}, e.value))] })]
|
|
289
292
|
}),
|
|
290
293
|
/* @__PURE__ */ O(M, {
|
|
291
|
-
value:
|
|
292
|
-
onValueChange:
|
|
294
|
+
value: j,
|
|
295
|
+
onValueChange: H,
|
|
293
296
|
children: [/* @__PURE__ */ D(F, {
|
|
294
297
|
className: "w-full sm:w-48",
|
|
295
|
-
"aria-label":
|
|
296
|
-
children: /* @__PURE__ */ D(I, { placeholder:
|
|
298
|
+
"aria-label": m("technospam.problems.roleFilterLabel", "Filter by role"),
|
|
299
|
+
children: /* @__PURE__ */ D(I, { placeholder: m("technospam.problems.rolePlaceholder", "Role") })
|
|
297
300
|
}), /* @__PURE__ */ O(N, { children: [/* @__PURE__ */ D(P, {
|
|
298
301
|
value: "ALL",
|
|
299
|
-
children:
|
|
302
|
+
children: m("technospam.problems.roleAll", "All roles")
|
|
300
303
|
}), ge.map((e) => /* @__PURE__ */ D(P, {
|
|
301
304
|
value: e.value,
|
|
302
305
|
children: e.label
|
|
@@ -304,7 +307,7 @@ function H() {
|
|
|
304
307
|
}),
|
|
305
308
|
/* @__PURE__ */ O("div", {
|
|
306
309
|
role: "group",
|
|
307
|
-
"aria-label":
|
|
310
|
+
"aria-label": m("technospam.problems.viewLabel", "Catalog layout"),
|
|
308
311
|
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",
|
|
309
312
|
children: [/* @__PURE__ */ O(A, {
|
|
310
313
|
type: "button",
|
|
@@ -312,38 +315,38 @@ function H() {
|
|
|
312
315
|
variant: W === "table" ? "secondary" : "ghost",
|
|
313
316
|
"aria-pressed": W === "table",
|
|
314
317
|
onClick: () => G("table"),
|
|
315
|
-
children: [/* @__PURE__ */ D(
|
|
318
|
+
children: [/* @__PURE__ */ D(ue, {
|
|
316
319
|
size: 14,
|
|
317
320
|
className: "mr-1.5",
|
|
318
321
|
"aria-hidden": "true"
|
|
319
|
-
}),
|
|
322
|
+
}), m("technospam.problems.viewTable", "Table")]
|
|
320
323
|
}), /* @__PURE__ */ O(A, {
|
|
321
324
|
type: "button",
|
|
322
325
|
size: "sm",
|
|
323
326
|
variant: W === "cards" ? "secondary" : "ghost",
|
|
324
327
|
"aria-pressed": W === "cards",
|
|
325
328
|
onClick: () => G("cards"),
|
|
326
|
-
children: [/* @__PURE__ */ D(
|
|
329
|
+
children: [/* @__PURE__ */ D(le, {
|
|
327
330
|
size: 14,
|
|
328
331
|
className: "mr-1.5",
|
|
329
332
|
"aria-hidden": "true"
|
|
330
|
-
}),
|
|
333
|
+
}), m("technospam.problems.viewCards", "Cards")]
|
|
331
334
|
})]
|
|
332
335
|
})
|
|
333
336
|
]
|
|
334
337
|
}),
|
|
335
338
|
/* @__PURE__ */ O(R, {
|
|
336
|
-
value:
|
|
337
|
-
className:
|
|
339
|
+
value: C,
|
|
340
|
+
className: ce,
|
|
338
341
|
children: [
|
|
339
|
-
q && !X.length ? /* @__PURE__ */ D(
|
|
340
|
-
title:
|
|
342
|
+
q && !X.length ? /* @__PURE__ */ D(p, {}) : J ? /* @__PURE__ */ D(re, {
|
|
343
|
+
title: m("technospam.common.errorLoadProblems", "Couldn't load problems"),
|
|
341
344
|
error: J
|
|
342
|
-
}) : Q.length === 0 ? /* @__PURE__ */ D(
|
|
343
|
-
icon: /* @__PURE__ */ D(
|
|
344
|
-
title: U ||
|
|
345
|
-
description: U ||
|
|
346
|
-
}) : W === "table" ? /* @__PURE__ */ D(
|
|
345
|
+
}) : Q.length === 0 ? /* @__PURE__ */ D(ne, {
|
|
346
|
+
icon: /* @__PURE__ */ D(b, { size: 26 }),
|
|
347
|
+
title: U || C !== "ALL" || E !== "ALL" || j !== "ALL" ? m("technospam.problems.emptySearchTitle", "No matching problems") : m("technospam.problems.emptyDefaultTitle", "No problems published yet"),
|
|
348
|
+
description: U || C !== "ALL" || E !== "ALL" || j !== "ALL" ? m("technospam.problems.emptySearchDesc", "Try a different search, difficulty, company, or role filter.") : m("technospam.problems.emptyDefaultDesc", "Published problems will appear here. Check back soon — TechnoSpam is pre-launch.")
|
|
349
|
+
}) : W === "table" ? /* @__PURE__ */ D(ee, {
|
|
347
350
|
problems: Q,
|
|
348
351
|
solvedIds: Y,
|
|
349
352
|
onOpen: $
|
|
@@ -357,10 +360,10 @@ function H() {
|
|
|
357
360
|
}),
|
|
358
361
|
Q.length > 0 && !J ? /* @__PURE__ */ D("p", {
|
|
359
362
|
className: "mt-4 text-sm text-text-muted",
|
|
360
|
-
children: Q.length < X.length ?
|
|
363
|
+
children: Q.length < X.length ? m("technospam.problems.showingFiltered", "Showing {{shown}} of {{total}} loaded problems", {
|
|
361
364
|
shown: Q.length,
|
|
362
365
|
total: X.length
|
|
363
|
-
}) : Z ?
|
|
366
|
+
}) : Z ? m("technospam.problems.showingMore", "Showing 1 to {{count}} problems — load more to see the rest", { count: Q.length }) : m("technospam.problems.showingAll", "Showing all {{count}} problems", { count: Q.length })
|
|
364
367
|
}) : null,
|
|
365
368
|
/* @__PURE__ */ D(te, {
|
|
366
369
|
hasMore: Z,
|
|
@@ -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 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\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 // 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":";;;;;;;;;;;;;;;;;;;;AA6DA,SAAS,EAAY,EACnB,YACA,WACA,aAKC;CACD,IAAM,IAAK,GAAO;AAClB,QACE,kBAAC,GAAD;EACE,WAAU;EACV,SAAS;YAFX,CAIE,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;gBACX,EAAQ;MACN,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,MAAW,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,GAAwB,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,EAAiB;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,IAAD;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,GAAD;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,IAAD,EAAe,CAAA,GACb,IACF,kBAAC,GAAD;OACE,OAAO,EAAG,uCAAuC,yBAAyB;OACnE;OACP,CAAA,GACA,EAAS,WAAW,IACtB,kBAAC,GAAD;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,GAAD;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 {\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":"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"}
|