import { useEffect, useRef } from "react"; function drawGridAndAxes(ctx, w, h, pad, yMin, yMax) { const chartW = w - pad * 2; const chartH = h - pad * 2; ctx.clearRect(0, 0, w, h); // chart area background const bg = ctx.createLinearGradient(0, 0, 0, h); bg.addColorStop(0, "#060b12"); bg.addColorStop(1, "#03070d"); ctx.fillStyle = bg; ctx.fillRect(0, 0, w, h); ctx.strokeStyle = "#13202f"; ctx.lineWidth = 1; const gridRows = 5; for (let i = 0; i <= gridRows; i += 1) { const y = pad + (chartH * i) / gridRows; ctx.beginPath(); ctx.moveTo(pad, y); ctx.lineTo(w - pad, y); ctx.stroke(); } const gridCols = 8; for (let i = 0; i <= gridCols; i += 1) { const x = pad + (chartW * i) / gridCols; ctx.beginPath(); ctx.moveTo(x, pad); ctx.lineTo(x, h - pad); ctx.stroke(); } ctx.strokeStyle = "#2a3e54"; ctx.beginPath(); ctx.moveTo(pad, pad); ctx.lineTo(pad, h - pad); ctx.lineTo(w - pad, h - pad); ctx.stroke(); const zeroInRange = yMin <= 0 && yMax >= 0; if (zeroInRange) { const yRange = Math.max(1e-9, yMax - yMin); const y0 = pad + ((yMax - 0) / yRange) * chartH; ctx.strokeStyle = "#2d5f84"; ctx.setLineDash([4, 4]); ctx.beginPath(); ctx.moveTo(pad, y0); ctx.lineTo(w - pad, y0); ctx.stroke(); ctx.setLineDash([]); } } export function LineChart({ seriesA, seriesB, labelA = "A", labelB = "B" }) { const ref = useRef(null); useEffect(() => { const canvas = ref.current; if (!canvas) return; const ctx = canvas.getContext("2d"); const w = canvas.width; const h = canvas.height; const pad = 40; const all = [...seriesA, ...seriesB]; if (!all.length) return; const yMaxRaw = Math.max(...all); const yMinRaw = Math.min(...all); const margin = Math.max(1, (yMaxRaw - yMinRaw) * 0.12); const yMax = yMaxRaw + margin; const yMin = yMinRaw - margin; const yRange = Math.max(1e-9, yMax - yMin); const chartW = w - pad * 2; const chartH = h - pad * 2; drawGridAndAxes(ctx, w, h, pad, yMin, yMax); const yPx = (value) => pad + ((yMax - value) / yRange) * chartH; const draw = (arr, color, glowColor) => { if (!arr.length) return; ctx.shadowBlur = 8; ctx.shadowColor = glowColor; ctx.strokeStyle = color; ctx.lineWidth = 2.25; const stepX = chartW / Math.max(arr.length - 1, 1); ctx.beginPath(); arr.forEach((v, i) => { const x = pad + i * stepX; const y = yPx(Number(v || 0)); if (i === 0) ctx.moveTo(x, y); else ctx.lineTo(x, y); }); ctx.stroke(); ctx.shadowBlur = 0; // point markers ctx.fillStyle = color; arr.forEach((v, i) => { const x = pad + i * stepX; const y = yPx(Number(v || 0)); ctx.beginPath(); ctx.arc(x, y, 2.2, 0, Math.PI * 2); ctx.fill(); }); }; draw(seriesA, "#4fd6ff", "rgba(79, 214, 255, 0.7)"); draw(seriesB, "#ff8b1a", "rgba(255, 139, 26, 0.6)"); ctx.fillStyle = "#9ec3dd"; ctx.font = "12px Segoe UI"; ctx.fillText(`${labelA} (cyan)`, pad, 18); ctx.fillStyle = "#ffbb80"; ctx.fillText(`${labelB} (orange)`, pad + 170, 18); ctx.fillStyle = "#6f90aa"; ctx.fillText(`max ${yMaxRaw.toFixed(2)}`, 6, pad + 2); ctx.fillText(`min ${yMinRaw.toFixed(2)}`, 6, h - pad + 2); ctx.fillText("steps", w - 44, h - 10); }, [seriesA, seriesB, labelA, labelB]); return ; } export function CompareBars({ rows }) { const safeRows = Array.isArray(rows) ? rows : []; return (