chore(build): refactor codebase for production

This commit is contained in:
shafin-r
2026-03-12 02:39:34 +06:00
parent 121cc2bf71
commit bd35f6a852
123 changed files with 3501 additions and 3254 deletions

View File

@ -1,71 +0,0 @@
import { List, SquarePen, DecimalsArrowRight, MapPin } from "lucide-react";
import { Progress } from "../../components/ui/progress";
import { Button } from "../../components/ui/button";
import {
Card,
CardHeader,
CardTitle,
CardContent,
CardFooter,
} from "../../components/ui/card";
import { Field, FieldLabel } from "../../components/ui/field";
import { CircularProgress } from "../../components/CircularProgress";
export const Analytics = () => {
return (
<main className="min-h-screen max-w-7xl mx-auto px-8 sm:px-6 lg:px-8 py-8 space-y-4 lg:pl-[calc(17rem+2rem)] lg:mr-0">
<h1 className="font-satoshi-bold text-3xl text-center tracking-tight">
Analytics
</h1>
<section className="flex w-full gap-3 justify-between">
<Card className="w-1/3 relative bg-linear-to-br from-purple-600 to-purple-700 rounded-4xl">
<div className="space-y-4">
<CardContent className="md:w-full space-y-4 flex flex-col items-center justify-center h-50">
<MapPin size={60} color="white" />
<h1 className="text-4xl font-satoshi-bold text-white flex">
<span>145</span> <span className="text-xl">th</span>
</h1>
</CardContent>
</div>
<div className="overflow-hidden opacity-0 -rotate-45 absolute -top-2 -right-30 ">
<DecimalsArrowRight size={380} color="white" />
</div>
</Card>
<Card
className="w-2/3 relative bg-linear-to-br from-gray-100 to-gray-300 rounded-4xl
flex-row"
>
<div className="space-y-4">
<CardHeader className="md:w-full">
<CardTitle className="font-satoshi-bold tracking-tight text-3xl ">
Details
</CardTitle>
</CardHeader>
<CardContent className="md:w-full space-y-4"></CardContent>
<CardFooter className="flex justify-between"></CardFooter>
</div>
<div className="overflow-hidden opacity-30 -rotate-45 absolute -top-2 -right-30 ">
<DecimalsArrowRight size={380} color="white" />
</div>
</Card>
</section>
<section>
<Card>
<CardContent>
<Field className="w-full max-w-sm">
<FieldLabel htmlFor="progress-upload">
<span className="font-satoshi text-xl">Score</span>
<span className="ml-auto font-satoshi">
<span className="text-5xl">854</span>
<span className="text-lg">/1600</span>
</span>
</FieldLabel>
<Progress value={55} id="progress-upload" max={100} />
</Field>
</CardContent>
</Card>
</section>
</main>
);
};

View File

@ -7,7 +7,6 @@ import { formatStatus } from "../../lib/utils";
import { useNavigate } from "react-router-dom";
import { SearchOverlay } from "../../components/SearchOverlay";
import { InfoHeader } from "../../components/InfoHeader";
import { InventoryButton } from "../../components/InventoryButton";
// ─── Shared blob/dot background (same as break/results screens) ────────────────
const DOTS = [

View File

@ -491,7 +491,9 @@ export const Lessons = () => {
setLessonLoading(true);
const authStorage = localStorage.getItem("auth-storage");
if (!authStorage) return;
const {
// @ts-ignore
state: { token },
} = JSON.parse(authStorage) as { state?: { token?: string } };
if (!token) return;
@ -631,7 +633,7 @@ export const Lessons = () => {
lesson={lesson}
index={i}
searchQuery={searchQuery}
onClick={() => handleLessonClick(lesson.id)}
onClick={() => handleLessonClick(lesson.id, lesson.title)}
/>
))}
</div>

View File

