"use client"; import { useState, useCallback } from "react"; import { Card } from "@/components/ui/card"; import { StepControls } from "./step-controls"; import { MathDisplay } from "@/components/math/math-display"; import { toKatex } from "@/lib/math/fractions"; type FQMode = "fractionOf" | "findWhole"; interface Step { label: string; math: string; barFilled: number; // 0–1 proportion barLabel?: string; } function buildFractionOfSteps(num: number, den: number, quantity: number): Step[] { const steps: Step[] = []; steps.push({ label: `Find ${toKatexStr(num, den)} of ${quantity}`, math: `${toKatex(num, den)} \\text{ of } ${quantity}`, barFilled: 0, }); // Step 1: Divide quantity by denominator const onePart = quantity / den; steps.push({ label: `Divide ${quantity} by ${den} to find one part`, math: `${quantity} \\div ${den} = ${fmt(onePart)}`, barFilled: 1 / den, barLabel: fmt(onePart), }); // Step 2: Multiply by numerator const result = onePart * num; steps.push({ label: `Multiply one part by ${num}`, math: `${fmt(onePart)} \\times ${num} = ${fmt(result)}`, barFilled: num / den, barLabel: fmt(result), }); steps.push({ label: `Answer: ${toKatexStr(num, den)} of ${quantity} = ${fmt(result)}`, math: `${toKatex(num, den)} \\times ${quantity} = ${fmt(result)}`, barFilled: num / den, barLabel: fmt(result), }); return steps; } function buildFindWholeSteps(num: number, den: number, part: number): Step[] { const steps: Step[] = []; steps.push({ label: `If ${toKatexStr(num, den)} of the whole = ${part}, find the whole`, math: `${toKatex(num, den)} \\text{ of ? } = ${part}`, barFilled: num / den, barLabel: `${part}`, }); // Step 1: Find one part const onePart = part / num; steps.push({ label: `Divide ${part} by ${num} to find one ${den}th`, math: `${part} \\div ${num} = ${fmt(onePart)}`, barFilled: 1 / den, barLabel: fmt(onePart), }); // Step 2: Multiply by denominator const whole = onePart * den; steps.push({ label: `Multiply by ${den} to find the whole`, math: `${fmt(onePart)} \\times ${den} = ${fmt(whole)}`, barFilled: 1, barLabel: fmt(whole), }); steps.push({ label: `The whole is ${fmt(whole)}`, math: `\\text{Whole} = ${fmt(whole)}`, barFilled: 1, barLabel: fmt(whole), }); return steps; } function toKatexStr(n: number, d: number): string { return `${n}/${d}`; } function fmt(n: number): string { return Number.isInteger(n) ? n.toString() : n.toFixed(2); } function QuantityBar({ filled, label }: { filled: number; label?: string }) { const pct = Math.max(0, Math.min(1, filled)) * 100; return (
{label && pct > 15 ? label : ""}
{label && pct <= 15 && (

{label}

)}
0 {Math.round(pct)}%
); } export function FractionQuantityExplorer({ initialMode = "fractionOf", }: { initialMode?: FQMode; } = {}) { const [mode, setMode] = useState(initialMode); const [num, setNum] = useState("3"); const [den, setDen] = useState("4"); const [quantity, setQuantity] = useState("80"); const [error, setError] = useState(""); const [steps, setSteps] = useState(null); const [currentStep, setCurrentStep] = useState(0); const [isPlaying, setIsPlaying] = useState(false); const done = steps ? currentStep >= steps.length - 1 : false; function handleGo() { setError(""); const n = parseInt(num); const d = parseInt(den); const q = parseFloat(quantity); if (isNaN(n) || isNaN(d) || d === 0) { setError("Enter a valid fraction (denominator ≠ 0)."); return; } if (isNaN(q) || q <= 0) { setError("Enter a valid positive quantity."); return; } try { const s = mode === "fractionOf" ? buildFractionOfSteps(n, d, q) : buildFindWholeSteps(n, d, q); setSteps(s); setCurrentStep(0); setIsPlaying(false); } catch { setError("Could not compute. Check your inputs."); } } const stepForward = useCallback(() => { if (!steps || currentStep >= steps.length - 1) return; setCurrentStep((s) => s + 1); if (currentStep + 1 >= steps.length - 1) setIsPlaying(false); }, [steps, currentStep]); const stepBack = useCallback(() => { if (currentStep <= 0) return; setCurrentStep((s) => s - 1); }, [currentStep]); const togglePlay = useCallback(() => setIsPlaying((p) => !p), []); const reset = useCallback(() => { setSteps(null); setCurrentStep(0); setIsPlaying(false); }, []); const step = steps ? steps[currentStep] : null; return (
{/* Mode tabs */}
{/* Input */}

{mode === "fractionOf" ? "Enter a fraction and quantity" : "Enter a fraction and the part value"}

setNum(e.target.value)} className="w-14 rounded-lg border-2 border-border bg-surface px-2 py-1.5 text-center text-lg font-bold outline-none focus:border-unit-1" aria-label="Numerator" />
setDen(e.target.value)} className="w-14 rounded-lg border-2 border-border bg-surface px-2 py-1.5 text-center text-lg font-bold outline-none focus:border-unit-1" aria-label="Denominator" />
{mode === "fractionOf" ? "of" : "= "} setQuantity(e.target.value)} onKeyDown={(e) => e.key === "Enter" && handleGo()} className="w-24 rounded-lg border-2 border-border bg-surface px-3 py-2.5 text-center text-lg font-bold outline-none focus:border-unit-1" aria-label={mode === "fractionOf" ? "Quantity" : "Part value"} />
{error &&

{error}

} {/* Display */} {!step ? (

Enter values above and click Go

) : ( <>

{step.label}

)}
{/* Controls */} 0} /> {/* Result */} {!done || !steps ? (

Result will appear here when steps are complete

) : ( <>

Answer

)}
); }