Files
SolarEnergy/central_frontend/src/widgets/StatCard.tsx
2024-09-26 23:21:56 +02:00

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>
);
}