- /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>
46 lines
1.0 KiB
TypeScript
46 lines
1.0 KiB
TypeScript
import type { LucideIcon } from "lucide-react";
|
|
import { cn } from "~/lib/utils";
|
|
|
|
interface KpiCardProps {
|
|
label: string;
|
|
value: string | number;
|
|
sub?: string;
|
|
icon?: LucideIcon;
|
|
accent?: "default" | "blue" | "green" | "red";
|
|
}
|
|
|
|
const ACCENT: Record<string, string> = {
|
|
default: "text-BLACK",
|
|
blue: "text-VITROWN_BLUE",
|
|
green: "text-GREEN",
|
|
red: "text-RED",
|
|
};
|
|
|
|
export function KpiCard({
|
|
label,
|
|
value,
|
|
sub,
|
|
icon: Icon,
|
|
accent = "default",
|
|
}: KpiCardProps) {
|
|
return (
|
|
<div className="rounded-2xl border border-WHITE3 bg-WHITE p-4">
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-GRAY text-[13px]">{label}</span>
|
|
{Icon && <Icon size={18} className="text-GRAY shrink-0" />}
|
|
</div>
|
|
<p
|
|
className={cn(
|
|
"mt-2 text-[24px] font-extrabold tabular-nums leading-tight",
|
|
ACCENT[accent]
|
|
)}
|
|
>
|
|
{value}
|
|
</p>
|
|
{sub && <p className="text-[12px] text-GRAY mt-1">{sub}</p>}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default KpiCard;
|