feat: implement reports page with analytical charts and data grids, and add payment status tracking to submissions.
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
'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 => ({
|
||||
name: sub.order.title.length > 15 ? sub.order.title.substring(0, 15) + '...' : sub.order.title,
|
||||
fullTitle: sub.order.title,
|
||||
Total: sub.bill || 0,
|
||||
status: sub.payment_status
|
||||
}))
|
||||
} 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 dark:border-slate-800 bg-white dark:bg-slate-900 shadow-sm">
|
||||
<CardContent className="p-12 text-center">
|
||||
<p className="text-slate-500 text-xs font-medium">Belum ada data transaksi yang cukup untuk ditampilkan di grafik.</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
const CustomTooltip = ({ active, payload, label }: any) => {
|
||||
if (active && payload && payload.length) {
|
||||
return (
|
||||
<div className="bg-white dark:bg-slate-900 p-3 border border-slate-200 dark:border-slate-800 rounded-xl shadow-lg">
|
||||
<p className="font-bold text-xs text-slate-800 dark:text-slate-200 mb-1">{payload[0].payload.fullTitle}</p>
|
||||
<p className="text-sm font-black text-[#1B2CC1]">
|
||||
{formatRupiah(payload[0].value)}
|
||||
</p>
|
||||
<p className="text-[10px] text-slate-500 mt-1 uppercase font-bold tracking-wider">
|
||||
Status: {payload[0].payload.status}
|
||||
</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Card className="rounded-2xl border border-slate-200/90 dark:border-slate-800 bg-white dark:bg-slate-900 shadow-sm overflow-hidden">
|
||||
<CardHeader className="bg-slate-50/50 dark:bg-slate-800/40 border-b border-slate-100 dark:border-slate-800 p-4">
|
||||
<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="p-4 sm:p-6 h-[300px] w-full">
|
||||
<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 />} />
|
||||
<Bar dataKey="Total" radius={[4, 4, 0, 0]}>
|
||||
{chartData.map((entry, index) => (
|
||||
<Cell
|
||||
key={`cell-${index}`}
|
||||
fill={type === 'SUBMITTOR'
|
||||
? (entry.status === 'LUNAS' ? '#10b981' : '#f43f5e') // Emerald or Rose
|
||||
: '#1B2CC1' // Primary for creator
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</Bar>
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user