особые условия для вывода картинок
This commit is contained in:
parent
b0ff595f5d
commit
f4e8898400
@ -1,3 +1,4 @@
|
||||
1.0.4 _ 2025-09-14 _ особые условия для вывода картинок
|
||||
1.0.3 _ 2025-09-12 _ среднее время не учитывает нули
|
||||
1.0.2 _ 2025-09-07 _ добавлена автозапись в стейджинг
|
||||
1.0.1 Страница заявок корректно отображает мультиответ
|
||||
|
||||
@ -33,11 +33,17 @@ import { ReactComponent as ResetIcon } from "@icons/Analytics/reset.svg";
|
||||
import type { Moment } from "moment";
|
||||
import type { ReactNode } from "react";
|
||||
import type { Quiz } from "@model/quiz/quiz";
|
||||
import { useCurrentQuiz } from "@/stores/quizes/hooks";
|
||||
import { useQuestions } from "@/stores/questions/hooks";
|
||||
|
||||
export default function Analytics() {
|
||||
const { quizes, editQuizId } = useQuizStore();
|
||||
|
||||
const [quiz, setQuiz] = useState<Quiz>({} as Quiz);
|
||||
|
||||
const quiz = useCurrentQuiz();
|
||||
const globalQuestions = useQuestions({quizId: quiz?.backendId}).questions;
|
||||
|
||||
|
||||
|
||||
const [isOpen, setOpen] = useState<boolean>(false);
|
||||
const [isOpenEnd, setOpenEnd] = useState<boolean>(false);
|
||||
const [from, setFrom] = useState<Moment | null>(null);
|
||||
@ -47,29 +53,24 @@ export default function Analytics() {
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(600));
|
||||
|
||||
const { devices, general, questions, isLoading } = useAnalytics({
|
||||
ready: Boolean(Object.keys(quiz).length),
|
||||
quizId: editQuizId?.toString() || "",
|
||||
ready: quiz ? Boolean(Object.keys(quiz).length) : false,
|
||||
quizId: quiz?.backendId?.toString() || "",
|
||||
from,
|
||||
to,
|
||||
});
|
||||
|
||||
const resetTime = () => {
|
||||
setFrom(moment(new Date(quiz.created_at)));
|
||||
setFrom(moment(new Date(quiz?.created_at)));
|
||||
setTo(moment().add(1, "days"));
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (quizes.length > 0) {
|
||||
const quiz = quizes.find((q) => q.backendId === editQuizId);
|
||||
if (quiz === undefined) throw new Error("Не удалось получить квиз");
|
||||
setQuiz(quiz);
|
||||
setFrom(moment(new Date(quiz.created_at)));
|
||||
}
|
||||
}, [quizes]);
|
||||
if (quiz) setFrom(moment(new Date(quiz?.created_at)));
|
||||
}, [quiz]);
|
||||
|
||||
useEffect(() => {
|
||||
const getData = async (): Promise<void> => {
|
||||
if (editQuizId !== null) {
|
||||
if (quiz?.backendId !== null) {
|
||||
const [gottenQuizes, gottenQuizesError] = await quizApi.getList();
|
||||
|
||||
if (gottenQuizesError) {
|
||||
@ -86,8 +87,8 @@ export default function Analytics() {
|
||||
}, []);
|
||||
|
||||
useLayoutEffect(() => {
|
||||
if (editQuizId === undefined) redirect("/list");
|
||||
}, [editQuizId]);
|
||||
if (quiz?.backendId === undefined) redirect("/list");
|
||||
}, [quiz?.backendId]);
|
||||
|
||||
const handleClose = () => {
|
||||
setOpen(false);
|
||||
@ -264,7 +265,7 @@ export default function Analytics() {
|
||||
data={general}
|
||||
day={86400 - moment(to).unix() - moment(from).unix() > 0}
|
||||
/>
|
||||
<AnswersStatistics data={questions} />
|
||||
<AnswersStatistics data={questions} globalQuestions={globalQuestions}/>
|
||||
<Devices data={devices} />
|
||||
</>
|
||||
)}
|
||||
|
||||
@ -3,6 +3,7 @@ import type { PaginationRenderItemParams } from "@mui/material";
|
||||
import {
|
||||
Box,
|
||||
ButtonBase,
|
||||
IconButton,
|
||||
Input,
|
||||
LinearProgress,
|
||||
Pagination as MuiPagination,
|
||||
@ -19,6 +20,10 @@ import { ReactComponent as LeftArrowIcon } from "@icons/Analytics/leftArrow.svg"
|
||||
import { ReactComponent as RightArrowIcon } from "@icons/Analytics/rightArrow.svg";
|
||||
import { extractOrder } from "@utils/extractOrder";
|
||||
import { parseTitle } from "../utils/parseTitle";
|
||||
import { AnyTypedQuizQuestion } from "@frontend/squzanswerer";
|
||||
import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile';
|
||||
import { timewebContent, timewebContentFile } from "@/pages/QuizAnswersPage/cardAnswers/helper";
|
||||
import { useCurrentQuiz } from "@/stores/quizes/hooks";
|
||||
|
||||
type AnswerProps = {
|
||||
title: string;
|
||||
@ -28,6 +33,7 @@ type AnswerProps = {
|
||||
|
||||
type AnswersProps = {
|
||||
data: Record<string, Record<string, number>> | null;
|
||||
globalQuestions: AnyTypedQuizQuestion[];
|
||||
};
|
||||
|
||||
type PaginationProps = {
|
||||
@ -40,8 +46,13 @@ const Answer = ({ title, percent, highlight }: AnswerProps) => {
|
||||
const theme = useTheme();
|
||||
const parsedTitle = parseTitle(title);
|
||||
|
||||
console.log("Привет, я Answer. И вот что я о себе знаю:")
|
||||
console.log("-------------------------------------------------------------------")
|
||||
console.log("{ title, percent, highlight }")
|
||||
console.log({ title, percent, highlight })
|
||||
|
||||
return (
|
||||
<Box sx={{ padding: "15px 25px" }}>
|
||||
<Box sx={{ padding: "15px 25px", width: "100%" }}>
|
||||
<Box
|
||||
sx={{
|
||||
position: "relative",
|
||||
@ -199,7 +210,8 @@ const Pagination = ({ page, setPage, pagesAmount }: PaginationProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const Answers: FC<AnswersProps> = ({ data }) => {
|
||||
export const Answers: FC<AnswersProps> = ({ data, globalQuestions }) => {
|
||||
const quiz = useCurrentQuiz();
|
||||
const [page, setPage] = useState<number>(1);
|
||||
const theme = useTheme();
|
||||
const answers = useMemo(() => {
|
||||
@ -209,17 +221,32 @@ export const Answers: FC<AnswersProps> = ({ data }) => {
|
||||
);
|
||||
}, [data]);
|
||||
const currentAnswer = answers[page - 1];
|
||||
const globalQuestion = globalQuestions.find(e => {
|
||||
console.log("---")
|
||||
console.log("Привет, я ищу глоб вопро ", currentAnswer[0].slice(0, -4))
|
||||
console.log(" c ", e.title)
|
||||
console.log("---")
|
||||
console.log(e.title === currentAnswer[0].slice(0, -4))
|
||||
return e.title === currentAnswer[0].slice(0, -4)
|
||||
});
|
||||
|
||||
console.log("globalQuestionglobalQuestionglobalQuestionglobalQuestionglobalQuestionglobalQuestion")
|
||||
console.log(globalQuestion)
|
||||
|
||||
const percentsSum = Object.values(currentAnswer?.[1] ?? {}).reduce(
|
||||
(total, item) => (total += item),
|
||||
0,
|
||||
);
|
||||
|
||||
console.log("currentAnswer")
|
||||
console.log(currentAnswer)
|
||||
const currentAnswerExtended =
|
||||
percentsSum >= 100
|
||||
? Object.entries(currentAnswer?.[1] ?? {})
|
||||
: [
|
||||
...Object.entries(currentAnswer?.[1] ?? {}),
|
||||
["Другое", 100 - percentsSum] as [string, number],
|
||||
];
|
||||
...Object.entries(currentAnswer?.[1] ?? {}),
|
||||
["Другое", 100 - percentsSum] as [string, number],
|
||||
];
|
||||
|
||||
if (!data) {
|
||||
return (
|
||||
@ -286,16 +313,92 @@ export const Answers: FC<AnswersProps> = ({ data }) => {
|
||||
<NextIcon />
|
||||
</ButtonBase> */}
|
||||
</Box>
|
||||
{currentAnswerExtended.map(([title, percent], index) => (
|
||||
<Answer
|
||||
key={index}
|
||||
title={title}
|
||||
percent={percent}
|
||||
highlight={!index}
|
||||
/>
|
||||
))}
|
||||
</Paper>
|
||||
{currentAnswerExtended.map(([title, percent], index) => {
|
||||
console.log("kdgjhskjdfhkhsdgksfdhgjsdhfgkjsfdhgkj")
|
||||
console.log("ewrqwrwr")
|
||||
console.log("checkFileExtension")
|
||||
console.log(checkFileExtension(title))
|
||||
console.log("quiz?.backendId")
|
||||
console.log(quiz?.backendId)
|
||||
console.log("globalQuestion?.backendId")
|
||||
console.log(globalQuestion?.backendId)
|
||||
console.log("data")
|
||||
console.log(data)
|
||||
if (checkFileExtension(title) && quiz?.backendId && globalQuestion?.backendId) {
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
display: "inline - flex",
|
||||
width: "100 %",
|
||||
alignItems: "center",
|
||||
padding: "0 48px",
|
||||
paddingLeft: "26px"
|
||||
}}
|
||||
>
|
||||
<IconButton
|
||||
// <IconButton target="_blank" href={`/image/${title}`}
|
||||
sx={{
|
||||
width: "50px",
|
||||
height: "40px",
|
||||
bgcolor: "#e9eaf0",
|
||||
borderRadius: "5px"
|
||||
}}
|
||||
onClick={() => {
|
||||
|
||||
|
||||
|
||||
console.log(timewebContentFile(quiz?.qid.toString(), title, globalQuestion?.backendId.toString()))
|
||||
|
||||
const link = document.createElement('a');
|
||||
link.href = timewebContentFile(quiz?.qid.toString(), title, globalQuestion?.backendId.toString());
|
||||
link.download = title
|
||||
link.style.display = 'none';
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
|
||||
}}
|
||||
>
|
||||
<InsertDriveFileIcon />
|
||||
</IconButton>
|
||||
< Answer
|
||||
key={index}
|
||||
title={title}
|
||||
percent={percent}
|
||||
highlight={!index}
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
} else {
|
||||
return (
|
||||
< Answer
|
||||
key={index}
|
||||
title={title}
|
||||
percent={percent}
|
||||
highlight={!index}
|
||||
/>
|
||||
)
|
||||
}
|
||||
})}
|
||||
</Paper >
|
||||
<Pagination page={page} setPage={setPage} pagesAmount={answers.length} />
|
||||
</Box>
|
||||
</Box >
|
||||
);
|
||||
};
|
||||
function checkFileExtension(filename: string, maxLength = 6) {
|
||||
if (typeof filename !== 'string') return false;
|
||||
|
||||
// Ищем последнюю точку в строке
|
||||
const lastDotIndex = filename.lastIndexOf('.');
|
||||
|
||||
// Если точки нет или она в конце строки - возвращаем false
|
||||
if (lastDotIndex === -1 || lastDotIndex === filename.length - 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Получаем расширение (часть после последней точки)
|
||||
const extension = filename.slice(lastDotIndex + 1);
|
||||
|
||||
// Проверяем что расширение состоит только из букв и не превышает максимальную длину
|
||||
return /^[a-zA-Zа-яА-ЯёЁ]+$/.test(extension) && extension.length <= maxLength;
|
||||
}
|
||||
@ -5,12 +5,14 @@ import { QuestionsResponse } from "@api/statistic";
|
||||
import { FC } from "react";
|
||||
import { Funnel } from "./FunnelAnswers/Funnel";
|
||||
import { Results } from "./Results";
|
||||
import { AnyTypedQuizQuestion } from "@frontend/squzanswerer";
|
||||
|
||||
type AnswersStatisticsProps = {
|
||||
data: QuestionsResponse | null;
|
||||
globalQuestions: AnyTypedQuizQuestion[];
|
||||
};
|
||||
|
||||
export const AnswersStatistics: FC<AnswersStatisticsProps> = ({ data }) => {
|
||||
export const AnswersStatistics: FC<AnswersStatisticsProps> = ({ data, globalQuestions }) => {
|
||||
const theme = useTheme();
|
||||
const isSmallMonitor = useMediaQuery(theme.breakpoints.down(1150));
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down(850));
|
||||
@ -33,7 +35,7 @@ export const AnswersStatistics: FC<AnswersStatisticsProps> = ({ data }) => {
|
||||
gap: "40px",
|
||||
}}
|
||||
>
|
||||
<Answers data={data?.Questions || null} />
|
||||
<Answers data={data?.Questions || null} globalQuestions={globalQuestions} />
|
||||
<Funnel data={data?.Funnel || []} funnelData={data?.FunnelData || []} />
|
||||
</Box>
|
||||
<Results data={data?.Results || null} />
|
||||
|
||||
@ -8,13 +8,20 @@ import { useQuestionsStore } from "./store";
|
||||
import { useCurrentQuiz } from "@root/quizes/hooks";
|
||||
import { useEffect } from "react";
|
||||
|
||||
export function useQuestions() {
|
||||
const quiz = useCurrentQuiz();
|
||||
const { isLoading, error, isValidating } = useSWR(
|
||||
["questions", quiz?.backendId],
|
||||
export function useQuestions({ quizId }: { quizId?: number } = {}) {
|
||||
const currentQuiz = useCurrentQuiz();
|
||||
const currentQuizId = quizId ?? currentQuiz?.backendId;
|
||||
|
||||
const { data, isLoading, error, isValidating } = useSWR(
|
||||
currentQuizId ? ["questions", currentQuizId] : null,
|
||||
([, id]) => questionApi.getList({ quiz_id: id }),
|
||||
{
|
||||
onSuccess: ([questions]) => setQuestions(questions),
|
||||
onSuccess: (data) => {
|
||||
// Добавляем проверку на существование данных
|
||||
if (data && Array.isArray(data[0])) {
|
||||
setQuestions(data[0]);
|
||||
}
|
||||
},
|
||||
onError: (error) => {
|
||||
const message = isAxiosError<string>(error)
|
||||
? error.response?.data ?? ""
|
||||
@ -25,7 +32,13 @@ export function useQuestions() {
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const questions = useQuestionsStore((state) => state.questions);
|
||||
|
||||
return { questions, isLoading, error, isValidating };
|
||||
return {
|
||||
questions: questions || [], // Гарантируем возврат массива
|
||||
isLoading,
|
||||
error,
|
||||
isValidating
|
||||
};
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user