fix(statistics): 월간/일자별 매출 — 좌측 리스트 / 우측 차트 50:50 레이아웃
Deploy momo-erp / deploy (push) Successful in 2m9s

This commit is contained in:
chpark
2026-05-15 00:57:57 +09:00
parent 08549146be
commit 17ae2b80d7
2 changed files with 116 additions and 101 deletions
@@ -94,58 +94,66 @@ export default function DailyStatsPage() {
<Card label="총 매출 (VAT)" value={`${fmt(total)}`} color="emerald" />
</div>
<div className="bg-white border rounded-xl p-4">
<h3 className="font-bold text-slate-700 mb-3 text-sm"> </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>
<ComposedChart data={chartData} margin={{ top: 10, right: 20, bottom: 0, left: 10 }}>
<CartesianGrid strokeDasharray="3 3" stroke="#e2e8f0" />
<XAxis dataKey="day" tick={{ fontSize: 10 }} />
<YAxis yAxisId="left" tick={{ fontSize: 10 }} tickFormatter={(v) => `${(v / 10000).toFixed(0)}`} />
<YAxis yAxisId="right" orientation="right" tick={{ fontSize: 10 }} />
<Tooltip
formatter={(v, name) => name === "건수" ? `${Number(v)}` : `${fmt(Number(v))}`}
/>
<Legend wrapperStyle={{ fontSize: 11 }} />
<Bar yAxisId="left" dataKey="면세" stackId="a" fill="#8b5cf6" />
<Bar yAxisId="left" dataKey="과세" stackId="a" fill="#f43f5e" />
<Line yAxisId="right" type="monotone" dataKey="건수" stroke="#0ea5e9" strokeWidth={2} dot={{ r: 3 }} />
</ComposedChart>
</ResponsiveContainer>
)}
{/* 좌: 일자별 리스트 / 우: 추이 차트 — 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 - 320px)" }}>
<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-[480px]">
<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>
</tr>
</thead>
<tbody className="tabular-nums">
{rows.length === 0 ? (
<tr><td colSpan={5} className="text-center py-12 text-slate-400">{loading ? "조회 중..." : "선택한 기간의 매출 데이터가 없습니다."}</td></tr>
) : rows.map((r) => (
<tr key={r.DAY} className="border-t border-slate-100 hover:bg-slate-50">
<td className="px-3 py-2 font-semibold">{r.DAY}</td>
<td className="px-3 py-2 text-right">{r.ORDER_CNT}</td>
<td className="px-3 py-2 text-right text-violet-700">{fmt(r.TAX_FREE)}</td>
<td className="px-3 py-2 text-right text-rose-700">{fmt(r.TAXABLE)}</td>
<td className="px-3 py-2 text-right font-bold">{fmt(r.TOTAL)}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
<div className="bg-white border rounded-xl overflow-x-auto">
<table className="w-full text-sm min-w-[600px]">
<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>
</tr>
</thead>
<tbody>
{rows.length === 0 ? (
<tr><td colSpan={5} className="text-center py-12 text-slate-400"> .</td></tr>
) : rows.map((r) => (
<tr key={r.DAY} className="border-t border-slate-100">
<td className="px-4 py-2.5 font-semibold">{r.DAY}</td>
<td className="px-4 py-2.5 text-right">{r.ORDER_CNT}</td>
<td className="px-4 py-2.5 text-right tabular-nums text-violet-700">{fmt(r.TAX_FREE)}</td>
<td className="px-4 py-2.5 text-right tabular-nums text-rose-700">{fmt(r.TAXABLE)}</td>
<td className="px-4 py-2.5 text-right tabular-nums font-bold">{fmt(r.TOTAL)}</td>
</tr>
))}
</tbody>
</table>
<div className="bg-white border rounded-xl p-4 flex flex-col" style={{ maxHeight: "calc(100vh - 320px)" }}>
<h3 className="font-bold text-slate-700 mb-3 text-sm shrink-0"> </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%">
<ComposedChart data={chartData} margin={{ top: 10, right: 20, bottom: 0, left: 10 }}>
<CartesianGrid strokeDasharray="3 3" stroke="#e2e8f0" />
<XAxis dataKey="day" tick={{ fontSize: 10 }} />
<YAxis yAxisId="left" tick={{ fontSize: 10 }} tickFormatter={(v) => `${(v / 10000).toFixed(0)}`} />
<YAxis yAxisId="right" orientation="right" tick={{ fontSize: 10 }} />
<Tooltip
formatter={(v, name) => name === "건수" ? `${Number(v)}` : `${fmt(Number(v))}`}
/>
<Legend wrapperStyle={{ fontSize: 11 }} />
<Bar yAxisId="left" dataKey="면세" stackId="a" fill="#8b5cf6" />
<Bar yAxisId="left" dataKey="과세" stackId="a" fill="#f43f5e" />
<Line yAxisId="right" type="monotone" dataKey="건수" stroke="#0ea5e9" strokeWidth={2} dot={{ r: 3 }} />
</ComposedChart>
</ResponsiveContainer>
)}
</div>
</div>
</div>
</div>
);
+58 -51
View File
@@ -100,59 +100,66 @@ export default function StatisticsPage() {
<Card label="총 매출 (VAT포함)" value={fmt(grandTotal)} color="emerald" />
</div>
{/* 차트 */}
<div className="bg-white border border-slate-200 rounded-xl p-4">
<h3 className="font-bold text-slate-700 mb-3 text-sm"> (TOP 15)</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: 10, bottom: 30, left: 10 }}>
<CartesianGrid strokeDasharray="3 3" stroke="#e2e8f0" />
<XAxis dataKey="name" tick={{ fontSize: 11 }} 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 ?? ""}
cursor={{ fill: "rgba(16, 185, 129, 0.05)" }}
/>
<Legend wrapperStyle={{ fontSize: 11 }} />
<Bar dataKey="면세" stackId="a" fill="#8b5cf6" />
<Bar dataKey="과세" stackId="a" fill="#f43f5e">
{chartData.map((_, i) => <Cell key={i} fill={COLORS[i % COLORS.length]} fillOpacity={0.7} />)}
</Bar>
</BarChart>
</ResponsiveContainer>
)}
{/* 좌: 업체별 리스트 / 우: 차트 — 50/50 */}
<div className="grid grid-cols-1 lg:grid-cols-2 gap-3">
<div className="bg-white border border-slate-200 rounded-xl overflow-hidden flex flex-col" style={{ maxHeight: "calc(100vh - 320px)" }}>
<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-[480px]">
<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>
</tr>
</thead>
<tbody className="tabular-nums">
{rows.length === 0 ? (
<tr><td colSpan={4} className="text-center py-12 text-slate-400">{loading ? "조회 중..." : "선택한 월의 매출 데이터가 없습니다."}</td></tr>
) : rows.map((r) => (
<tr key={r.COMPANY_NAME} className="border-t border-slate-100 hover:bg-slate-50">
<td className="px-3 py-2 font-semibold">{r.COMPANY_NAME}</td>
<td className="px-3 py-2 text-right text-violet-700">{fmt(r.TAX_FREE)}</td>
<td className="px-3 py-2 text-right text-rose-700">{fmt(r.TAXABLE)}</td>
<td className="px-3 py-2 text-right font-bold">{fmt(r.TOTAL)}</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
<div className="bg-white border border-slate-200 rounded-xl overflow-x-auto">
<table className="w-full text-sm min-w-[600px]">
<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>
</tr>
</thead>
<tbody>
{rows.length === 0 ? (
<tr><td colSpan={4} className="text-center py-12 text-slate-400"> .</td></tr>
) : rows.map((r) => (
<tr key={r.COMPANY_NAME} className="border-t border-slate-100">
<td className="px-4 py-3 font-semibold">{r.COMPANY_NAME}</td>
<td className="px-4 py-3 text-right tabular-nums text-violet-700">{fmt(r.TAX_FREE)}</td>
<td className="px-4 py-3 text-right tabular-nums text-rose-700">{fmt(r.TAXABLE)}</td>
<td className="px-4 py-3 text-right tabular-nums font-bold">{fmt(r.TOTAL)}</td>
</tr>
))}
</tbody>
</table>
<div className="bg-white border border-slate-200 rounded-xl p-4 flex flex-col" style={{ maxHeight: "calc(100vh - 320px)" }}>
<h3 className="font-bold text-slate-700 mb-3 text-sm shrink-0"> (TOP 15)</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: 10, bottom: 30, left: 10 }}>
<CartesianGrid strokeDasharray="3 3" stroke="#e2e8f0" />
<XAxis dataKey="name" tick={{ fontSize: 11 }} 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 ?? ""}
cursor={{ fill: "rgba(16, 185, 129, 0.05)" }}
/>
<Legend wrapperStyle={{ fontSize: 11 }} />
<Bar dataKey="면세" stackId="a" fill="#8b5cf6" />
<Bar dataKey="과세" stackId="a" fill="#f43f5e">
{chartData.map((_, i) => <Cell key={i} fill={COLORS[i % COLORS.length]} fillOpacity={0.7} />)}
</Bar>
</BarChart>
</ResponsiveContainer>
)}
</div>
</div>
</div>
</div>
);