@burdenoff/microfe-technospam 2026.1031.3 → 2026.1031.5

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