@burdenoff/microfe-technospam 2026.716.2 → 2026.719.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (32) hide show
  1. package/dist/node_modules/recharts/es6/cartesian/Brush.js.map +1 -1
  2. package/dist/node_modules/recharts/es6/cartesian/ErrorBar.js.map +1 -1
  3. package/dist/node_modules/recharts/es6/cartesian/ReferenceArea.js.map +1 -1
  4. package/dist/node_modules/recharts/es6/cartesian/ReferenceDot.js.map +1 -1
  5. package/dist/node_modules/recharts/es6/cartesian/ReferenceLine.js.map +1 -1
  6. package/dist/node_modules/recharts/es6/cartesian/XAxis.js.map +1 -1
  7. package/dist/node_modules/recharts/es6/cartesian/YAxis.js.map +1 -1
  8. package/dist/node_modules/recharts/es6/component/ResponsiveContainer.js.map +1 -1
  9. package/dist/node_modules/sonner/dist/index.js.map +1 -1
  10. package/dist/technospam/TechnoSpamRoot.d.ts.map +1 -1
  11. package/dist/technospam/TechnoSpamRoot.js +190 -250
  12. package/dist/technospam/TechnoSpamRoot.js.map +1 -1
  13. package/dist/technospam/TechnoSpamRoutes.d.ts.map +1 -1
  14. package/dist/technospam/TechnoSpamRoutes.js.map +1 -1
  15. package/dist/technospam/api/technospamApi.d.ts +20 -9
  16. package/dist/technospam/api/technospamApi.d.ts.map +1 -1
  17. package/dist/technospam/api/technospamApi.js +62 -16
  18. package/dist/technospam/api/technospamApi.js.map +1 -1
  19. package/dist/technospam/pages/AdminAdminDashboardPagePage.js +2 -2
  20. package/dist/technospam/pages/AdminAdminDashboardPagePage.js.map +1 -1
  21. package/dist/technospam/pages/AnalyticsAnalyticsPagePage.js +1 -1
  22. package/dist/technospam/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
  23. package/dist/technospam/pages/ApplicationsProblemsPagePage.d.ts.map +1 -1
  24. package/dist/technospam/pages/ApplicationsProblemsPagePage.js +176 -76
  25. package/dist/technospam/pages/ApplicationsProblemsPagePage.js.map +1 -1
  26. package/dist/technospam/pages/DashboardDashboardPagePage.js +1 -1
  27. package/dist/technospam/pages/DashboardDashboardPagePage.js.map +1 -1
  28. package/dist/technospam/pages/HackathonsHackathonDetailsPagePage.js +1 -1
  29. package/dist/technospam/pages/HackathonsHackathonDetailsPagePage.js.map +1 -1
  30. package/dist/technospam/pages/MentorshipSessionsPagePage.js +1 -1
  31. package/dist/technospam/pages/MentorshipSessionsPagePage.js.map +1 -1
  32. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"AnalyticsAnalyticsPagePage.js","names":[],"sources":["../../../src/technospam/pages/AnalyticsAnalyticsPagePage.tsx"],"sourcesContent":["import { useMemo, type CSSProperties, type SVGProps } from 'react';\nimport {\n Activity,\n Award,\n BarChart3,\n BookOpen,\n Code2,\n GraduationCap,\n Library,\n Percent,\n TrendingUp,\n Trophy,\n Users,\n type LucideIcon,\n} from 'lucide-react';\nimport { GlassCard, Button } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\nimport {\n Area,\n AreaChart,\n Bar,\n BarChart,\n CartesianGrid,\n Cell,\n Legend,\n Pie,\n PieChart,\n ResponsiveContainer,\n Tooltip as ReTooltip,\n XAxis,\n YAxis,\n} from 'recharts';\nimport {\n EmptyView,\n ErrorView,\n LoadingGrid,\n LoadingRows,\n PageLayout,\n StatCard,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n useCertificatesQuery,\n useCoursesQuery,\n useEnrollmentsQuery,\n useHackathonsQuery,\n useProblemsQuery,\n useSessionsQuery,\n useSubmissionsQuery,\n type TecEnrollmentStatus,\n type TecSubmissionVerdict,\n} from '../api/technospamApi';\nimport { humanizeEnum } from '../utils/format';\n\n/* ----------------------------------------------------------- chart config */\n\nconst ENROLL_STATUS_ORDER: TecEnrollmentStatus[] = [\n 'ENROLLED',\n 'IN_PROGRESS',\n 'COMPLETED',\n 'DROPPED',\n];\n\nconst ENROLL_STATUS_COLOR: Record<TecEnrollmentStatus, string> = {\n ENROLLED: 'var(--color-accent-primary)',\n IN_PROGRESS: 'var(--color-accent-teal)',\n COMPLETED: 'var(--color-status-success)',\n DROPPED: 'var(--color-status-warning)',\n};\n\nconst VERDICT_ORDER: TecSubmissionVerdict[] = [\n 'ACCEPTED',\n 'WRONG_ANSWER',\n 'TIME_LIMIT_EXCEEDED',\n 'RUNTIME_ERROR',\n 'COMPILE_ERROR',\n 'PENDING',\n];\n\nconst VERDICT_LABEL: Record<TecSubmissionVerdict, string> = {\n ACCEPTED: 'Accepted',\n WRONG_ANSWER: 'Wrong',\n TIME_LIMIT_EXCEEDED: 'TLE',\n RUNTIME_ERROR: 'Runtime',\n COMPILE_ERROR: 'Compile',\n PENDING: 'Pending',\n};\n\nconst VERDICT_COLOR: Record<TecSubmissionVerdict, string> = {\n ACCEPTED: 'var(--color-status-success)',\n WRONG_ANSWER: 'var(--color-status-error)',\n TIME_LIMIT_EXCEEDED: 'var(--color-status-warning)',\n RUNTIME_ERROR: 'var(--color-status-error)',\n COMPILE_ERROR: 'var(--color-status-error)',\n PENDING: 'var(--color-text-muted)',\n};\n\nconst TOOLTIP_STYLE: CSSProperties = {\n background: 'var(--color-bg-surface)',\n border: '1px solid var(--color-border-seam)',\n borderRadius: 12,\n color: 'var(--color-text-primary)',\n fontSize: 12,\n};\nconst TOOLTIP_LABEL_STYLE: CSSProperties = { color: 'var(--color-text-secondary)' };\nconst AXIS_TICK: SVGProps<SVGTextElement> = { fill: 'var(--color-text-muted)', fontSize: 12 };\n\ninterface StatusDatum {\n name: string;\n value: number;\n color: string;\n}\ninterface VerdictDatum {\n label: string;\n count: number;\n color: string;\n}\ninterface ActivityDatum {\n label: string;\n submissions: number;\n enrollments: number;\n}\n\n/** Normalize an ISO timestamp to the start of its (Sunday-based) week. */\nfunction weekBucket(iso: string): { key: number; label: string } {\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return { key: 0, label: '—' };\n const start = new Date(d.getFullYear(), d.getMonth(), d.getDate() - d.getDay());\n return {\n key: start.getTime(),\n label: start.toLocaleDateString(undefined, { month: 'short', day: 'numeric' }),\n };\n}\n\n/* -------------------------------------------------------- chart wrappers */\n\ntype ChipTone = 'blue' | 'teal' | 'purple' | 'indigo';\n\nconst CHIP: Record<ChipTone, string> = {\n blue: 'bg-accent-primary/15 text-accent-primary ring-accent-primary/20',\n teal: 'bg-accent-teal/15 text-accent-teal ring-accent-teal/20',\n purple: 'bg-accent-purple/15 text-accent-purple ring-accent-purple/20',\n indigo: 'bg-accent-indigo/15 text-accent-indigo ring-accent-indigo/20',\n};\n\nfunction ChartHeader({\n icon: Icon,\n title,\n subtitle,\n tone,\n}: {\n icon: LucideIcon;\n title: string;\n subtitle?: string;\n tone: ChipTone;\n}) {\n return (\n <div className=\"mb-4 flex items-center gap-3\">\n <span\n className={cn(\n 'flex h-9 w-9 shrink-0 items-center justify-center rounded-xl ring-1',\n CHIP[tone]\n )}\n >\n <Icon size={17} />\n </span>\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-sm font-semibold text-text-primary\">{title}</h3>\n {subtitle ? <p className=\"truncate text-xs text-text-muted\">{subtitle}</p> : null}\n </div>\n </div>\n );\n}\n\nfunction ChartCard({\n title,\n subtitle,\n icon,\n tone = 'teal',\n isEmpty,\n emptyLabel,\n children,\n}: {\n title: string;\n subtitle?: string;\n icon: LucideIcon;\n tone?: ChipTone;\n isEmpty: boolean;\n emptyLabel: string;\n children: React.ReactElement;\n}) {\n return (\n <GlassCard className=\"p-5 transition-shadow duration-200 hover:shadow-lg\">\n <ChartHeader icon={icon} title={title} subtitle={subtitle} tone={tone} />\n {isEmpty ? (\n <div className=\"flex h-[240px] items-center justify-center text-center text-sm text-text-muted\">\n {emptyLabel}\n </div>\n ) : (\n <ResponsiveContainer width=\"100%\" height={260}>\n {children}\n </ResponsiveContainer>\n )}\n </GlassCard>\n );\n}\n\n/* --------------------------------------------------------------- page */\n\nexport function AnalyticsAnalyticsPagePage() {\n const navigate = useTechnoSpamNavigate();\n const { currentUser } = useTechnoSpam();\n const learnerId = currentUser?.id;\n\n const enrollmentsQ = useEnrollmentsQuery(learnerId ? { learnerId } : {});\n const submissionsQ = useSubmissionsQuery(learnerId ? { learnerId } : {});\n const sessionsQ = useSessionsQuery(learnerId ? { menteeId: learnerId } : {});\n const certificatesQ = useCertificatesQuery(learnerId ? { learnerId } : {});\n const coursesQ = useCoursesQuery({ status: 'PUBLISHED' });\n const problemsQ = useProblemsQuery({ status: 'PUBLISHED' });\n const hackathonsQ = useHackathonsQuery({});\n\n const enrollments = useMemo(\n () => enrollmentsQ.data?.tecListEnrollments.items ?? [],\n [enrollmentsQ.data]\n );\n const submissions = useMemo(\n () => submissionsQ.data?.tecListSubmissions.items ?? [],\n [submissionsQ.data]\n );\n const sessions = sessionsQ.data?.tecListSessions ?? [];\n const certificates = certificatesQ.data?.tecListCertificates ?? [];\n const coursesTotal = coursesQ.data?.tecListCourses.items.length ?? 0;\n const problemsTotal = problemsQ.data?.tecListProblems.items.length ?? 0;\n const hackathonsTotal = hackathonsQ.data?.tecListHackathons.items.length ?? 0;\n\n const metrics = useMemo(() => {\n const coursesCompleted = enrollments.filter((e) => e.status === 'COMPLETED').length;\n const avgProgress = enrollments.length\n ? Math.round(enrollments.reduce((s, e) => s + e.progressPct, 0) / enrollments.length)\n : 0;\n const acceptedSubs = submissions.filter((s) => s.verdict === 'ACCEPTED');\n const problemsSolved = new Set(acceptedSubs.map((s) => s.problemId)).size;\n const acceptanceRate = submissions.length\n ? Math.round((acceptedSubs.length / submissions.length) * 100)\n : 0;\n return { coursesCompleted, avgProgress, problemsSolved, acceptanceRate };\n }, [enrollments, submissions]);\n\n const enrollmentsByStatus = useMemo<StatusDatum[]>(\n () =>\n ENROLL_STATUS_ORDER.map((s) => ({\n name: humanizeEnum(s),\n value: enrollments.filter((e) => e.status === s).length,\n color: ENROLL_STATUS_COLOR[s],\n })).filter((d) => d.value > 0),\n [enrollments]\n );\n\n const submissionsByVerdict = useMemo<VerdictDatum[]>(\n () =>\n VERDICT_ORDER.map((v) => ({\n label: VERDICT_LABEL[v],\n count: submissions.filter((s) => s.verdict === v).length,\n color: VERDICT_COLOR[v],\n })).filter((d) => d.count > 0),\n [submissions]\n );\n\n const activityData = useMemo<ActivityDatum[]>(() => {\n const buckets = new Map<number, ActivityDatum>();\n const add = (iso: string, key: 'submissions' | 'enrollments') => {\n const { key: bucketKey, label } = weekBucket(iso);\n const existing = buckets.get(bucketKey) ?? { label, submissions: 0, enrollments: 0 };\n existing[key] += 1;\n buckets.set(bucketKey, existing);\n };\n submissions.forEach((s) => add(s.createdAt, 'submissions'));\n enrollments.forEach((e) => add(e.createdAt, 'enrollments'));\n return [...buckets.entries()]\n .sort((a, b) => a[0] - b[0])\n .slice(-8)\n .map(([, v]) => v);\n }, [submissions, enrollments]);\n\n const hasActivity =\n enrollments.length > 0 ||\n submissions.length > 0 ||\n sessions.length > 0 ||\n certificates.length > 0;\n\n const learnerLoading =\n enrollmentsQ.loading || submissionsQ.loading || sessionsQ.loading || certificatesQ.loading;\n const learnerError =\n enrollmentsQ.error || submissionsQ.error || sessionsQ.error || certificatesQ.error;\n const showLoading = learnerLoading && !hasActivity;\n\n const workspaceOverview = (\n <div>\n <h2 className=\"mb-3 text-base font-semibold text-text-primary\">Workspace overview</h2>\n <p className=\"mb-4 max-w-2xl text-sm text-text-secondary\">\n Live content available across the TechnoSpam learning workspace right now.\n </p>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-3\">\n <StatCard label=\"Published courses\" value={coursesTotal} icon={Library} tone=\"blue\" />\n <StatCard label=\"Published problems\" value={problemsTotal} icon={Code2} tone=\"indigo\" />\n <StatCard label=\"Hackathons\" value={hackathonsTotal} icon={Trophy} tone=\"purple\" />\n </div>\n </div>\n );\n\n return (\n <PageLayout\n hero\n title=\"Analytics\"\n description=\"Your real learning performance across courses, problems, mentoring and certificates.\"\n actions={\n <Button variant=\"outline\" onClick={() => navigate('/courses/my-learning-page')}>\n <GraduationCap size={16} /> My Learning\n </Button>\n }\n >\n {!learnerId ? (\n <div className=\"space-y-8\">\n <EmptyView\n icon={<BarChart3 size={26} />}\n title=\"Sign in to view your analytics\"\n description=\"Your personal learning metrics appear here once you're signed in.\"\n />\n {workspaceOverview}\n </div>\n ) : showLoading ? (\n <div className=\"space-y-6\">\n <LoadingGrid count={4} height=\"h-24\" />\n <LoadingRows count={2} height=\"h-72\" />\n </div>\n ) : learnerError && !hasActivity ? (\n <ErrorView title=\"Couldn't load your analytics\" error={learnerError} />\n ) : (\n <div className=\"space-y-8\">\n <div className=\"grid grid-cols-2 gap-3 sm:grid-cols-3 xl:grid-cols-4\">\n <StatCard\n label=\"Courses enrolled\"\n value={enrollments.length}\n icon={BookOpen}\n tone=\"blue\"\n />\n <StatCard\n label=\"Courses completed\"\n value={metrics.coursesCompleted}\n icon={GraduationCap}\n tone=\"green\"\n />\n <StatCard\n label=\"Avg progress\"\n value={`${metrics.avgProgress}%`}\n icon={TrendingUp}\n tone=\"purple\"\n />\n <StatCard\n label=\"Problems solved\"\n value={metrics.problemsSolved}\n icon={Code2}\n tone=\"indigo\"\n />\n <StatCard\n label=\"Acceptance rate\"\n value={`${metrics.acceptanceRate}%`}\n icon={Percent}\n tone=\"teal\"\n />\n <StatCard label=\"Mentor sessions\" value={sessions.length} icon={Users} tone=\"blue\" />\n <StatCard label=\"Certificates\" value={certificates.length} icon={Award} tone=\"amber\" />\n </div>\n\n {!hasActivity ? (\n <EmptyView\n icon={<Activity size={26} />}\n title=\"No learning activity yet\"\n description=\"Enroll in a course or solve a problem and your progress charts will build here. TechnoSpam is pre-launch — start learning to see your data.\"\n action={\n <Button onClick={() => navigate('/courses/courses-page')}>Browse courses</Button>\n }\n />\n ) : (\n <div className=\"space-y-4\">\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-2\">\n <ChartCard\n title=\"Enrollments by status\"\n subtitle=\"How your courses are progressing\"\n icon={BookOpen}\n tone=\"blue\"\n isEmpty={enrollmentsByStatus.length === 0}\n emptyLabel=\"No enrollments yet\"\n >\n <PieChart>\n <Pie\n data={enrollmentsByStatus}\n dataKey=\"value\"\n nameKey=\"name\"\n innerRadius={60}\n outerRadius={92}\n paddingAngle={2}\n stroke=\"var(--color-bg-surface)\"\n strokeWidth={2}\n >\n {enrollmentsByStatus.map((d) => (\n <Cell key={d.name} fill={d.color} />\n ))}\n </Pie>\n <ReTooltip contentStyle={TOOLTIP_STYLE} labelStyle={TOOLTIP_LABEL_STYLE} />\n <Legend iconType=\"circle\" wrapperStyle={{ fontSize: 12 }} />\n </PieChart>\n </ChartCard>\n\n <ChartCard\n title=\"Submissions by verdict\"\n subtitle=\"Outcomes across your attempts\"\n icon={Code2}\n tone=\"purple\"\n isEmpty={submissionsByVerdict.length === 0}\n emptyLabel=\"No submissions yet\"\n >\n <BarChart\n data={submissionsByVerdict}\n margin={{ top: 8, right: 8, left: -18, bottom: 0 }}\n >\n <CartesianGrid\n strokeDasharray=\"3 3\"\n stroke=\"var(--color-border-seam)\"\n vertical={false}\n />\n <XAxis\n dataKey=\"label\"\n tick={AXIS_TICK}\n axisLine={{ stroke: 'var(--color-border-seam)' }}\n tickLine={false}\n />\n <YAxis\n allowDecimals={false}\n tick={AXIS_TICK}\n axisLine={false}\n tickLine={false}\n />\n <ReTooltip\n contentStyle={TOOLTIP_STYLE}\n labelStyle={TOOLTIP_LABEL_STYLE}\n cursor={{ fill: 'var(--color-accent-primary)', fillOpacity: 0.08 }}\n />\n <Bar dataKey=\"count\" name=\"Submissions\" radius={[6, 6, 0, 0]} maxBarSize={56}>\n {submissionsByVerdict.map((d) => (\n <Cell key={d.label} fill={d.color} />\n ))}\n </Bar>\n </BarChart>\n </ChartCard>\n </div>\n\n <GlassCard className=\"p-5 transition-shadow duration-200 hover:shadow-lg\">\n <ChartHeader\n icon={Activity}\n title=\"Learning activity over time\"\n subtitle=\"Submissions and enrollments per week\"\n tone=\"teal\"\n />\n {activityData.length === 0 ? (\n <div className=\"flex h-[240px] items-center justify-center text-sm text-text-muted\">\n No dated activity yet\n </div>\n ) : (\n <ResponsiveContainer width=\"100%\" height={280}>\n <AreaChart\n data={activityData}\n margin={{ top: 8, right: 12, left: -18, bottom: 0 }}\n >\n <defs>\n <linearGradient id=\"tec-grad-submissions\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop\n offset=\"0%\"\n stopColor=\"var(--color-accent-primary)\"\n stopOpacity={0.35}\n />\n <stop\n offset=\"100%\"\n stopColor=\"var(--color-accent-primary)\"\n stopOpacity={0}\n />\n </linearGradient>\n <linearGradient id=\"tec-grad-enrollments\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop\n offset=\"0%\"\n stopColor=\"var(--color-accent-teal)\"\n stopOpacity={0.35}\n />\n <stop\n offset=\"100%\"\n stopColor=\"var(--color-accent-teal)\"\n stopOpacity={0}\n />\n </linearGradient>\n </defs>\n <CartesianGrid\n strokeDasharray=\"3 3\"\n stroke=\"var(--color-border-seam)\"\n vertical={false}\n />\n <XAxis\n dataKey=\"label\"\n tick={AXIS_TICK}\n axisLine={{ stroke: 'var(--color-border-seam)' }}\n tickLine={false}\n />\n <YAxis\n allowDecimals={false}\n tick={AXIS_TICK}\n axisLine={false}\n tickLine={false}\n />\n <ReTooltip contentStyle={TOOLTIP_STYLE} labelStyle={TOOLTIP_LABEL_STYLE} />\n <Legend iconType=\"circle\" wrapperStyle={{ fontSize: 12 }} />\n <Area\n type=\"monotone\"\n dataKey=\"submissions\"\n name=\"Submissions\"\n stroke=\"var(--color-accent-primary)\"\n strokeWidth={2}\n fill=\"url(#tec-grad-submissions)\"\n />\n <Area\n type=\"monotone\"\n dataKey=\"enrollments\"\n name=\"Enrollments\"\n stroke=\"var(--color-accent-teal)\"\n strokeWidth={2}\n fill=\"url(#tec-grad-enrollments)\"\n />\n </AreaChart>\n </ResponsiveContainer>\n )}\n </GlassCard>\n </div>\n )}\n\n {workspaceOverview}\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAwDA,IAAM,KAA6C;CACjD;CACA;CACA;CACA;CACD,EAEK,KAA2D;CAC/D,UAAU;CACV,aAAa;CACb,WAAW;CACX,SAAS;CACV,EAEK,IAAwC;CAC5C;CACA;CACA;CACA;CACA;CACA;CACD,EAEK,IAAsD;CAC1D,UAAU;CACV,cAAc;CACd,qBAAqB;CACrB,eAAe;CACf,eAAe;CACf,SAAS;CACV,EAEK,IAAsD;CAC1D,UAAU;CACV,cAAc;CACd,qBAAqB;CACrB,eAAe;CACf,eAAe;CACf,SAAS;CACV,EAEK,IAA+B;CACnC,YAAY;CACZ,QAAQ;CACR,cAAc;CACd,OAAO;CACP,UAAU;CACX,EACK,IAAqC,EAAE,OAAO,+BAA+B,EAC7E,IAAsC;CAAE,MAAM;CAA2B,UAAU;CAAI;AAmB7F,SAAS,EAAW,GAA6C;CAC/D,IAAM,IAAI,IAAI,KAAK,EAAI;AACvB,KAAI,OAAO,MAAM,EAAE,SAAS,CAAC,CAAE,QAAO;EAAE,KAAK;EAAG,OAAO;EAAK;CAC5D,IAAM,IAAQ,IAAI,KAAK,EAAE,aAAa,EAAE,EAAE,UAAU,EAAE,EAAE,SAAS,GAAG,EAAE,QAAQ,CAAC;AAC/E,QAAO;EACL,KAAK,EAAM,SAAS;EACpB,OAAO,EAAM,mBAAmB,KAAA,GAAW;GAAE,OAAO;GAAS,KAAK;GAAW,CAAC;EAC/E;;AAOH,IAAM,IAAiC;CACrC,MAAM;CACN,MAAM;CACN,QAAQ;CACR,QAAQ;CACT;AAED,SAAS,EAAY,EACnB,MAAM,GACN,UACA,aACA,WAMC;AACD,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,QAAD;GACE,WAAW,EACT,uEACA,EAAK,GACN;aAED,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;GACb,CAAA,EACP,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,MAAD;IAAI,WAAU;cAAoD;IAAW,CAAA,EAC5E,IAAW,kBAAC,KAAD;IAAG,WAAU;cAAoC;IAAa,CAAA,GAAG,KACzE;KACF;;;AAIV,SAAS,EAAU,EACjB,UACA,aACA,SACA,UAAO,QACP,YACA,eACA,eASC;AACD,QACE,kBAAC,GAAD;EAAW,WAAU;YAArB,CACE,kBAAC,GAAD;GAAmB;GAAa;GAAiB;GAAgB;GAAQ,CAAA,EACxE,IACC,kBAAC,OAAD;GAAK,WAAU;aACZ;GACG,CAAA,GAEN,kBAAC,GAAD;GAAqB,OAAM;GAAO,QAAQ;GACvC;GACmB,CAAA,CAEd;;;AAMhB,SAAgB,IAA6B;CAC3C,IAAM,IAAW,GAAuB,EAClC,EAAE,mBAAgB,GAAe,EACjC,IAAY,GAAa,IAEzB,IAAe,GAAoB,IAAY,EAAE,cAAW,GAAG,EAAE,CAAC,EAClE,IAAe,GAAoB,IAAY,EAAE,cAAW,GAAG,EAAE,CAAC,EAClE,IAAY,GAAiB,IAAY,EAAE,UAAU,GAAW,GAAG,EAAE,CAAC,EACtE,IAAgB,GAAqB,IAAY,EAAE,cAAW,GAAG,EAAE,CAAC,EACpE,KAAW,GAAgB,EAAE,QAAQ,aAAa,CAAC,EACnD,KAAY,GAAiB,EAAE,QAAQ,aAAa,CAAC,EACrD,KAAc,GAAmB,EAAE,CAAC,EAEpC,IAAc,QACZ,EAAa,MAAM,mBAAmB,SAAS,EAAE,EACvD,CAAC,EAAa,KAAK,CACpB,EACK,IAAc,QACZ,EAAa,MAAM,mBAAmB,SAAS,EAAE,EACvD,CAAC,EAAa,KAAK,CACpB,EACK,IAAW,EAAU,MAAM,mBAAmB,EAAE,EAChD,IAAe,EAAc,MAAM,uBAAuB,EAAE,EAC5D,KAAe,GAAS,MAAM,eAAe,MAAM,UAAU,GAC7D,KAAgB,GAAU,MAAM,gBAAgB,MAAM,UAAU,GAChE,KAAkB,GAAY,MAAM,kBAAkB,MAAM,UAAU,GAEtE,IAAU,QAAc;EAC5B,IAAM,IAAmB,EAAY,QAAQ,MAAM,EAAE,WAAW,YAAY,CAAC,QACvE,IAAc,EAAY,SAC5B,KAAK,MAAM,EAAY,QAAQ,GAAG,MAAM,IAAI,EAAE,aAAa,EAAE,GAAG,EAAY,OAAO,GACnF,GACE,IAAe,EAAY,QAAQ,MAAM,EAAE,YAAY,WAAW;AAKxE,SAAO;GAAE;GAAkB;GAAa,gBAJjB,IAAI,IAAI,EAAa,KAAK,MAAM,EAAE,UAAU,CAAC,CAAC;GAIb,gBAHjC,EAAY,SAC/B,KAAK,MAAO,EAAa,SAAS,EAAY,SAAU,IAAI,GAC5D;GACoE;IACvE,CAAC,GAAa,EAAY,CAAC,EAExB,IAAsB,QAExB,GAAoB,KAAK,OAAO;EAC9B,MAAM,EAAa,EAAE;EACrB,OAAO,EAAY,QAAQ,MAAM,EAAE,WAAW,EAAE,CAAC;EACjD,OAAO,GAAoB;EAC5B,EAAE,CAAC,QAAQ,MAAM,EAAE,QAAQ,EAAE,EAChC,CAAC,EAAY,CACd,EAEK,IAAuB,QAEzB,EAAc,KAAK,OAAO;EACxB,OAAO,EAAc;EACrB,OAAO,EAAY,QAAQ,MAAM,EAAE,YAAY,EAAE,CAAC;EAClD,OAAO,EAAc;EACtB,EAAE,CAAC,QAAQ,MAAM,EAAE,QAAQ,EAAE,EAChC,CAAC,EAAY,CACd,EAEK,IAAe,QAA+B;EAClD,IAAM,oBAAU,IAAI,KAA4B,EAC1C,KAAO,GAAa,MAAuC;GAC/D,IAAM,EAAE,KAAK,GAAW,aAAU,EAAW,EAAI,EAC3C,IAAW,EAAQ,IAAI,EAAU,IAAI;IAAE;IAAO,aAAa;IAAG,aAAa;IAAG;AAEpF,GADA,EAAS,MAAQ,GACjB,EAAQ,IAAI,GAAW,EAAS;;AAIlC,SAFA,EAAY,SAAS,MAAM,EAAI,EAAE,WAAW,cAAc,CAAC,EAC3D,EAAY,SAAS,MAAM,EAAI,EAAE,WAAW,cAAc,CAAC,EACpD,CAAC,GAAG,EAAQ,SAAS,CAAC,CAC1B,MAAM,GAAG,MAAM,EAAE,KAAK,EAAE,GAAG,CAC3B,MAAM,GAAG,CACT,KAAK,GAAG,OAAO,EAAE;IACnB,CAAC,GAAa,EAAY,CAAC,EAExB,IACJ,EAAY,SAAS,KACrB,EAAY,SAAS,KACrB,EAAS,SAAS,KAClB,EAAa,SAAS,GAElB,KACJ,EAAa,WAAW,EAAa,WAAW,EAAU,WAAW,EAAc,SAC/E,IACJ,EAAa,SAAS,EAAa,SAAS,EAAU,SAAS,EAAc,OACzE,IAAc,MAAkB,CAAC,GAEjC,IACJ,kBAAC,OAAD,EAAA,UAAA;EACE,kBAAC,MAAD;GAAI,WAAU;aAAiD;GAAuB,CAAA;EACtF,kBAAC,KAAD;GAAG,WAAU;aAA6C;GAEtD,CAAA;EACJ,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,GAAD;KAAU,OAAM;KAAoB,OAAO;KAAc,MAAM;KAAS,MAAK;KAAS,CAAA;IACtF,kBAAC,GAAD;KAAU,OAAM;KAAqB,OAAO;KAAe,MAAM;KAAO,MAAK;KAAW,CAAA;IACxF,kBAAC,GAAD;KAAU,OAAM;KAAa,OAAO;KAAiB,MAAM;KAAQ,MAAK;KAAW,CAAA;IAC/E;;EACF,EAAA,CAAA;AAGR,QACE,kBAAC,GAAD;EACE,MAAA;EACA,OAAM;EACN,aAAY;EACZ,SACE,kBAAC,GAAD;GAAQ,SAAQ;GAAU,eAAe,EAAS,4BAA4B;aAA9E,CACE,kBAAC,GAAD,EAAe,MAAM,IAAM,CAAA,EAAA,eACpB;;YAGT,IASE,IACF,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,IAAD;IAAa,OAAO;IAAG,QAAO;IAAS,CAAA,EACvC,kBAAC,GAAD;IAAa,OAAO;IAAG,QAAO;IAAS,CAAA,CACnC;OACJ,KAAgB,CAAC,IACnB,kBAAC,IAAD;GAAW,OAAM;GAA+B,OAAO;GAAgB,CAAA,GAEvE,kBAAC,OAAD;GAAK,WAAU;aAAf;IACE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OACE,OAAM;OACN,OAAO,EAAY;OACnB,MAAM;OACN,MAAK;OACL,CAAA;MACF,kBAAC,GAAD;OACE,OAAM;OACN,OAAO,EAAQ;OACf,MAAM;OACN,MAAK;OACL,CAAA;MACF,kBAAC,GAAD;OACE,OAAM;OACN,OAAO,GAAG,EAAQ,YAAY;OAC9B,MAAM;OACN,MAAK;OACL,CAAA;MACF,kBAAC,GAAD;OACE,OAAM;OACN,OAAO,EAAQ;OACf,MAAM;OACN,MAAK;OACL,CAAA;MACF,kBAAC,GAAD;OACE,OAAM;OACN,OAAO,GAAG,EAAQ,eAAe;OACjC,MAAM;OACN,MAAK;OACL,CAAA;MACF,kBAAC,GAAD;OAAU,OAAM;OAAkB,OAAO,EAAS;OAAQ,MAAM;OAAO,MAAK;OAAS,CAAA;MACrF,kBAAC,GAAD;OAAU,OAAM;OAAe,OAAO,EAAa;OAAQ,MAAM;OAAO,MAAK;OAAU,CAAA;MACnF;;IAEJ,IAUA,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,GAAD;OACE,OAAM;OACN,UAAS;OACT,MAAM;OACN,MAAK;OACL,SAAS,EAAoB,WAAW;OACxC,YAAW;iBAEX,kBAAC,IAAD,EAAA,UAAA;QACE,kBAAC,IAAD;SACE,MAAM;SACN,SAAQ;SACR,SAAQ;SACR,aAAa;SACb,aAAa;SACb,cAAc;SACd,QAAO;SACP,aAAa;mBAEZ,EAAoB,KAAK,MACxB,kBAAC,GAAD,EAAmB,MAAM,EAAE,OAAS,EAAzB,EAAE,KAAuB,CACpC;SACE,CAAA;QACN,kBAAC,GAAD;SAAW,cAAc;SAAe,YAAY;SAAuB,CAAA;QAC3E,kBAAC,GAAD;SAAQ,UAAS;SAAS,cAAc,EAAE,UAAU,IAAI;SAAI,CAAA;QACnD,EAAA,CAAA;OACD,CAAA,EAEZ,kBAAC,GAAD;OACE,OAAM;OACN,UAAS;OACT,MAAM;OACN,MAAK;OACL,SAAS,EAAqB,WAAW;OACzC,YAAW;iBAEX,kBAAC,IAAD;QACE,MAAM;QACN,QAAQ;SAAE,KAAK;SAAG,OAAO;SAAG,MAAM;SAAK,QAAQ;SAAG;kBAFpD;SAIE,kBAAC,GAAD;UACE,iBAAgB;UAChB,QAAO;UACP,UAAU;UACV,CAAA;SACF,kBAAC,GAAD;UACE,SAAQ;UACR,MAAM;UACN,UAAU,EAAE,QAAQ,4BAA4B;UAChD,UAAU;UACV,CAAA;SACF,kBAAC,GAAD;UACE,eAAe;UACf,MAAM;UACN,UAAU;UACV,UAAU;UACV,CAAA;SACF,kBAAC,GAAD;UACE,cAAc;UACd,YAAY;UACZ,QAAQ;WAAE,MAAM;WAA+B,aAAa;WAAM;UAClE,CAAA;SACF,kBAAC,IAAD;UAAK,SAAQ;UAAQ,MAAK;UAAc,QAAQ;WAAC;WAAG;WAAG;WAAG;WAAE;UAAE,YAAY;oBACvE,EAAqB,KAAK,MACzB,kBAAC,GAAD,EAAoB,MAAM,EAAE,OAAS,EAA1B,EAAE,MAAwB,CACrC;UACE,CAAA;SACG;;OACD,CAAA,CACR;SAEN,kBAAC,GAAD;MAAW,WAAU;gBAArB,CACE,kBAAC,GAAD;OACE,MAAM;OACN,OAAM;OACN,UAAS;OACT,MAAK;OACL,CAAA,EACD,EAAa,WAAW,IACvB,kBAAC,OAAD;OAAK,WAAU;iBAAqE;OAE9E,CAAA,GAEN,kBAAC,GAAD;OAAqB,OAAM;OAAO,QAAQ;iBACxC,kBAAC,IAAD;QACE,MAAM;QACN,QAAQ;SAAE,KAAK;SAAG,OAAO;SAAI,MAAM;SAAK,QAAQ;SAAG;kBAFrD;SAIE,kBAAC,QAAD,EAAA,UAAA,CACE,kBAAC,kBAAD;UAAgB,IAAG;UAAuB,IAAG;UAAI,IAAG;UAAI,IAAG;UAAI,IAAG;oBAAlE,CACE,kBAAC,QAAD;WACE,QAAO;WACP,WAAU;WACV,aAAa;WACb,CAAA,EACF,kBAAC,QAAD;WACE,QAAO;WACP,WAAU;WACV,aAAa;WACb,CAAA,CACa;aACjB,kBAAC,kBAAD;UAAgB,IAAG;UAAuB,IAAG;UAAI,IAAG;UAAI,IAAG;UAAI,IAAG;oBAAlE,CACE,kBAAC,QAAD;WACE,QAAO;WACP,WAAU;WACV,aAAa;WACb,CAAA,EACF,kBAAC,QAAD;WACE,QAAO;WACP,WAAU;WACV,aAAa;WACb,CAAA,CACa;YACZ,EAAA,CAAA;SACP,kBAAC,GAAD;UACE,iBAAgB;UAChB,QAAO;UACP,UAAU;UACV,CAAA;SACF,kBAAC,GAAD;UACE,SAAQ;UACR,MAAM;UACN,UAAU,EAAE,QAAQ,4BAA4B;UAChD,UAAU;UACV,CAAA;SACF,kBAAC,GAAD;UACE,eAAe;UACf,MAAM;UACN,UAAU;UACV,UAAU;UACV,CAAA;SACF,kBAAC,GAAD;UAAW,cAAc;UAAe,YAAY;UAAuB,CAAA;SAC3E,kBAAC,GAAD;UAAQ,UAAS;UAAS,cAAc,EAAE,UAAU,IAAI;UAAI,CAAA;SAC5D,kBAAC,GAAD;UACE,MAAK;UACL,SAAQ;UACR,MAAK;UACL,QAAO;UACP,aAAa;UACb,MAAK;UACL,CAAA;SACF,kBAAC,GAAD;UACE,MAAK;UACL,SAAQ;UACR,MAAK;UACL,QAAO;UACP,aAAa;UACb,MAAK;UACL,CAAA;SACQ;;OACQ,CAAA,CAEd;QACR;SApKN,kBAAC,GAAD;KACE,MAAM,kBAAC,GAAD,EAAU,MAAM,IAAM,CAAA;KAC5B,OAAM;KACN,aAAY;KACZ,QACE,kBAAC,GAAD;MAAQ,eAAe,EAAS,wBAAwB;gBAAE;MAAuB,CAAA;KAEnF,CAAA;IAgKH;IACG;OA7NN,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,GAAD;IACE,MAAM,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA;IAC7B,OAAM;IACN,aAAY;IACZ,CAAA,EACD,EACG;;EAwNG,CAAA"}
