frontPanel/src/pages/Analytics/Analytics.tsx

261 lines
7.5 KiB
TypeScript
Raw Normal View History

2024-04-15 14:32:00 +00:00
import moment from "moment";
import { useEffect, useLayoutEffect, useState } from "react";
2024-03-20 12:18:09 +00:00
import {
Box,
Button,
IconButton,
Typography,
useMediaQuery,
useTheme,
} from "@mui/material";
2024-04-03 14:22:51 +00:00
import { redirect } from "react-router-dom";
import { LocalizationProvider, DatePicker } from "@mui/x-date-pickers";
import { AdapterMoment } from "@mui/x-date-pickers/AdapterMoment";
2024-03-20 14:44:51 +00:00
import HeaderFull from "@ui_kit/Header/HeaderFull";
import SectionWrapper from "@ui_kit/SectionWrapper";
2024-03-21 14:24:56 +00:00
import { General } from "./General";
2024-04-01 18:09:26 +00:00
import { AnswersStatistics } from "./Answers/AnswersStatistics";
2024-03-20 14:44:51 +00:00
import { Devices } from "./Devices";
2024-04-15 14:32:00 +00:00
import { setQuizes } from "@root/quizes/actions";
import { useQuizStore } from "@root/quizes/store";
import { useAnalytics } from "@utils/hooks/useAnalytics";
import { quizApi } from "@api/quiz";
2024-03-19 06:56:21 +00:00
import CalendarIcon from "@icons/CalendarIcon";
2024-04-03 14:22:51 +00:00
import { ReactComponent as ResetIcon } from "@icons/Analytics/reset.svg";
2024-03-20 14:44:51 +00:00
2024-04-03 14:22:51 +00:00
import type { Moment } from "moment";
2024-04-15 14:32:00 +00:00
import type { ReactNode } from "react";
import type { Quiz } from "@model/quiz/quiz";
2024-04-03 10:50:17 +00:00
2024-03-20 12:18:09 +00:00
export default function Analytics() {
const { quizes, editQuizId } = useQuizStore();
2024-03-28 09:18:24 +00:00
const [quiz, setQuiz] = useState<Quiz>({} as Quiz);
2024-04-01 18:09:26 +00:00
const [isOpen, setOpen] = useState<boolean>(false);
const [isOpenEnd, setOpenEnd] = useState<boolean>(false);
2024-04-03 14:22:51 +00:00
const [from, setFrom] = useState<Moment | null>(null);
const [to, setTo] = useState<Moment | null>(moment().add(1, "days"));
2024-04-03 14:22:51 +00:00
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down(600));
2024-03-28 09:18:24 +00:00
const { devices, general, questions } = useAnalytics({
ready: Boolean(Object.keys(quiz).length),
2024-04-03 10:50:17 +00:00
quizId: editQuizId?.toString() || "",
from,
2024-04-03 14:22:51 +00:00
to,
});
2024-03-20 14:44:51 +00:00
2024-03-29 06:10:33 +00:00
const resetTime = () => {
2024-04-03 14:22:51 +00:00
setFrom(moment(0));
setTo(moment(Date.now()));
};
2024-04-01 18:09:26 +00:00
2024-04-03 14:22:51 +00:00
useEffect(() => {
if (quizes.length > 0) {
const quiz = quizes.find((q) => q.backendId === editQuizId);
if (quiz === undefined) throw new Error("Не удалось получить квиз");
setQuiz(quiz);
2024-04-03 14:22:51 +00:00
setFrom(moment(new Date(quiz.created_at)));
}
}, [quizes]);
useEffect(() => {
const getData = async (): Promise<void> => {
try {
if (editQuizId !== null) {
const gottenQuizes = await quizApi.getList();
setQuizes(gottenQuizes);
}
} catch (error) {
console.error("Не удалось получить квизы", error);
}
};
getData();
2024-04-03 14:22:51 +00:00
}, []);
2024-03-28 09:18:24 +00:00
useLayoutEffect(() => {
if (editQuizId === undefined) redirect("/list");
}, [editQuizId]);
2024-03-19 06:56:21 +00:00
2024-03-20 12:18:09 +00:00
const handleClose = () => {
setOpen(false);
};
2024-04-01 18:09:26 +00:00
2024-03-20 12:18:09 +00:00
const handleOpen = () => {
setOpen(true);
};
2024-04-01 18:09:26 +00:00
2024-03-20 12:18:09 +00:00
const onAdornmentClick = () => {
setOpen((old) => !old);
2024-04-01 18:09:26 +00:00
if (isOpenEnd) {
2024-03-20 12:18:09 +00:00
handleCloseEnd();
2024-03-19 06:56:21 +00:00
}
2024-03-20 12:18:09 +00:00
};
2024-03-19 06:56:21 +00:00
2024-03-20 12:18:09 +00:00
const handleCloseEnd = () => {
setOpenEnd(false);
};
2024-04-01 18:09:26 +00:00
2024-03-20 12:18:09 +00:00
const handleOpenEnd = () => {
setOpenEnd(true);
};
2024-04-01 18:09:26 +00:00
2024-03-20 12:18:09 +00:00
const onAdornmentClickEnd = () => {
setOpenEnd((old) => !old);
2024-04-01 18:09:26 +00:00
if (isOpen) {
2024-03-20 12:18:09 +00:00
handleClose();
2024-03-19 06:56:21 +00:00
}
2024-03-20 12:18:09 +00:00
};
2024-03-19 06:56:21 +00:00
2024-03-20 12:18:09 +00:00
return (
2024-04-03 14:22:51 +00:00
<LocalizationProvider dateAdapter={AdapterMoment}>
2024-03-21 14:24:56 +00:00
<HeaderFull isRequest />
<SectionWrapper component={"section"} sx={{ padding: "60px 20px" }}>
2024-03-20 12:18:09 +00:00
<Typography variant={"h4"}>Аналитика</Typography>
<Box
sx={{
display: "flex",
gap: isMobile ? "15px" : "20px",
2024-04-03 14:22:51 +00:00
alignItems: isMobile ? "center" : "end",
2024-03-20 12:18:09 +00:00
justifyContent: "space-between",
padding: "40px 0 20px",
borderBottom: `1px solid ${theme.palette.grey2.main}`,
}}
>
2024-04-03 14:22:51 +00:00
<Box
sx={{
display: "flex",
flexWrap: "wrap",
gap: isMobile ? "15px" : "20px",
}}
>
2024-03-20 12:18:09 +00:00
<Box>
<Typography
sx={{
fontSize: "16px",
marginBottom: "5px",
fontWeight: 500,
color: "4D4D4D",
}}
>
Дата начала
</Typography>
<DatePicker
format="DD/MM/YYYY"
2024-03-20 12:18:09 +00:00
open={isOpen}
onClose={handleClose}
onOpen={handleOpen}
2024-04-17 14:15:13 +00:00
minDate={moment(quiz?.created_at)}
2024-03-20 12:18:09 +00:00
sx={{
2024-04-03 14:22:51 +00:00
width: isMobile ? "285px" : "170px",
2024-03-20 12:18:09 +00:00
"& .MuiOutlinedInput-root": {
2024-04-03 14:22:51 +00:00
background: theme.palette.background.paper,
2024-03-20 12:18:09 +00:00
borderRadius: "10px",
fontSize: "16px",
},
"& .MuiInputBase-input": {
padding: "12.5px 14px",
},
}}
slotProps={{
2024-04-01 18:09:26 +00:00
//@ts-ignore
//TODO: fix types in @mui/x-date-pickers
2024-03-20 12:18:09 +00:00
textField: {
InputProps: {
endAdornment: (
<IconButton onClick={onAdornmentClick}>
<CalendarIcon />
</IconButton>
2024-04-01 18:09:26 +00:00
) as ReactNode,
2024-03-20 12:18:09 +00:00
},
},
}}
2024-04-03 14:22:51 +00:00
value={from}
onChange={setFrom}
2024-03-20 12:18:09 +00:00
/>
</Box>
<Box>
<Typography
sx={{
fontSize: "16px",
marginBottom: "5px",
fontWeight: 500,
color: "4D4D4D",
}}
>
Дата окончания
</Typography>
<DatePicker
format="DD/MM/YYYY"
2024-03-20 12:18:09 +00:00
open={isOpenEnd}
onClose={handleCloseEnd}
onOpen={handleOpenEnd}
2024-04-17 14:15:13 +00:00
minDate={moment(quiz?.created_at)}
2024-03-20 12:18:09 +00:00
sx={{
2024-04-03 14:22:51 +00:00
width: isMobile ? "285px" : "170px",
2024-03-20 12:18:09 +00:00
"& .MuiOutlinedInput-root": {
2024-04-03 14:22:51 +00:00
background: theme.palette.background.paper,
2024-03-20 12:18:09 +00:00
borderRadius: "10px",
fontSize: "16px",
},
"& .MuiInputBase-input": {
padding: "12.5px 14px",
},
}}
slotProps={{
textField: {
InputProps: {
endAdornment: (
<IconButton onClick={onAdornmentClickEnd}>
<CalendarIcon />
</IconButton>
),
},
},
}}
2024-04-03 14:22:51 +00:00
value={to}
onChange={setTo}
2024-03-20 12:18:09 +00:00
/>
</Box>
</Box>
2024-03-19 06:56:21 +00:00
2024-03-20 12:18:09 +00:00
<Button
onClick={resetTime}
2024-03-20 12:18:09 +00:00
variant="outlined"
sx={{
2024-04-03 14:22:51 +00:00
padding: isMobile ? "8px" : "9px 48px",
minWidth: "auto",
marginTop: isMobile ? "25px" : null,
2024-03-20 12:18:09 +00:00
color: theme.palette.brightPurple.main,
"&:hover": {
backgroundColor: "#581CA7",
color: "#FFFFFF",
},
"&:active": {
backgroundColor: "#000000",
color: "#FFFFFF",
},
}}
>
2024-04-03 14:22:51 +00:00
{isMobile ? <ResetIcon /> : "Сбросить"}
2024-03-20 12:18:09 +00:00
</Button>
</Box>
2024-04-12 21:05:15 +00:00
<General
data={general}
day={86400 - moment(to).unix() - moment(from).unix() > 0}
/>
<AnswersStatistics data={questions} />
<Devices data={devices} />
2024-03-20 12:18:09 +00:00
</SectionWrapper>
2024-04-03 14:22:51 +00:00
</LocalizationProvider>
2024-03-20 12:18:09 +00:00
);
}