@ -8,8 +8,6 @@ import {
Zap,
} from "lucide-react";
import { useNavigate } from "react-router-dom";
import { useExamConfigStore } from "../../stores/useExamConfigStore";
import { LevelBar } from "../../components/LevelBar";
import { InfoHeader } from "../../components/InfoHeader";
const DOTS = [

View File

@ -756,9 +756,9 @@ const RouteSegment = ({
isNext,
accent,
}: RouteSegmentProps) => {
const lineRef = useRef<THREE.Line>(null!);
const glowRef = useRef<THREE.Mesh>(null!);
const shipRef = useRef<THREE.Group>(null!);
const lineRef = useRef<THREE.Line | null>(null);
const glowRef = useRef<THREE.Mesh | null>(null);
const shipRef = useRef<THREE.Group | null>(null);
const shipT = useRef(0);
// CatmullRom curve bowing sideways — alternate direction per segment
@ -799,8 +799,10 @@ const RouteSegment = ({
useFrame((_, dt) => {
// Scroll dashes forward along the route
if (lineRef.current) {
const mat = lineRef.current.material as THREE.LineDashedMaterial;
if (dashSpeed > 0) mat.dashOffset -= dt * dashSpeed;
// material typings may not include dashOffset; use any and guard the value
const lineMat = lineRef.current.material as any;
if (dashSpeed > 0)
lineMat.dashOffset = (lineMat.dashOffset ?? 0) - dt * dashSpeed;
}
// Pulse glow on active segments
if (glowRef.current && (isActive || isNext)) {
@ -837,9 +839,14 @@ const RouteSegment = ({
{/* Dashed route line */}
<line
ref={lineRef}
ref={(r: any) => {
// r may be an SVGLineElement in JSX DOM typings; treat as any to satisfy TS and assign to Line ref
lineRef.current = r as THREE.Line | null;
}}
// @ts-ignore - geometry is a three.js prop, not an SVG attribute
geometry={lineGeo}
onUpdate={(self) => self.computeLineDistances()}
// onUpdate receives a three.js Line; use any to avoid DOM typings
onUpdate={(self: any) => self.computeLineDistances()}
>
<lineDashedMaterial
color={color}
@ -1275,7 +1282,7 @@ const LeftPanel = ({
arcs: QuestArc[];
activeArcId: string;
onSelectArc: (id: string) => void;
scrollRef: React.RefObject<HTMLDivElement>;
scrollRef: React.RefObject<HTMLDivElement | null>;
user: any;
onClaim: (n: QuestNode) => void;
}) => {
@ -1557,7 +1564,7 @@ export const QuestMap = () => {
const [claimResult, setClaimResult] = useState<ClaimedRewardResponse | null>(
null,
);
const [claimLoading, setClaimLoading] = useState(false);
const [claimError, setClaimError] = useState<string | null>(null);
const [selectedNode, setSelectedNode] = useState<QuestNode | null>(null);
@ -1597,14 +1604,12 @@ export const QuestMap = () => {
setClaimingNode(node);
setClaimResult(null);
setClaimError(null);
setClaimLoading(true);
try {
const result = await api.claimReward(token, node.node_id);
setClaimResult(result);
} catch (err) {
setClaimError(err instanceof Error ? err.message : "Claim failed");
} finally {
setClaimLoading(false);
}
},
[token],

View File

@ -434,9 +434,10 @@ export const Rewards = () => {
if (!user) return;
const authStorage = localStorage.getItem("auth-storage");
if (!authStorage) return;
const {
state: { token },
} = JSON.parse(authStorage) as { state?: { token?: string } };
const parsed = JSON.parse(authStorage) as {
state?: { token?: string };
} | null;
const token = parsed?.state?.token;
if (!token) return;
try {
setLoading(true);
@ -481,7 +482,7 @@ export const Rewards = () => {
// ✅ FIX 2: Safely cast user_rank — null becomes undefined so all optional chaining works
const ur = (leaderboard?.user_rank ?? undefined) as
| Record<string, unknown>
| Record<string, number>
| undefined;
const islandStats = getIslandStats(ur, activeTab);

View File

@ -324,9 +324,10 @@ export const Drills = () => {
setLoading(true);
const authStorage = localStorage.getItem("auth-storage");
if (!authStorage) return;
const {
state: { token },
} = JSON.parse(authStorage) as { state?: { token?: string } };
const parsed = JSON.parse(authStorage) as {
state?: { token?: string };
} | null;
const token = parsed?.state?.token;
if (!token) return;
const response = await api.fetchAllTopics(token);
setTopics(response);

View File

@ -47,9 +47,9 @@ function FormulaCard({
/>
</div>
<div
className={`transition-all duration-300 ease-in-out ${open ? "max-h-[400px] opacity-100" : "max-h-0 opacity-0"}`}
className={`transition-all duration-300 ease-in-out ${open ? "max-h-100 opacity-100" : "max-h-0 opacity-0"}`}
>
<div className="border-t border-emerald-100 px-5 py-4 flex flex-col sm:flex-row items-center gap-5 bg-gradient-to-br from-emerald-50/50 to-white/80">
<div className="border-t border-emerald-100 px-5 py-4 flex flex-col sm:flex-row items-center gap-5 bg-linear-to-br from-emerald-50/50 to-white/80">
<div className="shrink-0">{diagram}</div>
<div className="text-sm text-slate-600 space-y-1 font-mono">
{example}

View File

@ -469,7 +469,7 @@ const CirclePropertiesLesson: React.FC<LessonProps> = ({ onFinish }) => {
<h2 className="text-4xl font-extrabold text-slate-900 mb-8">
Practice Time
</h2>
{CIRCLE_PROP_QUIZ_DATA.map((quiz, idx) => (
{CIRCLE_PROP_QUIZ_DATA.map((quiz) => (
<div key={quiz.id} className="mb-12">
<Quiz data={quiz} />
</div>

View File

@ -1,4 +1,3 @@
import React from "react";
import {
Circle,
Target,

View File

@ -490,7 +490,7 @@ const CongruenceSimilarityLesson: React.FC<LessonProps> = ({ onFinish }) => {
<h2 className="text-4xl font-extrabold text-slate-900 mb-8">
Practice Time
</h2>
{SIMILARITY_QUIZ_DATA.map((quiz, idx) => (
{SIMILARITY_QUIZ_DATA.map((quiz) => (
<div key={quiz.id} className="mb-12">
<Quiz data={quiz} />
</div>

View File

@ -266,7 +266,7 @@ const DataAnalysisLesson: React.FC<LessonProps> = ({ onFinish }) => {
<h2 className="text-4xl font-extrabold text-slate-900 mb-8">
Practice Time
</h2>
{DATA_ANALYSIS_QUIZ_DATA.map((quiz, idx) => (
{DATA_ANALYSIS_QUIZ_DATA.map((quiz) => (
<div key={quiz.id} className="mb-12">
<Quiz data={quiz} />
</div>

View File

@ -296,7 +296,7 @@ const EBRWBoundariesLesson: React.FC<LessonProps> = ({ onFinish }) => {
}: {
index: number;
title: string;
icon: React.ElementType;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
}) => {
const isActive = activeSection === index;
const isPast = activeSection > index;

View File

@ -142,7 +142,7 @@ const EBRWCentralIdeasLesson: React.FC<LessonProps> = ({ onFinish }) => {
}: {
index: number;
title: string;
icon: React.ElementType;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
}) => {
const isActive = activeSection === index;
const isPast = activeSection > index;

View File

@ -158,7 +158,7 @@ const EBRWCommandEvidenceLesson: React.FC<LessonProps> = ({ onFinish }) => {
}: {
index: number;
title: string;
icon: React.ElementType;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
}) => {
const isActive = activeSection === index;
const isPast = activeSection > index;

View File

@ -209,7 +209,7 @@ const EBRWCommasLesson: React.FC<LessonProps> = ({ onFinish }) => {
}: {
index: number;
title: string;
icon: React.ElementType;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
}) => {
const isActive = activeSection === index;
const isPast = activeSection > index;

View File

@ -88,7 +88,7 @@ const EBRWCraftStructureLesson: React.FC<LessonProps> = ({ onFinish }) => {
}: {
index: number;
title: string;
icon: React.ElementType;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
}) => {
const isActive = activeSection === index;
const isPast = activeSection > index;

View File

@ -125,7 +125,7 @@ const EBRWCrossTextLesson: React.FC<LessonProps> = ({ onFinish }) => {
}: {
index: number;
title: string;
icon: React.ElementType;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
}) => {
const isActive = activeSection === index;
const isPast = activeSection > index;

View File

@ -195,7 +195,7 @@ const EBRWDashesApostrophesLesson: React.FC<LessonProps> = ({ onFinish }) => {
}: {
index: number;
title: string;
icon: React.ElementType;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
}) => {
const isActive = activeSection === index;
const isPast = activeSection > index;

View File

@ -105,6 +105,7 @@ const EBRWExplicitMeaningLesson: React.FC<LessonProps> = ({ onFinish }) => {
{isPast ? (
<Check className="w-4 h-4" />
) : (
// @ts-ignore
<Icon className="w-4 h-4" />
)}
</div>

View File

@ -105,6 +105,7 @@ const EBRWExpressionIdeasLesson: React.FC<LessonProps> = ({ onFinish }) => {
{isPast ? (
<Check className="w-4 h-4" />
) : (
// @ts-ignore
<Icon className="w-4 h-4" />
)}
</div>

View File

@ -220,6 +220,7 @@ const EBRWFormStructureSenseLesson: React.FC<LessonProps> = ({ onFinish }) => {
{isPast ? (
<Check className="w-4 h-4" />
) : (
// @ts-ignore
<Icon className="w-4 h-4" />
)}
</div>

View File

@ -273,6 +273,7 @@ const EBRWGraphicDisplaysLesson: React.FC<LessonProps> = ({ onFinish }) => {
{isPast ? (
<Check className="w-4 h-4" />
) : (
// @ts-ignore
<Icon className="w-4 h-4" />
)}
</div>

View File

@ -155,6 +155,7 @@ const EBRWInferencesLesson: React.FC<LessonProps> = ({ onFinish }) => {
{isPast ? (
<Check className="w-4 h-4" />
) : (
// @ts-ignore
<Icon className="w-4 h-4" />
)}
</div>

View File

@ -102,6 +102,7 @@ const EBRWMainIdeaLesson: React.FC<LessonProps> = ({ onFinish }) => {
{isPast ? (
<Check className="w-4 h-4" />
) : (
// @ts-ignore
<Icon className="w-4 h-4" />
)}
</div>

View File

@ -208,7 +208,7 @@ const EBRWPronounsLesson: React.FC<LessonProps> = ({ onFinish }) => {
}: {
index: number;
title: string;
icon: React.ElementType;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
}) => {
const isActive = activeSection === index;
const isPast = activeSection > index;

View File

@ -190,6 +190,7 @@ const EBRWRhetoricalSynthesisLesson: React.FC<LessonProps> = ({ onFinish }) => {
{isPast ? (
<Check className="w-4 h-4" />
) : (
// @ts-ignore
<Icon className="w-4 h-4" />
)}
</div>

View File

@ -189,7 +189,7 @@ const EBRWSemicolonsColonsLesson: React.FC<LessonProps> = ({ onFinish }) => {
}: {
index: number;
title: string;
icon: React.ElementType;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
}) => {
const isActive = activeSection === index;
const isPast = activeSection > index;

View File

@ -214,7 +214,7 @@ const EBRWSentenceStructureLesson: React.FC<LessonProps> = ({ onFinish }) => {
}: {
index: number;
title: string;
icon: React.ElementType;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
}) => {
const isActive = activeSection === index;
const isPast = activeSection > index;

View File

@ -203,7 +203,7 @@ const EBRWSubjectVerbLesson: React.FC<LessonProps> = ({ onFinish }) => {
}: {
index: number;
title: string;
icon: React.ElementType;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
}) => {
const isActive = activeSection === index;
const isPast = activeSection > index;

View File

@ -270,7 +270,7 @@ const EBRWTextStructurePurposeLesson: React.FC<LessonProps> = ({
}: {
index: number;
title: string;
icon: React.ElementType;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
}) => {
const isActive = activeSection === index;
const isPast = activeSection > index;

View File

@ -171,7 +171,7 @@ const EBRWTransitionsLesson: React.FC<LessonProps> = ({ onFinish }) => {
}: {
index: number;
title: string;
icon: React.ElementType;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
}) => {
const isActive = activeSection === index;
const isPast = activeSection > index;

View File

@ -211,7 +211,7 @@ const EBRWVerbsLesson: React.FC<LessonProps> = ({ onFinish }) => {
}: {
index: number;
title: string;
icon: React.ElementType;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
}) => {
const isActive = activeSection === index;
const isPast = activeSection > index;

View File

@ -45,7 +45,7 @@ const EBRWVocabMeaningLesson: React.FC<LessonProps> = ({ onFinish }) => {
}: {
index: number;
title: string;
icon: React.ElementType;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
}) => {
const isActive = activeSection === index;
const isPast = activeSection > index;

View File

@ -45,7 +45,7 @@ const EBRWVocabPreciseLesson: React.FC<LessonProps> = ({ onFinish }) => {
}: {
index: number;
title: string;
icon: React.ElementType;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
}) => {
const isActive = activeSection === index;
const isPast = activeSection > index;

View File

@ -275,7 +275,7 @@ const EBRWWordsInContextLesson: React.FC<LessonProps> = ({ onFinish }) => {
}: {
index: number;
title: string;
icon: React.ElementType;
icon: React.ComponentType<React.SVGProps<SVGSVGElement>>;
}) => {
const isActive = activeSection === index;
const isPast = activeSection > index;
@ -288,11 +288,7 @@ const EBRWWordsInContextLesson: React.FC<LessonProps> = ({ onFinish }) => {
className={`w-8 h-8 rounded-full flex items-center justify-center shrink-0
${isActive ? "bg-fuchsia-600 text-white" : isPast ? "bg-fuchsia-400 text-white" : "bg-slate-200 text-slate-500"}`}
>
{isPast ? (
<Check className="w-4 h-4" />
) : (
<Icon className="w-4 h-4" />
)}
{isPast ? <Check className="w-4 h-4" /> : <Icon />}
</div>
<p
className={`text-sm font-bold ${isActive ? "text-fuchsia-900" : "text-slate-600"}`}

View File

@ -1,4 +1,3 @@
import React from "react";
import {
ArrowRight,
Layers,

View File

@ -1,4 +1,3 @@
import React from "react";
import {
Search,
GitBranch,

View File

@ -1,4 +1,4 @@
import React, { useState } from "react";
import { useState } from "react";
import {
Scale,
ArrowRight,
@ -64,9 +64,9 @@ const BalanceWidget = () => {
</button>
</div>
<div className="relative h-32 flex justify-center items-end mb-4">
<div className="absolute bottom-0 w-0 h-0 border-l-[16px] border-l-transparent border-r-[16px] border-r-transparent border-b-[32px] border-b-slate-800" />
<div className="absolute bottom-0 w-0 h-0 border-l-16 border-l-transparent border-r-16 border-r-transparent border-b-32 border-b-slate-800" />
<div
className="w-52 h-1.5 bg-slate-600 absolute bottom-[32px] transition-transform duration-500"
className="w-52 h-1.5 bg-slate-600 absolute bottom-8 transition-transform duration-500"
style={{ transform: `rotate(${tilt}deg)` }}
>
<div className="absolute left-0 -top-12 w-16 h-12 border-b-2 border-l border-r border-slate-300 rounded-b-lg bg-white/80 flex items-center justify-center">

View File

@ -1,12 +1,4 @@
import React from "react";
import {
Grid,
TrendingUp,
Layers,
ArrowRight,
Hash,
BookOpen,
} from "lucide-react";
import { Grid, TrendingUp, Layers, Hash, BookOpen } from "lucide-react";
import LessonShell, {
ConceptCard,
FormulaBox,

View File

@ -57,9 +57,9 @@ const BalanceScaleWidget = () => {
</button>
</div>
<div className="relative h-40 w-full mb-8 flex justify-center items-end">
<div className="absolute bottom-0 w-0 h-0 border-l-[20px] border-l-transparent border-r-[20px] border-r-transparent border-b-[40px] border-b-slate-800"></div>
<div className="absolute bottom-0 w-0 h-0 border-l-20 border-l-transparent border-r-20 border-r-transparent border-b-40 border-b-slate-800"></div>
<div
className="w-64 h-2 bg-slate-600 absolute bottom-[40px] transition-transform duration-700"
className="w-64 h-2 bg-slate-600 absolute bottom-10 transition-transform duration-700"
style={{ transform: `rotate(${tilt}deg)` }}
>
<div
@ -481,7 +481,7 @@ const LinearEquationsLesson: React.FC<LessonProps> = ({ onFinish }) => {
<h2 className="text-4xl font-extrabold text-slate-900 mb-8">
Practice Time
</h2>
{LINEAR_EQ_QUIZ_DATA.map((quiz, idx) => (
{LINEAR_EQ_QUIZ_DATA.map((quiz) => (
<div key={quiz.id} className="mb-12">
<Quiz data={quiz} />
</div>

View File

@ -1,4 +1,3 @@
import React from "react";
import {
TrendingUp,
Hash,
@ -186,7 +185,7 @@ export default function LinearFunctionsLesson({ onFinish }: LessonProps) {
key={c}
className="flex gap-3 items-center bg-white/60 rounded-lg p-3 border border-blue-100"
>
<code className="font-mono text-blue-700 font-bold min-w-[110px]">
<code className="font-mono text-blue-700 font-bold min-w-27.5">
{c}
</code>
<span className="text-slate-600">{d}</span>

View File

@ -1,4 +1,3 @@
import React from "react";
import {
Scale,
ArrowRight,
@ -9,7 +8,6 @@ import {
} from "lucide-react";
import LessonShell, {
ConceptCard,
FormulaBox,
ExampleCard,
TipCard,
PracticeFromDataset,

View File

@ -327,7 +327,7 @@ const LinearParallelPerpendicularLesson: React.FC<LessonProps> = ({
<h2 className="text-4xl font-extrabold text-slate-900 mb-8">
Practice Time
</h2>
{LINEAR_PARALLEL_PERP_QUIZ_DATA.map((quiz, idx) => (
{LINEAR_PARALLEL_PERP_QUIZ_DATA.map((quiz) => (
<div key={quiz.id} className="mb-12">
<Quiz data={quiz} />
</div>

View File

@ -303,7 +303,7 @@ const LinearTransformationsLesson: React.FC<LessonProps> = ({ onFinish }) => {
<h2 className="text-4xl font-extrabold text-slate-900 mb-8">
Practice Time
</h2>
{LINEAR_TRANSFORMATIONS_QUIZ_DATA.map((quiz, idx) => (
{LINEAR_TRANSFORMATIONS_QUIZ_DATA.map((quiz) => (
<div key={quiz.id} className="mb-12">
<Quiz data={quiz} />
</div>

View File

@ -1,4 +1,3 @@
import React from "react";
import {
ArrowRight,
Triangle,

View File

@ -1,4 +1,3 @@
import React from "react";
import { Layers, Hash, Target, Zap, RotateCcw, BookOpen } from "lucide-react";
import LessonShell, {
ConceptCard,

View File

@ -1,4 +1,3 @@
import React from "react";
import {
BarChart,
Box,

View File

@ -457,7 +457,7 @@ const PolynomialFunctionsLesson: React.FC<LessonProps> = ({ onFinish }) => {
<h2 className="text-4xl font-extrabold text-slate-900 mb-8">
Practice Time
</h2>
{ADV_POLYNOMIAL_QUIZ.map((quiz, idx) => (
{ADV_POLYNOMIAL_QUIZ.map((quiz) => (
<div key={quiz.id} className="mb-12">
<Quiz data={quiz} />
</div>

View File

@ -1,4 +1,3 @@
import React from "react";
import { Target, Hash, GitBranch, Layers, Table, BookOpen } from "lucide-react";
import LessonShell, {
ConceptCard,

View File

@ -618,7 +618,7 @@ const QuadraticEquationsLesson: React.FC<LessonProps> = ({ onFinish }) => {
<h2 className="text-4xl font-extrabold text-slate-900 mb-8">
Practice Time
</h2>
{QUADRATIC_EQ_QUIZ_DATA.map((quiz, idx) => (
{QUADRATIC_EQ_QUIZ_DATA.map((quiz) => (
<div key={quiz.id} className="mb-12">
<Quiz data={quiz} />
</div>

View File

@ -450,7 +450,7 @@ const RationalRadicalLesson: React.FC<LessonProps> = ({ onFinish }) => {
<h2 className="text-4xl font-extrabold text-slate-900 mb-8">
Practice Time
</h2>
{ADV_RATIONAL_QUIZ.map((quiz, idx) => (
{ADV_RATIONAL_QUIZ.map((quiz) => (
<div key={quiz.id} className="mb-12">
<Quiz data={quiz} />
</div>

View File

@ -32,7 +32,7 @@ const RatiosLesson: React.FC<LessonProps> = ({ onFinish }) => {
<h2 className="text-4xl font-extrabold text-slate-900 mb-8">
Practice Time
</h2>
{RATIOS_QUIZ_DATA.map((quiz, idx) => (
{RATIOS_QUIZ_DATA.map((quiz) => (
<div key={quiz.id} className="mb-12">
<Quiz data={quiz} />
</div>

View File

@ -1,4 +1,3 @@
import React from "react";
import {
Scale,
ArrowRight,

View File

@ -1,4 +1,3 @@
import React from "react";
import {
Triangle,
Ruler,

View File

@ -1,4 +1,3 @@
import React from "react";
import { Scale, Target, BarChart, Layers, Hash, BookOpen } from "lucide-react";
import LessonShell, {
ConceptCard,

View File

@ -1,4 +1,3 @@
import React from "react";
import {
Target,
ArrowRight,

View File

@ -384,7 +384,7 @@ const SystemsEquationsLesson: React.FC<LessonProps> = ({ onFinish }) => {
<h2 className="text-4xl font-extrabold text-slate-900 mb-8">
Practice Time
</h2>
{SYSTEMS_QUIZ_DATA.map((quiz, idx) => (
{SYSTEMS_QUIZ_DATA.map((quiz) => (
<div key={quiz.id} className="mb-12">
<Quiz data={quiz} />
</div>

View File

@ -1,8 +1,6 @@
import React from "react";
import { Layers, ArrowRight, Hash, Lightbulb, BookOpen } from "lucide-react";
import LessonShell, {
ConceptCard,
FormulaBox,
ExampleCard,
TipCard,
PracticeFromDataset,

View File

@ -688,7 +688,7 @@ const TrigLesson: React.FC<LessonProps> = ({ onFinish }) => {
<h2 className="text-4xl font-extrabold text-slate-900 mb-8">
Practice Time
</h2>
{TRIG_QUIZ_DATA.map((quiz, idx) => (
{TRIG_QUIZ_DATA.map((quiz) => (
<div key={quiz.id} className="mb-12">
<Quiz data={quiz} />
</div>

View File

@ -2,8 +2,9 @@ import { useNavigate } from "react-router-dom";
import { useResults } from "../../../stores/useResults";
import { LucideArrowLeft } from "lucide-react";
import { CircularLevelProgress } from "../../../components/CircularLevelProgress";
import { useEffect, useState } from "react";
import { useCallback, useEffect, useState } from "react";
import { useExamConfigStore } from "../../../stores/useExamConfigStore";
import { useAuthStore } from "../../../stores/authStore";
// ─── Shared styles injected once ─────────────────────────────────────────────
const STYLES = `
@ -404,13 +405,16 @@ export const Results = () => {
const clearResults = useResults((s) => s.clearResults);
const { payload } = useExamConfigStore();
const isTargeted = payload?.mode === "TARGETED";
const fetchUser = useAuthStore((s) => s.fetchUser);
function handleFinishExam() {
const handleFinishExam = useCallback(async () => {
useExamConfigStore.getState().clearPayload();
clearResults();
await fetchUser(); // ← refreshes user in authStore after exam completes
navigate("/student/home");
}
}, [clearResults, fetchUser, navigate]);
if (isTargeted) return <TargetedResults onFinish={handleFinishExam} />;

View File

@ -1,5 +1,6 @@
import { useEffect, useState, useRef } from "react";
import { Navigate, useNavigate } from "react-router-dom";
// @ts-ignore
import { BlockMath, InlineMath } from "react-katex";
import {
Binary,
@ -931,6 +932,7 @@ export const Test = () => {
if (!user) return;
const payload = useExamConfigStore.getState().payload;
try {
// @ts-ignore
const response = await api.startSession(token as string, payload);
setSessionId(response.id);
await loadSessionQuestions(response.id);
@ -1067,6 +1069,7 @@ export const Test = () => {
(e) => !correctedRef.current.has(e.questionId),
);
if (!remaining.length) {
// @ts-ignore
const next = await api.fetchNextModule(token!, sessionId);
if (next.status === "COMPLETED") finishExam();
return;
@ -2152,7 +2155,7 @@ export const Test = () => {
<p className="text-gray-500 font-semibold mb-8">
Take a breather next module coming up
</p>
<div className="t-card p-6 mb-8 text-center min-w-[200px]">
<div className="t-card p-6 mb-8 text-center min-w-50">
<p className="text-xs font-bold text-gray-400 uppercase tracking-widest mb-2">
Next module in
</p>
@ -2198,7 +2201,7 @@ export const Test = () => {
["🏆", "Nice!", "Results"],
["🔥", "100%", "Effort"],
].map(([e, v, l]) => (
<div key={l} className="t-card p-4 text-center min-w-[100px]">
<div key={l} className="t-card p-4 text-center min-w-25">
<span className="text-2xl block mb-1">{e}</span>
<span className="font-black text-lg text-[#1e1b4b]">{v}</span>
<span className="text-xs font-bold text-gray-400 uppercase block">

View File

@ -513,9 +513,10 @@ export const TargetedPractice = () => {
setLoading(true);
const authStorage = localStorage.getItem("auth-storage");
if (!authStorage) return;
const {
state: { token },
} = JSON.parse(authStorage) as { state?: { token?: string } };
const parsed = JSON.parse(authStorage) as {
state?: { token?: string };
} | null;
const token = parsed?.state?.token;
if (!token) return;
const response = await api.fetchAllTopics(token);
setTopics(response);
@ -707,9 +708,13 @@ export const TargetedPractice = () => {
color: meta.color,
}}
>
{t.section === "Reading & Writing"
? "R&W"
: t.section}
{(() => {
const s = String(t.section);
return s === "EBRW" ||
s === "Reading & Writing"
? "R&W"
: s;
})()}
</span>
)}