1
+ {"version":3,"file":"AnalyticsAnalyticsPagePage.js","names":[],"sources":["../../../src/technospam/pages/AnalyticsAnalyticsPagePage.tsx"],"sourcesContent":["import { useMemo, type CSSProperties, type SVGProps } from 'react';\nimport {\n Activity,\n Award,\n BarChart3,\n BookOpen,\n Code2,\n GraduationCap,\n Library,\n Percent,\n TrendingUp,\n Trophy,\n Users,\n type LucideIcon,\n} from 'lucide-react';\nimport { GlassCard, Button } from '@burdenoff/fe-libs/ui';\nimport { cn } from '../utils/cn';\nimport {\n Area,\n AreaChart,\n Bar,\n BarChart,\n CartesianGrid,\n Cell,\n Legend,\n Pie,\n PieChart,\n ResponsiveContainer,\n Tooltip as ReTooltip,\n XAxis,\n YAxis,\n} from 'recharts';\nimport {\n EmptyView,\n ErrorView,\n LoadingGrid,\n LoadingRows,\n PageLayout,\n StatCard,\n} from '../components';\nimport { useTechnoSpam, useTechnoSpamNavigate } from '../providers/TechnoSpamProvider';\nimport {\n useCertificatesQuery,\n useCoursesQuery,\n useEnrollmentsQuery,\n useHackathonsQuery,\n useProblemsQuery,\n useSessionsQuery,\n useSubmissionsQuery,\n type TecEnrollmentStatus,\n type TecSubmissionVerdict,\n} from '../api/technospamApi';\nimport { humanizeEnum } from '../utils/format';\n\n/* ----------------------------------------------------------- chart config */\n\nconst ENROLL_STATUS_ORDER: TecEnrollmentStatus[] = [\n 'ENROLLED',\n 'IN_PROGRESS',\n 'COMPLETED',\n 'DROPPED',\n];\n\nconst ENROLL_STATUS_COLOR: Record<TecEnrollmentStatus, string> = {\n ENROLLED: 'var(--color-accent-primary)',\n IN_PROGRESS: 'var(--color-accent-teal)',\n COMPLETED: 'var(--color-status-success)',\n DROPPED: 'var(--color-status-warning)',\n};\n\nconst VERDICT_ORDER: TecSubmissionVerdict[] = [\n 'ACCEPTED',\n 'WRONG_ANSWER',\n 'TIME_LIMIT_EXCEEDED',\n 'RUNTIME_ERROR',\n 'COMPILE_ERROR',\n 'PENDING',\n];\n\nconst VERDICT_LABEL: Record<TecSubmissionVerdict, string> = {\n ACCEPTED: 'Accepted',\n WRONG_ANSWER: 'Wrong',\n TIME_LIMIT_EXCEEDED: 'TLE',\n RUNTIME_ERROR: 'Runtime',\n COMPILE_ERROR: 'Compile',\n PENDING: 'Pending',\n};\n\nconst VERDICT_COLOR: Record<TecSubmissionVerdict, string> = {\n ACCEPTED: 'var(--color-status-success)',\n WRONG_ANSWER: 'var(--color-status-error)',\n TIME_LIMIT_EXCEEDED: 'var(--color-status-warning)',\n RUNTIME_ERROR: 'var(--color-status-error)',\n COMPILE_ERROR: 'var(--color-status-error)',\n PENDING: 'var(--color-text-muted)',\n};\n\nconst TOOLTIP_STYLE: CSSProperties = {\n background: 'var(--color-bg-surface)',\n border: '1px solid var(--color-border-seam)',\n borderRadius: 12,\n color: 'var(--color-text-primary)',\n fontSize: 12,\n};\nconst TOOLTIP_LABEL_STYLE: CSSProperties = { color: 'var(--color-text-secondary)' };\nconst AXIS_TICK: SVGProps<SVGTextElement> = { fill: 'var(--color-text-muted)', fontSize: 12 };\n\ninterface StatusDatum {\n name: string;\n value: number;\n color: string;\n}\ninterface VerdictDatum {\n label: string;\n count: number;\n color: string;\n}\ninterface ActivityDatum {\n label: string;\n submissions: number;\n enrollments: number;\n}\n\n/** Normalize an ISO timestamp to the start of its (Sunday-based) week. */\nfunction weekBucket(iso: string): { key: number; label: string } {\n const d = new Date(iso);\n if (Number.isNaN(d.getTime())) return { key: 0, label: '—' };\n const start = new Date(d.getFullYear(), d.getMonth(), d.getDate() - d.getDay());\n return {\n key: start.getTime(),\n label: start.toLocaleDateString(undefined, { month: 'short', day: 'numeric' }),\n };\n}\n\n/* -------------------------------------------------------- chart wrappers */\n\ntype ChipTone = 'blue' | 'teal' | 'purple' | 'indigo';\n\nconst CHIP: Record<ChipTone, string> = {\n blue: 'bg-accent-primary/15 text-accent-primary ring-accent-primary/20',\n teal: 'bg-accent-teal/15 text-accent-teal ring-accent-teal/20',\n purple: 'bg-accent-purple/15 text-accent-purple ring-accent-purple/20',\n indigo: 'bg-accent-indigo/15 text-accent-indigo ring-accent-indigo/20',\n};\n\nfunction ChartHeader({\n icon: Icon,\n title,\n subtitle,\n tone,\n}: {\n icon: LucideIcon;\n title: string;\n subtitle?: string;\n tone: ChipTone;\n}) {\n return (\n <div className=\"mb-4 flex items-center gap-3\">\n <span\n className={cn(\n 'flex h-9 w-9 shrink-0 items-center justify-center rounded-xl ring-1',\n CHIP[tone]\n )}\n >\n <Icon size={17} />\n </span>\n <div className=\"min-w-0\">\n <h3 className=\"truncate text-sm font-semibold text-text-primary\">{title}</h3>\n {subtitle ? <p className=\"truncate text-xs text-text-muted\">{subtitle}</p> : null}\n </div>\n </div>\n );\n}\n\nfunction ChartCard({\n title,\n subtitle,\n icon,\n tone = 'teal',\n isEmpty,\n emptyLabel,\n children,\n}: {\n title: string;\n subtitle?: string;\n icon: LucideIcon;\n tone?: ChipTone;\n isEmpty: boolean;\n emptyLabel: string;\n children: React.ReactElement;\n}) {\n return (\n <GlassCard className=\"p-5 transition-shadow duration-200 hover:shadow-lg\">\n <ChartHeader icon={icon} title={title} subtitle={subtitle} tone={tone} />\n {isEmpty ? (\n <div className=\"flex h-[240px] items-center justify-center text-center text-sm text-text-muted\">\n {emptyLabel}\n </div>\n ) : (\n <ResponsiveContainer width=\"100%\" height={260}>\n {children}\n </ResponsiveContainer>\n )}\n </GlassCard>\n );\n}\n\n/* --------------------------------------------------------------- page */\n\nexport function AnalyticsAnalyticsPagePage() {\n const navigate = useTechnoSpamNavigate();\n const { currentUser } = useTechnoSpam();\n const learnerId = currentUser?.id;\n\n const enrollmentsQ = useEnrollmentsQuery(learnerId ? { learnerId } : {});\n const submissionsQ = useSubmissionsQuery(learnerId ? { learnerId } : {});\n const sessionsQ = useSessionsQuery(learnerId ? { menteeId: learnerId } : {});\n const certificatesQ = useCertificatesQuery(learnerId ? { learnerId } : {});\n const coursesQ = useCoursesQuery({ status: 'PUBLISHED' });\n const problemsQ = useProblemsQuery({ status: 'PUBLISHED' });\n const hackathonsQ = useHackathonsQuery({});\n\n const enrollments = useMemo(\n () => enrollmentsQ.data?.tecListEnrollments.items ?? [],\n [enrollmentsQ.data]\n );\n const submissions = useMemo(\n () => submissionsQ.data?.tecListSubmissions.items ?? [],\n [submissionsQ.data]\n );\n const sessions = sessionsQ.data?.tecListSessions.items ?? [];\n const certificates = certificatesQ.data?.tecListCertificates ?? [];\n const coursesTotal = coursesQ.data?.tecListCourses.items.length ?? 0;\n const problemsTotal = problemsQ.data?.tecListProblems.items.length ?? 0;\n const hackathonsTotal = hackathonsQ.data?.tecListHackathons.items.length ?? 0;\n\n const metrics = useMemo(() => {\n const coursesCompleted = enrollments.filter((e) => e.status === 'COMPLETED').length;\n const avgProgress = enrollments.length\n ? Math.round(enrollments.reduce((s, e) => s + e.progressPct, 0) / enrollments.length)\n : 0;\n const acceptedSubs = submissions.filter((s) => s.verdict === 'ACCEPTED');\n const problemsSolved = new Set(acceptedSubs.map((s) => s.problemId)).size;\n const acceptanceRate = submissions.length\n ? Math.round((acceptedSubs.length / submissions.length) * 100)\n : 0;\n return { coursesCompleted, avgProgress, problemsSolved, acceptanceRate };\n }, [enrollments, submissions]);\n\n const enrollmentsByStatus = useMemo<StatusDatum[]>(\n () =>\n ENROLL_STATUS_ORDER.map((s) => ({\n name: humanizeEnum(s),\n value: enrollments.filter((e) => e.status === s).length,\n color: ENROLL_STATUS_COLOR[s],\n })).filter((d) => d.value > 0),\n [enrollments]\n );\n\n const submissionsByVerdict = useMemo<VerdictDatum[]>(\n () =>\n VERDICT_ORDER.map((v) => ({\n label: VERDICT_LABEL[v],\n count: submissions.filter((s) => s.verdict === v).length,\n color: VERDICT_COLOR[v],\n })).filter((d) => d.count > 0),\n [submissions]\n );\n\n const activityData = useMemo<ActivityDatum[]>(() => {\n const buckets = new Map<number, ActivityDatum>();\n const add = (iso: string, key: 'submissions' | 'enrollments') => {\n const { key: bucketKey, label } = weekBucket(iso);\n const existing = buckets.get(bucketKey) ?? { label, submissions: 0, enrollments: 0 };\n existing[key] += 1;\n buckets.set(bucketKey, existing);\n };\n submissions.forEach((s) => add(s.createdAt, 'submissions'));\n enrollments.forEach((e) => add(e.createdAt, 'enrollments'));\n return [...buckets.entries()]\n .sort((a, b) => a[0] - b[0])\n .slice(-8)\n .map(([, v]) => v);\n }, [submissions, enrollments]);\n\n const hasActivity =\n enrollments.length > 0 ||\n submissions.length > 0 ||\n sessions.length > 0 ||\n certificates.length > 0;\n\n const learnerLoading =\n enrollmentsQ.loading || submissionsQ.loading || sessionsQ.loading || certificatesQ.loading;\n const learnerError =\n enrollmentsQ.error || submissionsQ.error || sessionsQ.error || certificatesQ.error;\n const showLoading = learnerLoading && !hasActivity;\n\n const workspaceOverview = (\n <div>\n <h2 className=\"mb-3 text-base font-semibold text-text-primary\">Workspace overview</h2>\n <p className=\"mb-4 max-w-2xl text-sm text-text-secondary\">\n Live content available across the TechnoSpam learning workspace right now.\n </p>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-3\">\n <StatCard label=\"Published courses\" value={coursesTotal} icon={Library} tone=\"blue\" />\n <StatCard label=\"Published problems\" value={problemsTotal} icon={Code2} tone=\"indigo\" />\n <StatCard label=\"Hackathons\" value={hackathonsTotal} icon={Trophy} tone=\"purple\" />\n </div>\n </div>\n );\n\n return (\n <PageLayout\n hero\n title=\"Analytics\"\n description=\"Your real learning performance across courses, problems, mentoring and certificates.\"\n actions={\n <Button variant=\"outline\" onClick={() => navigate('/courses/my-learning-page')}>\n <GraduationCap size={16} /> My Learning\n </Button>\n }\n >\n {!learnerId ? (\n <div className=\"space-y-8\">\n <EmptyView\n icon={<BarChart3 size={26} />}\n title=\"Sign in to view your analytics\"\n description=\"Your personal learning metrics appear here once you're signed in.\"\n />\n {workspaceOverview}\n </div>\n ) : showLoading ? (\n <div className=\"space-y-6\">\n <LoadingGrid count={4} height=\"h-24\" />\n <LoadingRows count={2} height=\"h-72\" />\n </div>\n ) : learnerError && !hasActivity ? (\n <ErrorView title=\"Couldn't load your analytics\" error={learnerError} />\n ) : (\n <div className=\"space-y-8\">\n <div className=\"grid grid-cols-2 gap-3 sm:grid-cols-3 xl:grid-cols-4\">\n <StatCard\n label=\"Courses enrolled\"\n value={enrollments.length}\n icon={BookOpen}\n tone=\"blue\"\n />\n <StatCard\n label=\"Courses completed\"\n value={metrics.coursesCompleted}\n icon={GraduationCap}\n tone=\"green\"\n />\n <StatCard\n label=\"Avg progress\"\n value={`${metrics.avgProgress}%`}\n icon={TrendingUp}\n tone=\"purple\"\n />\n <StatCard\n label=\"Problems solved\"\n value={metrics.problemsSolved}\n icon={Code2}\n tone=\"indigo\"\n />\n <StatCard\n label=\"Acceptance rate\"\n value={`${metrics.acceptanceRate}%`}\n icon={Percent}\n tone=\"teal\"\n />\n <StatCard label=\"Mentor sessions\" value={sessions.length} icon={Users} tone=\"blue\" />\n <StatCard label=\"Certificates\" value={certificates.length} icon={Award} tone=\"amber\" />\n </div>\n\n {!hasActivity ? (\n <EmptyView\n icon={<Activity size={26} />}\n title=\"No learning activity yet\"\n description=\"Enroll in a course or solve a problem and your progress charts will build here. TechnoSpam is pre-launch — start learning to see your data.\"\n action={\n <Button onClick={() => navigate('/courses/courses-page')}>Browse courses</Button>\n }\n />\n ) : (\n <div className=\"space-y-4\">\n <div className=\"grid grid-cols-1 gap-4 lg:grid-cols-2\">\n <ChartCard\n title=\"Enrollments by status\"\n subtitle=\"How your courses are progressing\"\n icon={BookOpen}\n tone=\"blue\"\n isEmpty={enrollmentsByStatus.length === 0}\n emptyLabel=\"No enrollments yet\"\n >\n <PieChart>\n <Pie\n data={enrollmentsByStatus}\n dataKey=\"value\"\n nameKey=\"name\"\n innerRadius={60}\n outerRadius={92}\n paddingAngle={2}\n stroke=\"var(--color-bg-surface)\"\n strokeWidth={2}\n >\n {enrollmentsByStatus.map((d) => (\n <Cell key={d.name} fill={d.color} />\n ))}\n </Pie>\n <ReTooltip contentStyle={TOOLTIP_STYLE} labelStyle={TOOLTIP_LABEL_STYLE} />\n <Legend iconType=\"circle\" wrapperStyle={{ fontSize: 12 }} />\n </PieChart>\n </ChartCard>\n\n <ChartCard\n title=\"Submissions by verdict\"\n subtitle=\"Outcomes across your attempts\"\n icon={Code2}\n tone=\"purple\"\n isEmpty={submissionsByVerdict.length === 0}\n emptyLabel=\"No submissions yet\"\n >\n <BarChart\n data={submissionsByVerdict}\n margin={{ top: 8, right: 8, left: -18, bottom: 0 }}\n >\n <CartesianGrid\n strokeDasharray=\"3 3\"\n stroke=\"var(--color-border-seam)\"\n vertical={false}\n />\n <XAxis\n dataKey=\"label\"\n tick={AXIS_TICK}\n axisLine={{ stroke: 'var(--color-border-seam)' }}\n tickLine={false}\n />\n <YAxis\n allowDecimals={false}\n tick={AXIS_TICK}\n axisLine={false}\n tickLine={false}\n />\n <ReTooltip\n contentStyle={TOOLTIP_STYLE}\n labelStyle={TOOLTIP_LABEL_STYLE}\n cursor={{ fill: 'var(--color-accent-primary)', fillOpacity: 0.08 }}\n />\n <Bar dataKey=\"count\" name=\"Submissions\" radius={[6, 6, 0, 0]} maxBarSize={56}>\n {submissionsByVerdict.map((d) => (\n <Cell key={d.label} fill={d.color} />\n ))}\n </Bar>\n </BarChart>\n </ChartCard>\n </div>\n\n <GlassCard className=\"p-5 transition-shadow duration-200 hover:shadow-lg\">\n <ChartHeader\n icon={Activity}\n title=\"Learning activity over time\"\n subtitle=\"Submissions and enrollments per week\"\n tone=\"teal\"\n />\n {activityData.length === 0 ? (\n <div className=\"flex h-[240px] items-center justify-center text-sm text-text-muted\">\n No dated activity yet\n </div>\n ) : (\n <ResponsiveContainer width=\"100%\" height={280}>\n <AreaChart\n data={activityData}\n margin={{ top: 8, right: 12, left: -18, bottom: 0 }}\n >\n <defs>\n <linearGradient id=\"tec-grad-submissions\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop\n offset=\"0%\"\n stopColor=\"var(--color-accent-primary)\"\n stopOpacity={0.35}\n />\n <stop\n offset=\"100%\"\n stopColor=\"var(--color-accent-primary)\"\n stopOpacity={0}\n />\n </linearGradient>\n <linearGradient id=\"tec-grad-enrollments\" x1=\"0\" y1=\"0\" x2=\"0\" y2=\"1\">\n <stop\n offset=\"0%\"\n stopColor=\"var(--color-accent-teal)\"\n stopOpacity={0.35}\n />\n <stop\n offset=\"100%\"\n stopColor=\"var(--color-accent-teal)\"\n stopOpacity={0}\n />\n </linearGradient>\n </defs>\n <CartesianGrid\n strokeDasharray=\"3 3\"\n stroke=\"var(--color-border-seam)\"\n vertical={false}\n />\n <XAxis\n dataKey=\"label\"\n tick={AXIS_TICK}\n axisLine={{ stroke: 'var(--color-border-seam)' }}\n tickLine={false}\n />\n <YAxis\n allowDecimals={false}\n tick={AXIS_TICK}\n axisLine={false}\n tickLine={false}\n />\n <ReTooltip contentStyle={TOOLTIP_STYLE} labelStyle={TOOLTIP_LABEL_STYLE} />\n <Legend iconType=\"circle\" wrapperStyle={{ fontSize: 12 }} />\n <Area\n type=\"monotone\"\n dataKey=\"submissions\"\n name=\"Submissions\"\n stroke=\"var(--color-accent-primary)\"\n strokeWidth={2}\n fill=\"url(#tec-grad-submissions)\"\n />\n <Area\n type=\"monotone\"\n dataKey=\"enrollments\"\n name=\"Enrollments\"\n stroke=\"var(--color-accent-teal)\"\n strokeWidth={2}\n fill=\"url(#tec-grad-enrollments)\"\n />\n </AreaChart>\n </ResponsiveContainer>\n )}\n </GlassCard>\n </div>\n )}\n\n {workspaceOverview}\n </div>\n )}\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"ApplicationsProblemsPagePage.d.ts","sourceRoot":"","sources":["../../../src/technospam/pages/ApplicationsProblemsPagePage.tsx"],"names":[],"mappings":"AAmGA,wBAAgB,4BAA4B,gCAoI3C"}
1
+ {"version":3,"file":"ApplicationsProblemsPagePage.d.ts","sourceRoot":"","sources":["../../../src/technospam/pages/ApplicationsProblemsPagePage.tsx"],"names":[],"mappings":"AA6JA,wBAAgB,4BAA4B,gCAuK3C"}
@@ -5,15 +5,15 @@ import { StatCard as i } from "../components/StatCard.js";
5
5
  import { MediaCover as a, topicIcon as o } from "../components/MediaCover.js";
6
6
  import { StatusBadge as s } from "../components/StatusBadge.js";
7
7
  import { LoadMore as c } from "../components/LoadMore.js";
8
- import { EmptyView as l, ErrorView as u, LoadingGrid as d } from "../components/StateViews.js";
8
+ import { EmptyView as l, ErrorView as ee, LoadingGrid as u } from "../components/StateViews.js";
9
9
  import "../components/index.js";
10
- import { pageArg as f, usePageLimit as p, useProblemsQuery as m, useSubmissionsQuery as h } from "../api/technospamApi.js";
11
- import { useMemo as g, useState as _ } from "react";
12
- import { CheckCircle2 as v, Code2 as y, Flame as b, Gauge as x, Layers as S, Search as C, Tag as w, Trophy as T } from "lucide-react";
13
- import { jsx as E, jsxs as D } from "react/jsx-runtime";
14
- import { Badge as O, GlassCard as k, Input as A, Tabs as j, TabsList as M, TabsTrigger as N } from "@burdenoff/fe-libs/ui";
10
+ import { pageArg as te, usePageLimit as d, useProblemsQuery as f, useSubmissionsQuery as p } from "../api/technospamApi.js";
11
+ import { useMemo as m, useState as h } from "react";
12
+ import { Building2 as g, CheckCircle2 as _, Code2 as v, Flame as y, Gauge as b, Layers as x, Search as ne, Tag as S, Trophy as C } from "lucide-react";
13
+ import { jsx as w, jsxs as T } from "react/jsx-runtime";
14
+ import { Badge as E, GlassCard as D, Input as O, Select as k, SelectContent as A, SelectItem as j, SelectTrigger as M, SelectValue as N, Tabs as P, TabsList as F, TabsTrigger as I } from "@burdenoff/fe-libs/ui";
15
15
  //#region src/technospam/pages/ApplicationsProblemsPagePage.tsx
16
- var P = [
16
+ var L = [
17
17
  {
18
18
  value: "ALL",
19
19
  label: "All"
@@ -30,58 +30,108 @@ var P = [
30
30
  value: "HARD",
31
31
  label: "Hard"
32
32
  }
33
+ ], R = [
34
+ {
35
+ value: "google",
36
+ label: "Google"
37
+ },
38
+ {
39
+ value: "amazon",
40
+ label: "Amazon"
41
+ },
42
+ {
43
+ value: "meta",
44
+ label: "Meta"
45
+ },
46
+ {
47
+ value: "microsoft",
48
+ label: "Microsoft"
49
+ },
50
+ {
51
+ value: "apple",
52
+ label: "Apple"
53
+ },
54
+ {
55
+ value: "netflix",
56
+ label: "Netflix"
57
+ }
58
+ ], z = [
59
+ {
60
+ value: "frontend",
61
+ label: "Frontend"
62
+ },
63
+ {
64
+ value: "backend",
65
+ label: "Backend"
66
+ },
67
+ {
68
+ value: "fullstack",
69
+ label: "Full-Stack"
70
+ },
71
+ {
72
+ value: "data-science",
73
+ label: "Data Science"
74
+ },
75
+ {
76
+ value: "mobile",
77
+ label: "Mobile"
78
+ },
79
+ {
80
+ value: "sre",
81
+ label: "SRE / Infra"
82
+ }
33
83
  ];
34
- function F({ problem: e, solved: t, onOpen: n }) {
35
- return /* @__PURE__ */ D(k, {
84
+ function B({ problem: e, solved: t, onOpen: n }) {
85
+ return /* @__PURE__ */ T(D, {
36
86
  className: "group flex cursor-pointer flex-col overflow-hidden transition-all duration-200 hover:-translate-y-1 hover:shadow-lg",
37
87
  onClick: n,
38
- children: [/* @__PURE__ */ D(a, {
88
+ children: [/* @__PURE__ */ T(a, {
39
89
  seed: `${e.topic ?? ""} ${e.difficulty} ${e.title}`,
40
90
  icon: o(e.topic ?? e.title),
41
- children: [t ? /* @__PURE__ */ D("span", {
91
+ children: [t ? /* @__PURE__ */ T("span", {
42
92
  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",
43
- children: [/* @__PURE__ */ E(v, { size: 12 }), " Solved"]
44
- }) : /* @__PURE__ */ E("span", {}), /* @__PURE__ */ E(s, {
93
+ children: [/* @__PURE__ */ w(_, { size: 12 }), " Solved"]
94
+ }) : /* @__PURE__ */ w("span", {}), /* @__PURE__ */ w(s, {
45
95
  status: e.difficulty,
46
96
  kind: "level"
47
97
  })]
48
- }), /* @__PURE__ */ D("div", {
98
+ }), /* @__PURE__ */ T("div", {
49
99
  className: "flex flex-1 flex-col gap-3 p-4",
50
100
  children: [
51
- /* @__PURE__ */ D("div", {
101
+ /* @__PURE__ */ T("div", {
52
102
  className: "min-w-0",
53
- children: [/* @__PURE__ */ E("h3", {
103
+ children: [/* @__PURE__ */ w("h3", {
54
104
  className: "truncate text-base font-semibold text-text-primary group-hover:text-accent-primary",
55
105
  children: e.title
56
- }), e.statement ? /* @__PURE__ */ E("p", {
106
+ }), e.statement ? /* @__PURE__ */ w("p", {
57
107
  className: "mt-1 line-clamp-2 text-sm text-text-secondary",
58
108
  children: e.statement
59
109
  }) : null]
60
110
  }),
61
- /* @__PURE__ */ D("div", {
111
+ /* @__PURE__ */ T("div", {
62
112
  className: "mt-auto flex flex-wrap items-center gap-3 text-xs text-text-muted",
63
113
  children: [
64
- e.topic ? /* @__PURE__ */ D("span", {
114
+ e.topic ? /* @__PURE__ */ T("span", {
65
115
  className: "inline-flex items-center gap-1",
66
116
  children: [
67
- /* @__PURE__ */ E(S, { size: 13 }),
117
+ /* @__PURE__ */ w(x, { size: 13 }),
68
118
  " ",
69
119
  e.topic
70
120
  ]
71
121
  }) : null,
72
- /* @__PURE__ */ D("span", {
122
+ /* @__PURE__ */ T("span", {
73
123
  className: "inline-flex items-center gap-1",
74
124
  children: [
75
- /* @__PURE__ */ E(T, { size: 13 }),
125
+ /* @__PURE__ */ w(C, { size: 13 }),
76
126
  " ",
77
127
  e.points,
78
128
  " pts"
79
129
  ]
80
130
  }),
81
- e.languages.length ? /* @__PURE__ */ D("span", {
131
+ e.languages.length ? /* @__PURE__ */ T("span", {
82
132
  className: "inline-flex items-center gap-1",
83
133
  children: [
84
- /* @__PURE__ */ E(y, { size: 13 }),
134
+ /* @__PURE__ */ w(v, { size: 13 }),
85
135
  " ",
86
136
  e.languages.length,
87
137
  " lang",
@@ -90,12 +140,30 @@ function F({ problem: e, solved: t, onOpen: n }) {
90
140
  }) : null
91
141
  ]
92
142
  }),
93
- e.tags.length ? /* @__PURE__ */ E("div", {
143
+ e.companies.length ? /* @__PURE__ */ T("div", {
94
144
  className: "flex flex-wrap gap-1.5",
95
- children: e.tags.slice(0, 3).map((e) => /* @__PURE__ */ D(O, {
145
+ children: [e.companies.slice(0, 3).map((e) => /* @__PURE__ */ T(E, {
146
+ variant: "outline",
147
+ className: "text-[11px] capitalize",
148
+ children: [/* @__PURE__ */ w(g, {
149
+ size: 10,
150
+ className: "mr-1"
151
+ }), e.replace(/-/g, " ")]
152
+ }, e)), e.interviewFrequency === "HIGH" ? /* @__PURE__ */ T(E, {
153
+ variant: "secondary",
154
+ className: "text-[11px] text-status-warning-text",
155
+ children: [/* @__PURE__ */ w(y, {
156
+ size: 10,
157
+ className: "mr-1"
158
+ }), "Frequently asked"]
159
+ }) : null]
160
+ }) : null,
161
+ e.tags.length ? /* @__PURE__ */ w("div", {
162
+ className: "flex flex-wrap gap-1.5",
163
+ children: e.tags.slice(0, 3).map((e) => /* @__PURE__ */ T(E, {
96
164
  variant: "secondary",
97
165
  className: "text-[11px]",
98
- children: [/* @__PURE__ */ E(w, {
166
+ children: [/* @__PURE__ */ w(S, {
99
167
  size: 10,
100
168
  className: "mr-1"
101
169
  }), e]
@@ -105,71 +173,73 @@ function F({ problem: e, solved: t, onOpen: n }) {
105
173
  })]
106
174
  });
107
175
  }
108
- function I() {
109
- let a = t(), { currentUser: o, workspaceId: s } = e(), { emit: S } = n(), w = o?.id, [T, O] = _("ALL"), [k, I] = _(""), { limit: L, more: R } = p(), { data: z, loading: B, error: V } = m({
176
+ function V() {
177
+ let a = t(), { currentUser: o, workspaceId: s } = e(), { emit: g } = n(), x = o?.id, [S, C] = h("ALL"), [E, D] = h("ALL"), [V, H] = h("ALL"), [U, W] = h(""), { limit: G, more: K } = d(), { data: q, loading: J, error: Y } = f({
110
178
  status: "PUBLISHED",
111
- pagination: f(L),
112
- ...T === "ALL" ? {} : { difficulty: T }
113
- }), { data: H } = h(w ? { learnerId: w } : {}), U = z?.tecListProblems.items ?? [], W = z?.tecListProblems.pageInfo.hasMore ?? !1, G = g(() => new Set((H?.tecListSubmissions.items ?? []).filter((e) => e.verdict === "ACCEPTED").map((e) => e.problemId)), [H]), K = U.filter((e) => G.has(e.id)).length, q = U.filter((e) => e.difficulty === "EASY").length, J = U.filter((e) => e.difficulty === "HARD").length, Y = g(() => {
114
- let e = k.trim().toLowerCase();
115
- return e ? U.filter((t) => t.title.toLowerCase().includes(e) || (t.topic ?? "").toLowerCase().includes(e) || t.tags.some((t) => t.toLowerCase().includes(e))) : U;
116
- }, [U, k]);
117
- return /* @__PURE__ */ D(r, {
179
+ pagination: te(G),
180
+ ...S === "ALL" ? {} : { difficulty: S },
181
+ ...E === "ALL" ? {} : { company: E },
182
+ ...V === "ALL" ? {} : { role: V }
183
+ }), { data: X } = p(x ? { learnerId: x } : {}), Z = q?.tecListProblems.items ?? [], re = q?.tecListProblems.pageInfo.hasMore ?? !1, Q = m(() => new Set((X?.tecListSubmissions.items ?? []).filter((e) => e.verdict === "ACCEPTED").map((e) => e.problemId)), [X]), ie = Z.filter((e) => Q.has(e.id)).length, ae = Z.filter((e) => e.difficulty === "EASY").length, oe = Z.filter((e) => e.difficulty === "HARD").length, $ = m(() => {
184
+ let e = U.trim().toLowerCase();
185
+ return e ? Z.filter((t) => t.title.toLowerCase().includes(e) || (t.topic ?? "").toLowerCase().includes(e) || t.tags.some((t) => t.toLowerCase().includes(e))) : Z;
186
+ }, [Z, U]);
187
+ return /* @__PURE__ */ T(r, {
118
188
  hero: !0,
119
189
  title: "Coding Problems",
120
190
  description: "Sharpen your problem-solving with hands-on, mentor-judged challenges across topics and difficulty.",
121
191
  children: [
122
- /* @__PURE__ */ D("div", {
192
+ /* @__PURE__ */ T("div", {
123
193
  className: "mb-5 grid grid-cols-2 gap-3 sm:grid-cols-4",
124
194
  children: [
125
- /* @__PURE__ */ E(i, {
195
+ /* @__PURE__ */ w(i, {
126
196
  label: "Problems",
127
- value: U.length,
128
- icon: y,
197
+ value: Z.length,
198
+ icon: v,
129
199
  tone: "blue"
130
200
  }),
131
- /* @__PURE__ */ E(i, {
201
+ /* @__PURE__ */ w(i, {
132
202
  label: "Solved",
133
- value: K,
134
- icon: v,
203
+ value: ie,
204
+ icon: _,
135
205
  tone: "green"
136
206
  }),
137
- /* @__PURE__ */ E(i, {
207
+ /* @__PURE__ */ w(i, {
138
208
  label: "Easy",
139
- value: q,
140
- icon: x,
209
+ value: ae,
210
+ icon: b,
141
211
  tone: "teal"
142
212
  }),
143
- /* @__PURE__ */ E(i, {
213
+ /* @__PURE__ */ w(i, {
144
214
  label: "Hard",
145
- value: J,
146
- icon: b,
215
+ value: oe,
216
+ icon: y,
147
217
  tone: "amber"
148
218
  })
149
219
  ]
150
220
  }),
151
- /* @__PURE__ */ D("div", {
221
+ /* @__PURE__ */ T("div", {
152
222
  className: "mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between",
153
- children: [/* @__PURE__ */ E(j, {
154
- value: T,
155
- onValueChange: (e) => O(e),
156
- children: /* @__PURE__ */ E(M, { children: P.map((e) => /* @__PURE__ */ E(N, {
223
+ children: [/* @__PURE__ */ w(P, {
224
+ value: S,
225
+ onValueChange: (e) => C(e),
226
+ children: /* @__PURE__ */ w(F, { children: L.map((e) => /* @__PURE__ */ w(I, {
157
227
  value: e.value,
158
228
  children: e.label
159
229
  }, e.value)) })
160
- }), /* @__PURE__ */ D("div", {
230
+ }), /* @__PURE__ */ T("div", {
161
231
  className: "relative w-full sm:w-72",
162
- children: [/* @__PURE__ */ E(C, {
232
+ children: [/* @__PURE__ */ w(ne, {
163
233
  className: "pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-text-muted",
164
234
  size: 16
165
- }), /* @__PURE__ */ E(A, {
235
+ }), /* @__PURE__ */ w(O, {
166
236
  className: "pl-9",
167
237
  placeholder: "Search problems…",
168
- value: k,
169
- onChange: (e) => I(e.target.value),
238
+ value: U,
239
+ onChange: (e) => W(e.target.value),
170
240
  onBlur: () => {
171
- let e = k.trim();
172
- e && S("technospam.search.performed", {
241
+ let e = U.trim();
242
+ e && g("technospam.search.performed", {
173
243
  workspaceId: s,
174
244
  entityType: "problem",
175
245
  queryLength: e.length
@@ -178,20 +248,50 @@ function I() {
178
248
  })]
179
249
  })]
180
250
  }),
181
- B && !U.length ? /* @__PURE__ */ E(d, {}) : V ? /* @__PURE__ */ E(u, {
251
+ /* @__PURE__ */ T("div", {
252
+ className: "mb-4 flex flex-col gap-3 sm:flex-row sm:items-center",
253
+ children: [/* @__PURE__ */ T(k, {
254
+ value: E,
255
+ onValueChange: D,
256
+ children: [/* @__PURE__ */ w(M, {
257
+ className: "w-full sm:w-48",
258
+ children: /* @__PURE__ */ w(N, { placeholder: "Company" })
259
+ }), /* @__PURE__ */ T(A, { children: [/* @__PURE__ */ w(j, {
260
+ value: "ALL",
261
+ children: "All companies"
262
+ }), R.map((e) => /* @__PURE__ */ w(j, {
263
+ value: e.value,
264
+ children: e.label
265
+ }, e.value))] })]
266
+ }), /* @__PURE__ */ T(k, {
267
+ value: V,
268
+ onValueChange: H,
269
+ children: [/* @__PURE__ */ w(M, {
270
+ className: "w-full sm:w-48",
271
+ children: /* @__PURE__ */ w(N, { placeholder: "Role" })
272
+ }), /* @__PURE__ */ T(A, { children: [/* @__PURE__ */ w(j, {
273
+ value: "ALL",
274
+ children: "All roles"
275
+ }), z.map((e) => /* @__PURE__ */ w(j, {
276
+ value: e.value,
277
+ children: e.label
278
+ }, e.value))] })]
279
+ })]
280
+ }),
281
+ J && !Z.length ? /* @__PURE__ */ w(u, {}) : Y ? /* @__PURE__ */ w(ee, {
182
282
  title: "Couldn't load problems",
183
- error: V
184
- }) : Y.length === 0 ? /* @__PURE__ */ E(l, {
185
- icon: /* @__PURE__ */ E(y, { size: 26 }),
186
- title: k || T !== "ALL" ? "No matching problems" : "No problems published yet",
187
- description: k || T !== "ALL" ? "Try a different search or difficulty filter." : "Published problems will appear here. Check back soon — TechnoSpam is pre-launch."
188
- }) : /* @__PURE__ */ E("div", {
283
+ error: Y
284
+ }) : $.length === 0 ? /* @__PURE__ */ w(l, {
285
+ icon: /* @__PURE__ */ w(v, { size: 26 }),
286
+ title: U || S !== "ALL" || E !== "ALL" || V !== "ALL" ? "No matching problems" : "No problems published yet",
287
+ description: U || S !== "ALL" || E !== "ALL" || V !== "ALL" ? "Try a different search, difficulty, company, or role filter." : "Published problems will appear here. Check back soon — TechnoSpam is pre-launch."
288
+ }) : /* @__PURE__ */ w("div", {
189
289
  className: "grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3",
190
- children: Y.map((e) => /* @__PURE__ */ E(F, {
290
+ children: $.map((e) => /* @__PURE__ */ w(B, {
191
291
  problem: e,
192
- solved: G.has(e.id),
292
+ solved: Q.has(e.id),
193
293
  onOpen: () => {
194
- S("technospam.problem.viewed", {
294
+ g("technospam.problem.viewed", {
195
295
  workspaceId: s,
196
296
  entityId: e.id,
197
297
  entityType: "TEC_PROBLEM"
@@ -199,15 +299,15 @@ function I() {
199
299
  }
200
300
  }, e.id))
201
301
  }),
202
- /* @__PURE__ */ E(c, {
203
- hasMore: W,
204
- loading: B,
205
- onClick: R
302
+ /* @__PURE__ */ w(c, {
303
+ hasMore: re,
304
+ loading: J,
305
+ onClick: K
206
306
  })
207
307
  ]
208
308
  });
209
309
  }
210
310
  //#endregion
211
- export { I as ApplicationsProblemsPagePage };
311
+ export { V as ApplicationsProblemsPagePage };
212
312
 
213
313
  //# sourceMappingURL=ApplicationsProblemsPagePage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ApplicationsProblemsPagePage.js","names":[],"sources":["../../../src/technospam/pages/ApplicationsProblemsPagePage.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { CheckCircle2, Code2, Flame, Gauge, Layers, Search, Tag, Trophy } from 'lucide-react';\nimport { Badge, GlassCard, Input, Tabs, TabsList, TabsTrigger } from '@burdenoff/fe-libs/ui';\nimport {\n EmptyView,\n ErrorView,\n LoadMore,\n LoadingGrid,\n MediaCover,\n PageLayout,\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';\n\nconst DIFFICULTIES: Array<{ value: 'ALL' | TecProblemDifficulty; label: string }> = [\n { value: 'ALL', label: 'All' },\n { value: 'EASY', label: 'Easy' },\n { value: 'MEDIUM', label: 'Medium' },\n { value: 'HARD', label: 'Hard' },\n];\n\nfunction ProblemCard({\n problem,\n solved,\n onOpen,\n}: {\n problem: TecProblem;\n solved: boolean;\n onOpen: () => void;\n}) {\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} /> Solved\n </span>\n ) : (\n <span />\n )}\n <StatusBadge status={problem.difficulty} kind=\"level\" />\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} /> {problem.points} pts\n </span>\n {problem.languages.length ? (\n <span className=\"inline-flex items-center gap-1\">\n <Code2 size={13} /> {problem.languages.length} lang\n {problem.languages.length === 1 ? '' : 's'}\n </span>\n ) : null}\n </div>\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 learnerId = currentUser?.id;\n const [difficulty, setDifficulty] = useState<'ALL' | TecProblemDifficulty>('ALL');\n const [search, setSearch] = useState('');\n\n const { limit, more } = usePageLimit();\n const { data, loading, error } = useProblemsQuery({\n status: 'PUBLISHED',\n pagination: pageArg(limit),\n ...(difficulty !== 'ALL' ? { difficulty } : {}),\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=\"Coding Problems\"\n description=\"Sharpen your problem-solving with hands-on, mentor-judged challenges across topics and difficulty.\"\n >\n <div className=\"mb-5 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <StatCard label=\"Problems\" value={problems.length} icon={Code2} tone=\"blue\" />\n <StatCard label=\"Solved\" value={solvedCount} icon={CheckCircle2} tone=\"green\" />\n <StatCard label=\"Easy\" value={easyCount} icon={Gauge} tone=\"teal\" />\n <StatCard label=\"Hard\" value={hardCount} icon={Flame} tone=\"amber\" />\n </div>\n\n <div className=\"mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <Tabs\n value={difficulty}\n onValueChange={(v) => setDifficulty(v as 'ALL' | TecProblemDifficulty)}\n >\n <TabsList>\n {DIFFICULTIES.map((d) => (\n <TabsTrigger key={d.value} value={d.value}>\n {d.label}\n </TabsTrigger>\n ))}\n </TabsList>\n </Tabs>\n <div className=\"relative w-full sm:w-72\">\n <Search\n className=\"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-text-muted\"\n size={16}\n />\n <Input\n className=\"pl-9\"\n placeholder=\"Search 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 {loading && !problems.length ? (\n <LoadingGrid />\n ) : error ? (\n <ErrorView title=\"Couldn't load problems\" error={error} />\n ) : filtered.length === 0 ? (\n <EmptyView\n icon={<Code2 size={26} />}\n title={\n search || difficulty !== 'ALL' ? 'No matching problems' : 'No problems published yet'\n }\n description={\n search || difficulty !== 'ALL'\n ? 'Try a different search or difficulty filter.'\n : 'Published problems will appear here. Check back soon — TechnoSpam is pre-launch.'\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {filtered.map((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 </PageLayout>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AAyBA,IAAM,IAA8E;CAClF;EAAE,OAAO;EAAO,OAAO;EAAO;CAC9B;EAAE,OAAO;EAAQ,OAAO;EAAQ;CAChC;EAAE,OAAO;EAAU,OAAO;EAAU;CACpC;EAAE,OAAO;EAAQ,OAAO;EAAQ;CACjC;AAED,SAAS,EAAY,EACnB,YACA,WACA,aAKC;AACD,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,CACE,kBAAC,GAAD,EAAc,MAAM,IAAM,CAAA,EAAA,UACrB;QAEP,kBAAC,QAAD,EAAQ,CAAA,EAEV,kBAAC,GAAD;IAAa,QAAQ,EAAQ;IAAY,MAAK;IAAU,CAAA,CAC7C;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;;QAAE,EAAQ;QAAO;QAChC;;MACN,EAAQ,UAAU,SACjB,kBAAC,QAAD;OAAM,WAAU;iBAAhB;QACE,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;;QAAE,EAAQ,UAAU;QAAO;QAC7C,EAAQ,UAAU,WAAW,IAAI,KAAK;QAClC;WACL;MACA;;IACL,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,IAAY,GAAa,IACzB,CAAC,GAAY,KAAiB,EAAuC,MAAM,EAC3E,CAAC,GAAQ,KAAa,EAAS,GAAG,EAElC,EAAE,UAAO,YAAS,GAAc,EAChC,EAAE,SAAM,YAAS,aAAU,EAAiB;EAChD,QAAQ;EACR,YAAY,EAAQ,EAAM;EAC1B,GAAI,MAAe,QAAyB,EAAE,GAAnB,EAAE,eAAY;EAC1C,CAAC,EACI,EAAE,MAAM,MAAa,EAAoB,IAAY,EAAE,cAAW,GAAG,EAAE,CAAC,EAExE,IAAW,GAAM,gBAAgB,SAAS,EAAE,EAC5C,IAAU,GAAM,gBAAgB,SAAS,WAAW,IAEpD,IAAY,QAEd,IAAI,KACD,GAAU,mBAAmB,SAAS,EAAE,EACtC,QAAQ,MAAM,EAAE,YAAY,WAAW,CACvC,KAAK,MAAM,EAAE,UAAU,CAC3B,EACH,CAAC,EAAS,CACX,EAEK,IAAc,EAAS,QAAQ,MAAM,EAAU,IAAI,EAAE,GAAG,CAAC,CAAC,QAC1D,IAAY,EAAS,QAAQ,MAAM,EAAE,eAAe,OAAO,CAAC,QAC5D,IAAY,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,OAAM;EACN,aAAY;YAHd;GAKE,kBAAC,OAAD;IAAK,WAAU;cAAf;KACE,kBAAC,GAAD;MAAU,OAAM;MAAW,OAAO,EAAS;MAAQ,MAAM;MAAO,MAAK;MAAS,CAAA;KAC9E,kBAAC,GAAD;MAAU,OAAM;MAAS,OAAO;MAAa,MAAM;MAAc,MAAK;MAAU,CAAA;KAChF,kBAAC,GAAD;MAAU,OAAM;MAAO,OAAO;MAAW,MAAM;MAAO,MAAK;MAAS,CAAA;KACpE,kBAAC,GAAD;MAAU,OAAM;MAAO,OAAO;MAAW,MAAM;MAAO,MAAK;MAAU,CAAA;KACjE;;GAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,GAAD;KACE,OAAO;KACP,gBAAgB,MAAM,EAAc,EAAkC;eAEtE,kBAAC,GAAD,EAAA,UACG,EAAa,KAAK,MACjB,kBAAC,GAAD;MAA2B,OAAO,EAAE;gBACjC,EAAE;MACS,EAFI,EAAE,MAEN,CACd,EACO,CAAA;KACN,CAAA,EACP,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,GAAD;MACE,WAAU;MACV,MAAM;MACN,CAAA,EACF,kBAAC,GAAD;MACE,WAAU;MACV,aAAY;MACZ,OAAO;MACP,WAAW,MAAM,EAAU,EAAE,OAAO,MAAM;MAC1C,cAAc;OACZ,IAAM,IAAQ,EAAO,MAAM;AAC3B,OAAI,KACF,EAAK,+BAA+B;QAClC;QACA,YAAY;QACZ,aAAa,EAAM;QACpB,CAAC;;MAGN,CAAA,CACE;OACF;;GAEL,KAAW,CAAC,EAAS,SACpB,kBAAC,GAAD,EAAe,CAAA,GACb,IACF,kBAAC,GAAD;IAAW,OAAM;IAAgC;IAAS,CAAA,GACxD,EAAS,WAAW,IACtB,kBAAC,GAAD;IACE,MAAM,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;IACzB,OACE,KAAU,MAAe,QAAQ,yBAAyB;IAE5D,aACE,KAAU,MAAe,QACrB,iDACA;IAEN,CAAA,GAEF,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAS,KAAK,MACb,kBAAC,GAAD;KAEW;KACT,QAAQ,EAAU,IAAI,EAAQ,GAAG;KACjC,cAAc;AAMZ,MALA,EAAK,6BAA6B;OAChC;OACA,UAAU,EAAQ;OAClB,YAAY;OACb,CAAC,EACF,EAAS,yCAAyC,EAAQ,KAAK;;KAEjE,EAXK,EAAQ,GAWb,CACF;IACE,CAAA;GAER,kBAAC,GAAD;IAAmB;IAAkB;IAAS,SAAS;IAAQ,CAAA;GACpD"}
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 TabsList,\n TabsTrigger,\n} from '@burdenoff/fe-libs/ui';\nimport {\n EmptyView,\n ErrorView,\n LoadMore,\n LoadingGrid,\n MediaCover,\n PageLayout,\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';\n\nconst DIFFICULTIES: Array<{ value: 'ALL' | TecProblemDifficulty; label: string }> = [\n { value: 'ALL', label: 'All' },\n { value: 'EASY', label: 'Easy' },\n { value: 'MEDIUM', label: 'Medium' },\n { value: 'HARD', label: 'Hard' },\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).\nconst COMPANIES: Array<{ value: string; label: string }> = [\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\nconst ROLES: Array<{ value: string; label: string }> = [\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\nfunction ProblemCard({\n problem,\n solved,\n onOpen,\n}: {\n problem: TecProblem;\n solved: boolean;\n onOpen: () => void;\n}) {\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} /> Solved\n </span>\n ) : (\n <span />\n )}\n <StatusBadge status={problem.difficulty} kind=\"level\" />\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} /> {problem.points} pts\n </span>\n {problem.languages.length ? (\n <span className=\"inline-flex items-center gap-1\">\n <Code2 size={13} /> {problem.languages.length} lang\n {problem.languages.length === 1 ? '' : 's'}\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 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 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 { 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=\"Coding Problems\"\n description=\"Sharpen your problem-solving with hands-on, mentor-judged challenges across topics and difficulty.\"\n >\n <div className=\"mb-5 grid grid-cols-2 gap-3 sm:grid-cols-4\">\n <StatCard label=\"Problems\" value={problems.length} icon={Code2} tone=\"blue\" />\n <StatCard label=\"Solved\" value={solvedCount} icon={CheckCircle2} tone=\"green\" />\n <StatCard label=\"Easy\" value={easyCount} icon={Gauge} tone=\"teal\" />\n <StatCard label=\"Hard\" value={hardCount} icon={Flame} tone=\"amber\" />\n </div>\n\n <div className=\"mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <Tabs\n value={difficulty}\n onValueChange={(v) => setDifficulty(v as 'ALL' | TecProblemDifficulty)}\n >\n <TabsList>\n {DIFFICULTIES.map((d) => (\n <TabsTrigger key={d.value} value={d.value}>\n {d.label}\n </TabsTrigger>\n ))}\n </TabsList>\n </Tabs>\n <div className=\"relative w-full sm:w-72\">\n <Search\n className=\"pointer-events-none absolute left-3 top-1/2 -translate-y-1/2 text-text-muted\"\n size={16}\n />\n <Input\n className=\"pl-9\"\n placeholder=\"Search 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 className=\"w-full sm:w-48\">\n <SelectValue placeholder=\"Company\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">All companies</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 className=\"w-full sm:w-48\">\n <SelectValue placeholder=\"Role\" />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"ALL\">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 {loading && !problems.length ? (\n <LoadingGrid />\n ) : error ? (\n <ErrorView title=\"Couldn't load problems\" error={error} />\n ) : filtered.length === 0 ? (\n <EmptyView\n icon={<Code2 size={26} />}\n title={\n search || difficulty !== 'ALL' || company !== 'ALL' || role !== 'ALL'\n ? 'No matching problems'\n : 'No problems published yet'\n }\n description={\n search || difficulty !== 'ALL' || company !== 'ALL' || role !== 'ALL'\n ? 'Try a different search, difficulty, company, or role filter.'\n : 'Published problems will appear here. Check back soon — TechnoSpam is pre-launch.'\n }\n />\n ) : (\n <div className=\"grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3\">\n {filtered.map((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 </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -181,7 +181,7 @@ function H({ h: e, onOpen: t }) {
181
181
  });
182
182
  }
183
183
  function U() {
184
- let i = t(), { currentUser: a } = e(), s = a?.id, c = h(s ? { learnerId: s } : {}), l = v(s ? { learnerId: s } : {}), x = _(s ? { menteeId: s } : {}), w = p(s ? { learnerId: s } : {}), D = m({ status: "PUBLISHED" }), A = g({ status: "REGISTRATION_OPEN" }), R = c.data?.tecListEnrollments.items ?? [], U = l.data?.tecListSubmissions.items ?? [], W = x.data?.tecListSessions ?? [], G = w.data?.tecListCertificates ?? [], K = D.data?.tecListCourses.items ?? [], q = A.data?.tecListHackathons.items ?? [], ee = a?.firstName || a?.name || a?.email?.split("@")[0] || "there", J = y(() => R.filter((e) => e.status === "IN_PROGRESS" || e.status === "ENROLLED"), [R]), Y = y(() => [...J].sort((e, t) => new Date(t.lastActiveAt ?? t.startedAt).getTime() - new Date(e.lastActiveAt ?? e.startedAt).getTime()).slice(0, 3), [J]), te = y(() => new Set(U.filter((e) => e.verdict === "ACCEPTED").map((e) => e.problemId)).size, [U]), [X] = b(() => Date.now()), Z = y(() => [...W].filter((e) => (e.status === "CONFIRMED" || e.status === "REQUESTED") && new Date(e.scheduledAt).getTime() > X).sort((e, t) => new Date(e.scheduledAt).getTime() - new Date(t.scheduledAt).getTime()), [W, X]), Q = y(() => {
184
+ let i = t(), { currentUser: a } = e(), s = a?.id, c = h(s ? { learnerId: s } : {}), l = v(s ? { learnerId: s } : {}), x = _(s ? { menteeId: s } : {}), w = p(s ? { learnerId: s } : {}), D = m({ status: "PUBLISHED" }), A = g({ status: "REGISTRATION_OPEN" }), R = c.data?.tecListEnrollments.items ?? [], U = l.data?.tecListSubmissions.items ?? [], W = x.data?.tecListSessions.items ?? [], G = w.data?.tecListCertificates ?? [], K = D.data?.tecListCourses.items ?? [], q = A.data?.tecListHackathons.items ?? [], ee = a?.firstName || a?.name || a?.email?.split("@")[0] || "there", J = y(() => R.filter((e) => e.status === "IN_PROGRESS" || e.status === "ENROLLED"), [R]), Y = y(() => [...J].sort((e, t) => new Date(t.lastActiveAt ?? t.startedAt).getTime() - new Date(e.lastActiveAt ?? e.startedAt).getTime()).slice(0, 3), [J]), te = y(() => new Set(U.filter((e) => e.verdict === "ACCEPTED").map((e) => e.problemId)).size, [U]), [X] = b(() => Date.now()), Z = y(() => [...W].filter((e) => (e.status === "CONFIRMED" || e.status === "REQUESTED") && new Date(e.scheduledAt).getTime() > X).sort((e, t) => new Date(e.scheduledAt).getTime() - new Date(t.scheduledAt).getTime()), [W, X]), Q = y(() => {
185
185
  let e = new Set(R.map((e) => e.courseId));
186
186
  return K.filter((t) => !e.has(t.id)).slice(0, 4);
187
187
  }, [K, R]), $ = q.slice(0, 4);