136 lines
3.7 KiB
TypeScript
136 lines
3.7 KiB
TypeScript
import { useTheme } from "@mui/material/styles";
|
|
import Box from "@mui/material/Box";
|
|
import Card from "@mui/material/Card";
|
|
import CardContent from "@mui/material/CardContent";
|
|
import Chip from "@mui/material/Chip";
|
|
import Stack from "@mui/material/Stack";
|
|
import Typography from "@mui/material/Typography";
|
|
import { SparkLineChart } from "@mui/x-charts/SparkLineChart";
|
|
import { areaElementClasses } from "@mui/x-charts/LineChart";
|
|
|
|
export type StatCardProps = {
|
|
title: string;
|
|
value: string;
|
|
interval?: string;
|
|
trend?: "up" | "down" | "neutral";
|
|
data?: number[];
|
|
};
|
|
|
|
function last24Hours(): string[] {
|
|
let res: Array<string> = [];
|
|
|
|
for (let index = 0; index < 3600 * 24; index += 60 * 10) {
|
|
const date = new Date();
|
|
date.setTime(date.getTime() - index * 1000);
|
|
res.push(date.getHours() + "h" + date.getMinutes());
|
|
}
|
|
|
|
res.reverse();
|
|
|
|
console.log(res);
|
|
|
|
return res;
|
|
}
|
|
|
|
function AreaGradient({ color, id }: { color: string; id: string }) {
|
|
return (
|
|
<defs>
|
|
<linearGradient id={id} x1="50%" y1="0%" x2="50%" y2="100%">
|
|
<stop offset="0%" stopColor={color} stopOpacity={0.3} />
|
|
<stop offset="100%" stopColor={color} stopOpacity={0} />
|
|
</linearGradient>
|
|
</defs>
|
|
);
|
|
}
|
|
|
|
export default function StatCard({
|
|
title,
|
|
value,
|
|
interval,
|
|
trend,
|
|
data,
|
|
}: StatCardProps) {
|
|
const theme = useTheme();
|
|
|
|
const trendColors = {
|
|
up:
|
|
theme.palette.mode === "light"
|
|
? theme.palette.success.main
|
|
: theme.palette.success.dark,
|
|
down:
|
|
theme.palette.mode === "light"
|
|
? theme.palette.error.main
|
|
: theme.palette.error.dark,
|
|
neutral:
|
|
theme.palette.mode === "light"
|
|
? theme.palette.grey[400]
|
|
: theme.palette.grey[700],
|
|
};
|
|
|
|
const labelColors = {
|
|
up: "success" as const,
|
|
down: "error" as const,
|
|
neutral: "default" as const,
|
|
};
|
|
|
|
const color = labelColors[trend ?? "neutral"];
|
|
const chartColor = trendColors[trend ?? "neutral"];
|
|
const trendValues = { up: "+25%", down: "-25%", neutral: "+5%" };
|
|
|
|
return (
|
|
<Card variant="outlined" sx={{ height: "100%", flexGrow: 1 }}>
|
|
<CardContent>
|
|
<Typography component="h2" variant="subtitle2" gutterBottom>
|
|
{title}
|
|
</Typography>
|
|
<Stack
|
|
direction="column"
|
|
sx={{ justifyContent: "space-between", flexGrow: "1", gap: 1 }}
|
|
>
|
|
<Stack sx={{ justifyContent: "space-between" }}>
|
|
<Stack
|
|
direction="row"
|
|
sx={{ justifyContent: "space-between", alignItems: "center" }}
|
|
>
|
|
<Typography variant="h4" component="p">
|
|
{value}
|
|
</Typography>
|
|
{trend && (
|
|
<Chip size="small" color={color} label={trendValues[trend]} />
|
|
)}
|
|
</Stack>
|
|
<Typography variant="caption" sx={{ color: "text.secondary" }}>
|
|
{interval}
|
|
</Typography>
|
|
</Stack>
|
|
<Box sx={{ width: "100%", height: 100 }}>
|
|
{data && interval && (
|
|
<SparkLineChart
|
|
colors={[chartColor]}
|
|
data={data}
|
|
area
|
|
showHighlight
|
|
showTooltip
|
|
xAxis={{
|
|
scaleType: "band",
|
|
data: last24Hours(),
|
|
}}
|
|
sx={{
|
|
[`& .${areaElementClasses.root}`]: {
|
|
fill: `url(#area-gradient-${value})`,
|
|
},
|
|
}}
|
|
>
|
|
<AreaGradient
|
|
color={chartColor}
|
|
id={`area-gradient-${value}`}
|
|
/>
|
|
</SparkLineChart>
|
|
)}
|
|
</Box>
|
|
</Stack>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|