All checks were successful
		
		
	
	continuous-integration/drone/push Build is passing
				
			
		
			
				
	
	
		
			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
 | 
						|
                color={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>
 | 
						|
  );
 | 
						|
}
 |