@burdenoff/microfe-technospam 2026.828.1 → 2026.829.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +4 -3
- package/dist/node_modules/recharts/es6/cartesian/Brush.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/ErrorBar.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/ReferenceArea.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/ReferenceDot.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/ReferenceLine.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/XAxis.js.map +1 -1
- package/dist/node_modules/recharts/es6/cartesian/YAxis.js.map +1 -1
- package/dist/node_modules/recharts/es6/component/ResponsiveContainer.js.map +1 -1
- package/dist/node_modules/sonner/dist/index.js.map +1 -1
- package/dist/technospam/TechnoSpamRoot.js +4 -4
- package/dist/technospam/components/PageLayout.d.ts.map +1 -1
- package/dist/technospam/components/PageLayout.js +24 -23
- package/dist/technospam/components/PageLayout.js.map +1 -1
- package/dist/technospam/components/StateViews.d.ts +1 -1
- package/dist/technospam/components/StateViews.d.ts.map +1 -1
- package/dist/technospam/components/StateViews.js +28 -26
- package/dist/technospam/components/StateViews.js.map +1 -1
- package/dist/technospam/components/TagInput.d.ts +1 -1
- package/dist/technospam/components/TagInput.d.ts.map +1 -1
- package/dist/technospam/components/TagInput.js +39 -34
- package/dist/technospam/components/TagInput.js.map +1 -1
- package/dist/technospam/hooks/useTr.d.ts +17 -0
- package/dist/technospam/hooks/useTr.d.ts.map +1 -0
- package/dist/technospam/hooks/useTr.js +12 -0
- package/dist/technospam/hooks/useTr.js.map +1 -0
- package/dist/technospam/index.d.ts +1 -0
- package/dist/technospam/index.d.ts.map +1 -1
- package/dist/technospam/index.js +1 -0
- package/dist/technospam/pages/AnalyticsAnalyticsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/AnalyticsAnalyticsPagePage.js +198 -189
- package/dist/technospam/pages/AnalyticsAnalyticsPagePage.js.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/ApplicationsProblemsPagePage.js +165 -163
- package/dist/technospam/pages/ApplicationsProblemsPagePage.js.map +1 -1
- package/dist/technospam/pages/CoursesCoursesPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/CoursesCoursesPagePage.js +117 -113
- package/dist/technospam/pages/CoursesCoursesPagePage.js.map +1 -1
- package/dist/technospam/pages/DashboardDashboardPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/DashboardDashboardPagePage.js +211 -193
- package/dist/technospam/pages/DashboardDashboardPagePage.js.map +1 -1
- package/dist/technospam/pages/HackathonsHackathonsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/HackathonsHackathonsPagePage.js +110 -110
- package/dist/technospam/pages/HackathonsHackathonsPagePage.js.map +1 -1
- package/dist/technospam/pages/MentorshipMentorsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/MentorshipMentorsPagePage.js +174 -160
- package/dist/technospam/pages/MentorshipMentorsPagePage.js.map +1 -1
- package/dist/technospam/pages/MentorshipSessionsPagePage.d.ts.map +1 -1
- package/dist/technospam/pages/MentorshipSessionsPagePage.js +156 -151
- package/dist/technospam/pages/MentorshipSessionsPagePage.js.map +1 -1
- package/dist/technospam/utils/i18n.js +1 -1
- package/dist/technospam/utils/i18n.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"AnalyticsAnalyticsPagePage.js","names":[],"sources":["../../../src/technospam/pages/AnalyticsAnalyticsPagePage.tsx"],"sourcesContent":["import { useMemo, useState, type CSSProperties, type SVGProps } from 'react';\nimport {\n Activity,\n Award,\n BarChart3,\n BookOpen,\n CalendarRange,\n Code2,\n GraduationCap,\n Library,\n Percent,\n Target,\n TrendingUp,\n Trophy,\n Users,\n X,\n type LucideIcon,\n} from 'lucide-react';\nimport { GlassCard, Button, Input } 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}\ninterface TopicRateDatum {\n topic: string;\n accepted: number;\n attempted: number;\n rate: 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/**\n * Accepted rate per problem topic.\n *\n * Built entirely from the submissions this page already loads: each\n * TecSubmission carries its `problem { topic }` and its `verdict`, so the\n * rate is `ACCEPTED / attempts` per topic -- a count, not an inference. The\n * lowest row is called out as exactly that (the lowest measured rate), never\n * as a diagnosis or a recommendation.\n */\nfunction TopicAcceptanceCard({ data }: { data: TopicRateDatum[] }) {\n const weakest = data.length > 1 ? data[data.length - 1] : undefined;\n return (\n <GlassCard className=\"p-5 transition-shadow duration-200 hover:shadow-lg\">\n <ChartHeader\n icon={Target}\n title=\"Accepted rate by topic\"\n subtitle=\"Where your solutions land, and where they don't\"\n tone=\"indigo\"\n />\n {data.length === 0 ? (\n <div className=\"flex h-[240px] items-center justify-center text-center text-sm text-text-muted\">\n Solve a practice problem and your per-topic rate builds here\n </div>\n ) : (\n <div className=\"space-y-3\">\n {data.map((row) => {\n const lowest = weakest?.topic === row.topic;\n return (\n <div key={row.topic}>\n <div className=\"mb-1 flex flex-wrap items-baseline gap-x-2\">\n <span className=\"truncate text-sm font-medium text-text-primary\">\n {row.topic}\n </span>\n <span\n className={cn(\n 'text-sm font-semibold tabular-nums',\n lowest ? 'text-status-warning-text' : 'text-text-secondary'\n )}\n >\n {row.rate}%\n </span>\n <span className=\"ml-auto shrink-0 rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-muted\">\n {row.accepted} of {row.attempted} accepted\n </span>\n </div>\n <div className=\"h-2.5 w-full overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className={cn(\n 'h-full rounded-full transition-all duration-500',\n lowest ? 'bg-status-warning-text' : 'bg-accent-primary'\n )}\n style={{ width: `${row.rate}%` }}\n />\n </div>\n </div>\n );\n })}\n {weakest ? (\n <p className=\"pt-1 text-xs text-text-muted\">\n Lowest measured rate: <span className=\"text-text-secondary\">{weakest.topic}</span> —{' '}\n {weakest.accepted} of {weakest.attempted} attempts accepted in the loaded range.\n </p>\n ) : null}\n </div>\n )}\n </GlassCard>\n );\n}\n\n/* ---------------------------------------------------------- date range */\n\n/** Convert a native `<input type=\"date\">` value (YYYY-MM-DD) into inclusive\n * ISO bounds so the server-side createdAt filter covers the whole day. */\nfunction dayStartIso(date: string): string | undefined {\n return date ? `${date}T00:00:00.000Z` : undefined;\n}\nfunction dayEndIso(date: string): string | undefined {\n return date ? `${date}T23:59:59.999Z` : undefined;\n}\n\nfunction DateRangeControls({\n start,\n end,\n onStart,\n onEnd,\n onPreset,\n onClear,\n}: {\n start: string;\n end: string;\n onStart: (v: string) => void;\n onEnd: (v: string) => void;\n onPreset: (days: number) => void;\n onClear: () => void;\n}) {\n const active = Boolean(start || end);\n return (\n <GlassCard className=\"mb-6 flex flex-col gap-3 p-4 lg:flex-row lg:items-end lg:justify-between\">\n <div className=\"flex flex-wrap items-end gap-3\">\n <div className=\"flex items-center gap-2 self-center text-sm font-medium text-text-secondary\">\n <CalendarRange size={16} className=\"text-accent-teal\" />\n Date range\n </div>\n <div>\n <label\n className=\"mb-1 block text-xs font-medium text-text-muted\"\n htmlFor=\"tec-range-from\"\n >\n From\n </label>\n <Input\n id=\"tec-range-from\"\n type=\"date\"\n value={start}\n max={end || undefined}\n onChange={(e) => onStart(e.target.value)}\n className=\"w-40\"\n />\n </div>\n <div>\n <label className=\"mb-1 block text-xs font-medium text-text-muted\" htmlFor=\"tec-range-to\">\n To\n </label>\n <Input\n id=\"tec-range-to\"\n type=\"date\"\n value={end}\n min={start || undefined}\n onChange={(e) => onEnd(e.target.value)}\n className=\"w-40\"\n />\n </div>\n {active ? (\n <Button variant=\"ghost\" size=\"sm\" onClick={onClear}>\n <X size={14} /> Clear\n </Button>\n ) : null}\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => onPreset(7)}>\n Last 7 days\n </Button>\n <Button variant=\"outline\" size=\"sm\" onClick={() => onPreset(30)}>\n Last 30 days\n </Button>\n <Button variant=\"outline\" size=\"sm\" onClick={() => onPreset(90)}>\n Last 90 days\n </Button>\n </div>\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 [rangeStart, setRangeStart] = useState('');\n const [rangeEnd, setRangeEnd] = useState('');\n const startDate = dayStartIso(rangeStart);\n const endDate = dayEndIso(rangeEnd);\n\n const applyPreset = (days: number) => {\n const now = new Date();\n const from = new Date();\n from.setDate(from.getDate() - days);\n setRangeStart(from.toISOString().slice(0, 10));\n setRangeEnd(now.toISOString().slice(0, 10));\n };\n const clearRange = () => {\n setRangeStart('');\n setRangeEnd('');\n };\n\n const dateVars = startDate || endDate ? { startDate, endDate } : {};\n const enrollmentsQ = useEnrollmentsQuery(learnerId ? { learnerId, ...dateVars } : {});\n const submissionsQ = useSubmissionsQuery(learnerId ? { learnerId, ...dateVars } : {});\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 acceptedRateByTopic = useMemo<TopicRateDatum[]>(() => {\n const buckets = new Map<string, { accepted: number; attempted: number }>();\n submissions.forEach((s) => {\n const topic = s.problem?.topic?.trim() || 'Untagged';\n const bucket = buckets.get(topic) ?? { accepted: 0, attempted: 0 };\n bucket.attempted += 1;\n if (s.verdict === 'ACCEPTED') bucket.accepted += 1;\n buckets.set(topic, bucket);\n });\n return [...buckets.entries()]\n .map(([topic, bucket]) => ({\n topic,\n accepted: bucket.accepted,\n attempted: bucket.attempted,\n rate: Math.round((bucket.accepted / bucket.attempted) * 100),\n }))\n .sort((a, b) => b.rate - a.rate || b.attempted - a.attempted);\n }, [submissions]);\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 <DateRangeControls\n start={rangeStart}\n end={rangeEnd}\n onStart={setRangeStart}\n onEnd={setRangeEnd}\n onPreset={applyPreset}\n onClear={clearRange}\n />\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 <TopicAcceptanceCard data={acceptedRateByTopic} />\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
|
+
{"version":3,"file":"AnalyticsAnalyticsPagePage.js","names":[],"sources":["../../../src/technospam/pages/AnalyticsAnalyticsPagePage.tsx"],"sourcesContent":["import { useMemo, useState, type CSSProperties, type SVGProps } from 'react';\nimport {\n Activity,\n Award,\n BarChart3,\n BookOpen,\n CalendarRange,\n Code2,\n GraduationCap,\n Library,\n Percent,\n Target,\n TrendingUp,\n Trophy,\n Users,\n X,\n type LucideIcon,\n} from 'lucide-react';\nimport { GlassCard, Button, Input } 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';\nimport { useTr } from '../hooks/useTr';\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}\ninterface TopicRateDatum {\n topic: string;\n accepted: number;\n attempted: number;\n rate: 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/**\n * Accepted rate per problem topic.\n *\n * Built entirely from the submissions this page already loads: each\n * TecSubmission carries its `problem { topic }` and its `verdict`, so the\n * rate is `ACCEPTED / attempts` per topic -- a count, not an inference. The\n * lowest row is called out as exactly that (the lowest measured rate), never\n * as a diagnosis or a recommendation.\n */\nfunction TopicAcceptanceCard({\n data,\n title,\n subtitle,\n noData,\n lowestRateLabel,\n}: {\n data: TopicRateDatum[];\n title: string;\n subtitle: string;\n noData: string;\n lowestRateLabel: (topic: string, accepted: number, attempted: number) => string;\n}) {\n const weakest = data.length > 1 ? data[data.length - 1] : undefined;\n return (\n <GlassCard className=\"p-5 transition-shadow duration-200 hover:shadow-lg\">\n <ChartHeader icon={Target} title={title} subtitle={subtitle} tone=\"indigo\" />\n {data.length === 0 ? (\n <div className=\"flex h-[240px] items-center justify-center text-center text-sm text-text-muted\">\n {noData}\n </div>\n ) : (\n <div className=\"space-y-3\">\n {data.map((row) => {\n const lowest = weakest?.topic === row.topic;\n return (\n <div key={row.topic}>\n <div className=\"mb-1 flex flex-wrap items-baseline gap-x-2\">\n <span className=\"truncate text-sm font-medium text-text-primary\">\n {row.topic}\n </span>\n <span\n className={cn(\n 'text-sm font-semibold tabular-nums',\n lowest ? 'text-status-warning-text' : 'text-text-secondary'\n )}\n >\n {row.rate}%\n </span>\n <span className=\"ml-auto shrink-0 rounded-full bg-bg-sunken px-2 py-0.5 text-xs text-text-muted\">\n {row.accepted} of {row.attempted} accepted\n </span>\n </div>\n <div className=\"h-2.5 w-full overflow-hidden rounded-full bg-bg-sunken\">\n <div\n className={cn(\n 'h-full rounded-full transition-all duration-500',\n lowest ? 'bg-status-warning-text' : 'bg-accent-primary'\n )}\n style={{ width: `${row.rate}%` }}\n />\n </div>\n </div>\n );\n })}\n {weakest ? (\n <p className=\"pt-1 text-xs text-text-muted\">\n {lowestRateLabel(weakest.topic, weakest.accepted, weakest.attempted)}\n </p>\n ) : null}\n </div>\n )}\n </GlassCard>\n );\n}\n\n/* ---------------------------------------------------------- date range */\n\n/** Convert a native `<input type=\"date\">` value (YYYY-MM-DD) into inclusive\n * ISO bounds so the server-side createdAt filter covers the whole day. */\nfunction dayStartIso(date: string): string | undefined {\n return date ? `${date}T00:00:00.000Z` : undefined;\n}\nfunction dayEndIso(date: string): string | undefined {\n return date ? `${date}T23:59:59.999Z` : undefined;\n}\n\nfunction DateRangeControls({\n start,\n end,\n onStart,\n onEnd,\n onPreset,\n onClear,\n tr,\n}: {\n start: string;\n end: string;\n onStart: (v: string) => void;\n onEnd: (v: string) => void;\n onPreset: (days: number) => void;\n onClear: () => void;\n tr: ReturnType<typeof useTr>;\n}) {\n const active = Boolean(start || end);\n return (\n <GlassCard className=\"mb-6 flex flex-col gap-3 p-4 lg:flex-row lg:items-end lg:justify-between\">\n <div className=\"flex flex-wrap items-end gap-3\">\n <div className=\"flex items-center gap-2 self-center text-sm font-medium text-text-secondary\">\n <CalendarRange size={16} className=\"text-accent-teal\" />\n {tr('technospam.analytics.dateRangeTitle', 'Date range')}\n </div>\n <div>\n <label\n className=\"mb-1 block text-xs font-medium text-text-muted\"\n htmlFor=\"tec-range-from\"\n >\n {tr('technospam.analytics.from', 'From')}\n </label>\n <Input\n id=\"tec-range-from\"\n type=\"date\"\n value={start}\n max={end || undefined}\n onChange={(e) => onStart(e.target.value)}\n className=\"w-40\"\n />\n </div>\n <div>\n <label className=\"mb-1 block text-xs font-medium text-text-muted\" htmlFor=\"tec-range-to\">\n {tr('technospam.analytics.to', 'To')}\n </label>\n <Input\n id=\"tec-range-to\"\n type=\"date\"\n value={end}\n min={start || undefined}\n onChange={(e) => onEnd(e.target.value)}\n className=\"w-40\"\n />\n </div>\n {active ? (\n <Button variant=\"ghost\" size=\"sm\" onClick={onClear}>\n <X size={14} /> {tr('technospam.analytics.clear', 'Clear')}\n </Button>\n ) : null}\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <Button variant=\"outline\" size=\"sm\" onClick={() => onPreset(7)}>\n {tr('technospam.analytics.last7Days', 'Last 7 days')}\n </Button>\n <Button variant=\"outline\" size=\"sm\" onClick={() => onPreset(30)}>\n {tr('technospam.analytics.last30Days', 'Last 30 days')}\n </Button>\n <Button variant=\"outline\" size=\"sm\" onClick={() => onPreset(90)}>\n {tr('technospam.analytics.last90Days', 'Last 90 days')}\n </Button>\n </div>\n </GlassCard>\n );\n}\n\n/* --------------------------------------------------------------- page */\n\nexport function AnalyticsAnalyticsPagePage() {\n const navigate = useTechnoSpamNavigate();\n const { currentUser } = useTechnoSpam();\n const tr = useTr();\n const learnerId = currentUser?.id;\n\n const [rangeStart, setRangeStart] = useState('');\n const [rangeEnd, setRangeEnd] = useState('');\n const startDate = dayStartIso(rangeStart);\n const endDate = dayEndIso(rangeEnd);\n\n const applyPreset = (days: number) => {\n const now = new Date();\n const from = new Date();\n from.setDate(from.getDate() - days);\n setRangeStart(from.toISOString().slice(0, 10));\n setRangeEnd(now.toISOString().slice(0, 10));\n };\n const clearRange = () => {\n setRangeStart('');\n setRangeEnd('');\n };\n\n const dateVars = startDate || endDate ? { startDate, endDate } : {};\n const enrollmentsQ = useEnrollmentsQuery(learnerId ? { learnerId, ...dateVars } : {});\n const submissionsQ = useSubmissionsQuery(learnerId ? { learnerId, ...dateVars } : {});\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 acceptedRateByTopic = useMemo<TopicRateDatum[]>(() => {\n const buckets = new Map<string, { accepted: number; attempted: number }>();\n submissions.forEach((s) => {\n const topic = s.problem?.topic?.trim() || 'Untagged';\n const bucket = buckets.get(topic) ?? { accepted: 0, attempted: 0 };\n bucket.attempted += 1;\n if (s.verdict === 'ACCEPTED') bucket.accepted += 1;\n buckets.set(topic, bucket);\n });\n return [...buckets.entries()]\n .map(([topic, bucket]) => ({\n topic,\n accepted: bucket.accepted,\n attempted: bucket.attempted,\n rate: Math.round((bucket.accepted / bucket.attempted) * 100),\n }))\n .sort((a, b) => b.rate - a.rate || b.attempted - a.attempted);\n }, [submissions]);\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\">\n {tr('technospam.analytics.workspaceOverviewTitle', 'Workspace overview')}\n </h2>\n <p className=\"mb-4 max-w-2xl text-sm text-text-secondary\">\n {tr(\n 'technospam.analytics.workspaceOverviewDesc',\n 'Live content available across the TechnoSpam learning workspace right now.'\n )}\n </p>\n <div className=\"grid grid-cols-1 gap-3 sm:grid-cols-3\">\n <StatCard\n label={tr('technospam.analytics.statPublishedCourses', 'Published courses')}\n value={coursesTotal}\n icon={Library}\n tone=\"blue\"\n />\n <StatCard\n label={tr('technospam.analytics.statPublishedProblems', 'Published problems')}\n value={problemsTotal}\n icon={Code2}\n tone=\"indigo\"\n />\n <StatCard\n label={tr('technospam.analytics.statHackathons', 'Hackathons')}\n value={hackathonsTotal}\n icon={Trophy}\n tone=\"purple\"\n />\n </div>\n </div>\n );\n\n return (\n <PageLayout\n hero\n title={tr('technospam.analytics.title', 'Analytics')}\n description={tr(\n 'technospam.analytics.description',\n 'Your real learning performance across courses, problems, mentoring and certificates.'\n )}\n actions={\n <Button variant=\"outline\" onClick={() => navigate('/courses/my-learning-page')}>\n <GraduationCap size={16} /> {tr('technospam.common.myLearning', 'My Learning')}\n </Button>\n }\n >\n {!learnerId ? (\n <div className=\"space-y-8\">\n <EmptyView\n icon={<BarChart3 size={26} />}\n title={tr('technospam.analytics.signInTitle', 'Sign in to view your analytics')}\n description={tr(\n 'technospam.analytics.signInDesc',\n \"Your personal learning metrics appear here once you're signed in.\"\n )}\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\n title={tr('technospam.common.errorLoadAnalytics', \"Couldn't load your analytics\")}\n error={learnerError}\n />\n ) : (\n <div className=\"space-y-8\">\n <DateRangeControls\n start={rangeStart}\n end={rangeEnd}\n onStart={setRangeStart}\n onEnd={setRangeEnd}\n onPreset={applyPreset}\n onClear={clearRange}\n tr={tr}\n />\n <div className=\"grid grid-cols-2 gap-3 sm:grid-cols-3 xl:grid-cols-4\">\n <StatCard\n label={tr('technospam.analytics.statCoursesEnrolled', 'Courses enrolled')}\n value={enrollments.length}\n icon={BookOpen}\n tone=\"blue\"\n />\n <StatCard\n label={tr('technospam.analytics.statCoursesCompleted', 'Courses completed')}\n value={metrics.coursesCompleted}\n icon={GraduationCap}\n tone=\"green\"\n />\n <StatCard\n label={tr('technospam.analytics.statAvgProgress', 'Avg progress')}\n value={`${metrics.avgProgress}%`}\n icon={TrendingUp}\n tone=\"purple\"\n />\n <StatCard\n label={tr('technospam.analytics.statProblemsSolved', 'Problems solved')}\n value={metrics.problemsSolved}\n icon={Code2}\n tone=\"indigo\"\n />\n <StatCard\n label={tr('technospam.analytics.statAcceptanceRate', 'Acceptance rate')}\n value={`${metrics.acceptanceRate}%`}\n icon={Percent}\n tone=\"teal\"\n />\n <StatCard\n label={tr('technospam.analytics.statMentorSessions', 'Mentor sessions')}\n value={sessions.length}\n icon={Users}\n tone=\"blue\"\n />\n <StatCard\n label={tr('technospam.analytics.statCertificates', 'Certificates')}\n value={certificates.length}\n icon={Award}\n tone=\"amber\"\n />\n </div>\n\n {!hasActivity ? (\n <EmptyView\n icon={<Activity size={26} />}\n title={tr('technospam.analytics.noActivityTitle', 'No learning activity yet')}\n description={tr(\n 'technospam.analytics.noActivityDesc',\n '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 )}\n action={\n <Button onClick={() => navigate('/courses/courses-page')}>\n {tr('technospam.common.browseCourses', 'Browse courses')}\n </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={tr(\n 'technospam.analytics.enrollmentsByStatusTitle',\n 'Enrollments by status'\n )}\n subtitle={tr(\n 'technospam.analytics.enrollmentsByStatusSubtitle',\n 'How your courses are progressing'\n )}\n icon={BookOpen}\n tone=\"blue\"\n isEmpty={enrollmentsByStatus.length === 0}\n emptyLabel={tr('technospam.analytics.noEnrollments', '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={tr(\n 'technospam.analytics.submissionsByVerdictTitle',\n 'Submissions by verdict'\n )}\n subtitle={tr(\n 'technospam.analytics.submissionsByVerdictSubtitle',\n 'Outcomes across your attempts'\n )}\n icon={Code2}\n tone=\"purple\"\n isEmpty={submissionsByVerdict.length === 0}\n emptyLabel={tr('technospam.analytics.noSubmissions', '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 <TopicAcceptanceCard\n data={acceptedRateByTopic}\n title={tr('technospam.analytics.topicAcceptanceTitle', 'Accepted rate by topic')}\n subtitle={tr(\n 'technospam.analytics.topicAcceptanceSubtitle',\n \"Where your solutions land, and where they don't\"\n )}\n noData={tr(\n 'technospam.analytics.noTopicData',\n 'Solve a practice problem and your per-topic rate builds here'\n )}\n lowestRateLabel={(topic, accepted, attempted) =>\n tr(\n 'technospam.analytics.lowestMeasuredRate',\n 'Lowest measured rate: {{topic}} — {{accepted}} of {{attempted}} attempts accepted in the loaded range.',\n { topic, accepted, attempted }\n )\n }\n />\n\n <GlassCard className=\"p-5 transition-shadow duration-200 hover:shadow-lg\">\n <ChartHeader\n icon={Activity}\n title={tr(\n 'technospam.analytics.activityOverTimeTitle',\n 'Learning activity over time'\n )}\n subtitle={tr(\n 'technospam.analytics.activityOverTimeSubtitle',\n 'Submissions and enrollments per week'\n )}\n tone=\"teal\"\n />\n {activityData.length === 0 ? (\n <div className=\"flex h-[240px] items-center justify-center text-sm text-text-muted\">\n {tr('technospam.analytics.noDatedActivity', '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":"
|
|
1
|
+
{"version":3,"file":"ApplicationsProblemsPagePage.d.ts","sourceRoot":"","sources":["../../../src/technospam/pages/ApplicationsProblemsPagePage.tsx"],"names":[],"mappings":"AA+IA,wBAAgB,4BAA4B,gCAuP3C"}
|