diff --git a/package.json b/package.json
index 274e4d00..60c2386d 100755
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/src/App.tsx b/src/App.tsx
index 036f0f64..def36464 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -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() {
} />
} />
} />
+ } />
}>
{routeslink.map((e, i) => (
({
+ url: process.env.REACT_APP_DOMAIN + `/squiz/results/getResults/${quizId}`,
+ method: "POST",
+ body: { page: 0, limit: 10, ...body },
+ });
+}
+
+function deleteResult(resultId: number) {
+ return makeRequest({
+ url: process.env.REACT_APP_DOMAIN + `/squiz/results/delete/${resultId}`,
+ body: {},
+ method: "DELETE",
+ });
+}
+
+// export const obsolescenceResult = async (idResultArray: string[]) => {
+// try {
+// const response = await makeRequest({
+// 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({
+ url: process.env.REACT_APP_DOMAIN + `/squiz/result/seen`,
+ body: {
+ answers: idResultArray,
+ },
+ method: "PATCH",
+ });
+}
+
+function getAnswerResultList(resultId: number) {
+ return makeRequest({
+ url: process.env.REACT_APP_DOMAIN + `/squiz/result/${resultId}`,
+ method: "GET",
+ });
+}
+
+function AnswerResultListEx(quizId: number, fromDate: string, toDate: string) {
+ return makeRequest({
+ 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,
+};
diff --git a/src/assets/icons/ClockWiseIcon.tsx b/src/assets/icons/ClockWiseIcon.tsx
new file mode 100644
index 00000000..b43bb2cc
--- /dev/null
+++ b/src/assets/icons/ClockWiseIcon.tsx
@@ -0,0 +1,14 @@
+export const ClockWiseIcon = () => (
+
+);
diff --git a/src/index.tsx b/src/index.tsx
index 4f78a2a2..47fac85b 100755
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -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;
diff --git a/src/model/result/result.ts b/src/model/result/result.ts
new file mode 100644
index 00000000..3f335462
--- /dev/null
+++ b/src/model/result/result.ts
@@ -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,
+ };
+}
diff --git a/src/pages/Questions/Select.tsx b/src/pages/Questions/Select.tsx
index 19d04a3a..cce14e37 100644
--- a/src/pages/Questions/Select.tsx
+++ b/src/pages/Questions/Select.tsx
@@ -85,7 +85,6 @@ export const Select = ({
borderRadius: "8px",
"& .MuiOutlinedInput-notchedOutline": {
border: `1px solid ${colorMain} !important`,
- height: "48px",
borderRadius: "10px",
},
}}
diff --git a/src/pages/QuizAnswersPage/CardAnswer.tsx b/src/pages/QuizAnswersPage/CardAnswer.tsx
new file mode 100644
index 00000000..b599253e
--- /dev/null
+++ b/src/pages/QuizAnswersPage/CardAnswer.tsx
@@ -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(false);
+ const [openDelete, setOpenDelete] = useState(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 (
+ {
+ 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)",
+ }}
+ >
+
+
+
+
+
+ {idResult}
+
+ {
+ setIsOpen(!isOpen);
+ if (isOpen) {
+ let resAnswer = await resultApi.getAnswerList(
+ Number(idResult),
+ );
+ resAnswer = resAnswer.filter((res) => res.Result !== true);
+ setResultsAnswer(resAnswer);
+ console.log("тут хранятся ответы", resAnswer);
+ }
+ }}
+ >
+
+
+
+
+
+ {dayResult} в {timeResult}
+
+
+
+
+ {name && (
+
+
+
+ {name}
+
+
+ )}
+ {email && (
+
+
+
+ {email}
+
+
+ )}
+ {phone && (
+
+
+
+ {phone}
+
+
+ )}
+
+
+ {!isTablet && isNew && (
+
+ Новая
+
+ )}
+
+
+
+ {isTablet && isNew ? (
+ <>
+
+ Новая
+
+
+ setOpenDelete(true)}>
+
+
+
+ >
+ ) : (
+ setOpenDelete(true)}>
+
+
+ )}
+ setOpenDelete(false)}>
+
+
+ Вы уверены, что хотите удалить этот результат?
+
+
+
+
+
+
+
+
+
+
+ {isOpen && (
+
+
+
+ Ответы
+
+
+
+
+
+ {resultsAnswer.map((answer, id) => {
+ console.log(answer.QuizId);
+
+ return (
+
+
+ {id + 1}.
+
+
+ {answer.content}
+
+
+ );
+ })}
+
+
+
+
+
+ Результаты
+
+
+
+
+
+ Заголовок
+
+ Текст
+
+
+
+ )}
+
+ );
+};
diff --git a/src/pages/QuizAnswersPage/DeleteModal.tsx b/src/pages/QuizAnswersPage/DeleteModal.tsx
new file mode 100644
index 00000000..bb828823
--- /dev/null
+++ b/src/pages/QuizAnswersPage/DeleteModal.tsx
@@ -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 (
+ setOpenDelete(false)}>
+
+
+ Вы уверены, что хотите удалить этот результат?
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/pages/QuizAnswersPage/QuizAnswersPage.tsx b/src/pages/QuizAnswersPage/QuizAnswersPage.tsx
new file mode 100644
index 00000000..14012e2f
--- /dev/null
+++ b/src/pages/QuizAnswersPage/QuizAnswersPage.tsx
@@ -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 = 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(false);
+ const [exportContactsModalOpen, setExportContactsModalOpen] =
+ useState(false);
+ const [filterNew, setFilterNew] = useState("Все заявки");
+ const [filterDate, setFilterDate] = useState("За все время");
+
+ 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 (
+
+ );
+
+ return (
+
+
+
+
+ {quiz.name}
+
+
+
+
+
+ Ответы на квиз
+
+
+ ({total_count})
+
+
+
+
+
+ {
+ // answerResultListExport(editQuizId)
+ setExportContactsModalOpen(true);
+ }}
+ sx={{
+ width: "44px",
+ height: "44px",
+ borderRadius: "8px",
+ border: "1px solid #7E2AEA",
+ }}
+ >
+
+
+ {
+ const result = await resultApi.getList(editQuizId);
+ console.log(result);
+ setResults(result);
+ }}
+ >
+
+
+
+ {isTablet && (
+ setFilterModalOpen(true)}
+ sx={{
+ background: "#fff",
+ width: "44px",
+ height: "44px",
+ borderRadius: "8px",
+ border: "1px solid #7E2AEA",
+ }}
+ >
+
+
+ )}
+
+
+ {!isTablet && (
+
+
+
+
+
+
+
+ {/* */}
+
+
+
+ )}
+
+
+
+ {!isTablet && (
+
+
+ № заявки
+
+
+ Дата
+
+
+ Контакты
+
+
+ )}
+
+
+ {results.map((result) => {
+ const dataResult = new Date(result.created_at);
+ let dayResult = DateDefinition(result.created_at);
+ let timeResult = TimeDefinition(result.created_at);
+ return (
+
+ );
+ })}
+
+
+
+ setFilterModalOpen(false)}
+ filterNew={filterNew}
+ filterDate={filterDate}
+ setFilterNew={setFilterNew}
+ setFilterDate={setFilterDate}
+ />
+ setExportContactsModalOpen(false)}
+ />
+
+ );
+};
diff --git a/src/pages/QuizAnswersPage/icons/ContactIcon.tsx b/src/pages/QuizAnswersPage/icons/ContactIcon.tsx
new file mode 100644
index 00000000..549200a0
--- /dev/null
+++ b/src/pages/QuizAnswersPage/icons/ContactIcon.tsx
@@ -0,0 +1,23 @@
+export const ContactIcon = () => (
+
+);
diff --git a/src/pages/QuizAnswersPage/icons/EyeIcon.tsx b/src/pages/QuizAnswersPage/icons/EyeIcon.tsx
new file mode 100644
index 00000000..d4b309aa
--- /dev/null
+++ b/src/pages/QuizAnswersPage/icons/EyeIcon.tsx
@@ -0,0 +1,16 @@
+export const EyeIcon = () => (
+
+);
diff --git a/src/pages/QuizAnswersPage/icons/FileExporIcon.tsx b/src/pages/QuizAnswersPage/icons/FileExporIcon.tsx
new file mode 100644
index 00000000..d3a5cf3e
--- /dev/null
+++ b/src/pages/QuizAnswersPage/icons/FileExporIcon.tsx
@@ -0,0 +1,24 @@
+export const FileExportIcon = () => (
+
+);
diff --git a/src/pages/QuizAnswersPage/icons/FilterIcon.tsx b/src/pages/QuizAnswersPage/icons/FilterIcon.tsx
new file mode 100644
index 00000000..3518bdc9
--- /dev/null
+++ b/src/pages/QuizAnswersPage/icons/FilterIcon.tsx
@@ -0,0 +1,15 @@
+export const FilterIcon = () => (
+
+);
diff --git a/src/pages/QuizAnswersPage/icons/MessageIcon.tsx b/src/pages/QuizAnswersPage/icons/MessageIcon.tsx
new file mode 100644
index 00000000..b95047ec
--- /dev/null
+++ b/src/pages/QuizAnswersPage/icons/MessageIcon.tsx
@@ -0,0 +1,27 @@
+export const MessageIcon = () => (
+
+);
diff --git a/src/pages/QuizAnswersPage/icons/PhoneIcon.tsx b/src/pages/QuizAnswersPage/icons/PhoneIcon.tsx
new file mode 100644
index 00000000..5d762154
--- /dev/null
+++ b/src/pages/QuizAnswersPage/icons/PhoneIcon.tsx
@@ -0,0 +1,16 @@
+export const PhoneIcon = () => (
+
+);
diff --git a/src/pages/QuizAnswersPage/icons/UpdateIcon.tsx b/src/pages/QuizAnswersPage/icons/UpdateIcon.tsx
new file mode 100644
index 00000000..752397f7
--- /dev/null
+++ b/src/pages/QuizAnswersPage/icons/UpdateIcon.tsx
@@ -0,0 +1,16 @@
+export const UpdateIcon = () => (
+
+);
diff --git a/src/pages/QuizAnswersPage/images/home.png b/src/pages/QuizAnswersPage/images/home.png
new file mode 100644
index 00000000..f10be8a2
Binary files /dev/null and b/src/pages/QuizAnswersPage/images/home.png differ
diff --git a/src/pages/QuizAnswersPage/images/videoFrame.png b/src/pages/QuizAnswersPage/images/videoFrame.png
new file mode 100644
index 00000000..f6898f33
Binary files /dev/null and b/src/pages/QuizAnswersPage/images/videoFrame.png differ
diff --git a/src/pages/createQuize/MyQuizzesFull.tsx b/src/pages/createQuize/MyQuizzesFull.tsx
index 48286e12..23e7956a 100644
--- a/src/pages/createQuize/MyQuizzesFull.tsx
+++ b/src/pages/createQuize/MyQuizzesFull.tsx
@@ -34,7 +34,7 @@ export default function MyQuizzesFull({
return (
<>
-
+
{quizes.length === 0 ? (
) : (
diff --git a/src/pages/createQuize/QuizCard.tsx b/src/pages/createQuize/QuizCard.tsx
index b45ee381..67e104b4 100755
--- a/src/pages/createQuize/QuizCard.tsx
+++ b/src/pages/createQuize/QuizCard.tsx
@@ -157,14 +157,18 @@ export default function QuizCard({
gap: isMobile ? "10px" : "20px",
}}
>
- {/* */}
+
}
diff --git a/src/stores/results/actions.ts b/src/stores/results/actions.ts
new file mode 100644
index 00000000..62e6fe61
--- /dev/null
+++ b/src/stores/results/actions.ts
@@ -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;
+
+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 = 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(
+ recipe: (state: ResultStore) => void,
+ action?: A,
+) {
+ useResultStore.setState((state) => produce(state, recipe), false, action);
+}
diff --git a/src/stores/results/hooks.ts b/src/stores/results/hooks.ts
new file mode 100644
index 00000000..c2cbd4b6
--- /dev/null
+++ b/src/stores/results/hooks.ts
@@ -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(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()
+// )
+// }
diff --git a/src/stores/results/store.ts b/src/stores/results/store.ts
new file mode 100644
index 00000000..920dfe63
--- /dev/null
+++ b/src/stores/results/store.ts
@@ -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()(
+ 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",
+ },
+ ),
+);
diff --git a/src/ui_kit/CheckboxSelect.tsx b/src/ui_kit/CheckboxSelect.tsx
new file mode 100644
index 00000000..25b6b67b
--- /dev/null
+++ b/src/ui_kit/CheckboxSelect.tsx
@@ -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 = ({
+ options,
+ sx,
+ placeholder,
+}: CheckboxSelectProps) => {
+ const [selectedValues, setSelectedValues] = useState([]);
+
+ const handleChange = (event: SelectChangeEvent) => {
+ const values = event.target.value as string[];
+
+ if (values.includes("SelectAll")) {
+ setSelectedValues(options.map((option) => option.value));
+ } else {
+ setSelectedValues(values);
+ }
+ };
+
+ return (
+
+
+
+ );
+};
diff --git a/src/ui_kit/Header/HeaderFull.tsx b/src/ui_kit/Header/HeaderFull.tsx
index 8da43757..8a18d827 100644
--- a/src/ui_kit/Header/HeaderFull.tsx
+++ b/src/ui_kit/Header/HeaderFull.tsx
@@ -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() {
+ {isRequest && (
+ navigate("/list")}>
+
+
+ )}
void;
+}
+
+export const ExportContactsModal: FC = ({
+ 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 (
+
+
+
+
+ Настройте фильтры отображения
+
+
+
+
+ c
+ {
+ setFromDate(date);
+ }}
+ slots={{
+ openPickerIcon: () => ,
+ }}
+ 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",
+ },
+ },
+ }}
+ />
+
+
+ до
+
+ {
+ setToDate(date);
+ }}
+ slots={{
+ openPickerIcon: () => ,
+ }}
+ 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",
+ },
+ },
+ }}
+ />
+
+
+
+ {/**/}
+ {/* */}
+ {/* */}
+ {/* */}
+ {/**/}
+
+
+
+
+
+
+
+ );
+};
diff --git a/src/ui_kit/Modal/FilterModal/FilterModal.tsx b/src/ui_kit/Modal/FilterModal/FilterModal.tsx
new file mode 100644
index 00000000..b7b5f37f
--- /dev/null
+++ b/src/ui_kit/Modal/FilterModal/FilterModal.tsx
@@ -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 = ({
+ 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 (
+
+
+
+
+ Настройте фильтры отображения
+
+
+
+
+
+
+
+ {/* */}
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/yarn.lock b/yarn.lock
index fb6d2079..54553512 100755
--- a/yarn.lock
+++ b/yarn.lock
@@ -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"