151 lines
5.1 KiB
TypeScript
151 lines
5.1 KiB
TypeScript
'use client'
|
|
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
|
import {
|
|
BarChart,
|
|
Bar,
|
|
XAxis,
|
|
YAxis,
|
|
Tooltip,
|
|
ResponsiveContainer,
|
|
CartesianGrid,
|
|
Cell,
|
|
} from 'recharts'
|
|
|
|
export default function ReportCharts({
|
|
type,
|
|
data,
|
|
}: {
|
|
type: 'SUBMITTOR' | 'CREATOR'
|
|
data: any[]
|
|
}) {
|
|
const formatRupiah = (value: number) => {
|
|
return new Intl.NumberFormat('id-ID', {
|
|
style: 'currency',
|
|
currency: 'IDR',
|
|
maximumFractionDigits: 0,
|
|
}).format(value)
|
|
}
|
|
|
|
// Prepare chart data based on type
|
|
let chartData: any[] = []
|
|
|
|
if (type === 'SUBMITTOR') {
|
|
chartData = [...data]
|
|
.filter((sub) => sub.order.status === 'CLOSE')
|
|
.sort((a, b) => new Date(a.order.date).getTime() - new Date(b.order.date).getTime()) // oldest → newest
|
|
.map((sub) => {
|
|
const bill = sub.bill || 0
|
|
const paid =
|
|
(sub.paid_amount ?? (sub.payment_status === 'LUNAS' ? bill : 0)) + (sub.saldo_used || 0)
|
|
const unpaid = Math.max(0, bill - paid)
|
|
return {
|
|
name:
|
|
sub.order.title.length > 15
|
|
? sub.order.title.substring(0, 15) + '...'
|
|
: sub.order.title,
|
|
fullTitle: sub.order.title,
|
|
Total: bill,
|
|
Paid: paid,
|
|
Unpaid: unpaid,
|
|
status: unpaid > 0 ? (paid > 0 ? 'SEBAGIAN' : 'BELUM BAYAR') : 'LUNAS',
|
|
}
|
|
})
|
|
} else {
|
|
chartData = [...data]
|
|
.filter((order) => order.status === 'CLOSE')
|
|
.sort((a, b) => new Date(a.date).getTime() - new Date(b.date).getTime()) // oldest → newest
|
|
.map((order) => {
|
|
const total = order.submissions.reduce((acc: number, sub: any) => acc + (sub.bill || 0), 0)
|
|
return {
|
|
name: order.title.length > 15 ? order.title.substring(0, 15) + '...' : order.title,
|
|
fullTitle: order.title,
|
|
Total: total,
|
|
status: order.status,
|
|
}
|
|
})
|
|
}
|
|
|
|
// If no data to show
|
|
if (chartData.length === 0) {
|
|
return (
|
|
<Card className="rounded-2xl border border-slate-200/90 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900">
|
|
<CardContent className="p-12 text-center">
|
|
<p className="text-xs font-medium text-slate-500">
|
|
Belum ada data transaksi yang cukup untuk ditampilkan di grafik.
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
)
|
|
}
|
|
|
|
const CustomTooltip = ({ active, payload, label }: any) => {
|
|
if (active && payload && payload.length) {
|
|
const data = payload[0].payload
|
|
return (
|
|
<div className="rounded-xl border border-slate-200 bg-white p-3 shadow-lg dark:border-slate-800 dark:bg-slate-900">
|
|
<p className="mb-1 text-xs font-bold text-slate-800 dark:text-slate-200">
|
|
{data.fullTitle}
|
|
</p>
|
|
<p className="text-sm font-black text-[#1B2CC1]">Total: {formatRupiah(data.Total)}</p>
|
|
{type === 'SUBMITTOR' && (
|
|
<>
|
|
<p className="mt-1 text-[11px] font-bold text-emerald-600">
|
|
Dibayar: {formatRupiah(data.Paid)}
|
|
</p>
|
|
{data.Unpaid > 0 && (
|
|
<p className="text-[11px] font-bold text-rose-600">
|
|
Kurang: {formatRupiah(data.Unpaid)}
|
|
</p>
|
|
)}
|
|
</>
|
|
)}
|
|
<p className="mt-1 text-[10px] font-bold tracking-wider text-slate-500 uppercase">
|
|
Status: {data.status}
|
|
</p>
|
|
</div>
|
|
)
|
|
}
|
|
return null
|
|
}
|
|
|
|
return (
|
|
<Card className="overflow-hidden rounded-2xl border border-slate-200/90 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900">
|
|
<CardHeader className="border-b border-slate-100 bg-slate-50/50 p-4 dark:border-slate-800 dark:bg-slate-800/40">
|
|
<CardTitle className="text-sm font-black text-slate-800 dark:text-slate-200">
|
|
{type === 'SUBMITTOR' ? 'Grafik Pengeluaran per PO' : 'Grafik Omzet per PO'}
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="h-[300px] w-full p-4 sm:p-6">
|
|
<ResponsiveContainer width="100%" height="100%">
|
|
<BarChart data={chartData} margin={{ top: 10, right: 10, left: -20, bottom: 0 }}>
|
|
<CartesianGrid strokeDasharray="3 3" vertical={false} stroke="#e2e8f0" />
|
|
<XAxis
|
|
dataKey="name"
|
|
axisLine={false}
|
|
tickLine={false}
|
|
tick={{ fontSize: 10, fill: '#64748b' }}
|
|
dy={10}
|
|
/>
|
|
<YAxis
|
|
axisLine={false}
|
|
tickLine={false}
|
|
tick={{ fontSize: 10, fill: '#64748b' }}
|
|
tickFormatter={(value) => `Rp${value / 1000}k`}
|
|
/>
|
|
<Tooltip cursor={{ fill: 'rgba(27, 44, 193, 0.05)' }} content={<CustomTooltip />} />
|
|
{type === 'SUBMITTOR' ? (
|
|
<>
|
|
<Bar dataKey="Paid" stackId="a" fill="#10b981" />
|
|
<Bar dataKey="Unpaid" stackId="a" fill="#f43f5e" radius={[4, 4, 0, 0]} />
|
|
</>
|
|
) : (
|
|
<Bar dataKey="Total" radius={[4, 4, 0, 0]} fill="#1B2CC1" />
|
|
)}
|
|
</BarChart>
|
|
</ResponsiveContainer>
|
|
</CardContent>
|
|
</Card>
|
|
)
|
|
}
|