import type { ReactNode } from "react";
import { AlertCircle, RefreshCw } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Skeleton } from "@/components/ui/skeleton";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
/** One compact, responsive definition list, rather than a wall of KPI cards. */
export function MetricSummary({
items,
}: {
items: { label: string; value: ReactNode; detail?: ReactNode }[];
}) {
return (
{items.map((item) => (
- {item.label}
-
{item.value}
{item.detail && (
- {item.detail}
)}
))}
);
}
export function ReportSection({
title,
description,
actions,
children,
}: {
title: string;
description?: ReactNode;
actions?: ReactNode;
children: ReactNode;
}) {
return (
{title}
{description && (
{description}
)}
{actions}
{children}
);
}
export function ReportPeriodSelect({
value,
onChange,
options,
label = "Reporting period",
}: {
value: string;
onChange: (value: string) => void;
options: { value: string; label: string }[];
label?: string;
}) {
return (
);
}
export function ReportToolbar({
title,
description,
children,
loading,
updatedAt,
onRefresh,
}: {
title: string;
description?: ReactNode;
children?: ReactNode;
loading?: boolean;
updatedAt?: Date | null;
onRefresh: () => void;
}) {
return (
{title}
{description && (
{description}
)}
{loading
? "Updating data…"
: updatedAt
? `Updated ${updatedAt.toLocaleTimeString(undefined, { hour: "numeric", minute: "2-digit" })}`
: ""}
{children}
);
}
export function ReportError({
message,
stale = false,
onRetry,
}: {
message: string;
stale?: boolean;
onRetry: () => void;
}) {
return (
{message}
{stale && (
Showing the last successful result. It may be out of date.
)}
);
}
export function DataEmptyState({
title,
children,
action,
}: {
title: string;
children?: ReactNode;
action?: ReactNode;
}) {
return (
{title}
{children && (
{children}
)}
{action &&
{action}
}
);
}
export function ReportSkeleton({
label = "Loading report",
}: {
label?: string;
}) {
return (
{label}
);
}
export function DataPagination({
page,
pages,
total,
perPage,
busy,
onPageChange,
}: {
page: number;
pages: number;
total: number;
perPage: number;
busy?: boolean;
onPageChange: (page: number) => void;
}) {
if (total === 0) return null;
return (
);
}