2024-04-03 14:22:51 +00:00
|
|
|
|
import { ReactNode, 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 12:18:09 +00:00
|
|
|
|
import moment from "moment";
|
2024-03-28 09:18:24 +00:00
|
|
|
|
import { useQuizStore } from "@root/quizes/store";
|
2024-04-03 10:50:17 +00:00
|
|
|
|
import { useCurrentQuiz } from "@root/quizes/hooks";
|
2024-03-28 09:18:24 +00:00
|
|
|
|
import { useAnalytics } from "@utils/hooks/useAnalytics";
|
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-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-03 23:35:36 +00:00
|
|
|
|
import { setQuizes } from "@root/quizes/actions";
|
|
|
|
|
import { quizApi } from "@api/quiz";
|
|
|
|
|
import { 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() {
|
2024-04-03 23:35:36 +00:00
|
|
|
|
const { quizes, editQuizId } = useQuizStore();
|
2024-03-28 09:18:24 +00:00
|
|
|
|
|
2024-04-03 23:35:36 +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);
|
2024-04-08 23:11:19 +00:00
|
|
|
|
const [to, setTo] = useState<Moment | null>(moment().add(1, "days"));
|
2024-04-12 21:05:15 +00:00
|
|
|
|
console.log(moment(to).unix() - moment(from).unix());
|
|
|
|
|
console.log(86400 - (moment(to).unix() - moment(from).unix()));
|
|
|
|
|
console.log(86400 - (moment(to).unix() - moment(from).unix()) > 0);
|
2024-04-03 14:22:51 +00:00
|
|
|
|
|
|
|
|
|
const theme = useTheme();
|
|
|
|
|
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
|
|
|
|
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
2024-03-28 09:18:24 +00:00
|
|
|
|
|
2024-03-30 18:54:28 +00:00
|
|
|
|
const { devices, general, questions } = useAnalytics({
|
2024-04-03 23:35:36 +00:00
|
|
|
|
ready: Boolean(Object.keys(quiz).length),
|
2024-04-03 10:50:17 +00:00
|
|
|
|
quizId: editQuizId?.toString() || "",
|
2024-03-30 18:54:28 +00:00
|
|
|
|
from,
|
2024-04-03 14:22:51 +00:00
|
|
|
|
to,
|
2024-03-30 18:54:28 +00:00
|
|
|
|
});
|
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-03-30 18:54:28 +00:00
|
|
|
|
};
|
2024-04-01 18:09:26 +00:00
|
|
|
|
|
2024-04-03 14:22:51 +00:00
|
|
|
|
useEffect(() => {
|
2024-04-03 23:35:36 +00:00
|
|
|
|
if (quizes.length > 0) {
|
|
|
|
|
const quiz = quizes.find((q) => q.backendId === editQuizId);
|
2024-04-05 16:29:20 +00:00
|
|
|
|
if (quiz === undefined) throw new Error("Не удалось получить квиз");
|
2024-04-03 23:35:36 +00:00
|
|
|
|
setQuiz(quiz);
|
2024-04-03 14:22:51 +00:00
|
|
|
|
setFrom(moment(new Date(quiz.created_at)));
|
|
|
|
|
}
|
2024-04-03 23:35:36 +00:00
|
|
|
|
}, [quizes]);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const getData = async (): Promise<void> => {
|
|
|
|
|
try {
|
|
|
|
|
if (editQuizId !== null) {
|
|
|
|
|
const gottenQuizes = await quizApi.getList();
|
2024-04-05 16:29:20 +00:00
|
|
|
|
setQuizes(gottenQuizes);
|
2024-04-03 23:35:36 +00:00
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error("Не удалось получить квизы", error);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
getData();
|
2024-04-03 14:22:51 +00:00
|
|
|
|
}, []);
|
|
|
|
|
|
2024-03-28 09:18:24 +00:00
|
|
|
|
useLayoutEffect(() => {
|
2024-03-30 18:54:28 +00:00
|
|
|
|
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-04-10 22:29:02 +00:00
|
|
|
|
console.log(general);
|
2024-03-20 12:18:09 +00:00
|
|
|
|
const now = moment();
|
|
|
|
|
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
|
2024-04-07 23:03:44 +00:00
|
|
|
|
format="DD/MM/YYYY"
|
2024-03-20 12:18:09 +00:00
|
|
|
|
open={isOpen}
|
|
|
|
|
onClose={handleClose}
|
|
|
|
|
onOpen={handleOpen}
|
|
|
|
|
// defaultValue={now}
|
|
|
|
|
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
|
2024-04-07 23:03:44 +00:00
|
|
|
|
format="DD/MM/YYYY"
|
2024-03-20 12:18:09 +00:00
|
|
|
|
open={isOpenEnd}
|
|
|
|
|
onClose={handleCloseEnd}
|
|
|
|
|
onOpen={handleOpenEnd}
|
|
|
|
|
// defaultValue={now}
|
|
|
|
|
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
|
2024-03-30 18:54:28 +00:00
|
|
|
|
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}
|
|
|
|
|
/>
|
2024-03-30 18:54:28 +00:00
|
|
|
|
<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
|
|
|
|
);
|
|
|
|
|
}
|