fix(margin): 좌측 리스트 / 우측 차트 50:50 레이아웃 + 리스트 스크롤
Deploy momo-erp / deploy (push) Successful in 2m6s

This commit is contained in:
chpark
2026-05-15 00:37:00 +09:00
parent e37d6eaa13
commit 08549146be
@@ -98,62 +98,70 @@ export default function MarginPage() {
<Card label="마진율" value={`${marginPct}%`} color="violet" />
</div>
<div className="bg-white border rounded-xl p-4">
<h3 className="font-bold text-slate-700 mb-3 text-sm"> TOP 10 </h3>
<div className="w-full h-72 sm:h-80">
{loading ? (
<div className="h-full flex items-center justify-center text-slate-400"> ...</div>
) : chartData.length === 0 ? (
<div className="h-full flex items-center justify-center text-slate-400"> .</div>
) : (
<ResponsiveContainer>
<BarChart data={chartData} margin={{ top: 10, right: 20, bottom: 30, left: 10 }}>
<CartesianGrid strokeDasharray="3 3" stroke="#e2e8f0" />
<XAxis dataKey="name" tick={{ fontSize: 10 }} interval={0} angle={-25} textAnchor="end" height={50} />
<YAxis tick={{ fontSize: 10 }} tickFormatter={(v) => `${(v / 10000).toFixed(0)}`} />
<Tooltip
formatter={(v) => `${fmt(Number(v))}`}
labelFormatter={(_, payload) => (payload?.[0]?.payload as { fullName: string })?.fullName ?? ""}
/>
<Legend wrapperStyle={{ fontSize: 11 }} />
<Bar dataKey="원가" fill="#f59e0b" />
<Bar dataKey="마진" fill="#10b981" />
</BarChart>
</ResponsiveContainer>
)}
</div>
</div>
<div className="bg-white border rounded-xl overflow-x-auto">
<table className="w-full text-sm min-w-[700px]">
<thead className="bg-slate-50 text-slate-600">
<tr>
<th className="text-left px-4 py-3"></th>
<th className="text-right px-4 py-3"></th>
<th className="text-right px-4 py-3"></th>
<th className="text-right px-4 py-3"></th>
<th className="text-right px-4 py-3"></th>
<th className="text-right px-4 py-3"></th>
</tr>
</thead>
<tbody>
{rows.length === 0 ? (
<tr><td colSpan={6} className="text-center py-12 text-slate-400"> .</td></tr>
) : rows.map((r) => {
const pct = Number(r.REVENUE) ? ((Number(r.MARGIN) / Number(r.REVENUE)) * 100).toFixed(1) : "0.0";
return (
<tr key={r.ITEM_CODE} className="border-t border-slate-100">
<td className="px-4 py-2.5 font-semibold">{r.ITEM_NAME}</td>
<td className="px-4 py-2.5 text-right">{fmt(r.QTY)}</td>
<td className="px-4 py-2.5 text-right tabular-nums">{fmt(r.REVENUE)}</td>
<td className="px-4 py-2.5 text-right tabular-nums text-amber-700">{fmt(r.COST)}</td>
<td className="px-4 py-2.5 text-right tabular-nums font-bold text-emerald-700">{fmt(r.MARGIN)}</td>
<td className="px-4 py-2.5 text-right tabular-nums">{pct}%</td>
{/* 좌: 품목 리스트 / 우: 차트 — 50/50, 화면 높이 내 스크롤 */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-3">
<div className="bg-white border rounded-xl overflow-hidden flex flex-col" style={{ maxHeight: "calc(100vh - 280px)" }}>
<div className="px-3 py-2 bg-slate-50 border-b border-slate-200 text-xs font-semibold text-slate-600">
({rows.length})
</div>
<div className="flex-1 overflow-y-auto overflow-x-auto">
<table className="w-full text-sm min-w-[560px]">
<thead className="bg-slate-50 text-slate-600 sticky top-0">
<tr>
<th className="text-left px-3 py-2"></th>
<th className="text-right px-3 py-2"></th>
<th className="text-right px-3 py-2"></th>
<th className="text-right px-3 py-2"></th>
<th className="text-right px-3 py-2"></th>
<th className="text-right px-3 py-2">%</th>
</tr>
);
})}
</tbody>
</table>
</thead>
<tbody className="tabular-nums">
{rows.length === 0 ? (
<tr><td colSpan={6} className="text-center py-12 text-slate-400">{loading ? "조회 중..." : "데이터가 없습니다."}</td></tr>
) : rows.map((r) => {
const pct = Number(r.REVENUE) ? ((Number(r.MARGIN) / Number(r.REVENUE)) * 100).toFixed(1) : "0.0";
return (
<tr key={r.ITEM_CODE} className="border-t border-slate-100 hover:bg-slate-50">
<td className="px-3 py-2 font-semibold">{r.ITEM_NAME}</td>
<td className="px-3 py-2 text-right">{fmt(r.QTY)}</td>
<td className="px-3 py-2 text-right">{fmt(r.REVENUE)}</td>
<td className="px-3 py-2 text-right text-amber-700">{fmt(r.COST)}</td>
<td className="px-3 py-2 text-right font-bold text-emerald-700">{fmt(r.MARGIN)}</td>
<td className="px-3 py-2 text-right">{pct}%</td>
</tr>
);
})}
</tbody>
</table>
</div>
</div>
<div className="bg-white border rounded-xl p-4 flex flex-col" style={{ maxHeight: "calc(100vh - 280px)" }}>
<h3 className="font-bold text-slate-700 mb-3 text-sm shrink-0"> TOP 10 </h3>
<div className="flex-1 min-h-0">
{loading ? (
<div className="h-full flex items-center justify-center text-slate-400"> ...</div>
) : chartData.length === 0 ? (
<div className="h-full flex items-center justify-center text-slate-400"> .</div>
) : (
<ResponsiveContainer width="100%" height="100%">
<BarChart data={chartData} margin={{ top: 10, right: 20, bottom: 30, left: 10 }}>
<CartesianGrid strokeDasharray="3 3" stroke="#e2e8f0" />
<XAxis dataKey="name" tick={{ fontSize: 10 }} interval={0} angle={-25} textAnchor="end" height={50} />
<YAxis tick={{ fontSize: 10 }} tickFormatter={(v) => `${(v / 10000).toFixed(0)}`} />
<Tooltip
formatter={(v) => `${fmt(Number(v))}`}
labelFormatter={(_, payload) => (payload?.[0]?.payload as { fullName: string })?.fullName ?? ""}
/>
<Legend wrapperStyle={{ fontSize: 11 }} />
<Bar dataKey="원가" fill="#f59e0b" />
<Bar dataKey="마진" fill="#10b981" />
</BarChart>
</ResponsiveContainer>
)}
</div>
</div>
</div>
</div>
);