/** * Dashboard charts — bar (تعداد بیماران) + line/area (میزان درآمد). * * The source (clinic-pro-tauri) draws these with @mui/x-charts. MUI is not used * in clinicpro, so they are reproduced with plain DOM + inline SVG, matching the * source visuals: bars #5559CE, dashed horizontal grid, y-axis ticks #858D9D, * x-axis labels #7E7E7E, and a line #5559CE over a #3A6FF8 gradient area. */ import React from 'react'; export interface ChartPoint { label: string; value: number; } /** ~5 rounded gridline ticks covering [0, max], top → bottom. Deduped so tiny * integer ranges (e.g. max=1) never render repeated labels. */ function niceTicks(max: number, count = 4): number[] { const safeMax = max > 0 ? max : 1; const rawStep = safeMax / count; const mag = Math.pow(10, Math.floor(Math.log10(rawStep))); const norm = rawStep / mag; const niceNorm = norm <= 1 ? 1 : norm <= 2 ? 2 : norm <= 5 ? 5 : 10; const step = niceNorm * mag; const ticks: number[] = []; for (let i = count; i >= 0; i--) ticks.push(Math.round(step * i)); // collapse duplicate rounded labels (keeps gridlines proportional via ticks[0]) const deduped = ticks.filter((t, i) => i === 0 || t !== ticks[i - 1]); return deduped.length >= 2 ? deduped : [ticks[0], 0]; } const faNum = new Intl.NumberFormat('fa-IR'); /** * Shared plot frame: left y-axis tick column + dashed gridlines + bottom x-axis * labels. `render(top, bottom)` receives the plot-area vertical bounds (px kept * implicit via fl/percentages) and returns the plot content. */ function ChartFrame({ ticks, labels, yWidth, children, }: { ticks: number[]; labels: string[]; yWidth: number; children: React.ReactNode; }) { return (