вывод ответов на вопросы схематично, сохранение и сброс фильтров

This commit is contained in:
Tamara 2024-02-10 04:41:13 +03:00
parent 7dc9cbe8d2
commit ccee8eb5e3
7 changed files with 359 additions and 217 deletions

@ -16,20 +16,30 @@ function deleteResult(resultId: number) {
});
}
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);
}
};
// 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>({
@ -38,13 +48,13 @@ function getAnswerResultList(resultId: number) {
});
}
function AnswerResultListEx(quizId: number) {
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(),
from: new Date("2024-01-24"),
to: new Date(toDate),
from: new Date(fromDate),
new: true,
},
});
@ -55,4 +65,5 @@ export const resultApi = {
delete: deleteResult,
getAnswerList: getAnswerResultList,
export: AnswerResultListEx,
obsolescence: obsolescenceResult,
};

@ -13,7 +13,7 @@ import "./index.css";
import lightTheme from "./utils/themes/light";
import { SWRConfig } from "swr";
import { BrowserRouter } from "react-router-dom";
import moment from "moment"
import moment from "moment";
dayjs.locale("ru");
moment.locale("ru");

@ -17,8 +17,10 @@ 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 } from "@root/results/actions";
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;
@ -29,7 +31,7 @@ interface Props {
name?: string;
phone?: string;
email?: string;
onLossNew: (id: string) => void;
onLossNew?: (id: string) => void;
}
export const CardAnswer = ({
@ -47,11 +49,17 @@ export const CardAnswer = ({
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={() => {
if (isNew) onLossNew(idResult);
obsolescenceResult(idResult, editQuizId);
}}
sx={{
borderRadius: "12px",
@ -121,8 +129,9 @@ export const CardAnswer = ({
let resAnswer = await resultApi.getAnswerList(
Number(idResult),
);
resAnswer = resAnswer.filter((res) => res.Result !== true);
setResultsAnswer(resAnswer);
console.log("тут хранятся ответы", resultsAnswer);
console.log("тут хранятся ответы", resAnswer);
}
}}
>
@ -319,102 +328,22 @@ export const CardAnswer = ({
mt: "20px",
}}
>
<Box sx={{ display: "flex", alignItems: "center", gap: "13px" }}>
<Typography sx={{ fontSize: "18px", color: "#9A9AAF" }}>
1.
</Typography>
<Typography sx={{ fontSize: "18px" }}>
Ответ на 1 вопрос
</Typography>
</Box>
<Box sx={{ display: "flex", alignItems: "center", gap: "13px" }}>
<Typography sx={{ fontSize: "18px", color: "#9A9AAF" }}>
2.
</Typography>
<img src={homeImg} style={{ width: "40px", height: "40px" }} />
</Box>
<Box sx={{ display: "flex", alignItems: "center", gap: "13px" }}>
<Typography sx={{ fontSize: "18px", color: "#9A9AAF" }}>
3.
</Typography>
<Typography
sx={{
fontSize: "18px",
color: "#7E2AEA",
borderBottom: "1px solid #7E2AEA",
}}
>
ответ.doc
</Typography>
</Box>
<Box sx={{ display: "flex", alignItems: "center", gap: "13px" }}>
<Typography sx={{ fontSize: "18px", color: "#9A9AAF" }}>
4.
</Typography>
<Typography sx={{ fontSize: "18px" }}>
Ответ на 4 вопрос
</Typography>
</Box>
<Box sx={{ display: "flex", alignItems: "center", gap: "13px" }}>
<Typography sx={{ fontSize: "18px", color: "#9A9AAF" }}>
5.
</Typography>
<Typography sx={{ fontSize: "18px" }}>
Ответ на 5 вопрос
</Typography>
</Box>
<Box sx={{ display: "flex", alignItems: "center", gap: "13px" }}>
<Typography sx={{ fontSize: "18px", color: "#9A9AAF" }}>
6.
</Typography>
<Typography sx={{ fontSize: "18px" }}>
Ответ на 6 вопрос
</Typography>
</Box>
</Box>
{resultsAnswer.map((answer, id) => {
console.log(answer.QuizId);
<Box
sx={{
display: "flex",
flexDirection: "column",
gap: "15px",
mt: "20px",
}}
>
<Box sx={{ display: "flex", alignItems: "center", gap: "13px" }}>
<Typography sx={{ fontSize: "18px", color: "#9A9AAF" }}>
7.
</Typography>
<Typography sx={{ fontSize: "18px" }}>
Ответ на 7 вопрос
</Typography>
</Box>
<Box sx={{ display: "flex", alignItems: "center", gap: "13px" }}>
<Typography sx={{ fontSize: "18px", color: "#9A9AAF" }}>
8.
</Typography>
<Typography sx={{ fontSize: "18px" }}>
Ответ на 8 вопрос
</Typography>
</Box>
<Box sx={{ display: "flex", alignItems: "center", gap: "13px" }}>
<Typography sx={{ fontSize: "18px", color: "#9A9AAF" }}>
9.
</Typography>
<Typography sx={{ fontSize: "18px" }}>
Ответ на 9 вопрос
</Typography>
</Box>
<Box sx={{ display: "flex", alignItems: "center", gap: "13px" }}>
<Typography sx={{ fontSize: "18px", color: "#9A9AAF" }}>
10.
</Typography>
<img
src={videoFrame}
style={{ width: "40px", height: "40px" }}
/>
</Box>
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>

@ -3,78 +3,88 @@ import {
Button,
IconButton,
Typography,
Select as MuiSelect,
MenuItem,
useTheme,
useMediaQuery,
Skeleton,
FormControl,
Select,
} from "@mui/material";
import moment from "moment"
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 { 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 {
AnswerResultListEx,
getResultsList,
obsolescenceResult,
resultApi,
} from "@api/result";
import { resultApi } from "@api/result";
import { useObsolescenceIdResult, useResultStore } from "@root/results/store";
import { setResults } from "@root/results/actions";
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" },
];
let lossDebouncer: null | ReturnType<typeof setTimeout> = null;
let lossId: string[] = [] as string[];
const itemsTime = [
"За все время",
"Сегодня",
"Вчера",
"Последние 7 дней",
"Последние 30 дней",
"Этот месяц",
];
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);
// 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 ");
};
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 [exportContactsModalOpen, setExportContactsModalOpen] =
useState<boolean>(false);
const [filterNew, setFilterNew] = useState<string>("Все заявки");
const [filterDate, setFilterDate] = useState<string>("За все время");
const quizList = useQuizStore();
const quiz = useCurrentQuiz();
@ -82,13 +92,15 @@ export const QuizAnswersPage: FC = () => {
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);
}
@ -166,34 +178,9 @@ export const QuizAnswersPage: FC = () => {
>
<Box sx={{ display: "flex", alignItems: "center", gap: "10px" }}>
<IconButton
onClick={async () => {
//setExportContactsModalOpen(true)
const resAnswer = await AnswerResultListEx(quizList.editQuizId)
console.log("ответы респондентов на экспорт по клику", resAnswer)
const download = function () {
// Creating a Blob for having a csv file format
// and passing the data with type
const blob = new Blob([resAnswer], { type: 'text/csv' });
// 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()
onClick={() => {
// answerResultListExport(editQuizId)
setExportContactsModalOpen(true);
}}
sx={{
width: "44px",
@ -246,23 +233,154 @@ export const QuizAnswersPage: FC = () => {
justifyContent: "flex-end",
}}
>
<Select
<FormControl
fullWidth
sx={{ maxWidth: "223px", width: "100%" }}
items={[
"За все время",
"Сегодня",
"Вчера",
"Последние 7 дней",
"Последние 30 дней",
"Этот месяц",
]}
onChange={(e) => setFilterNew(e.value)}
/>
<Select
>
<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%" }}
items={["Все заявки", "Новые", ]}
/>
>
<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="Выберите город"
@ -270,6 +388,10 @@ export const QuizAnswersPage: FC = () => {
/> */}
<Button
onClick={() => {
setFilterNew("Все заявки");
setFilterDate("За все время");
}}
sx={{
maxWidth: "200px",
width: "100%",
@ -330,7 +452,6 @@ export const QuizAnswersPage: FC = () => {
idResult={result.id}
dayResult={dayResult}
timeResult={timeResult}
onLossNew={onLossNew}
/>
);
})}
@ -340,12 +461,13 @@ export const QuizAnswersPage: FC = () => {
<FilterModal
open={filterModalOpen}
handleClose={() => setFilterModalOpen(false)}
filterNew = {filterNew}
filterDate = {filterDate}
setFilterNew = {setFilterNew}
setFilterDate = {setFilterDate}
filterNew={filterNew}
filterDate={filterDate}
setFilterNew={setFilterNew}
setFilterDate={setFilterDate}
/>
<ExportContactsModal
editQuizId={editQuizId}
open={exportContactsModalOpen}
handleClose={() => setExportContactsModalOpen(false)}
/>

@ -5,6 +5,7 @@ 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();
@ -13,8 +14,6 @@ let requestTimeoutId: ReturnType<typeof setTimeout>;
export const setResults = (results: RawResult[] | null) =>
setProducedState(
(state) => {
// console.log("!!!!!!!!!!!", results)
// console.log("65876567", Object.values(results)[1]?.map(rawResultToResult))
state.results = Object.values(results)[1]?.map(rawResultToResult) ?? [];
state.total_count = Object.values(results)[0];
},
@ -51,6 +50,67 @@ export const deleteResult = async (resultId: number) =>
}
});
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,

@ -6,19 +6,30 @@ import {
useMediaQuery,
useTheme,
} from "@mui/material";
import { FC } from "react";
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 }) => {
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",
@ -71,6 +82,9 @@ export const ExportContactsModal: FC<Iprops> = ({ open, handleClose }) => {
>
<Typography>c</Typography>
<DatePicker
onChange={(date) => {
setFromDate(date);
}}
slots={{
openPickerIcon: () => <CalendarIcon />,
}}
@ -112,6 +126,9 @@ export const ExportContactsModal: FC<Iprops> = ({ open, handleClose }) => {
<Typography>до</Typography>
<DatePicker
onChange={(date) => {
setToDate(date);
}}
slots={{
openPickerIcon: () => <CalendarIcon />,
}}
@ -145,11 +162,11 @@ export const ExportContactsModal: FC<Iprops> = ({ open, handleClose }) => {
</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", flexDirection: "column", gap: "14px" }}>*/}
{/* <CustomCheckbox label="Ответы в отдельных полях" />*/}
{/* <CustomCheckbox label="Cookies в отдельных полях" />*/}
{/* <CustomCheckbox label="Только верифицированные номера" />*/}
{/*</Box>*/}
<Box
sx={{
display: "flex",
@ -171,6 +188,9 @@ export const ExportContactsModal: FC<Iprops> = ({ open, handleClose }) => {
</Button>
<Button
variant="contained"
onClick={() =>
answerResultListExport(editQuizId, toDate, fromDate)
}
sx={{
width: isMobile ? "100%" : "130px",
height: "48px",

@ -19,10 +19,10 @@ const options = [
interface Iprops {
open: boolean;
handleClose: () => void;
filterNew: boolean
filterDate:string
setFilterNew: (a:string)=>void
setFilterDate:(a:string)=>void
filterNew: boolean;
filterDate: string;
setFilterNew: (a: string) => void;
setFilterDate: (a: string) => void;
}
export const FilterModal: FC<Iprops> = ({
@ -93,7 +93,7 @@ export const FilterModal: FC<Iprops> = ({
/>
<Select
sx={{ width: "100%" }}
items={["Все заявки", "Новые", ]}
items={["Все заявки", "Новые"]}
data={filterNew}
setData={setFilterNew}
/>