Merge branch 'refactoring-applications-page' into dev

This commit is contained in:
Nastya 2024-03-28 01:36:23 +03:00
commit 30e97416fa
15 changed files with 694 additions and 567 deletions

@ -1,7 +1,36 @@
import { makeRequest } from "@frontend/kitui";
import { RawResult } from "@model/result/result";
async function getResultList(quizId, page: number, body: any) {
return makeRequest<unknown, unknown>({
interface IResultListBody {
to: number;
from: string;
new: boolean;
page: number;
limit: number;
}
export interface IAnswerResult {
Browser: string;
CreatedAt: string;
Deleted: boolean;
Device: string;
DeviceType: string;
Email: string;
Fingerprint: string;
IP: string;
Id: number;
OS: string;
QuizId: number;
Result: boolean;
Session: string;
Start: boolean;
content: string;
new: boolean;
question_id: number;
}
async function getResultList(quizId: number, page: number, body: any) {
return makeRequest<IResultListBody, RawResult>({
url: process.env.REACT_APP_DOMAIN + `/squiz/results/getResults/${quizId}`,
method: "POST",
body: { page: page, limit: 10, ...body },
@ -31,7 +60,7 @@ function deleteResult(resultId: number) {
// }
// };
function obsolescenceResult(idResultArray: string[]) {
function obsolescenceResult(idResultArray: number[]) {
return makeRequest<unknown, unknown>({
url: process.env.REACT_APP_DOMAIN + `/squiz/result/seen`,
body: {
@ -42,7 +71,7 @@ function obsolescenceResult(idResultArray: string[]) {
}
function getAnswerResultList(resultId: number) {
return makeRequest<unknown, unknown>({
return makeRequest<unknown, IAnswerResult[]>({
url: process.env.REACT_APP_DOMAIN + `/squiz/result/${resultId}`,
method: "GET",
});

@ -1,11 +1,13 @@
export interface RawResult {
total_count: number;
results: {
content: string;
id: number;
new: boolean;
created_at: string;
};
results: RawResultItem[];
}
export interface RawResultItem {
content: string;
id: number;
new: boolean;
created_at: string;
}
export interface ResultContent {
name?: string;
@ -35,12 +37,11 @@ export const defaultResultContent: ResultContent = {
messenger: "",
};
export function rawResultToResult(rawResult): RawResult {
export function rawResultToResult(rawResult: RawResultItem) {
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",

@ -0,0 +1,71 @@
import { FC } from "react";
import { Box, Typography } from "@mui/material";
import { DateDefinition, TimeDefinition } from "./helper";
import { CardAnswer } from "./CardAnswer";
import { Result } from "@root/results/store";
interface AnswerListProps {
isTablet: boolean;
results: Result[] | [];
setPrePaymentModalOpen: (value: boolean) => void;
}
export const AnswerList: FC<AnswerListProps> = ({
isTablet,
results,
setPrePaymentModalOpen,
}) => {
return (
<>
{!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 &&
results.map((result) => {
const dayResult = DateDefinition(result.created_at);
const timeResult = TimeDefinition(result.created_at);
return (
<CardAnswer
key={result.id}
name={result.content.name}
email={result.content.email}
phone={result.content.phone}
address={result.content.address}
isNew={result.new}
idResult={result.id}
dayResult={dayResult}
timeResult={timeResult}
openPrePaymentModal={() => setPrePaymentModalOpen(true)}
/>
);
})}
</Box>
</>
);
};

@ -1,25 +1,20 @@
import { ArrowDownIcon } from "@icons/questionsPage/ArrowDownIcon";
import {
Box,
Button,
IconButton,
Link,
Modal,
Typography,
useMediaQuery,
useTheme,
} from "@mui/material";
import { FC, useState } from "react";
import { FC, MouseEvent, 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 { IAnswerResult, resultApi } from "@api/result";
import { useQuizStore } from "@root/quizes/store";
import { useQuestionsStore } from "@root/questions/store";
import AddressIcon from "@icons/ContactFormIcon/AddressIcon";
@ -27,21 +22,19 @@ import AddressIcon from "@icons/ContactFormIcon/AddressIcon";
import type { AxiosError } from "axios";
import { DeleteModal } from "./DeleteModal";
interface Props {
interface CardAnswerProps {
isNew: boolean;
idResult: string;
onClick: () => void;
idResult: number;
dayResult: string;
timeResult: string;
name?: string;
phone?: string;
email?: string;
address?: string;
onLossNew?: (id: string) => void;
openPrePaymentModal: () => void;
}
export const CardAnswer = ({
export const CardAnswer: FC<CardAnswerProps> = ({
name,
phone,
email,
@ -50,35 +43,29 @@ export const CardAnswer = ({
idResult,
timeResult,
dayResult,
onLossNew,
openPrePaymentModal,
}: 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 [resultQuiz, setResultQuiz] = useState([]);
const [resultsAnswer, setResultsAnswer] = useState<IAnswerResult[]>([]);
const [questionsResultState, setQuestionsResultState] = useState([]);
const { editQuizId } = useQuizStore();
const { questions } = useQuestionsStore();
const openResults = async () => {
setIsOpen(!isOpen);
if (!isOpen) {
try {
let resAnswer = await resultApi.getAnswerList(Number(idResult));
let resAnswerOnly = resAnswer.filter((res) => res.Result !== true);
let resQuiz = resAnswer.filter((res) => res.Result === true);
setResultQuiz(resQuiz);
const resAnswer = await resultApi.getAnswerList(idResult);
const resAnswerOnly = resAnswer.filter((res) => res.Result !== true);
const resQuiz = resAnswer.filter((res) => res.Result === true);
setResultsAnswer(resAnswerOnly);
let idResults = resQuiz[0].question_id;
let questionsResult = questions.filter(
const idResults = resQuiz[0].question_id;
const questionsResult = questions.filter(
(q) => q.backendId === idResults,
);
setQuestionsResultState(questionsResult);
console.log("тут хранятся ответы", resAnswerOnly);
} catch (nativeError) {
const error = nativeError as AxiosError;
@ -89,167 +76,152 @@ export const CardAnswer = ({
}
};
const onClickDelete = (e: MouseEvent) => {
e.stopPropagation();
setOpenDelete(true);
};
return (
<Box
onClick={() => {
obsolescenceResult(idResult, editQuizId);
openResults();
}}
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
onClick={() => {
if (editQuizId !== null) {
obsolescenceResult(idResult, editQuizId);
openResults();
}
}}
sx={{
borderRadius: "12px",
maxWidth: isTablet ? "450px" : "auto",
width: "100%",
display: "flex",
justifyContent: "space-between",
flexDirection: isTablet ? "column" : "-moz-initial",
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={{
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%",
display: "flex",
justifyContent: "space-between",
flexDirection: isTablet ? "column" : "-moz-initial",
}}
>
<Box
sx={{
width: isTablet ? "100%" : "auto",
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",
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",
background: "#FFF",
width: "100%",
}}
>
<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={openResults}>
<ArrowDownIcon
style={{
transform: isOpen ? "rotate(180deg)" : "rotate(360deg)",
}}
fontSize="10px"
/>
</IconButton>
</Box>
<Typography
<Box
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>
)}
{address && (
<Box sx={{ display: "flex", alignItems: "center", gap: "13px" }}>
<AddressIcon color={"#9A9AAF"} />
<Typography sx={{ fontSize: "18px", color: "#4D4D4D" }}>
{address}
</Typography>
</Box>
)}
</Box>
{!isTablet && isNew && (
<Typography
sx={{
ml: "auto",
width: isTablet ? "100%" : "auto",
display: "flex",
alignItems: "center",
justifyContent: "center",
background: "#FB5607",
borderRadius: "8px",
width: "77px",
height: "36px",
color: "white",
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",
}}
>
Новая
</Typography>
)}
</Box>
<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={openResults}>
<ArrowDownIcon
style={{
transform: isOpen ? "rotate(180deg)" : "rotate(360deg)",
}}
fontSize="10px"
/>
</IconButton>
</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={{
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>
)}
{address && (
<Box
sx={{ display: "flex", alignItems: "center", gap: "13px" }}
>
<AddressIcon color={"#9A9AAF"} />
<Typography sx={{ fontSize: "18px", color: "#4D4D4D" }}>
{address}
</Typography>
</Box>
)}
</Box>
{!isTablet && isNew && (
<Typography
sx={{
ml: "auto",
display: "flex",
alignItems: "center",
justifyContent: "center",
@ -262,202 +234,241 @@ export const CardAnswer = ({
>
Новая
</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>
)}
<DeleteModal
openDelete={openDelete}
handleClose={() => setOpenDelete(false)}
onClick={() => {
deleteResult(Number(idResult));
setOpenDelete(false);
}}
/>
</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={{
borderRadius: "8px",
p: isTablet ? "20px" : "0",
cursor: "pointer",
display: "flex",
gap: isTablet ? "0" : "149px",
p: "20px",
flexDirection: isTablet ? "column" : "-moz-initial",
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={onClickDelete}>
<DeleteIcon color="#4D4D4D" fontSize="34px" />
</IconButton>
</Box>
</>
) : (
<IconButton onClick={onClickDelete}>
<DeleteIcon color="#4D4D4D" fontSize="34px" />
</IconButton>
)}
</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={{
display: "flex",
flexDirection: "column",
gap: "15px",
mt: "20px",
p: "20px",
borderTop: "1px solid #F1F2F6",
background: "#FFF",
borderRadius: "0 0 12px 12px",
}}
>
{resultsAnswer.map((answer, id) => {
let titleQuestion;
let typeOuestion;
let typeQuestionFile;
let quest = questions; //массив с вопросами
let i;
let idAnswer = answer.question_id; //айди вопроса у ответа
for (i in quest) {
if (quest[i].backendId === idAnswer) {
titleQuestion = quest[i].title;
typeOuestion = quest[i].type;
typeQuestionFile = quest[i].content.type;
}
}
return (
<Box
sx={{ display: "flex", alignItems: "center", gap: "13px" }}
>
<Typography sx={{ fontSize: "18px", color: "#9A9AAF" }}>
{id + 1}. {titleQuestion}.
</Typography>
{typeOuestion === "file" && (
<Link
download
href={answer.content}
style={{
color: "#7E2AEA",
display: "flex",
gap: "10px",
}}
>
{typeQuestionFile === "video" && (
<video
src={answer.content}
width={40}
height={40}
></video>
)}
{typeQuestionFile === "picture" && (
<img src={answer.content} width={40} height={40} />
)}
клик для скачивания
</Link>
)}
{(typeOuestion === "images" ||
typeOuestion === "varimg") && (
<>
<img
width={40}
height={40}
src={
answer.content
.split("<")[1]
.split('src="')[1]
.split('"/>')[0]
}
/>
<Typography sx={{ fontSize: "18px" }}>
{answer.content.split("<")[0]}
</Typography>
</>
)}
{!(
typeOuestion === "file" ||
typeOuestion === "images" ||
typeOuestion === "varimg"
) && (
<Typography sx={{ fontSize: "18px" }}>
{answer.content}
</Typography>
)}
</Box>
);
})}
<Typography sx={{ fontSize: "18px", color: "#9A9AAF" }}>
Ответы
</Typography>
</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>
{questionsResultState.map((res) => {
console.log(questionsResultState, "что за результат тут");
return (
<Box
sx={{
display: "flex",
gap: isTablet ? "0" : "149px",
p: "20px",
flexDirection: isTablet ? "column" : "-moz-initial",
}}
>
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "15px",
p: "20px",
mt: "20px",
}}
>
<Typography
{resultsAnswer.map((answer, id) => {
let titleQuestion;
let typeOuestion;
let typeQuestionFile;
let quest = questions; //массив с вопросами
let i;
let idAnswer = answer.question_id; //айди вопроса у ответа
for (i in quest) {
if (quest[i].backendId === idAnswer) {
titleQuestion = quest[i].title;
typeOuestion = quest[i].type;
typeQuestionFile = quest[i].content.type;
}
}
return (
<Box
key={answer.id}
sx={{
display: "flex",
alignItems: "center",
gap: "13px",
}}
>
<Typography sx={{ fontSize: "18px", color: "#9A9AAF" }}>
{id + 1}. {titleQuestion}.
</Typography>
{typeOuestion === "file" && (
<Link
download
href={answer.content}
style={{
color: "#7E2AEA",
display: "flex",
gap: "10px",
}}
>
{typeQuestionFile === "video" && (
<video
src={answer.content}
width={40}
height={40}
></video>
)}
{typeQuestionFile === "picture" && (
<img src={answer.content} width={40} height={40} />
)}
клик для скачивания
</Link>
)}
{(typeOuestion === "images" ||
typeOuestion === "varimg") && (
<>
<img
width={40}
height={40}
src={
answer.content
.split("<")[1]
.split('src="')[1]
.split('"/>')[0]
}
/>
<Typography sx={{ fontSize: "18px" }}>
{answer.content.split("<")[0]}
</Typography>
</>
)}
{!(
typeOuestion === "file" ||
typeOuestion === "images" ||
typeOuestion === "varimg"
) && (
<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>
{questionsResultState.map((res) => {
return (
<Box
key={res.id}
sx={{
fontSize: "18px",
fontWeight: "500",
wordBreak: "break-word",
display: "flex",
flexDirection: "column",
gap: "15px",
p: "20px",
}}
>
{res.description}
</Typography>
<Typography sx={{ fontSize: "18px", wordBreak: "break-word" }}>
{res.title}
</Typography>
{res.content.useImage &&
res.content.back &&
res.content.back !== " " && (
<img
src={res.content.back}
style={{ width: "40px", height: "40px" }}
alt={""}
/>
)}
{!res.content.useImage &&
res.content.back &&
res.content.back !== " " && (
<video
src={res.content.back}
style={{ width: "40px", height: "40px" }}
></video>
)}
</Box>
);
})}
</Box>
)}
</Box>
<Typography
sx={{
fontSize: "18px",
fontWeight: "500",
wordBreak: "break-word",
}}
>
{res.description}
</Typography>
<Typography
sx={{ fontSize: "18px", wordBreak: "break-word" }}
>
{res.title}
</Typography>
{res.content.useImage &&
res.content.back &&
res.content.back !== " " && (
<img
src={res.content.back}
style={{ width: "40px", height: "40px" }}
alt={""}
/>
)}
{!res.content.useImage &&
res.content.back &&
res.content.back !== " " && (
<video
src={res.content.back}
style={{ width: "40px", height: "40px" }}
></video>
)}
</Box>
);
})}
</Box>
)}
</Box>
<DeleteModal
openDelete={openDelete}
handleClose={() => setOpenDelete(false)}
onClick={() => {
deleteResult(Number(idResult));
setOpenDelete(false);
}}
/>
</>
);
};

@ -1,37 +1,22 @@
import {
Box,
Typography,
useTheme,
useMediaQuery,
Skeleton,
} from "@mui/material";
import { Box, Skeleton, useMediaQuery, useTheme } from "@mui/material";
import HeaderFull from "@ui_kit/Header/HeaderFull";
import SectionWrapper from "@ui_kit/SectionWrapper";
import { FC, useEffect, useState } from "react";
import { CardAnswer } from "./CardAnswer";
import { ChangeEvent, FC, useEffect, useState } from "react";
import { PrePaymentModal } from "./PrePaymentModal";
import { FilterModal } from "@ui_kit/Modal/FilterModal/FilterModal";
import { resultApi } from "@api/result";
import { useResultStore } from "@root/results/store";
import { ExportResults, setResults } from "@root/results/actions";
import { quizApi } from "@api/quiz";
import { setQuizes } from "@root/quizes/actions";
import { setResults } from "@root/results/actions";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { useQuizStore } from "@root/quizes/store";
import { questionApi } from "@api/question";
import { setQuestions } from "@root/questions/actions";
import { useUserStore } from "@root/user";
import CustomPagination from "@ui_kit/CustomPagination";
import SettingResults from "./SettingResults";
import { DateDefinition, parseFilters, TimeDefinition } from "./helper";
const itemsCities = [
{ label: "Муром (1)", value: "option1" },
{ label: "Москва (1)", value: "option2" },
];
import { parseFilters } from "./helper";
import { QuizSettingsMenu } from "./QuizSettingsMenu";
import { AnswerList } from "./AnswerList";
import { useGetData } from "./useGetData";
const itemsTime = [
"За всё время",
@ -50,8 +35,6 @@ export const QuizAnswersPage: FC = () => {
const [filterModalOpen, setFilterModalOpen] = useState<boolean>(false);
const [page, setPage] = useState(1);
const [exportContactsModalOpen, setExportContactsModalOpen] =
useState<boolean>(false);
const [filterNew, setFilterNew] = useState<string>("Все заявки");
const [filterDate, setFilterDate] = useState<string>("За всё время");
const [prePaymentModalOpen, setPrePaymentModalOpen] =
@ -68,14 +51,13 @@ export const QuizAnswersPage: FC = () => {
const quiz = useCurrentQuiz();
const { editQuizId } = useQuizStore();
const { results, total_count } = useResultStore();
const countPagination =
typeof total_count === "number" ? Math.ceil(total_count / 10) : 0;
useGetData(filterNew, filterDate);
console.log("Перерендер компонента ", results);
const countPagination = Math.ceil(total_count / 10);
// const {idResultArray, addIdResult, clearIdResultArray} = useObsolescenceIdResult()
useEffect(() => {
getData();
}, [filterNew, filterDate]);
return setResults([]);
}, []);
useEffect(() => {
if (!userAccount) {
@ -94,31 +76,20 @@ export const QuizAnswersPage: FC = () => {
setPrePaymentModalOpen(false);
}, [userAccount]);
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,
0,
parseFilters(filterNew, filterDate),
);
setResults(result);
}
};
const PaginationHC = async (e, value) => {
const PaginationHC = async (e: ChangeEvent<unknown>, value: number) => {
setPage(value);
const result = await resultApi.getList(
editQuizId,
value - 1,
parseFilters(filterNew, filterDate),
);
setResults(result);
try {
if (editQuizId !== null) {
const result = await resultApi.getList(
editQuizId,
value - 1,
parseFilters(filterNew, filterDate),
);
setResults(result);
}
} catch (error) {
console.error("An error occurred while receiving data: ", error);
}
};
if (quiz === undefined)
@ -133,137 +104,34 @@ export const QuizAnswersPage: FC = () => {
sx={{ padding: isMobile ? "115px 16px" : "115px 20px 20px" }}
maxWidth="lg"
>
<Typography
sx={{
fontSize: "36px",
fontWeight: "500",
mb: "50px",
lineHeight: "normal",
wordBreak: "break-word",
}}
>
{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%",
}}
>
<SettingResults
onclickExport={() =>
ExportResults(
filterNew,
filterDate,
() => setPrePaymentModalOpen(true),
editQuizId,
)
}
onclickUpdate={async () => {
const result = await resultApi.getList(
editQuizId,
page - 1,
parseFilters(filterNew, filterDate),
);
setResults(result);
}}
onclickFilterTablet={() => setFilterModalOpen(true)}
onclickResetFilers={() => {
setFilterNew("Все заявки");
setFilterDate("За всё время");
}}
filterNewHC={filterNewHC}
filterDateHC={filterDateHC}
itemsTime={itemsTime}
itemsNews={itemsNews}
filterDate={filterDate}
filterNew={filterNew}
/>
</Box>
</Box>
<QuizSettingsMenu
quiz={quiz}
total_count={total_count}
isTablet={isTablet}
filterNew={filterNew}
filterDate={filterDate}
setPrePaymentModalOpen={setPrePaymentModalOpen}
editQuizId={editQuizId}
page={page}
setFilterModalOpen={setFilterModalOpen}
setFilterNew={setFilterNew}
setFilterDate={setFilterDate}
filterNewHC={filterNewHC}
filterDateHC={filterDateHC}
itemsTime={itemsTime}
itemsNews={itemsNews}
/>
<CustomPagination
countPagination={countPagination}
page={page}
onChange={PaginationHC}
/>
{!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}
phone={result.content.phone}
address={result.content.address}
isNew={result.new}
idResult={result.id}
dayResult={dayResult}
timeResult={timeResult}
openPrePaymentModal={() => setPrePaymentModalOpen(true)}
/>
);
})}
</Box>
<AnswerList
isTablet={isTablet}
results={results}
setPrePaymentModalOpen={setPrePaymentModalOpen}
/>
</SectionWrapper>
<FilterModal
open={filterModalOpen}
handleClose={() => setFilterModalOpen(false)}

@ -0,0 +1,127 @@
import SettingResults from "./SettingResults";
import { ExportResults, setResults } from "@root/results/actions";
import { resultApi } from "@api/result";
import { parseFilters } from "./helper";
import { Box, Typography } from "@mui/material";
import { FC } from "react";
import { Quiz } from "@model/quiz/quiz";
interface QuizSettingsMenuProps {
quiz: Quiz;
total_count: number;
isTablet: boolean;
filterNew: string;
filterDate: string;
setPrePaymentModalOpen: (value: boolean) => void;
editQuizId: number | null;
page: number;
setFilterModalOpen: (value: boolean) => void;
setFilterNew: (value: string) => void;
setFilterDate: (value: string) => void;
filterNewHC: (value: string) => void;
filterDateHC: (value: string) => void;
itemsTime: string[];
itemsNews: string[];
}
export const QuizSettingsMenu: FC<QuizSettingsMenuProps> = ({
quiz,
total_count,
isTablet,
filterNew,
filterDate,
setPrePaymentModalOpen,
editQuizId,
page,
setFilterModalOpen,
setFilterNew,
setFilterDate,
filterNewHC,
filterDateHC,
itemsTime,
itemsNews,
}) => {
return (
<>
<Typography
sx={{
fontSize: "36px",
fontWeight: "500",
mb: "50px",
lineHeight: "normal",
wordBreak: "break-word",
}}
>
{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%",
}}
>
<SettingResults
onclickExport={() => {
if (editQuizId !== null) {
ExportResults(
filterNew,
filterDate,
() => setPrePaymentModalOpen(true),
editQuizId,
);
} else {
console.error("editQuizId is null");
}
}}
onclickUpdate={async () => {
if (editQuizId !== null) {
const result = await resultApi.getList(
editQuizId,
page - 1,
parseFilters(filterNew, filterDate),
);
setResults(result);
} else {
console.error("editQuizId is null");
}
}}
onclickFilterTablet={() => setFilterModalOpen(true)}
onclickResetFilers={() => {
setFilterNew("Все заявки");
setFilterDate("За всё время");
}}
filterNewHC={filterNewHC}
filterDateHC={filterDateHC}
itemsTime={itemsTime}
itemsNews={itemsNews}
filterDate={filterDate}
filterNew={filterNew}
/>
</Box>
</Box>
</>
);
};

@ -37,7 +37,6 @@ export default function SettingResults({
}: Props) {
const theme = useTheme();
const isTablet = useMediaQuery(theme.breakpoints.down(1000));
const isMobile = useMediaQuery(theme.breakpoints.down(600));
return (
<>
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>

@ -9,8 +9,6 @@ export const parseFilters = (filterNew: string, filterDate: string) => {
if (filterNew === "Новые") filters.new = true;
if (filterDate.length !== 0 && filterDate !== "За всё время") {
console.log(filterDate);
filters.to = new Date();
let resetedCurrentTime = Number(resetTime());

@ -1,16 +0,0 @@
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"
strokeWidth="1.5"
/>
<circle cx="15" cy="15" r="4" stroke="#4D4D4D" strokeWidth="1.5" />
</svg>
);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 64 KiB

@ -0,0 +1,41 @@
import { useEffect } from "react";
import { useQuizStore } from "@root/quizes/store";
import { quizApi } from "@api/quiz";
import { setQuizes } from "@root/quizes/actions";
import { questionApi } from "@api/question";
import { setQuestions } from "@root/questions/actions";
import { resultApi } from "@api/result";
import { parseFilters } from "./helper";
import { setResults } from "@root/results/actions";
export const useGetData = (filterNew: string, filterDate: string): void => {
const { editQuizId } = useQuizStore();
useEffect(() => {
const getData = async (): Promise<void> => {
try {
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,
0,
parseFilters(filterNew, filterDate),
);
if (result.total_count === 0) {
console.log("No results found");
}
setResults(result);
}
} catch (error) {
console.error("An error occurred while receiving data: ", error);
}
};
getData();
}, [editQuizId, filterNew, filterDate, setResults]);
};

@ -5,7 +5,6 @@ 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";
import { AxiosError } from "axios";
import { parseFilters } from "../../pages/QuizAnswersPage/helper";
@ -13,7 +12,7 @@ const REQUEST_DEBOUNCE = 200;
const requestQueue = new RequestQueue();
let requestTimeoutId: ReturnType<typeof setTimeout>;
export const setResults = (results: RawResult[] | null) =>
export const setResults = (results: RawResult | []) =>
setProducedState(
(state) => {
state.results = Object.values(results)[1]?.map(rawResultToResult) ?? [];
@ -22,11 +21,10 @@ export const setResults = (results: RawResult[] | null) =>
{
type: "setResults",
results,
// total_count
},
);
const removeResult = (resultId: string) =>
const removeResult = (resultId: number) =>
setProducedState((state) => {
const index = state.results.findIndex((r) => r.id === resultId);
if (index === -1) return;
@ -53,9 +51,9 @@ export const deleteResult = async (resultId: number) =>
});
export const obsolescenceResult = async (
resultId: string,
resultId: number,
editQuizId: number,
) =>
) => {
requestQueue.enqueue(
`obsolescenceResult-${resultId}-${editQuizId}`,
async () => {
@ -65,7 +63,7 @@ export const obsolescenceResult = async (
if (!result) return;
if (result.new === false) return;
let lossDebouncer: null | ReturnType<typeof setTimeout> = null;
let lossId: string[] = [] as string[];
let lossId: number[] = [];
if (!lossId.includes(resultId)) lossId.push(resultId);
if (typeof lossDebouncer === "number") clearTimeout(lossDebouncer);
lossDebouncer = setTimeout(async () => {
@ -85,12 +83,13 @@ export const obsolescenceResult = async (
setResults(resultList);
},
);
};
export const ExportResults = async (
filterNew: string,
filterDate: string,
openPrePaymentModal: () => void,
editQuizId: number | null,
editQuizId: number,
) => {
try {
const data = await resultApi.export(
@ -101,7 +100,7 @@ export const ExportResults = async (
const blob = data;
const link = document.createElement("a");
link.href = window.URL.createObjectURL(blob);
link.href = window.URL.createObjectURL(blob as Blob);
link.download = `report_${new Date().getTime()}.xlsx`;
link.click();
} catch (nativeError) {

@ -1,20 +1,19 @@
import { create } from "zustand";
import { devtools, persist } from "zustand/middleware";
import { ResultContent } from "@model/result/result";
export type ResultStore = {
results: [
{
results: {
content: string;
id: number;
new: boolean;
created_at: string;
};
},
];
results: Result[];
total_count: number;
};
export type Result = {
content: ResultContent;
id: number;
new: boolean;
created_at: string;
};
const initialState: ResultStore = {
results: [],
total_count: 0,
@ -40,7 +39,6 @@ export const useObsolescenceIdResult = create()(
idResultArray: [],
addIdResult: (idResult: number) => {
const state = get()["idResultArray"] || [];
console.log(state);
const newState = [...state, idResult];
set({ idResultArray: newState });
},

@ -1,9 +1,10 @@
import { Pagination } from "@mui/material";
import { ChangeEvent } from "react";
interface Props {
countPagination: number;
page: number;
onChange: (e: ChangeEvent, value: number) => void;
onChange: (e: ChangeEvent<unknown>, value: number) => void;
}
export default function CustomPagination({