Merge branch 'resultsPageLogic' into dev
This commit is contained in:
commit
0c2894b5fb
@ -33,6 +33,7 @@
|
||||
"html-to-image": "^1.11.11",
|
||||
"immer": "^10.0.3",
|
||||
"jszip": "^3.10.1",
|
||||
"moment": "^2.30.1",
|
||||
"nanoid": "^5.0.3",
|
||||
"notistack": "^3.0.1",
|
||||
"react": "^18.2.0",
|
||||
|
@ -50,7 +50,7 @@ import { isAxiosError } from "axios";
|
||||
import { useEffect, useLayoutEffect, useRef } from "react";
|
||||
import RecoverPassword from "./pages/auth/RecoverPassword";
|
||||
import OutdatedLink from "./pages/auth/OutdatedLink";
|
||||
|
||||
import { QuizAnswersPage } from "./pages/QuizAnswersPage/QuizAnswersPage";
|
||||
export function useUserAccountFetcher({
|
||||
onError,
|
||||
onNewUserAccount,
|
||||
@ -238,6 +238,7 @@ export default function App() {
|
||||
<Route path="/list" element={<MyQuizzesFull />} />
|
||||
<Route path={"/view"} element={<ViewPage />} />
|
||||
<Route path={"/tariffs"} element={<Tariffs />} />
|
||||
<Route path={"/results/:quizId"} element={<QuizAnswersPage />} />
|
||||
<Route element={<PrivateRoute />}>
|
||||
{routeslink.map((e, i) => (
|
||||
<Route
|
||||
|
69
src/api/result.ts
Normal file
69
src/api/result.ts
Normal file
@ -0,0 +1,69 @@
|
||||
import { makeRequest } from "@frontend/kitui";
|
||||
|
||||
async function getResultList(quizId, body?: any) {
|
||||
return makeRequest<unknown, unknown>({
|
||||
url: process.env.REACT_APP_DOMAIN + `/squiz/results/getResults/${quizId}`,
|
||||
method: "POST",
|
||||
body: { page: 0, limit: 10, ...body },
|
||||
});
|
||||
}
|
||||
|
||||
function deleteResult(resultId: number) {
|
||||
return makeRequest<unknown, unknown>({
|
||||
url: process.env.REACT_APP_DOMAIN + `/squiz/results/delete/${resultId}`,
|
||||
body: {},
|
||||
method: "DELETE",
|
||||
});
|
||||
}
|
||||
|
||||
// export const obsolescenceResult = async (idResultArray: string[]) => {
|
||||
// try {
|
||||
// const response = await makeRequest<unknown, unknown>({
|
||||
// url: process.env.REACT_APP_DOMAIN + `/squiz/result/seen`,
|
||||
// body: {
|
||||
// answers: idResultArray,
|
||||
// },
|
||||
// method: "PATCH",
|
||||
// });
|
||||
// return response;
|
||||
// } catch (e) {
|
||||
// console.log("ошибка", e);
|
||||
// }
|
||||
// };
|
||||
|
||||
function obsolescenceResult(idResultArray: string[]) {
|
||||
return makeRequest<unknown, unknown>({
|
||||
url: process.env.REACT_APP_DOMAIN + `/squiz/result/seen`,
|
||||
body: {
|
||||
answers: idResultArray,
|
||||
},
|
||||
method: "PATCH",
|
||||
});
|
||||
}
|
||||
|
||||
function getAnswerResultList(resultId: number) {
|
||||
return makeRequest<unknown, unknown>({
|
||||
url: process.env.REACT_APP_DOMAIN + `/squiz/result/${resultId}`,
|
||||
method: "GET",
|
||||
});
|
||||
}
|
||||
|
||||
function AnswerResultListEx(quizId: number, fromDate: string, toDate: string) {
|
||||
return makeRequest<unknown, unknown>({
|
||||
url: process.env.REACT_APP_DOMAIN + `/squiz/results/${quizId}/export`,
|
||||
method: "POST",
|
||||
body: {
|
||||
to: new Date(toDate),
|
||||
from: new Date(fromDate),
|
||||
new: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export const resultApi = {
|
||||
getList: getResultList,
|
||||
delete: deleteResult,
|
||||
getAnswerList: getAnswerResultList,
|
||||
export: AnswerResultListEx,
|
||||
obsolescence: obsolescenceResult,
|
||||
};
|
14
src/assets/icons/ClockWiseIcon.tsx
Normal file
14
src/assets/icons/ClockWiseIcon.tsx
Normal file
@ -0,0 +1,14 @@
|
||||
export const ClockWiseIcon = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M10.4115 22.35L11.1615 22.35L11.1615 20.85L10.4115 20.85L10.4115 22.35ZM19.2553 12L19.2553 12.75L20.7553 12.75L20.7553 12L19.2553 12ZM20.0053 13.6L19.4748 14.1302L20.0053 14.661L20.5358 14.1302L20.0053 13.6ZM10.4115 20.85C5.52885 20.85 1.56777 16.887 1.56777 12L0.0677734 12C0.0677731 17.7146 4.6995 22.35 10.4115 22.35L10.4115 20.85ZM1.56777 12C1.56777 7.11295 5.52885 3.15 10.4115 3.15L10.4115 1.65C4.6995 1.65 0.0677736 6.28545 0.0677734 12L1.56777 12ZM10.4115 3.15C15.2942 3.15 19.2553 7.11295 19.2553 12L20.7553 12C20.7553 6.28545 16.1235 1.65 10.4115 1.65L10.4115 3.15ZM22.6726 9.86985L19.4748 13.0698L20.5358 14.1302L23.7336 10.9302L22.6726 9.86985ZM20.5358 13.0698L17.3377 9.86983L16.2767 10.9302L19.4748 14.1302L20.5358 13.0698Z"
|
||||
fill="#7E2AEA"
|
||||
/>
|
||||
</svg>
|
||||
);
|
@ -13,8 +13,10 @@ import "./index.css";
|
||||
import lightTheme from "./utils/themes/light";
|
||||
import { SWRConfig } from "swr";
|
||||
import { BrowserRouter } from "react-router-dom";
|
||||
import moment from "moment";
|
||||
|
||||
dayjs.locale("ru");
|
||||
moment.locale("ru");
|
||||
|
||||
const localeText =
|
||||
ruRU.components.MuiLocalizationProvider.defaultProps.localeText;
|
||||
|
55
src/model/result/result.ts
Normal file
55
src/model/result/result.ts
Normal file
@ -0,0 +1,55 @@
|
||||
export interface RawResult {
|
||||
total_count: number;
|
||||
results: {
|
||||
content: string;
|
||||
id: number;
|
||||
new: boolean;
|
||||
created_at: string;
|
||||
};
|
||||
}
|
||||
export interface ResultContent {
|
||||
name?: string;
|
||||
email?: string;
|
||||
phone?: string;
|
||||
address?: string;
|
||||
telegram?: string;
|
||||
wechat?: string;
|
||||
viber?: string;
|
||||
vk?: string;
|
||||
skype?: string;
|
||||
whatsup?: string;
|
||||
messenger?: string;
|
||||
}
|
||||
|
||||
export const defaultResultContent: ResultContent = {
|
||||
name: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
address: "",
|
||||
telegram: "",
|
||||
wechat: "",
|
||||
viber: "",
|
||||
vk: "",
|
||||
skype: "",
|
||||
whatsup: "",
|
||||
messenger: "",
|
||||
};
|
||||
|
||||
export function rawResultToResult(rawResult): RawResult {
|
||||
let content = defaultResultContent;
|
||||
|
||||
try {
|
||||
content = JSON.parse(rawResult.content);
|
||||
console.log("Content", content);
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
"Cannot parse result from string, using default config",
|
||||
error,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
...rawResult,
|
||||
content,
|
||||
};
|
||||
}
|
@ -85,7 +85,6 @@ export const Select = ({
|
||||
borderRadius: "8px",
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
border: `1px solid ${colorMain} !important`,
|
||||
height: "48px",
|
||||
borderRadius: "10px",
|
||||
},
|
||||
}}
|
||||
|
381
src/pages/QuizAnswersPage/CardAnswer.tsx
Normal file
381
src/pages/QuizAnswersPage/CardAnswer.tsx
Normal file
@ -0,0 +1,381 @@
|
||||
import { ArrowDownIcon } from "@icons/questionsPage/ArrowDownIcon";
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
IconButton,
|
||||
Modal,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import { FC, useState } from "react";
|
||||
import { ContactIcon } from "./icons/ContactIcon";
|
||||
import { MessageIcon } from "./icons/MessageIcon";
|
||||
import { PhoneIcon } from "./icons/PhoneIcon";
|
||||
import { DeleteIcon } from "@icons/questionsPage/deleteIcon";
|
||||
|
||||
import homeImg from "./images/home.png";
|
||||
import videoFrame from "./images/videoFrame.png";
|
||||
import { EyeIcon } from "./icons/EyeIcon";
|
||||
import { deleteResult, obsolescenceResult } from "@root/results/actions";
|
||||
import { resultApi } from "@api/result";
|
||||
import { useQuizStore } from "@root/quizes/store";
|
||||
import { useQuestionsStore } from "@root/questions/store";
|
||||
|
||||
interface Props {
|
||||
isNew: boolean;
|
||||
idResult: string;
|
||||
onClick: () => void;
|
||||
dayResult: string;
|
||||
timeResult: string;
|
||||
name?: string;
|
||||
phone?: string;
|
||||
email?: string;
|
||||
onLossNew?: (id: string) => void;
|
||||
}
|
||||
|
||||
export const CardAnswer = ({
|
||||
name,
|
||||
phone,
|
||||
email,
|
||||
isNew = true,
|
||||
idResult,
|
||||
timeResult,
|
||||
dayResult,
|
||||
onLossNew,
|
||||
}: Props) => {
|
||||
const [isOpen, setIsOpen] = useState<boolean>(false);
|
||||
const [openDelete, setOpenDelete] = useState<boolean>(false);
|
||||
const theme = useTheme();
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||
const [resultsAnswer, setResultsAnswer] = useState([]);
|
||||
const { editQuizId } = useQuizStore();
|
||||
const { questions } = useQuestionsStore();
|
||||
console.log(questions);
|
||||
// console.log(questions[0].backendId)
|
||||
// console.log(questions[1].backendId)
|
||||
// console.log(questions[2].backendId)
|
||||
// console.log(questions[3].backendId)
|
||||
return (
|
||||
<Box
|
||||
onClick={() => {
|
||||
obsolescenceResult(idResult, editQuizId);
|
||||
}}
|
||||
sx={{
|
||||
borderRadius: "12px",
|
||||
maxWidth: isTablet ? "450px" : "auto",
|
||||
width: "100%",
|
||||
boxShadow:
|
||||
"0px 2.767px 8.551px 0px rgba(210, 208, 225, 0.07), 0px 6.65px 20.549px 0px rgba(210, 208, 225, 0.10), 0px 12.522px 38.692px 0px rgba(210, 208, 225, 0.12), 0px 22.336px 69.019px 0px rgba(210, 208, 225, 0.14), 0px 41.778px 129.093px 0px rgba(210, 208, 225, 0.17), 0px 100px 309px 0px rgba(210, 208, 225, 0.24)",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
flexDirection: isTablet ? "column" : "-moz-initial",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
flexDirection: isTablet ? "column" : "-moz-initial",
|
||||
cursor: "pointer",
|
||||
p: "20px",
|
||||
borderRadius: isTablet
|
||||
? "12px 12px 0 0"
|
||||
: isOpen
|
||||
? "12px 0 0 0"
|
||||
: "12px 0 0 12px",
|
||||
display: "flex",
|
||||
alignItems: "flex-start",
|
||||
background: "#FFF",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
width: isTablet ? "100%" : "auto",
|
||||
display: "flex",
|
||||
alignItems: "flex-start",
|
||||
flexDirection: isTablet ? "row-reverse" : "-moz-initial",
|
||||
justifyContent: isTablet ? "space-between" : "-moz-initial",
|
||||
pb: isTablet ? "20px" : "0",
|
||||
mb: isTablet ? "20px" : "0",
|
||||
borderBottom: isTablet
|
||||
? "1px solid rgba(154, 154, 175, 0.50)"
|
||||
: "0",
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: "6px" }}>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
width: "30px",
|
||||
height: "30px",
|
||||
borderRadius: "50%",
|
||||
background: "#EEE4FC",
|
||||
color: "#7E2AEA",
|
||||
}}
|
||||
>
|
||||
{idResult}
|
||||
</Box>
|
||||
<IconButton
|
||||
onClick={async () => {
|
||||
setIsOpen(!isOpen);
|
||||
if (isOpen) {
|
||||
let resAnswer = await resultApi.getAnswerList(
|
||||
Number(idResult),
|
||||
);
|
||||
resAnswer = resAnswer.filter((res) => res.Result !== true);
|
||||
setResultsAnswer(resAnswer);
|
||||
console.log("тут хранятся ответы", resAnswer);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ArrowDownIcon
|
||||
style={{
|
||||
transform: isOpen ? "rotate(180deg)" : "rotate(360deg)",
|
||||
}}
|
||||
fontSize="10px"
|
||||
/>
|
||||
</IconButton>
|
||||
</Box>
|
||||
|
||||
<Typography
|
||||
sx={{
|
||||
ml: isTablet ? "0" : "50px",
|
||||
mr: isTablet ? "0" : "188px",
|
||||
fontSize: "18px",
|
||||
color: "#7E2AEA",
|
||||
maxWidth: "143px",
|
||||
width: "100%",
|
||||
}}
|
||||
>
|
||||
{dayResult} в {timeResult}
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box sx={{ display: "flex", flexDirection: "column", gap: "10px" }}>
|
||||
{name && (
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: "13px" }}>
|
||||
<ContactIcon />
|
||||
<Typography sx={{ fontSize: "18px", color: "#4D4D4D" }}>
|
||||
{name}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
{email && (
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: "13px" }}>
|
||||
<MessageIcon />
|
||||
<Typography sx={{ fontSize: "18px", color: "#4D4D4D" }}>
|
||||
{email}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
{phone && (
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: "13px" }}>
|
||||
<PhoneIcon />
|
||||
<Typography sx={{ fontSize: "18px", color: "#4D4D4D" }}>
|
||||
{phone}
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{!isTablet && isNew && (
|
||||
<Typography
|
||||
sx={{
|
||||
ml: "auto",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
background: "#FB5607",
|
||||
borderRadius: "8px",
|
||||
width: "77px",
|
||||
height: "36px",
|
||||
color: "white",
|
||||
}}
|
||||
>
|
||||
Новая
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
borderRadius: "8px",
|
||||
p: isTablet ? "20px" : "0",
|
||||
cursor: "pointer",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: isTablet ? "space-between" : "center",
|
||||
width: isTablet ? "100%" : "100px",
|
||||
background: "#F2F3F7",
|
||||
}}
|
||||
>
|
||||
{isTablet && isNew ? (
|
||||
<>
|
||||
<Typography
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
background: "#FB5607",
|
||||
borderRadius: "8px",
|
||||
width: "77px",
|
||||
height: "36px",
|
||||
color: "white",
|
||||
}}
|
||||
>
|
||||
Новая
|
||||
</Typography>
|
||||
<Box sx={{ display: "flex", alignItems: "center" }}>
|
||||
<IconButton onClick={() => setOpenDelete(true)}>
|
||||
<DeleteIcon color="#4D4D4D" fontSize="34px" />
|
||||
</IconButton>
|
||||
</Box>
|
||||
</>
|
||||
) : (
|
||||
<IconButton onClick={() => setOpenDelete(true)}>
|
||||
<DeleteIcon color="#4D4D4D" fontSize="34px" />
|
||||
</IconButton>
|
||||
)}
|
||||
<Modal open={openDelete} onClose={() => setOpenDelete(false)}>
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
padding: "30px",
|
||||
borderRadius: "10px",
|
||||
background: "#FFFFFF",
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" sx={{ textAlign: "center" }}>
|
||||
Вы уверены, что хотите удалить этот результат?
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
marginTop: "30px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
gap: "15px",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
sx={{ minWidth: "150px" }}
|
||||
onClick={() => setOpenDelete(false)}
|
||||
>
|
||||
Отмена
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
sx={{ minWidth: "150px" }}
|
||||
onClick={() => {
|
||||
deleteResult(Number(idResult));
|
||||
setOpenDelete(false);
|
||||
}}
|
||||
>
|
||||
Подтвердить
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Modal>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{isOpen && (
|
||||
<Box
|
||||
sx={{
|
||||
borderRadius: "12px",
|
||||
maxWidth: isTablet ? "450px" : "auto",
|
||||
width: "100%",
|
||||
boxShadow:
|
||||
"0px 2.767px 8.551px 0px rgba(210, 208, 225, 0.07), 0px 6.65px 20.549px 0px rgba(210, 208, 225, 0.10), 0px 12.522px 38.692px 0px rgba(210, 208, 225, 0.12), 0px 22.336px 69.019px 0px rgba(210, 208, 225, 0.14), 0px 41.778px 129.093px 0px rgba(210, 208, 225, 0.17), 0px 100px 309px 0px rgba(210, 208, 225, 0.24)",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
p: "20px",
|
||||
borderTop: "1px solid #F1F2F6",
|
||||
background: "#FFF",
|
||||
borderRadius: "0 0 12px 12px",
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ fontSize: "18px", color: "#9A9AAF" }}>
|
||||
Ответы
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: isTablet ? "0" : "149px",
|
||||
p: "20px",
|
||||
flexDirection: isTablet ? "column" : "-moz-initial",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "15px",
|
||||
mt: "20px",
|
||||
}}
|
||||
>
|
||||
{resultsAnswer.map((answer, id) => {
|
||||
console.log(answer.QuizId);
|
||||
|
||||
return (
|
||||
<Box
|
||||
sx={{ display: "flex", alignItems: "center", gap: "13px" }}
|
||||
>
|
||||
<Typography sx={{ fontSize: "18px", color: "#9A9AAF" }}>
|
||||
{id + 1}.
|
||||
</Typography>
|
||||
<Typography sx={{ fontSize: "18px" }}>
|
||||
{answer.content}
|
||||
</Typography>
|
||||
</Box>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
p: "20px",
|
||||
borderTop: "1px solid #F1F2F6",
|
||||
background: "#FFF",
|
||||
borderRadius: "0 0 12px 12px",
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ fontSize: "18px", color: "#9A9AAF" }}>
|
||||
Результаты
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "15px",
|
||||
p: "20px",
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ fontSize: "18px", fontWeight: "500" }}>
|
||||
Заголовок
|
||||
</Typography>
|
||||
<Typography sx={{ fontSize: "18px" }}>Текст</Typography>
|
||||
<img src={homeImg} style={{ width: "40px", height: "40px" }} />
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
55
src/pages/QuizAnswersPage/DeleteModal.tsx
Normal file
55
src/pages/QuizAnswersPage/DeleteModal.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
import { Box, Button, Modal, Typography } from "@mui/material";
|
||||
|
||||
interface Props {
|
||||
openDelete: boolean;
|
||||
setOpenDelete: (open: boolean) => void;
|
||||
}
|
||||
|
||||
export const DeleteModal = ({ openDelete, setOpenDelete }: Props) => {
|
||||
return (
|
||||
<Modal open={openDelete} onClose={() => setOpenDelete(false)}>
|
||||
<Box
|
||||
sx={{
|
||||
position: "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
padding: "30px",
|
||||
borderRadius: "10px",
|
||||
background: "#FFFFFF",
|
||||
}}
|
||||
>
|
||||
<Typography variant="h6" sx={{ textAlign: "center" }}>
|
||||
Вы уверены, что хотите удалить этот результат?
|
||||
</Typography>
|
||||
<Box
|
||||
sx={{
|
||||
marginTop: "30px",
|
||||
display: "flex",
|
||||
justifyContent: "center",
|
||||
gap: "15px",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
sx={{ minWidth: "150px" }}
|
||||
onClick={() => setOpenDelete(false)}
|
||||
>
|
||||
Отмена
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
sx={{ minWidth: "150px" }}
|
||||
onClick={() => {
|
||||
// deleteQuestionWithTimeout(question.id, () =>
|
||||
// DeleteFunction(questions, question, quiz),
|
||||
// );
|
||||
}}
|
||||
>
|
||||
Подтвердить
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
};
|
476
src/pages/QuizAnswersPage/QuizAnswersPage.tsx
Normal file
476
src/pages/QuizAnswersPage/QuizAnswersPage.tsx
Normal file
@ -0,0 +1,476 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
IconButton,
|
||||
Typography,
|
||||
Select as MuiSelect,
|
||||
MenuItem,
|
||||
useTheme,
|
||||
useMediaQuery,
|
||||
Skeleton,
|
||||
FormControl,
|
||||
Select,
|
||||
} from "@mui/material";
|
||||
import moment from "moment";
|
||||
import HeaderFull from "@ui_kit/Header/HeaderFull";
|
||||
import SectionWrapper from "@ui_kit/SectionWrapper";
|
||||
import { FC, useEffect, useState } from "react";
|
||||
import { FileExportIcon } from "./icons/FileExporIcon";
|
||||
import { UpdateIcon } from "./icons/UpdateIcon";
|
||||
// import { Select } from "../../pages/Questions/Select";
|
||||
import { CheckboxSelect } from "../../ui_kit/CheckboxSelect";
|
||||
import { CardAnswer } from "./CardAnswer";
|
||||
import { FilterIcon } from "./icons/FilterIcon";
|
||||
import { FilterModal } from "@ui_kit/Modal/FilterModal/FilterModal";
|
||||
import { ExportContactsModal } from "@ui_kit/Modal/ExportContactsModal";
|
||||
|
||||
import { resultApi } from "@api/result";
|
||||
import { useObsolescenceIdResult, useResultStore } from "@root/results/store";
|
||||
import { answerResultListExport, setResults } from "@root/results/actions";
|
||||
|
||||
import { quizApi } from "@api/quiz";
|
||||
import { setQuizes } from "@root/quizes/actions";
|
||||
import { useCurrentQuiz, useQuizes } from "@root/quizes/hooks";
|
||||
import { useQuizStore } from "@root/quizes/store";
|
||||
import { questionApi } from "@api/question";
|
||||
import { setQuestions } from "@root/questions/actions";
|
||||
import ArrowDown from "@icons/ArrowDownIcon";
|
||||
|
||||
const options = [
|
||||
{ label: "Муром (1)", value: "option1" },
|
||||
{ label: "Москва (1)", value: "option2" },
|
||||
];
|
||||
|
||||
const itemsTime = [
|
||||
"За все время",
|
||||
"Сегодня",
|
||||
"Вчера",
|
||||
"Последние 7 дней",
|
||||
"Последние 30 дней",
|
||||
"Этот месяц",
|
||||
];
|
||||
|
||||
// let lossDebouncer: null | ReturnType<typeof setTimeout> = null;
|
||||
// let lossId: string[] = [] as string[];
|
||||
//
|
||||
// const onLossNew = (id: string) => {
|
||||
// //Если в массиве ещё нет такого id - добавляем
|
||||
// if (!lossId.includes(id)) lossId.push(id);
|
||||
// //Если таймер есть - сбрасываем
|
||||
// if (typeof lossDebouncer === "number") clearTimeout(lossDebouncer);
|
||||
// //Назначем новый таймер
|
||||
// lossDebouncer = setTimeout(async () => {
|
||||
// //стреляем на лишение новизны
|
||||
// await obsolescenceResult(lossId);
|
||||
// //сбрасываем массив
|
||||
// lossId = [];
|
||||
// }, 3000);
|
||||
// };
|
||||
|
||||
const resetTime = (date: number) => {
|
||||
console.log(date);
|
||||
let a = Math.round(date / 86400) * 86400 - 97200;
|
||||
console.log(a);
|
||||
console.log(moment.unix(a).format("dddd, MMMM Do YYYY, h:mm:ss "));
|
||||
return moment.unix(a).format("dddd, MMMM Do YYYY, h:mm:ss ");
|
||||
};
|
||||
|
||||
export const QuizAnswersPage: FC = () => {
|
||||
const theme = useTheme();
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
|
||||
const [filterModalOpen, setFilterModalOpen] = useState<boolean>(false);
|
||||
const [exportContactsModalOpen, setExportContactsModalOpen] =
|
||||
useState<boolean>(false);
|
||||
const [filterNew, setFilterNew] = useState<string>("Все заявки");
|
||||
const [filterDate, setFilterDate] = useState<string>("За все время");
|
||||
|
||||
const quizList = useQuizStore();
|
||||
const quiz = useCurrentQuiz();
|
||||
const { editQuizId } = useQuizStore();
|
||||
const { results } = useResultStore();
|
||||
const { total_count } = useResultStore();
|
||||
// const {idResultArray, addIdResult, clearIdResultArray} = useObsolescenceIdResult()
|
||||
useEffect(() => {
|
||||
const getData = async () => {
|
||||
if (editQuizId !== null) {
|
||||
const quizes = await quizApi.getList();
|
||||
setQuizes(quizes);
|
||||
|
||||
const questions = await questionApi.getList({ quiz_id: editQuizId });
|
||||
setQuestions(questions);
|
||||
|
||||
const result = await resultApi.getList(editQuizId);
|
||||
setResults(result);
|
||||
}
|
||||
};
|
||||
getData();
|
||||
}, []);
|
||||
|
||||
const DateDefinition = (result: string) => {
|
||||
//определяем когда был получен результат - вчера, сегодня или число и месяц
|
||||
let restime = new Date(result);
|
||||
let timeCompleting = Date.parse(String(result));
|
||||
const timeNow = Date.now();
|
||||
let dayResult;
|
||||
if (timeNow - timeCompleting < 86400000) {
|
||||
dayResult = "Сегодня";
|
||||
}
|
||||
if (172800000 > timeNow - timeCompleting > 86400000) {
|
||||
dayResult = "Вчера";
|
||||
} else {
|
||||
dayResult = restime.toLocaleDateString();
|
||||
}
|
||||
return dayResult;
|
||||
};
|
||||
const TimeDefinition = (result: string) => {
|
||||
//достаём время
|
||||
let timeResult = new Date(result).toLocaleTimeString().slice(0, -3);
|
||||
return timeResult;
|
||||
};
|
||||
|
||||
if (quiz === undefined)
|
||||
return (
|
||||
<Skeleton sx={{ width: "100vw", height: "100vh", transform: "none" }} />
|
||||
);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<HeaderFull isRequest={true} />
|
||||
<SectionWrapper
|
||||
sx={{ padding: isMobile ? "0 16px" : "20px" }}
|
||||
maxWidth="lg"
|
||||
>
|
||||
<Typography
|
||||
sx={{ fontSize: "36px", fontWeight: "500", mb: "50px", mt: "60px" }}
|
||||
>
|
||||
{quiz.name}
|
||||
</Typography>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
width: "100%",
|
||||
display: isTablet ? "flex" : "-moz-initial",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||
<Typography sx={{ fontSize: "18px", color: "#4D4D4D" }}>
|
||||
Ответы на квиз
|
||||
</Typography>
|
||||
<Typography sx={{ fontSize: "18px", color: "#9A9AAF" }}>
|
||||
({total_count})
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
mt: "20px",
|
||||
mb: "31px",
|
||||
gap: "30px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
width: isTablet ? "auto" : "100%",
|
||||
}}
|
||||
>
|
||||
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
|
||||
<IconButton
|
||||
onClick={() => {
|
||||
// answerResultListExport(editQuizId)
|
||||
setExportContactsModalOpen(true);
|
||||
}}
|
||||
sx={{
|
||||
width: "44px",
|
||||
height: "44px",
|
||||
borderRadius: "8px",
|
||||
border: "1px solid #7E2AEA",
|
||||
}}
|
||||
>
|
||||
<FileExportIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
sx={{
|
||||
width: "44px",
|
||||
height: "44px",
|
||||
borderRadius: "8px",
|
||||
border: "1px solid #7E2AEA",
|
||||
}}
|
||||
onClick={async () => {
|
||||
const result = await resultApi.getList(editQuizId);
|
||||
console.log(result);
|
||||
setResults(result);
|
||||
}}
|
||||
>
|
||||
<UpdateIcon />
|
||||
</IconButton>
|
||||
|
||||
{isTablet && (
|
||||
<IconButton
|
||||
onClick={() => setFilterModalOpen(true)}
|
||||
sx={{
|
||||
background: "#fff",
|
||||
width: "44px",
|
||||
height: "44px",
|
||||
borderRadius: "8px",
|
||||
border: "1px solid #7E2AEA",
|
||||
}}
|
||||
>
|
||||
<FilterIcon />
|
||||
</IconButton>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{!isTablet && (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "20px",
|
||||
width: "100%",
|
||||
justifyContent: "flex-end",
|
||||
}}
|
||||
>
|
||||
<FormControl
|
||||
fullWidth
|
||||
sx={{ maxWidth: "223px", width: "100%" }}
|
||||
>
|
||||
<Select
|
||||
id="display-select"
|
||||
variant="outlined"
|
||||
value={filterDate}
|
||||
onChange={(event) => {
|
||||
setFilterDate(event.target.value as string);
|
||||
}}
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "48px",
|
||||
borderRadius: "8px",
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
border: `1px solid #7E2AEA !important`,
|
||||
borderRadius: "10px",
|
||||
},
|
||||
}}
|
||||
MenuProps={{
|
||||
PaperProps: {
|
||||
sx: {
|
||||
mt: "8px",
|
||||
p: "4px",
|
||||
borderRadius: "8px",
|
||||
backgroundColor: theme.palette.background.default,
|
||||
border: "1px solid #EEE4FC",
|
||||
boxShadow: "0px 8px 24px rgba(210, 208, 225, 0.4)",
|
||||
},
|
||||
},
|
||||
MenuListProps: {
|
||||
sx: {
|
||||
py: 0,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "8px",
|
||||
"& .Mui-selected": {
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: "#7E2AEA",
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
inputProps={{
|
||||
sx: {
|
||||
color: "#7E2AEA",
|
||||
display: "block",
|
||||
px: "9px",
|
||||
gap: "20px",
|
||||
width: "90%",
|
||||
},
|
||||
}}
|
||||
IconComponent={(props) => <ArrowDown {...props} />}
|
||||
>
|
||||
{itemsTime.map((item, index) => (
|
||||
<MenuItem
|
||||
key={item + index}
|
||||
value={item}
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "20px",
|
||||
padding: "10px",
|
||||
borderRadius: "5px",
|
||||
color: "#9A9AAF",
|
||||
whiteSpace: "normal",
|
||||
}}
|
||||
>
|
||||
{item}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormControl
|
||||
fullWidth
|
||||
sx={{ maxWidth: "223px", width: "100%" }}
|
||||
>
|
||||
<Select
|
||||
id="display-select"
|
||||
variant="outlined"
|
||||
value={filterNew}
|
||||
onChange={(event) => {
|
||||
setFilterNew(event.target.value as string);
|
||||
}}
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "48px",
|
||||
borderRadius: "8px",
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
border: `1px solid #7E2AEA !important`,
|
||||
borderRadius: "10px",
|
||||
},
|
||||
}}
|
||||
MenuProps={{
|
||||
PaperProps: {
|
||||
sx: {
|
||||
mt: "8px",
|
||||
p: "4px",
|
||||
borderRadius: "8px",
|
||||
backgroundColor: theme.palette.background.default,
|
||||
border: "1px solid #EEE4FC",
|
||||
boxShadow: "0px 8px 24px rgba(210, 208, 225, 0.4)",
|
||||
},
|
||||
},
|
||||
MenuListProps: {
|
||||
sx: {
|
||||
py: 0,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "8px",
|
||||
"& .Mui-selected": {
|
||||
backgroundColor: theme.palette.background.default,
|
||||
color: "#7E2AEA",
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
inputProps={{
|
||||
sx: {
|
||||
color: "#7E2AEA",
|
||||
display: "block",
|
||||
px: "9px",
|
||||
gap: "20px",
|
||||
width: "90%",
|
||||
},
|
||||
}}
|
||||
IconComponent={(props) => <ArrowDown {...props} />}
|
||||
>
|
||||
{["Все заявки", "Новые"].map((item, index) => (
|
||||
<MenuItem
|
||||
key={item + index}
|
||||
value={item}
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "20px",
|
||||
padding: "10px",
|
||||
borderRadius: "5px",
|
||||
color: "#9A9AAF",
|
||||
whiteSpace: "normal",
|
||||
}}
|
||||
>
|
||||
{item}
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
{/* <CheckboxSelect
|
||||
sx={{ maxWidth: "223px", width: "100%" }}
|
||||
placeholder="Выберите город"
|
||||
options={options}
|
||||
/> */}
|
||||
|
||||
<Button
|
||||
onClick={() => {
|
||||
setFilterNew("Все заявки");
|
||||
setFilterDate("За все время");
|
||||
}}
|
||||
sx={{
|
||||
maxWidth: "200px",
|
||||
width: "100%",
|
||||
height: "48px",
|
||||
borderRadius: "8px",
|
||||
border: "1px solid #7E2AEA",
|
||||
color: "#7E2AEA",
|
||||
}}
|
||||
>
|
||||
Сбросить фильтры
|
||||
</Button>
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{!isTablet && (
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
width: "100%",
|
||||
alignItems: "center",
|
||||
mb: "15px",
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ fontSize: "18px", color: "#9A9AAF", mr: "40px" }}>
|
||||
№ заявки
|
||||
</Typography>
|
||||
<Typography sx={{ fontSize: "18px", color: "#9A9AAF" }}>
|
||||
Дата
|
||||
</Typography>
|
||||
<Typography
|
||||
sx={{ fontSize: "18px", color: "#9A9AAF", ml: "288px" }}
|
||||
>
|
||||
Контакты
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "flex-start",
|
||||
flexDirection: isTablet ? "-moz-initial" : "column",
|
||||
flexWrap: isTablet ? "wrap" : "nowrap",
|
||||
gap: "20px",
|
||||
}}
|
||||
>
|
||||
{results.map((result) => {
|
||||
const dataResult = new Date(result.created_at);
|
||||
let dayResult = DateDefinition(result.created_at);
|
||||
let timeResult = TimeDefinition(result.created_at);
|
||||
return (
|
||||
<CardAnswer
|
||||
name={result.content.name}
|
||||
email={result.content.email}
|
||||
isNew={result.new}
|
||||
idResult={result.id}
|
||||
dayResult={dayResult}
|
||||
timeResult={timeResult}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</Box>
|
||||
</SectionWrapper>
|
||||
|
||||
<FilterModal
|
||||
open={filterModalOpen}
|
||||
handleClose={() => setFilterModalOpen(false)}
|
||||
filterNew={filterNew}
|
||||
filterDate={filterDate}
|
||||
setFilterNew={setFilterNew}
|
||||
setFilterDate={setFilterDate}
|
||||
/>
|
||||
<ExportContactsModal
|
||||
editQuizId={editQuizId}
|
||||
open={exportContactsModalOpen}
|
||||
handleClose={() => setExportContactsModalOpen(false)}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
};
|
23
src/pages/QuizAnswersPage/icons/ContactIcon.tsx
Normal file
23
src/pages/QuizAnswersPage/icons/ContactIcon.tsx
Normal file
@ -0,0 +1,23 @@
|
||||
export const ContactIcon = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
>
|
||||
<g opacity="0.5">
|
||||
<circle
|
||||
cx="12.0166"
|
||||
cy="7.89626"
|
||||
r="3.79782"
|
||||
fill="#9A9AAF"
|
||||
stroke="#9A9AAF"
|
||||
/>
|
||||
<path
|
||||
d="M18.1455 13.8094H5.85784C4.81766 13.8094 3.95773 14.6326 4.30614 15.6127C5.09317 17.8266 7.48227 20.4969 12.002 20.4969C16.5224 20.4969 18.9107 17.8266 19.6974 15.6127C20.0456 14.6326 19.1857 13.8094 18.1455 13.8094Z"
|
||||
fill="#9A9AAF"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
16
src/pages/QuizAnswersPage/icons/EyeIcon.tsx
Normal file
16
src/pages/QuizAnswersPage/icons/EyeIcon.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
export const EyeIcon = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="30"
|
||||
height="30"
|
||||
viewBox="0 0 30 30"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M3.26743 16.0459C2.91085 15.3939 2.91086 14.6059 3.26744 13.9539C5.53334 9.8107 9.93781 7 15 7C20.0622 7 24.4667 9.81079 26.7326 13.9541C27.0891 14.6061 27.0891 15.3941 26.7326 16.0461C24.4667 20.1893 20.0622 23 15 23C9.93782 23 5.53329 20.1892 3.26743 16.0459Z"
|
||||
stroke="#4D4D4D"
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
<circle cx="15" cy="15" r="4" stroke="#4D4D4D" stroke-width="1.5" />
|
||||
</svg>
|
||||
);
|
24
src/pages/QuizAnswersPage/icons/FileExporIcon.tsx
Normal file
24
src/pages/QuizAnswersPage/icons/FileExporIcon.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
export const FileExportIcon = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M19 11.0263V8.18421M9.625 20.5H4V2.5H13.375M13.375 2.5H14.3125L19 7.23684V8.18421M13.375 2.5V8.18421H19"
|
||||
stroke="#7E2AEA"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M16.5 14.5L16.5 22.5M16.5 22.5L20 18.8871M16.5 22.5L13 18.8871"
|
||||
stroke="#7E2AEA"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
15
src/pages/QuizAnswersPage/icons/FilterIcon.tsx
Normal file
15
src/pages/QuizAnswersPage/icons/FilterIcon.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
export const FilterIcon = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M2 4C2 2.89543 2.89543 2 4 2H20C21.1046 2 22 2.89543 22 4V4.81751C22 5.57739 21.7116 6.30895 21.1932 6.86447L15.5379 12.9237C15.1922 13.294 15 13.7817 15 14.2883V18.382C15 18.7607 14.786 19.107 14.4472 19.2764L10.4472 21.2764C9.78231 21.6088 9 21.1253 9 20.382V14.2883C9 13.7817 8.80776 13.294 8.46211 12.9237L2.80683 6.86446C2.28836 6.30895 2 5.57739 2 4.81751V4Z"
|
||||
stroke="#7E2AEA"
|
||||
stroke-width="1.5"
|
||||
/>
|
||||
</svg>
|
||||
);
|
27
src/pages/QuizAnswersPage/icons/MessageIcon.tsx
Normal file
27
src/pages/QuizAnswersPage/icons/MessageIcon.tsx
Normal file
@ -0,0 +1,27 @@
|
||||
export const MessageIcon = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
>
|
||||
<g opacity="0.5">
|
||||
<rect
|
||||
x="2.5"
|
||||
y="4.30312"
|
||||
width="19"
|
||||
height="15.2"
|
||||
rx="5"
|
||||
fill="#9A9AAF"
|
||||
/>
|
||||
<path
|
||||
d="M6.29688 9.05312L10.7969 12.4281C11.508 12.9615 12.4858 12.9615 13.1969 12.4281L17.6969 9.05312"
|
||||
stroke="white"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
16
src/pages/QuizAnswersPage/icons/PhoneIcon.tsx
Normal file
16
src/pages/QuizAnswersPage/icons/PhoneIcon.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
export const PhoneIcon = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
>
|
||||
<g opacity="0.5">
|
||||
<path
|
||||
d="M19.0621 20.2183C17.1077 22.1726 12.1028 20.3363 7.88327 16.1167C3.66372 11.8972 1.82739 6.89227 3.7817 4.93795L5.06847 3.65118C5.9568 2.76286 7.42054 2.78634 8.33784 3.70363L10.3309 5.69672C11.2482 6.61401 11.2717 8.07776 10.3834 8.96609L10.107 9.24247C9.62737 9.72209 9.58045 10.4958 10.0261 11.0359C10.456 11.5568 10.9194 12.0756 11.4219 12.5781C11.9244 13.0806 12.4432 13.544 12.9641 13.9739C13.5042 14.4196 14.2779 14.3726 14.7575 13.893L15.0339 13.6166C15.9222 12.7283 17.386 12.7518 18.3033 13.6691L20.2964 15.6622C21.2137 16.5795 21.2371 18.0432 20.3488 18.9315L19.0621 20.2183Z"
|
||||
fill="#9A9AAF"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
);
|
16
src/pages/QuizAnswersPage/icons/UpdateIcon.tsx
Normal file
16
src/pages/QuizAnswersPage/icons/UpdateIcon.tsx
Normal file
@ -0,0 +1,16 @@
|
||||
export const UpdateIcon = () => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M5.07435 8.00009C5.81204 6.72238 6.8887 5.67355 8.18528 4.96956C9.48187 4.26557 10.9479 3.93384 12.4212 4.01106C13.8946 4.08827 15.3179 4.57142 16.5338 5.40708C17.7497 6.24275 18.7108 7.39838 19.3109 8.7462M3.23438 5.51546L3.96379 8.67499C4.08802 9.21312 4.62497 9.54866 5.1631 9.42442L8.32264 8.69501M18.9308 16C18.1931 17.2777 17.1165 18.3265 15.8199 19.0305C14.5233 19.7345 13.0573 20.0662 11.5839 19.989C10.1105 19.9118 8.68724 19.4287 7.47134 18.593C6.25545 17.7573 5.29432 16.6017 4.69423 15.2539M20.7708 18.4846L20.0414 15.3251C19.9171 14.787 19.3802 14.4514 18.842 14.5757L15.6825 15.3051"
|
||||
stroke="#7E2AEA"
|
||||
stroke-width="1.5"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
</svg>
|
||||
);
|
BIN
src/pages/QuizAnswersPage/images/home.png
Normal file
BIN
src/pages/QuizAnswersPage/images/home.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 42 KiB |
BIN
src/pages/QuizAnswersPage/images/videoFrame.png
Normal file
BIN
src/pages/QuizAnswersPage/images/videoFrame.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 64 KiB |
@ -34,7 +34,7 @@ export default function MyQuizzesFull({
|
||||
|
||||
return (
|
||||
<>
|
||||
<HeaderFull />
|
||||
<HeaderFull isRequest={false} />
|
||||
{quizes.length === 0 ? (
|
||||
<FirstQuiz />
|
||||
) : (
|
||||
|
@ -157,14 +157,18 @@ export default function QuizCard({
|
||||
gap: isMobile ? "10px" : "20px",
|
||||
}}
|
||||
>
|
||||
{/* <Button
|
||||
<Button
|
||||
variant="contained"
|
||||
sx={{
|
||||
padding: "10px 39px",
|
||||
}}
|
||||
onClick={() => {
|
||||
navigate(`/results/${quiz.backendId}`);
|
||||
setEditQuizId(quiz.backendId);
|
||||
}}
|
||||
>
|
||||
Заявки
|
||||
</Button> */}
|
||||
</Button>
|
||||
<Button
|
||||
variant="outlined"
|
||||
startIcon={<PencilIcon />}
|
||||
|
119
src/stores/results/actions.ts
Normal file
119
src/stores/results/actions.ts
Normal file
@ -0,0 +1,119 @@
|
||||
import { produce } from "immer";
|
||||
import { ResultStore, useResultStore } from "@root/results/store";
|
||||
import { RawResult, rawResultToResult } from "@model/result/result";
|
||||
import { RequestQueue } from "@utils/requestQueue";
|
||||
import { resultApi } from "@api/result";
|
||||
import { devlog, getMessageFromFetchError } from "@frontend/kitui";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { useQuizStore } from "@root/quizes/store";
|
||||
|
||||
const REQUEST_DEBOUNCE = 200;
|
||||
const requestQueue = new RequestQueue();
|
||||
let requestTimeoutId: ReturnType<typeof setTimeout>;
|
||||
|
||||
export const setResults = (results: RawResult[] | null) =>
|
||||
setProducedState(
|
||||
(state) => {
|
||||
state.results = Object.values(results)[1]?.map(rawResultToResult) ?? [];
|
||||
state.total_count = Object.values(results)[0];
|
||||
},
|
||||
{
|
||||
type: "setResults",
|
||||
results,
|
||||
// total_count
|
||||
},
|
||||
);
|
||||
|
||||
const removeResult = (resultId: string) =>
|
||||
setProducedState((state) => {
|
||||
const index = state.results.findIndex((r) => r.id === resultId);
|
||||
if (index === -1) return;
|
||||
|
||||
state.results.splice(index, 1);
|
||||
});
|
||||
|
||||
export const deleteResult = async (resultId: number) =>
|
||||
requestQueue.enqueue(async () => {
|
||||
const result = useResultStore
|
||||
.getState()
|
||||
.results.find((r) => r.id === resultId);
|
||||
if (!result) return;
|
||||
|
||||
try {
|
||||
await resultApi.delete(Number(result.id));
|
||||
removeResult(resultId);
|
||||
} catch (error) {
|
||||
devlog("Error delete result", error);
|
||||
|
||||
const message = getMessageFromFetchError(error) ?? "";
|
||||
enqueueSnackbar(`Не удалось удалить результат. ${message}`);
|
||||
}
|
||||
});
|
||||
|
||||
export const obsolescenceResult = async (
|
||||
resultId: string,
|
||||
editQuizId: number,
|
||||
) =>
|
||||
requestQueue.enqueue(async () => {
|
||||
const result = useResultStore
|
||||
.getState()
|
||||
.results.find((r) => r.id === resultId);
|
||||
if (!result) return;
|
||||
if (result.new === false) return;
|
||||
let lossDebouncer: null | ReturnType<typeof setTimeout> = null;
|
||||
let lossId: string[] = [] as string[];
|
||||
if (!lossId.includes(resultId)) lossId.push(resultId);
|
||||
if (typeof lossDebouncer === "number") clearTimeout(lossDebouncer);
|
||||
lossDebouncer = setTimeout(async () => {
|
||||
//стреляем на лишение новизны
|
||||
try {
|
||||
await resultApi.obsolescence(lossId);
|
||||
//сбрасываем массив
|
||||
lossId = [];
|
||||
} catch (error) {
|
||||
devlog("Error", error);
|
||||
|
||||
const message = getMessageFromFetchError(error) ?? "";
|
||||
enqueueSnackbar(`Ошибка. ${message}`);
|
||||
}
|
||||
}, 3000);
|
||||
const resultList = await resultApi.getList(editQuizId);
|
||||
setResults(resultList);
|
||||
});
|
||||
|
||||
export const answerResultListExport = async (
|
||||
editQuizId: number,
|
||||
toDate: string,
|
||||
fromDate: string,
|
||||
) => {
|
||||
const resAnswer = await resultApi.export(editQuizId, toDate, fromDate);
|
||||
const download = function () {
|
||||
// Creating a Blob for having a csv file format
|
||||
// and passing the data with type
|
||||
const blob = new Blob([resAnswer], { type: "application/json" });
|
||||
|
||||
// Creating an object for downloading url
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
|
||||
// Creating an anchor(a) tag of HTML
|
||||
const a = document.createElement("a");
|
||||
|
||||
// Passing the blob downloading url
|
||||
a.setAttribute("href", url);
|
||||
|
||||
// Setting the anchor tag attribute for downloading
|
||||
// and passing the download file name
|
||||
a.setAttribute("download", "download.csv");
|
||||
|
||||
// Performing a download with click
|
||||
a.click();
|
||||
};
|
||||
download();
|
||||
};
|
||||
|
||||
function setProducedState<A extends string | { type: unknown }>(
|
||||
recipe: (state: ResultStore) => void,
|
||||
action?: A,
|
||||
) {
|
||||
useResultStore.setState((state) => produce(state, recipe), false, action);
|
||||
}
|
34
src/stores/results/hooks.ts
Normal file
34
src/stores/results/hooks.ts
Normal file
@ -0,0 +1,34 @@
|
||||
import useSWR from "swr";
|
||||
import { isAxiosError } from "axios";
|
||||
import { devlog } from "@frontend/kitui";
|
||||
import { enqueueSnackbar } from "notistack";
|
||||
import { resultApi } from "@api/result";
|
||||
import { useResultStore } from "@root/results/store";
|
||||
|
||||
export function useResults(quizId: string) {
|
||||
const { isLoading, error, isValidating } = useSWR(
|
||||
"results",
|
||||
() => resultApi.getList(quizId),
|
||||
{
|
||||
// onSuccess: setResults,
|
||||
onError: (error: unknown) => {
|
||||
const message = isAxiosError<string>(error)
|
||||
? error.response?.data ?? ""
|
||||
: "";
|
||||
|
||||
devlog("Error getting results list", error);
|
||||
enqueueSnackbar("Не удалось получить результаты");
|
||||
},
|
||||
},
|
||||
);
|
||||
const results = useResultStore((state) => state.results);
|
||||
|
||||
return { results, isLoading, error, isValidating };
|
||||
}
|
||||
|
||||
// export function useAnswerList() {
|
||||
// const { isLoading, error, isValidating } = useSWR(
|
||||
// "answers",
|
||||
// () => resultApi.getAnswerList()
|
||||
// )
|
||||
// }
|
58
src/stores/results/store.ts
Normal file
58
src/stores/results/store.ts
Normal file
@ -0,0 +1,58 @@
|
||||
import { create } from "zustand";
|
||||
import { devtools, persist } from "zustand/middleware";
|
||||
|
||||
export type ResultStore = {
|
||||
results: [
|
||||
{
|
||||
results: {
|
||||
content: string;
|
||||
id: number;
|
||||
new: boolean;
|
||||
created_at: string;
|
||||
};
|
||||
},
|
||||
];
|
||||
total_count: number;
|
||||
};
|
||||
|
||||
const initialState: ResultStore = {
|
||||
results: [],
|
||||
total_count: 0,
|
||||
};
|
||||
|
||||
export const useResultStore = create<ResultStore>()(
|
||||
persist(
|
||||
devtools(() => initialState, {
|
||||
name: "ResultStore",
|
||||
enabled: process.env.NODE_ENV === "development",
|
||||
trace: process.env.NODE_ENV === "development",
|
||||
}),
|
||||
{
|
||||
name: "ResultStore",
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
//сохранение id результата в массив для отправки на сервер и снятия пометки new с карточки
|
||||
export const useObsolescenceIdResult = create()(
|
||||
persist(
|
||||
(set, get) => ({
|
||||
idResultArray: [],
|
||||
addIdResult: (idResult: number) => {
|
||||
const state = get()["idResultArray"] || [];
|
||||
console.log(state);
|
||||
const newState = [...state, idResult];
|
||||
set({ idResultArray: newState });
|
||||
},
|
||||
clearIdResultArray: () => {
|
||||
const state = get()["idResultArray"] || {};
|
||||
|
||||
const newState = [];
|
||||
set({ idResultArray: newState });
|
||||
},
|
||||
}),
|
||||
{
|
||||
name: "idResultArr",
|
||||
},
|
||||
),
|
||||
);
|
128
src/ui_kit/CheckboxSelect.tsx
Normal file
128
src/ui_kit/CheckboxSelect.tsx
Normal file
@ -0,0 +1,128 @@
|
||||
import React, { useState } from "react";
|
||||
import {
|
||||
FormControl,
|
||||
MenuItem,
|
||||
Select,
|
||||
SelectChangeEvent,
|
||||
} from "@mui/material";
|
||||
import ArrowDown from "@icons/ArrowDownIcon";
|
||||
import CustomCheckbox from "./CustomCheckbox";
|
||||
|
||||
type CheckboxSelectProps = {
|
||||
options: { label: string; value: string }[];
|
||||
sx?: any;
|
||||
placeholder: string;
|
||||
};
|
||||
|
||||
export const CheckboxSelect: React.FC<CheckboxSelectProps> = ({
|
||||
options,
|
||||
sx,
|
||||
placeholder,
|
||||
}: CheckboxSelectProps) => {
|
||||
const [selectedValues, setSelectedValues] = useState<string[]>([]);
|
||||
|
||||
const handleChange = (event: SelectChangeEvent<string[]>) => {
|
||||
const values = event.target.value as string[];
|
||||
|
||||
if (values.includes("SelectAll")) {
|
||||
setSelectedValues(options.map((option) => option.value));
|
||||
} else {
|
||||
setSelectedValues(values);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<FormControl
|
||||
fullWidth
|
||||
size="small"
|
||||
sx={{ width: "100%", height: "48px", ...sx }}
|
||||
>
|
||||
<Select
|
||||
displayEmpty
|
||||
multiple
|
||||
value={selectedValues}
|
||||
onChange={handleChange}
|
||||
sx={{
|
||||
width: "100%",
|
||||
height: "48px",
|
||||
borderRadius: "8px",
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
border: "1px solid #7E2AEA !important",
|
||||
borderRadius: "10px",
|
||||
},
|
||||
}}
|
||||
MenuProps={{
|
||||
PaperProps: {
|
||||
sx: {
|
||||
mt: "8px",
|
||||
p: "4px",
|
||||
borderRadius: "8px",
|
||||
backgroundColor: "#fff",
|
||||
border: "1px solid #EEE4FC",
|
||||
boxShadow: "0px 8px 24px rgba(210, 208, 225, 0.4)",
|
||||
},
|
||||
},
|
||||
MenuListProps: {
|
||||
sx: {
|
||||
py: 0,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "8px",
|
||||
"& .Mui-selected": {
|
||||
backgroundColor: "#fff",
|
||||
color: "#7E2AEA",
|
||||
},
|
||||
},
|
||||
},
|
||||
}}
|
||||
inputProps={{
|
||||
sx: {
|
||||
color: "#7E2AEA",
|
||||
display: "block",
|
||||
px: "9px",
|
||||
gap: "20px",
|
||||
width: "90%",
|
||||
},
|
||||
}}
|
||||
IconComponent={(props) => <ArrowDown {...props} />}
|
||||
renderValue={(selected) => {
|
||||
if (selected.length === 0) {
|
||||
return <span style={{ color: "#7E2AEA" }}>{placeholder}</span>;
|
||||
}
|
||||
|
||||
return (
|
||||
<span>
|
||||
{options
|
||||
.filter((option) => selected.includes(option.value))
|
||||
.map((option) => option.label)
|
||||
.join(", ")}
|
||||
</span>
|
||||
);
|
||||
}}
|
||||
>
|
||||
<MenuItem
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
}}
|
||||
value="SelectAll"
|
||||
>
|
||||
<CustomCheckbox
|
||||
checked={selectedValues.includes("SelectAll")}
|
||||
label="Выбрать всё"
|
||||
/>
|
||||
</MenuItem>
|
||||
{options.map((option) => (
|
||||
<MenuItem key={option.value} value={option.value}>
|
||||
<CustomCheckbox
|
||||
sx={{ padding: "0", borderRadius: "6px" }}
|
||||
label={option.label}
|
||||
checked={selectedValues.includes(option.value)}
|
||||
/>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
@ -18,8 +18,9 @@ import { enqueueSnackbar } from "notistack";
|
||||
import { clearUserData } from "@root/user";
|
||||
import { LogoutButton } from "@ui_kit/LogoutButton";
|
||||
import { ToTariffsButton } from "@ui_kit/Toolbars/ToTariffsButton";
|
||||
import ArrowLeft from "@icons/questionsPage/arrowLeft";
|
||||
|
||||
export default function HeaderFull() {
|
||||
export default function HeaderFull({ isRequest }: boolean) {
|
||||
const theme = useTheme();
|
||||
const navigate = useNavigate();
|
||||
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
|
||||
@ -57,6 +58,11 @@ export default function HeaderFull() {
|
||||
<Link to="/">
|
||||
<Logotip width={124} />
|
||||
</Link>
|
||||
{isRequest && (
|
||||
<IconButton onClick={() => navigate("/list")}>
|
||||
<ArrowLeft color="black" />
|
||||
</IconButton>
|
||||
)}
|
||||
<Box sx={{ display: "flex", ml: "auto" }}>
|
||||
<ToTariffsButton />
|
||||
<LogoutButton
|
||||
|
210
src/ui_kit/Modal/ExportContactsModal.tsx
Normal file
210
src/ui_kit/Modal/ExportContactsModal.tsx
Normal file
@ -0,0 +1,210 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Modal,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import { FC, useState } from "react";
|
||||
import { DatePicker } from "@mui/x-date-pickers";
|
||||
import CustomCheckbox from "@ui_kit/CustomCheckbox";
|
||||
import CalendarIcon from "@icons/CalendarIcon";
|
||||
import moment from "moment";
|
||||
import { answerResultListExport } from "@root/results/actions";
|
||||
|
||||
interface Iprops {
|
||||
open: boolean;
|
||||
editQuizId: string;
|
||||
handleClose: () => void;
|
||||
}
|
||||
|
||||
export const ExportContactsModal: FC<Iprops> = ({
|
||||
open,
|
||||
handleClose,
|
||||
editQuizId,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||
const [fromDate, setFromDate] = useState("");
|
||||
const [toDate, setToDate] = useState("");
|
||||
console.log("from", fromDate);
|
||||
console.log("to", toDate);
|
||||
|
||||
const style = {
|
||||
position: "absolute" as "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
width: "100%",
|
||||
maxWidth: isMobile ? 343 : 620,
|
||||
bgcolor: "background.paper",
|
||||
boxShadow: 24,
|
||||
borderRadius: "12px",
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
aria-labelledby="modal-modal-title"
|
||||
aria-describedby="modal-modal-description"
|
||||
>
|
||||
<Box sx={style}>
|
||||
<Box
|
||||
sx={{
|
||||
background: "#F2F3F7",
|
||||
p: "25px 20px",
|
||||
borderTopLeftRadius: "12px",
|
||||
borderTopRightRadius: "12px",
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ fontSize: "18px", color: "#9A9AAF" }}>
|
||||
Настройте фильтры отображения
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
mt: "10px",
|
||||
display: "flex",
|
||||
flexDirection: isMobile ? "column" : "-moz-initial",
|
||||
alignItems: "center",
|
||||
gap: isMobile ? "8px" : "18px",
|
||||
p: "0 20px 20px",
|
||||
}}
|
||||
>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: isMobile ? "6px" : "18px",
|
||||
}}
|
||||
>
|
||||
<Typography>c</Typography>
|
||||
<DatePicker
|
||||
onChange={(date) => {
|
||||
setFromDate(date);
|
||||
}}
|
||||
slots={{
|
||||
openPickerIcon: () => <CalendarIcon />,
|
||||
}}
|
||||
slotProps={{
|
||||
openPickerButton: {
|
||||
sx: {
|
||||
p: 0,
|
||||
},
|
||||
"data-cy": "open-datepicker",
|
||||
},
|
||||
}}
|
||||
sx={{
|
||||
p: "10px",
|
||||
"& .MuiInputBase-root": {
|
||||
maxWidth: "250px",
|
||||
width: "100%",
|
||||
backgroundColor: "#F2F3F7",
|
||||
borderRadius: "10px",
|
||||
pr: "31px",
|
||||
"& input": {
|
||||
py: "11px",
|
||||
pl: "13px",
|
||||
lineHeight: "19px",
|
||||
},
|
||||
"& fieldset": {
|
||||
borderColor: "#9A9AAF",
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: isMobile ? "6px" : "18px",
|
||||
}}
|
||||
>
|
||||
<Typography>до</Typography>
|
||||
|
||||
<DatePicker
|
||||
onChange={(date) => {
|
||||
setToDate(date);
|
||||
}}
|
||||
slots={{
|
||||
openPickerIcon: () => <CalendarIcon />,
|
||||
}}
|
||||
slotProps={{
|
||||
openPickerButton: {
|
||||
sx: {
|
||||
p: 0,
|
||||
},
|
||||
"data-cy": "open-datepicker",
|
||||
},
|
||||
}}
|
||||
sx={{
|
||||
p: "10px",
|
||||
"& .MuiInputBase-root": {
|
||||
maxWidth: "250px",
|
||||
width: "100%",
|
||||
backgroundColor: "#F2F3F7",
|
||||
borderRadius: "10px",
|
||||
pr: "31px",
|
||||
"& input": {
|
||||
py: "11px",
|
||||
pl: "13px",
|
||||
lineHeight: "19px",
|
||||
},
|
||||
"& fieldset": {
|
||||
borderColor: "#9A9AAF",
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
<Box sx={{ p: "0 20px 20px" }}>
|
||||
{/*<Box sx={{ display: "flex", flexDirection: "column", gap: "14px" }}>*/}
|
||||
{/* <CustomCheckbox label="Ответы в отдельных полях" />*/}
|
||||
{/* <CustomCheckbox label="Cookies в отдельных полях" />*/}
|
||||
{/* <CustomCheckbox label="Только верифицированные номера" />*/}
|
||||
{/*</Box>*/}
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
width: "100%",
|
||||
justifyContent: "flex-end",
|
||||
mt: "40px",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
sx={{
|
||||
width: isMobile ? "100%" : "130px",
|
||||
border: "1px solid #9A9AAF",
|
||||
}}
|
||||
onClick={handleClose}
|
||||
variant="outlined"
|
||||
>
|
||||
Отмена
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
onClick={() =>
|
||||
answerResultListExport(editQuizId, toDate, fromDate)
|
||||
}
|
||||
sx={{
|
||||
width: isMobile ? "100%" : "130px",
|
||||
height: "48px",
|
||||
fontSize: "18px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
Готово
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
};
|
163
src/ui_kit/Modal/FilterModal/FilterModal.tsx
Normal file
163
src/ui_kit/Modal/FilterModal/FilterModal.tsx
Normal file
@ -0,0 +1,163 @@
|
||||
import {
|
||||
Box,
|
||||
Button,
|
||||
Modal,
|
||||
Typography,
|
||||
useMediaQuery,
|
||||
useTheme,
|
||||
} from "@mui/material";
|
||||
import { CheckboxSelect } from "@ui_kit/CheckboxSelect";
|
||||
import { Select } from "../../../pages/Questions/Select";
|
||||
import { FC } from "react";
|
||||
import { ClockWiseIcon } from "@icons/ClockWiseIcon";
|
||||
|
||||
const options = [
|
||||
{ label: "Муром (1)", value: "option1" },
|
||||
{ label: "Москва (1)", value: "option2" },
|
||||
];
|
||||
|
||||
interface Iprops {
|
||||
open: boolean;
|
||||
handleClose: () => void;
|
||||
filterNew: boolean;
|
||||
filterDate: string;
|
||||
setFilterNew: (a: string) => void;
|
||||
setFilterDate: (a: string) => void;
|
||||
}
|
||||
|
||||
export const FilterModal: FC<Iprops> = ({
|
||||
open,
|
||||
handleClose,
|
||||
filterNew,
|
||||
filterDate,
|
||||
setFilterNew,
|
||||
setFilterDate,
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(650));
|
||||
|
||||
const style = {
|
||||
position: "absolute" as "absolute",
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
transform: "translate(-50%, -50%)",
|
||||
width: "100%",
|
||||
maxWidth: isMobile ? 343 : 620,
|
||||
bgcolor: "background.paper",
|
||||
boxShadow: 24,
|
||||
borderRadius: "12px",
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
open={open}
|
||||
onClose={handleClose}
|
||||
aria-labelledby="modal-modal-title"
|
||||
aria-describedby="modal-modal-description"
|
||||
>
|
||||
<Box sx={style}>
|
||||
<Box
|
||||
sx={{
|
||||
background: "#F2F3F7",
|
||||
p: "25px 20px",
|
||||
borderTopLeftRadius: "12px",
|
||||
borderTopRightRadius: "12px",
|
||||
}}
|
||||
>
|
||||
<Typography sx={{ fontSize: "18px", color: "#9A9AAF" }}>
|
||||
Настройте фильтры отображения
|
||||
</Typography>
|
||||
</Box>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "15px",
|
||||
mb: "35px",
|
||||
p: "20px",
|
||||
}}
|
||||
>
|
||||
<Select
|
||||
sx={{ width: "100%" }}
|
||||
items={[
|
||||
"За все время",
|
||||
"Сегодня",
|
||||
"Вчера",
|
||||
"Последние 7 дней",
|
||||
"Последние 30 дней",
|
||||
"Этот месяц",
|
||||
]}
|
||||
data={filterDate}
|
||||
setData={setFilterDate}
|
||||
/>
|
||||
<Select
|
||||
sx={{ width: "100%" }}
|
||||
items={["Все заявки", "Новые"]}
|
||||
data={filterNew}
|
||||
setData={setFilterNew}
|
||||
/>
|
||||
|
||||
{/* <CheckboxSelect
|
||||
sx={{ width: "100%" }}
|
||||
placeholder="Выберите город"
|
||||
options={options}
|
||||
/> */}
|
||||
</Box>
|
||||
|
||||
<Box sx={{ p: "0 20px 20px" }}>
|
||||
<Button
|
||||
sx={{
|
||||
width: "234px",
|
||||
height: "48px",
|
||||
gap: "6px",
|
||||
fontSize: "18px",
|
||||
color: "#7E2AEA",
|
||||
background: "#F2F3F7",
|
||||
borderRadius: "8px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
Сбросить фильтры <ClockWiseIcon />
|
||||
</Button>
|
||||
|
||||
<Box
|
||||
sx={{
|
||||
display: "flex",
|
||||
gap: "10px",
|
||||
width: "100%",
|
||||
justifyContent: "flex-end",
|
||||
mt: "35px",
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
sx={{
|
||||
width: isMobile ? "100%" : "130px",
|
||||
border: "1px solid #9A9AAF",
|
||||
}}
|
||||
onClick={handleClose}
|
||||
variant="outlined"
|
||||
>
|
||||
Отмена
|
||||
</Button>
|
||||
<Button
|
||||
variant="contained"
|
||||
sx={{
|
||||
width: isMobile ? "100%" : "130px",
|
||||
height: "48px",
|
||||
fontSize: "18px",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
Готово
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</Modal>
|
||||
);
|
||||
};
|
@ -7459,6 +7459,11 @@ mkdirp@~0.5.1:
|
||||
dependencies:
|
||||
minimist "^1.2.6"
|
||||
|
||||
moment@^2.30.1:
|
||||
version "2.30.1"
|
||||
resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae"
|
||||
integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==
|
||||
|
||||
ms@2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
|
||||
|
Loading…
Reference in New Issue
Block a user