- Add matching lucide-react icons to tab/mode switcher buttons on payments, at-the-door, manual, email-attendees, whatsapp-attendees, and admin cashup pages, mirroring icons already used in their help menus - Fix navbar Logout button sitting lower than other nav links (missing border/padding classes that other links use for their active-underline) - Fix stat card labels getting truncated on mobile by removing the ellipsis-cut label and widening the mobile grid to one column - Fix Revenue trend / Top performing events rendering outside the viewport on mobile by containing horizontal overflow and truncating long event titles in the table
72 lines
2.5 KiB
TypeScript
72 lines
2.5 KiB
TypeScript
"use client";
|
|
|
|
import React from "react";
|
|
import Link from "next/link";
|
|
import { ArrowRight, ArrowUp, ArrowDown, type LucideIcon } from "lucide-react";
|
|
import { TONES, type Tone } from "./tones";
|
|
|
|
/** A "vs last month"-style delta line. `value` is a percentage; null means no baseline to compare against (shown as "New"). */
|
|
export type StatDelta = { value: number | null; label?: string };
|
|
|
|
/**
|
|
* Richer stat tile for dashboard KPI rows (icon chip + label + big value +
|
|
* optional delta + optional "View X" link) — scaffolding for Phase 2's
|
|
* dashboard rebuilds. Not the same component as reports/StatTile.tsx, which
|
|
* stays as-is for Reports' own denser tiles.
|
|
*/
|
|
export function StatCard({
|
|
icon: Icon,
|
|
label,
|
|
value,
|
|
tone = "brand",
|
|
href,
|
|
linkLabel,
|
|
delta,
|
|
}: {
|
|
icon: LucideIcon;
|
|
label: string;
|
|
value: string | number;
|
|
tone?: Tone;
|
|
href?: string;
|
|
linkLabel?: string;
|
|
delta?: StatDelta;
|
|
}) {
|
|
const t = TONES[tone] || TONES.brand;
|
|
return (
|
|
<div className="rounded-xl border border-gray-200 bg-white p-4 shadow-sm">
|
|
<div className="flex items-center gap-3">
|
|
<div className={"w-10 h-10 rounded-lg flex items-center justify-center shrink-0 " + t.bg}>
|
|
<Icon className={"w-5 h-5 " + t.icon} />
|
|
</div>
|
|
<div className="min-w-0">
|
|
<div className="text-xs text-gray-500 leading-snug">{label}</div>
|
|
<div className="text-xl font-semibold text-gray-900 truncate">{value}</div>
|
|
</div>
|
|
</div>
|
|
{delta && (
|
|
<div className={"mt-2 flex items-center gap-1 text-xs font-medium " + (delta.value == null ? "text-gray-400" : delta.value >= 0 ? "text-emerald-600" : "text-rose-600")}>
|
|
{delta.value == null ? (
|
|
<span>New this month</span>
|
|
) : (
|
|
<>
|
|
{delta.value >= 0 ? <ArrowUp className="w-3 h-3" /> : <ArrowDown className="w-3 h-3" />}
|
|
<span>{Math.abs(delta.value).toFixed(0)}%</span>
|
|
<span className="text-gray-400 font-normal">{delta.label || "vs last month"}</span>
|
|
</>
|
|
)}
|
|
</div>
|
|
)}
|
|
{href && (
|
|
<Link href={href} className="mt-3 inline-flex items-center gap-1 text-xs font-medium text-brand-600 hover:underline">
|
|
{linkLabel || "View"}
|
|
<ArrowRight className="w-3 h-3" />
|
|
</Link>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export function StatCardRow({ children }: { children: React.ReactNode }) {
|
|
return <div className="grid grid-cols-1 sm:grid-cols-3 lg:grid-cols-5 gap-4">{children}</div>;
|
|
}
|