Files
property-management-network/components/dashboard/revenue-chart.tsx
T

98 lines
3.7 KiB
TypeScript
Raw Normal View History

"use client"
import { formatCurrency } from "@/lib/utils"
interface MonthData {
month: string
label: string
amount: number
}
interface RevenueChartProps {
data: MonthData[]
thisMonth: number
pending: number
}
export function RevenueChart({ data, thisMonth, pending }: RevenueChartProps) {
const max = Math.max(...data.map((d) => d.amount), 1)
const total = data.reduce((sum, d) => sum + d.amount, 0)
const currentMonthKey = new Date().toISOString().slice(0, 7)
const lastEntry = data[data.length - 1]
return (
<div className="rounded-2xl border border-white/[0.06] bg-[#16161f] p-5 h-full">
{/* Header */}
<div className="flex items-start justify-between mb-6">
<div>
<p className="text-xs font-medium uppercase tracking-wider text-white/40 mb-1">Revenue Overview</p>
<p className="text-2xl font-bold text-white tabular-nums">{formatCurrency(thisMonth)}</p>
<p className="text-xs text-white/40 mt-0.5">
Collected this month
{pending > 0 && <span className="text-amber-400 ml-1.5">· {formatCurrency(pending)} pending</span>}
</p>
</div>
<div className="flex items-center gap-4 text-xs text-white/40">
<div className="flex items-center gap-1.5">
<div className="h-2 w-2 rounded-full bg-indigo-500" />
Collected
</div>
</div>
</div>
{/* Bar chart */}
<div className="flex items-end justify-between gap-2 h-28">
{data.map((d) => {
const isCurrentMonth = d.month === currentMonthKey
const heightPct = max > 0 ? (d.amount / max) * 100 : 0
const minHeight = d.amount > 0 ? 8 : 4
return (
<div key={d.month} className="group flex flex-col items-center gap-1.5 flex-1">
{/* Tooltip */}
<div className="opacity-0 group-hover:opacity-100 transition-opacity text-[10px] text-white/70 font-medium whitespace-nowrap -mt-6 absolute">
{formatCurrency(d.amount)}
</div>
{/* Bar */}
<div className="relative w-full flex items-end" style={{ height: "100px" }}>
<div
className={`w-full rounded-t-lg transition-all duration-500 ${
isCurrentMonth
? "bg-gradient-to-t from-indigo-600 to-indigo-400"
: d.amount > 0
? "bg-white/[0.12] group-hover:bg-white/[0.2]"
: "bg-white/[0.04]"
}`}
style={{ height: `${Math.max(heightPct, minHeight)}%` }}
/>
</div>
{/* Label */}
<span className={`text-[10px] font-medium ${isCurrentMonth ? "text-indigo-400" : "text-white/30"}`}>
{d.label}
</span>
</div>
)
})}
</div>
{/* Summary row */}
<div className="mt-5 pt-4 border-t border-white/[0.06] grid grid-cols-3 gap-4">
<div>
<p className="text-[10px] text-white/30 uppercase tracking-wider mb-1">6-mo total</p>
<p className="text-sm font-bold text-white tabular-nums">{formatCurrency(total)}</p>
</div>
<div>
<p className="text-[10px] text-white/30 uppercase tracking-wider mb-1">Monthly avg</p>
<p className="text-sm font-bold text-white tabular-nums">{formatCurrency(Math.round(total / 6))}</p>
</div>
<div>
<p className="text-[10px] text-white/30 uppercase tracking-wider mb-1">Best month</p>
<p className="text-sm font-bold text-white tabular-nums">{formatCurrency(max === 1 ? 0 : max)}</p>
</div>
</div>
</div>
)
}