190 lines
5.2 KiB
TypeScript
190 lines
5.2 KiB
TypeScript
import { Box, Paper, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||
import { LineChart } from "@mui/x-charts";
|
||
import moment from "moment";
|
||
|
||
import type { GeneralResponse } from "@api/statistic";
|
||
import { FC } from "react";
|
||
|
||
type GeneralItemsProps = {
|
||
title: string;
|
||
general: Record<string, number>;
|
||
color: string;
|
||
numberType: "sum" | "percent" | "time";
|
||
calculateTime?: boolean;
|
||
};
|
||
|
||
type GeneralProps = {
|
||
data: GeneralResponse | null;
|
||
};
|
||
|
||
const COLORS: Record<number, string> = {
|
||
0: "#61BB1A",
|
||
1: "#7E2AEA",
|
||
2: "#FB5607",
|
||
3: "#0886FB",
|
||
};
|
||
|
||
const dateParser = (object: Record<string, number>): Record<string, number> => {
|
||
const result = {} as Record<string, number>;
|
||
for (var key in object) {
|
||
result[moment.utc(Number(key) * 1000).format("DD/MM/YYYY")] = object[key];
|
||
console.log(result);
|
||
}
|
||
return result;
|
||
};
|
||
|
||
const GeneralItem = ({
|
||
title,
|
||
general,
|
||
color,
|
||
numberType,
|
||
calculateTime = false,
|
||
}: GeneralItemsProps) => {
|
||
const theme = useTheme();
|
||
const isMobile = useMediaQuery(theme.breakpoints.down(700));
|
||
|
||
const numberValue =
|
||
numberType === "sum"
|
||
? Object.values(general).reduce((total, item) => total + item, 0)
|
||
: 0;
|
||
Object.entries(general).reduce(
|
||
(total, [key, value]) => total + (value / Number(key)) * 100,
|
||
0,
|
||
) / Object.keys(general).length || Number(0);
|
||
|
||
if (Object.keys(general).length === 0) {
|
||
return (
|
||
<Typography textAlign="center">{`${title} - нет данных`}</Typography>
|
||
);
|
||
}
|
||
|
||
const getCalculatedTime = (time: number) => {
|
||
const hours = String(Math.floor(time / 3600)).padStart(2, "0");
|
||
const minutes = String(Math.floor((time % 3600) / 60)).padStart(2, "0");
|
||
const seconds = String(Math.floor((time % 3600) % 60)).padStart(2, "0");
|
||
|
||
return `${hours}:${minutes}:${seconds}`;
|
||
};
|
||
|
||
return (
|
||
<Paper
|
||
sx={{
|
||
overflow: "hidden",
|
||
borderRadius: "12px",
|
||
boxShadow: "0 0 20px rgba(0, 0, 0, 0.15)",
|
||
}}
|
||
>
|
||
<Typography sx={{ margin: "20px 20px 0" }}>{title}</Typography>
|
||
<Typography sx={{ margin: "10px 20px 0", fontWeight: "bold" }}>
|
||
{numberType === "sum" ? numberValue : `${numberValue.toFixed()}%`}
|
||
</Typography>
|
||
<LineChart
|
||
xAxis={[
|
||
{
|
||
data: Object.keys(general),
|
||
valueFormatter: (value) =>
|
||
moment.utc(Number(value) * 1000).format("DD/MM/YYYY"),
|
||
},
|
||
]}
|
||
series={[
|
||
{
|
||
data: Object.values(general),
|
||
valueFormatter: (value) =>
|
||
calculateTime ? getCalculatedTime(value) : String(value),
|
||
},
|
||
]}
|
||
// dataset={Object.entries(general).map(([, v]) => moment.unix(v).format("ss/mm/HH")).reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {})}
|
||
height={220}
|
||
colors={[color]}
|
||
sx={{
|
||
transform: isMobile ? "scale(1.1)" : "scale(1.2)",
|
||
"& .MuiChartsAxis-tickContainer": { display: "none" },
|
||
}}
|
||
/>
|
||
</Paper>
|
||
);
|
||
};
|
||
|
||
export const General: FC<GeneralProps> = ({ data }) => {
|
||
const theme = useTheme();
|
||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||
const isMobile = useMediaQuery(theme.breakpoints.down(700));
|
||
|
||
if (!data) {
|
||
return (
|
||
<Typography textAlign="center" m="10px 0">
|
||
нет данных о ключевых метриках
|
||
</Typography>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<Box sx={{ marginTop: "45px" }}>
|
||
<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: "40px",
|
||
}}
|
||
>
|
||
<GeneralItem
|
||
title="Открыли квиз"
|
||
numberType="sum"
|
||
general={
|
||
Object.entries(data.Open)
|
||
.filter(([, v]) => v > 0)
|
||
.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {}) || { 0: 0 }
|
||
}
|
||
color={COLORS[0]}
|
||
/>
|
||
<GeneralItem
|
||
title="Получено заявок"
|
||
numberType="sum"
|
||
general={
|
||
Object.entries(data.Result)
|
||
.filter(([, v]) => v > 0)
|
||
.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {}) || { 0: 0 }
|
||
}
|
||
color={COLORS[1]}
|
||
/>
|
||
<GeneralItem
|
||
title="Конверсия"
|
||
numberType="percent"
|
||
general={
|
||
Object.entries(data.Conversion)
|
||
.filter(([, v]) => v > 0)
|
||
.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {}) || { 0: 0 }
|
||
}
|
||
color={COLORS[2]}
|
||
/>
|
||
<GeneralItem
|
||
title="Среднее время прохождения квиза"
|
||
numberType="time"
|
||
calculateTime
|
||
general={
|
||
Object.entries(data.AvTime)
|
||
.filter(([, v]) => v > 0)
|
||
.reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {}) || { 0: 0 }
|
||
}
|
||
color={COLORS[3]}
|
||
/>
|
||
</Box>
|
||
</Box>
|
||
);
|
||
};
|