feat(mui): kit primitives — PageHeader, EmptyState, LoadingState, FilterBar, StatCard + DataTable onRowClick/rowDanger
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
85
src/admin/ui/StatCard.tsx
Normal file
85
src/admin/ui/StatCard.tsx
Normal file
@@ -0,0 +1,85 @@
|
||||
import type { ReactNode } from "react";
|
||||
import Box from "@mui/material/Box";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Card from "./Card";
|
||||
|
||||
export type StatCardColor =
|
||||
| "default"
|
||||
| "primary"
|
||||
| "success"
|
||||
| "warning"
|
||||
| "error"
|
||||
| "info";
|
||||
|
||||
export interface StatCardProps {
|
||||
label: string;
|
||||
value: ReactNode;
|
||||
icon?: ReactNode;
|
||||
color?: StatCardColor;
|
||||
footer?: ReactNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* KPI / metric tile. Dense Soft-SaaS look: tinted icon square, big value,
|
||||
* small uppercase label, optional footer caption.
|
||||
*/
|
||||
export default function StatCard({
|
||||
label,
|
||||
value,
|
||||
icon,
|
||||
color = "default",
|
||||
footer,
|
||||
}: StatCardProps) {
|
||||
const iconBg = color === "default" ? "action.hover" : `${color}.light`;
|
||||
const iconColor = color === "default" ? "text.secondary" : `${color}.main`;
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<Box sx={{ display: "flex", alignItems: "flex-start", gap: 1.5 }}>
|
||||
{icon && (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: 2,
|
||||
bgcolor: iconBg,
|
||||
color: iconColor,
|
||||
flexShrink: 0,
|
||||
}}
|
||||
>
|
||||
{icon}
|
||||
</Box>
|
||||
)}
|
||||
<Box sx={{ minWidth: 0 }}>
|
||||
<Typography
|
||||
variant="h5"
|
||||
sx={{ fontFamily: "'DM Mono', Menlo, monospace", lineHeight: 1.2 }}
|
||||
>
|
||||
{value}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="caption"
|
||||
color="text.secondary"
|
||||
sx={{
|
||||
textTransform: "uppercase",
|
||||
letterSpacing: ".06em",
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
{footer && (
|
||||
<Box sx={{ mt: 1.5, pt: 1.5, borderTop: 1, borderColor: "divider" }}>
|
||||
<Typography variant="caption" color="text.secondary">
|
||||
{footer}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user