frontPanel/src/pages/Analytics/Answers/Answers.tsx

300 lines
8.2 KiB
TypeScript
Raw Normal View History

2024-04-05 14:09:17 +00:00
import { FC, useEffect, useMemo, useState } from "react";
import type { PaginationRenderItemParams } from "@mui/material";
2024-03-25 15:02:24 +00:00
import {
Box,
2024-04-01 18:09:26 +00:00
ButtonBase,
Input,
2024-03-25 15:02:24 +00:00
LinearProgress,
Pagination as MuiPagination,
PaginationItem,
2024-04-01 18:09:26 +00:00
Paper,
Typography,
2024-03-25 15:02:24 +00:00
useMediaQuery,
2024-04-01 18:09:26 +00:00
useTheme,
2024-03-25 15:02:24 +00:00
} 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";
2024-04-05 14:09:17 +00:00
2024-03-25 15:02:24 +00:00
type AnswerProps = {
title: string;
percent: number;
highlight?: boolean;
};
2024-04-01 18:09:26 +00:00
type AnswersProps = {
data: Record<string, Record<string, number>> | null;
};
2024-04-05 14:09:17 +00:00
type PaginationProps = {
page: number;
pagesAmount: number;
setPage: (page: number) => void;
2024-03-25 15:02:24 +00:00
};
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}"` : `"Без имени"`,
2024-03-25 15:02:24 +00:00
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>
2024-03-25 15:02:24 +00:00
</Box>
</Box>
</Box>
);
};
2024-04-05 14:09:17 +00:00
const Pagination = ({ page, setPage, pagesAmount }: PaginationProps) => {
const [count, setCount] = useState<number>(0);
2024-03-25 15:02:24 +00:00
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down(855));
2024-04-05 14:09:17 +00:00
useEffect(() => {
setCount(pagesAmount);
}, [pagesAmount]);
2024-03-25 15:02:24 +00:00
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}
2024-04-05 14:09:17 +00:00
onChange={({ target }) => {
const newPage = Number(target.value.replace(/\D/, ""));
if (newPage <= count) {
setPage(newPage);
}
}}
2024-03-25 15:02:24 +00:00
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>
);
};
2024-04-01 18:09:26 +00:00
export const Answers: FC<AnswersProps> = ({ data }) => {
2024-04-05 14:09:17 +00:00
const [page, setPage] = useState<number>(1);
2024-03-25 15:02:24 +00:00
const theme = useTheme();
const answers = useMemo(() => {
const unsortedAnswers = data === null ? [] : Object.entries(data ?? {});
return unsortedAnswers.sort(
([titleA], [titleB]) => extractOrder(titleA) - extractOrder(titleB),
);
}, [data]);
2024-04-09 14:24:44 +00:00
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],
];
2024-04-05 14:09:17 +00:00
2024-04-01 18:09:26 +00:00
if (!data) {
return (
<Typography textAlign="center" m="10px 0">
нет данных об ответах
</Typography>
);
2024-04-01 18:09:26 +00:00
}
2024-03-25 15:02:24 +00:00
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,
},
}}
>
Заголовок вопроса.
2024-04-09 14:24:44 +00:00
{currentAnswer?.[0].split("(")[0].trim()
2024-04-09 15:08:11 +00:00
? ` ${currentAnswer?.[0]}`
2024-04-09 14:24:44 +00:00
: "Без заголовка"}
2024-03-25 15:02:24 +00:00
</Typography>
<ButtonBase>
<DoubleCheckIcon />
</ButtonBase>
<ButtonBase>
<NextIcon />
</ButtonBase>
</Box>
2024-04-09 14:26:51 +00:00
{currentAnswerExtended.map(([title, percent], index) => (
2024-04-08 14:22:19 +00:00
<Answer
key={index}
2024-04-09 14:26:51 +00:00
title={title}
percent={percent}
2024-04-08 14:22:19 +00:00
highlight={!index}
/>
))}
2024-03-25 15:02:24 +00:00
</Paper>
2024-04-08 12:56:28 +00:00
<Pagination page={page} setPage={setPage} pagesAmount={answers.length} />
2024-03-25 15:02:24 +00:00
</Box>
);
};