frontPanel/src/pages/Analytics/Analytics.tsx
2024-04-26 17:41:36 +03:00

261 lines
7.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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