Add payment module, BFF proxy, hub/dashboard/ops/PSP/merchant screens wired to real APIs, and mark payment-frontend complete in project status. Co-authored-by: Cursor <cursoragent@cursor.com>
85 lines
2.5 KiB
TypeScript
85 lines
2.5 KiB
TypeScript
"use client";
|
||
|
||
import {
|
||
Bar,
|
||
BarChart,
|
||
CartesianGrid,
|
||
Cell,
|
||
Pie,
|
||
PieChart,
|
||
ResponsiveContainer,
|
||
Tooltip,
|
||
XAxis,
|
||
YAxis,
|
||
} from "recharts";
|
||
|
||
const COLORS = ["#0d9488", "#0284c7", "#d97706", "#e11d48", "#6366f1", "#64748b"];
|
||
|
||
export function PaymentBarChart({
|
||
data,
|
||
dataKey = "value",
|
||
nameKey = "name",
|
||
}: {
|
||
data: { name: string; value: number }[];
|
||
dataKey?: string;
|
||
nameKey?: string;
|
||
}) {
|
||
if (!data.length) {
|
||
return <p className="text-sm text-[var(--muted)]">دادهای برای نمودار نیست.</p>;
|
||
}
|
||
return (
|
||
<div className="h-56 w-full" role="img" aria-label="نمودار میلهای">
|
||
<ResponsiveContainer width="100%" height="100%">
|
||
<BarChart data={data}>
|
||
<CartesianGrid strokeDasharray="3 3" opacity={0.3} />
|
||
<XAxis dataKey={nameKey} tick={{ fontSize: 11 }} />
|
||
<YAxis tick={{ fontSize: 11 }} />
|
||
<Tooltip />
|
||
<Bar dataKey={dataKey} fill="#0d9488" radius={[6, 6, 0, 0]} />
|
||
</BarChart>
|
||
</ResponsiveContainer>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export function PaymentDonutChart({ data }: { data: { name: string; value: number }[] }) {
|
||
if (!data.length) {
|
||
return <p className="text-sm text-[var(--muted)]">دادهای برای نمودار نیست.</p>;
|
||
}
|
||
return (
|
||
<div className="h-56 w-full" role="img" aria-label="نمودار دایرهای">
|
||
<ResponsiveContainer width="100%" height="100%">
|
||
<PieChart>
|
||
<Pie data={data} dataKey="value" nameKey="name" innerRadius={50} outerRadius={80}>
|
||
{data.map((_, i) => (
|
||
<Cell key={i} fill={COLORS[i % COLORS.length]} />
|
||
))}
|
||
</Pie>
|
||
<Tooltip />
|
||
</PieChart>
|
||
</ResponsiveContainer>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export function PaymentStatCards({
|
||
items,
|
||
}: {
|
||
items: { label: string; value: string | number; hint?: string }[];
|
||
}) {
|
||
return (
|
||
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-4">
|
||
{items.map((item) => (
|
||
<div
|
||
key={item.label}
|
||
className="rounded-xl border border-[var(--border)] bg-[var(--surface)] p-4"
|
||
>
|
||
<p className="text-xs text-[var(--muted)]">{item.label}</p>
|
||
<p className="mt-1 text-2xl font-semibold text-secondary">{item.value}</p>
|
||
{item.hint ? <p className="mt-1 text-[11px] text-[var(--muted)]">{item.hint}</p> : null}
|
||
</div>
|
||
))}
|
||
</div>
|
||
);
|
||
}
|