157 lines
3.9 KiB
TypeScript
157 lines
3.9 KiB
TypeScript
import { useEffect, useState } from "react";
|
||
import { Box, Paper, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||
import { PieChart } from "@mui/x-charts";
|
||
import { enqueueSnackbar } from "notistack";
|
||
|
||
import { getDevices } from "@api/statistic";
|
||
|
||
import type { DevicesResponse } from "@api/statistic";
|
||
|
||
type DeviceProps = {
|
||
title: string;
|
||
devices: Record<string, number>;
|
||
};
|
||
|
||
const COLORS: Record<number, string> = {
|
||
0: "#7E2AEA",
|
||
1: "#FA7738",
|
||
2: "#62BB1C",
|
||
3: "#0886FB",
|
||
};
|
||
|
||
const DEVICES_MOCK: DevicesResponse = {
|
||
device: { PC: 75, Mobile: 25 },
|
||
os: { Windows: 44, AndroidOS: 25, "OS X": 19, Linux: 13 },
|
||
browser: { Chrome: 75, Firefox: 25 },
|
||
};
|
||
|
||
const Device = ({ title, devices }: DeviceProps) => {
|
||
const theme = useTheme();
|
||
const data = Object.entries(devices).map(([id, value], index) => ({
|
||
id,
|
||
value,
|
||
color: COLORS[index],
|
||
}));
|
||
|
||
return (
|
||
<Paper
|
||
sx={{
|
||
overflow: "hidden",
|
||
minHeight: "500px",
|
||
display: "flex",
|
||
flexDirection: "column",
|
||
gap: "30px",
|
||
borderRadius: "12px",
|
||
boxShadow: "0 0 20px rgba(0, 0, 0, 0.15)",
|
||
}}
|
||
>
|
||
<Typography sx={{ margin: "20px" }}>{title}</Typography>
|
||
<Box sx={{ flexGrow: 1 }}>
|
||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||
<PieChart
|
||
height={245}
|
||
width={245}
|
||
margin={{ right: 0 }}
|
||
series={[{ data, innerRadius: 50 }]}
|
||
/>
|
||
</Box>
|
||
</Box>
|
||
<Box
|
||
sx={{ background: theme.palette.background.default, padding: "20px" }}
|
||
>
|
||
{data.map(({ id, value, color }) => (
|
||
<Box
|
||
key={id}
|
||
sx={{
|
||
display: "flex",
|
||
marginBottom: "10px",
|
||
"&:last-child": { margin: 0 },
|
||
}}
|
||
>
|
||
<Typography
|
||
sx={{
|
||
flexGrow: 1,
|
||
position: "relative",
|
||
paddingLeft: "30px",
|
||
"&::before": {
|
||
content: "''",
|
||
display: "block",
|
||
position: "absolute",
|
||
left: "0",
|
||
background: color,
|
||
height: "20px",
|
||
width: "20px",
|
||
borderRadius: "6px",
|
||
},
|
||
}}
|
||
>
|
||
{id}
|
||
</Typography>
|
||
<Typography>{value} %</Typography>
|
||
</Box>
|
||
))}
|
||
</Box>
|
||
</Paper>
|
||
);
|
||
};
|
||
|
||
export const Devices = () => {
|
||
const [devices, setDevices] = useState<DevicesResponse>(DEVICES_MOCK);
|
||
const theme = useTheme();
|
||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||
const isMobile = useMediaQuery(theme.breakpoints.down(700));
|
||
|
||
useEffect(() => {
|
||
const requestDevices = async () => {
|
||
const [devicesResponse, devicesError] = await getDevices("14761");
|
||
|
||
if (devicesError) {
|
||
enqueueSnackbar(devicesError);
|
||
|
||
return;
|
||
}
|
||
|
||
if (!devicesResponse) {
|
||
enqueueSnackbar("Список девайсов пуст.");
|
||
|
||
return;
|
||
}
|
||
|
||
setDevices(devicesResponse);
|
||
};
|
||
|
||
// requestDevices();
|
||
}, []);
|
||
|
||
return (
|
||
<Box sx={{ marginTop: "120px" }}>
|
||
<Typography
|
||
component="h3"
|
||
sx={{
|
||
fontSize: "24px",
|
||
fontWeight: "bold",
|
||
color: theme.palette.text.primary,
|
||
}}
|
||
>
|
||
Статистика пользователей
|
||
</Typography>
|
||
<Box
|
||
sx={{
|
||
display: "grid",
|
||
gridTemplateColumns: isTablet
|
||
? isMobile
|
||
? "1fr"
|
||
: "1fr 1fr"
|
||
: "1fr 1fr 1fr",
|
||
gap: "20px",
|
||
marginTop: "30px",
|
||
}}
|
||
>
|
||
<Device title="Устройства" devices={devices.device} />
|
||
<Device title="Операционные системы" devices={devices.os} />
|
||
<Device title="Браузеры" devices={devices.browser} />
|
||
</Box>
|
||
</Box>
|
||
);
|
||
};
|