- /admin opted out of shopper chrome in MyLayout; /admin added to server protectedRoutes; useRequireAdmin() gates to superusers (UserProfile.isSuperuser). - Admin shell (admin.tsx) with sidebar nav (Overview live; other sections stubbed) and a mobile top bar; Overview dashboard (KPI cards + 14-day orders chart) via use-admin-hooks.ts hitting /api/adminpanel/v1/overview/. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
62 lines
1.9 KiB
TypeScript
62 lines
1.9 KiB
TypeScript
/**
|
||
* Dependency-free 14-day orders bar chart for the admin overview.
|
||
* (A richer charting lib can replace this in a later slice.)
|
||
*/
|
||
interface Point {
|
||
date: string;
|
||
orders: number;
|
||
revenue: number;
|
||
}
|
||
|
||
function faNum(n: number): string {
|
||
return Math.round(n).toLocaleString("fa-IR");
|
||
}
|
||
|
||
function dayLabel(iso: string): string {
|
||
// day-of-month in Persian digits
|
||
try {
|
||
const d = new Date(iso);
|
||
return d.toLocaleDateString("fa-IR", { day: "numeric" });
|
||
} catch {
|
||
return "";
|
||
}
|
||
}
|
||
|
||
export function OrdersBarChart({ data }: { data: Point[] }) {
|
||
const max = Math.max(1, ...data.map((d) => d.orders));
|
||
|
||
return (
|
||
<div className="rounded-2xl border border-WHITE3 bg-WHITE p-4">
|
||
<div className="flex items-center justify-between mb-4">
|
||
<h3 className="font-bold text-[15px]">سفارشهای ۱۴ روز اخیر</h3>
|
||
<span className="text-[12px] text-GRAY">
|
||
مجموع: {faNum(data.reduce((s, d) => s + d.orders, 0))} سفارش
|
||
</span>
|
||
</div>
|
||
<div className="flex items-end gap-1.5 h-[160px]" dir="ltr">
|
||
{data.map((d) => (
|
||
<div
|
||
key={d.date}
|
||
className="flex-1 h-full flex flex-col items-center justify-end gap-1 group"
|
||
title={`${dayLabel(d.date)} — ${faNum(d.orders)} سفارش، ${faNum(
|
||
d.revenue
|
||
)} تومان`}
|
||
>
|
||
<div className="w-full flex-1 flex items-end">
|
||
<div
|
||
className="w-full rounded-t-md bg-VITROWN_BLUE/80 group-hover:bg-VITROWN_BLUE transition-colors min-h-[2px]"
|
||
style={{ height: `${(d.orders / max) * 100}%` }}
|
||
/>
|
||
</div>
|
||
<span className="text-[10px] text-GRAY tabular-nums">
|
||
{dayLabel(d.date)}
|
||
</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default OrdersBarChart;
|