frontPanel/src/pages/Analytics/Answers/Answers.tsx
2024-04-16 19:42:55 +04:00

300 lines
8.2 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 { FC, useEffect, useMemo, useState } from "react";
import type { PaginationRenderItemParams } from "@mui/material";
import {
Box,
ButtonBase,
Input,
LinearProgress,
Pagination as MuiPagination,
PaginationItem,
Paper,
Typography,
useMediaQuery,
useTheme,
} from "@mui/material";
import { ReactComponent as DoubleCheckIcon } from "@icons/Analytics/doubleCheck.svg";
import { ReactComponent as NextIcon } from "@icons/Analytics/next.svg";
import { ReactComponent as LeftArrowIcon } from "@icons/Analytics/leftArrow.svg";
import { ReactComponent as RightArrowIcon } from "@icons/Analytics/rightArrow.svg";
import { extractOrder } from "@utils/extractOrder";
type AnswerProps = {
title: string;
percent: number;
highlight?: boolean;
};
type AnswersProps = {
data: Record<string, Record<string, number>> | null;
};
type PaginationProps = {
page: number;
pagesAmount: number;
setPage: (page: number) => void;
};
const Answer = ({ title, percent, highlight }: AnswerProps) => {
const theme = useTheme();
return (
<Box sx={{ padding: "15px 25px" }}>
<Box
sx={{
position: "relative",
display: "flex",
gap: "15px",
alignItems: "center",
flexGrow: 1,
}}
>
<LinearProgress
variant="determinate"
title={title}
value={percent}
sx={{
width: "100%",
height: "44px",
background: theme.palette.background.default,
borderRadius: "10px",
border: `1px solid ${highlight ? theme.palette.brightPurple.main : theme.palette.grey2.main}`,
"& > span": { background: highlight ? "#D9C0F9" : "#9A9AAF1A" },
"&::before": {
content: title ? `"${title}"` : `"Без имени"`,
position: "absolute",
zIndex: 1,
left: "20px",
top: "50%",
transform: "translateY(-50%)",
color: highlight
? theme.palette.brightPurple.main
: theme.palette.grey3.main,
},
}}
/>
<Box sx={{ minWidth: 35 }}>
<Typography
sx={{
minWidth: "45px",
fontWeight: highlight ? "bold" : "normal",
color: highlight
? theme.palette.brightPurple.main
: theme.palette.text.primary,
}}
>{`${percent.toFixed(1)}%`}</Typography>
</Box>
</Box>
</Box>
);
};
const Pagination = ({ page, setPage, pagesAmount }: PaginationProps) => {
const [count, setCount] = useState<number>(0);
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down(855));
useEffect(() => {
setCount(pagesAmount);
}, [pagesAmount]);
const getPaginationItem = (props: PaginationRenderItemParams) => {
if (props.type === "start-ellipsis" || props.type === "end-ellipsis") {
return;
}
if (props.type !== "previous" && props.type !== "next" && props.page) {
if (isMobile) {
const allowedPages = [
page - 1 < 1 ? page + 2 : page - 1,
page,
page + 1 > count ? page - 2 : page + 1,
];
if (!allowedPages.includes(props.page)) {
return;
}
}
const allowedPages = [
page - 2 < 1 ? page + 3 : page - 2,
page - 1 < 1 ? page + 4 : page - 1,
page,
page + 1 > count ? page - 4 : page + 1,
page + 2 > count ? page - 3 : page + 2,
];
if (!allowedPages.includes(props.page)) {
return;
}
}
return (
<PaginationItem
component="div"
slots={{ previous: LeftArrowIcon, next: RightArrowIcon }}
{...{ ...props, variant: undefined }}
/>
);
};
return (
<Box
sx={{
display: "flex",
alignItems: "center",
justifyContent: "center",
marginTop: "30px",
}}
>
<Input
disableUnderline
placeholder="1"
value={page}
onChange={({ target }) => {
const newPage = Number(target.value.replace(/\D/, ""));
if (newPage <= count) {
setPage(newPage);
}
}}
sx={{
height: "30px",
width: "65px",
borderRadius: "5px",
padding: "10px",
marginRight: "5px",
boxShadow: "0 0 20px rgba(0, 0, 0, 0.15)",
backgroundColor: theme.palette.background.paper,
}}
/>
<MuiPagination
page={page}
count={count}
siblingCount={isMobile ? 1 : 2}
boundaryCount={isMobile ? 1 : 2}
onChange={(_, page) => setPage(page)}
renderItem={getPaginationItem}
sx={{
"& .MuiButtonBase-root": {
borderRadius: "5px",
margin: "0 5px",
height: "30px",
background: theme.palette.background.paper,
boxShadow: "0 0 20px rgba(0, 0, 0, 0.15)",
"&.Mui-selected": {
background: theme.palette.background.paper,
color: theme.palette.brightPurple.main,
},
"&.MuiPaginationItem-previousNext": {
margin: "0 15px",
background: theme.palette.brightPurple.main,
},
},
}}
/>
</Box>
);
};
export const Answers: FC<AnswersProps> = ({ data }) => {
const [page, setPage] = useState<number>(1);
const theme = useTheme();
const answers = useMemo(() => {
const unsortedAnswers = data === null ? [] : Object.entries(data ?? {});
return unsortedAnswers.sort(
([titleA], [titleB]) => extractOrder(titleA) - extractOrder(titleB),
);
}, [data]);
const currentAnswer = answers[page - 1];
const percentsSum = Object.values(currentAnswer?.[1] ?? {}).reduce(
(total, item) => (total += item),
0,
);
const currentAnswerExtended =
percentsSum >= 100
? Object.entries(currentAnswer?.[1] ?? {})
: [
...Object.entries(currentAnswer?.[1] ?? {}),
["Другое", 100 - percentsSum] as [string, number],
];
if (!data) {
return (
<Typography textAlign="center" m="10px 0">
нет данных об ответах
</Typography>
);
}
return (
<Box sx={{ flexGrow: 1 }}>
<Paper
sx={{
borderRadius: "12px",
boxShadow: "0 0 20px rgba(0, 0, 0, 0.15)",
marginTop: "20px",
}}
>
<Box
sx={{
display: "flex",
gap: "10px",
alignItems: "center",
padding: "25px",
}}
>
<Typography
component="h3"
sx={{
flexGrow: 1,
position: "relative",
fontSize: "18px",
fontWeight: "bold",
paddingLeft: "40px",
color: theme.palette.text.primary,
"&::before": {
content: "'1'",
position: "absolute",
top: "50%",
left: "0",
transform: "translateY(-50%)",
display: "flex",
alignItems: "center",
justifyContent: "center",
width: "30px",
height: "30px",
borderRadius: "50%",
fontSize: "14px",
fontWeight: "normal",
background: "#EEE4FC",
color: theme.palette.brightPurple.main,
},
}}
>
Заголовок вопроса.
{currentAnswer?.[0].split("(")[0].trim()
? ` ${currentAnswer?.[0]}`
: "Без заголовка"}
</Typography>
<ButtonBase>
<DoubleCheckIcon />
</ButtonBase>
<ButtonBase>
<NextIcon />
</ButtonBase>
</Box>
{currentAnswerExtended.map(([title, percent], index) => (
<Answer
key={index}
title={title}
percent={percent}
highlight={!index}
/>
))}
</Paper>
<Pagination page={page} setPage={setPage} pagesAmount={answers.length} />
</Box>
);
};