chore: restore MVP dashboard components from backup
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
// ============================================================
|
||||
// ویجت ۸ — تراکم پروژهها (Project Density)
|
||||
// نمودار میلهای افقی: توزیع بار تدارکات روی پروژههای فعال —
|
||||
// سفارشهای باز (ستهشده با پیشنهادهای باز).
|
||||
// ============================================================
|
||||
|
||||
import { useMemo } from "react";
|
||||
import {
|
||||
Bar,
|
||||
BarChart,
|
||||
CartesianGrid,
|
||||
Legend,
|
||||
ResponsiveContainer,
|
||||
Tooltip,
|
||||
XAxis,
|
||||
YAxis,
|
||||
} from "recharts";
|
||||
import { useProcurementWorkload } from "../../hooks/useDashboardData";
|
||||
import { faNum } from "../../lib/formatters";
|
||||
import {
|
||||
CHART_COLORS,
|
||||
ChartCard,
|
||||
ChartEmpty,
|
||||
ChartLoading,
|
||||
ChartTooltip,
|
||||
faAxisTick,
|
||||
} from "./chartShared";
|
||||
|
||||
export function ProjectDensityChart() {
|
||||
const query = useProcurementWorkload();
|
||||
|
||||
const rows = useMemo(() => {
|
||||
const projects = query.data?.projects ?? [];
|
||||
return projects
|
||||
.filter((p) => p.open_orders > 0 || p.offered_quotes > 0 || p.chosen_items > 0)
|
||||
.map((p) => ({
|
||||
name: p.name,
|
||||
"سفارش باز (در راه)": p.open_orders,
|
||||
"پیشنهاد باز": p.offered_quotes,
|
||||
_ordered: p.ordered_qty,
|
||||
_received: p.received_qty,
|
||||
}))
|
||||
.sort((a, b) => (b["سفارش باز (در راه)"] + b["پیشنهاد باز"]) - (a["سفارش باز (در راه)"] + a["پیشنهاد باز"]));
|
||||
}, [query.data]);
|
||||
|
||||
return (
|
||||
<ChartCard
|
||||
title="تراکم بار تدارکات روی پروژهها"
|
||||
sub="تعداد سفارشهای باز و پیشنهادهای در جریان به تفکیک پروژه"
|
||||
>
|
||||
{query.isLoading ? (
|
||||
<ChartLoading />
|
||||
) : rows.length === 0 ? (
|
||||
<ChartEmpty message="بار تدارکاتی فعالی روی پروژهها وجود ندارد" />
|
||||
) : (
|
||||
<div dir="ltr" className="w-full" style={{ height: Math.max(180, rows.length * 56 + 60) }}>
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<BarChart
|
||||
data={rows}
|
||||
layout="vertical"
|
||||
margin={{ top: 8, right: 16, left: 8, bottom: 4 }}
|
||||
barCategoryGap="28%"
|
||||
>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="#e5e9f0" horizontal={false} />
|
||||
<XAxis
|
||||
type="number"
|
||||
tick={{ fontSize: 11, fill: "#64748b" }}
|
||||
tickFormatter={faAxisTick}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
allowDecimals={false}
|
||||
/>
|
||||
<YAxis
|
||||
type="category"
|
||||
dataKey="name"
|
||||
width={150}
|
||||
tick={{ fontSize: 11, fill: "#0f172a", fontFamily: "Vazirmatn" }}
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
/>
|
||||
<Tooltip
|
||||
cursor={{ fill: "rgba(79, 70, 229, 0.05)" }}
|
||||
content={
|
||||
<ChartTooltip
|
||||
labelFormatter={(l) => String(l)}
|
||||
valueFormatter={(v) => `${faNum(v)} مورد`}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Legend
|
||||
verticalAlign="top"
|
||||
height={26}
|
||||
iconType="circle"
|
||||
iconSize={8}
|
||||
formatter={(value: unknown) => (
|
||||
<span dir="rtl" className="text-[11px] text-muted">
|
||||
{String(value)}
|
||||
</span>
|
||||
)}
|
||||
/>
|
||||
<Bar
|
||||
dataKey="سفارش باز (در راه)"
|
||||
stackId="load"
|
||||
fill={CHART_COLORS.blue}
|
||||
maxBarSize={22}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
<Bar
|
||||
dataKey="پیشنهاد باز"
|
||||
stackId="load"
|
||||
fill={CHART_COLORS.amber}
|
||||
radius={[0, 4, 4, 0]}
|
||||
maxBarSize={22}
|
||||
isAnimationActive={false}
|
||||
/>
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
)}
|
||||
</ChartCard>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user