Merge branch 'dev' into staging

This commit is contained in:
Nastya 2024-04-17 19:47:14 +03:00
commit 9bbc1f6d4c
20 changed files with 149 additions and 88 deletions

@ -6,26 +6,38 @@ import { clearUserData } from "@root/user";
import { clearQuizData } from "@root/quizes/store";
import { redirect } from "react-router-dom";
interface MakeRequest {
method?: Method | undefined;
url: string;
body?: unknown;
useToken?: boolean | undefined;
contentType?: boolean | undefined;
responseType?: ResponseType | undefined;
signal?: AbortSignal | undefined;
withCredentials?: boolean | undefined;
}
interface MakeRequest { method?: Method | undefined; url: string; body?: unknown; useToken?: boolean | undefined; contentType?: boolean | undefined; responseType?: ResponseType | undefined; signal?: AbortSignal | undefined; withCredentials?: boolean | undefined; }
async function makeRequest<TRequest = unknown, TResponse = unknown>(
data: MakeRequest,
): Promise<TResponse> {
try {
const response = await KIT.makeRequest<unknown>(data);
async function makeRequest<TRequest = unknown, TResponse = unknown>(data: MakeRequest): Promise<TResponse> {
try {
const response = await KIT.makeRequest<unknown>(data)
return response as TResponse
} catch (e) {
const error = e as AxiosError;
//@ts-ignore
if (error.response?.status === 400 && error.response?.data?.message === "refreshToken is empty") {
cleanAuthTicketData();
clearAuthToken();
clearUserData();
clearQuizData();
redirect("/");
}
throw e
};
};
export default makeRequest;
return response as TResponse;
} catch (e) {
const error = e as AxiosError;
//@ts-ignore
if (
error.response?.status === 400 &&
error.response?.data?.message === "refreshToken is empty"
) {
cleanAuthTicketData();
clearAuthToken();
clearUserData();
clearQuizData();
redirect("/");
}
throw e;
}
}
export default makeRequest;

Binary file not shown.

After

Width:  |  Height:  |  Size: 591 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 433 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 299 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 409 KiB

@ -154,6 +154,7 @@ export default function Analytics() {
onClose={handleClose}
onOpen={handleOpen}
// defaultValue={now}
minDate={moment(quiz?.created_at)}
sx={{
width: isMobile ? "285px" : "170px",
"& .MuiOutlinedInput-root": {
@ -199,6 +200,7 @@ export default function Analytics() {
onClose={handleCloseEnd}
onOpen={handleOpenEnd}
// defaultValue={now}
minDate={moment(quiz?.created_at)}
sx={{
width: isMobile ? "285px" : "170px",
"& .MuiOutlinedInput-root": {

@ -112,30 +112,48 @@ const GeneralItemTimeConv = ({
numberType,
calculateTime = false,
conversionValue,
day,
}: GeneralItemsProps) => {
const theme = useTheme();
const isMobile = useMediaQuery(theme.breakpoints.down(700));
const data = Object.entries(general).sort(
([nextValue], [currentValue]) => Number(nextValue) - Number(currentValue),
);
const days = data.map(([value]) => value);
const time = data.map(([_, value]) => value);
const data = Object.entries(general)
.sort((a, b) => a[0] - b[0]);
const numberValue = calculateTime
? time.reduce((total, value) => total + value, 0) / days.length
: conversionValue;
const days = [...data].map(e => e[0])
let buffer = 0
const time = [...data].map(e => {
if (e[1] > 0) {
buffer = e[1]
}
return buffer
})
console.log("data", data)
console.log("time", time.reduce((a, b) => (Number(a) + Number(b)), 0))
console.log("time", getCalculatedTime(time.reduce((a, b) => (Number(a) + Number(b)), 0)))
console.log("days", days.length)
const numberValue = calculateTime ?
(
(time.reduce((a, b) => (Number(a) + Number(b)), 0))
/
(days.length)
) || 0
:
conversionValue
if (
Object.keys(general).length === 0 ||
Object.values(general).every((item) => item === 0)
Object.values(general).every((x) => x === 0)
) {
return (
<Typography textAlign="center">{`${title} - нет данных`}</Typography>
);
}
return (
<Paper
sx={{
@ -146,25 +164,24 @@ const GeneralItemTimeConv = ({
>
<Typography sx={{ margin: "20px 20px 0" }}>{title}</Typography>
<Typography sx={{ margin: "10px 20px 0", fontWeight: "bold" }}>
{calculateTime
? `${getCalculatedTime(numberValue ?? 0)} с`
: `${numberValue?.toFixed(2) ?? 0}%`}
{calculateTime ? `${getCalculatedTime(numberValue)} с` : `${numberValue.toFixed(2)}%`}
</Typography>
<LineChart
xAxis={[
{
data: days,
valueFormatter: (value) =>
moment.unix(Number(value)).format("DD/MM/YYYY HH") + "ч",
moment.utc(Number(value) * 1000).format("DD/MM/YYYY"),
},
]}
series={[
{
data: Object.values(time),
valueFormatter: (value) =>
calculateTime
? getCalculatedTime(value)
: String((value * 100).toFixed(2)) + "%",
valueFormatter: (value) => {
console.log("log", value)
return calculateTime ? getCalculatedTime(value) : String((value*100).toFixed(2)) + "%"
}
,
},
]}
// dataset={Object.entries(general).map(([, v]) => moment.unix(v).format("ss:mm:HH")).reduce((acc, [k, v]) => ({ ...acc, [k]: v }), {})}
@ -179,6 +196,7 @@ const GeneralItemTimeConv = ({
);
};
export const General: FC<GeneralProps> = ({ data, day }) => {
const theme = useTheme();
const isTablet = useMediaQuery(theme.breakpoints.down(1000));

@ -8,20 +8,19 @@ import {
} from "@mui/material";
import { updateQuiz } from "@root/quizes/actions";
import { useCurrentQuiz } from "@root/quizes/hooks";
import type { DesignItem } from "./DesignGroup";
import { DesignGroup } from "./DesignGroup";
import Desgin1 from "@icons/designs/design1.jpg";
import Desgin2 from "@icons/designs/design2.jpg";
import Desgin3 from "@icons/designs/design3.jpg";
import Desgin4 from "@icons/designs/design4.jpg";
import Desgin5 from "@icons/designs/design5.jpg";
import Desgin6 from "@icons/designs/design6.jpg";
import Desgin7 from "@icons/designs/design7.jpg";
import Desgin8 from "@icons/designs/design8.jpg";
import Desgin9 from "@icons/designs/design9.jpg";
import Desgin10 from "@icons/designs/design10.jpg";
import type { DesignItem } from "./DesignGroup";
import Desgin1 from "@icons/designs/smallSize/design1.jpg";
import Desgin2 from "@icons/designs/smallSize/design2.jpg";
import Desgin3 from "@icons/designs/smallSize/design3.jpg";
import Desgin4 from "@icons/designs/smallSize/design4.jpg";
import Desgin5 from "@icons/designs/smallSize/design5.jpg";
import Desgin6 from "@icons/designs/smallSize/design6.jpg";
import Desgin7 from "@icons/designs/smallSize/design7.jpg";
import Desgin8 from "@icons/designs/smallSize/design8.jpg";
import Desgin9 from "@icons/designs/smallSize/design9.jpg";
import Desgin10 from "@icons/designs/smallSize/design10.jpg";
const LIGHT_THEME_BUTTONS: DesignItem[] = [
{

@ -76,8 +76,10 @@ export default function InstallQuiz() {
const isMobile = useMediaQuery(theme.breakpoints.down(600));
const isSmallMonitor = useMediaQuery(theme.breakpoints.down(1065));
const CopyLink = () => {
let one = document.getElementById("inputLinkone").value;
let text = document.getElementById("inputLink").value;
let one = (document.getElementById("inputLinkone") as HTMLInputElement)
?.value;
let text = (document.getElementById("inputLink") as HTMLInputElement)
?.value;
// text.select();
navigator.clipboard.writeText(one + text);
// document.execCommand("copy");
@ -408,7 +410,7 @@ export default function InstallQuiz() {
id="outlined-multiline-static"
multiline
rows={9}
value={`<div id="idpena"></div> <script type="module"> import widget from "https://s.hbpn.link/export/pub.js"; widget.create({ selector: "idpena", quizId: ${quiz.qid} }) </script>`}
value={`<div id="idpena"></div> <script type="module"> import widget from "https://${isTestServer ? "s." : ""}hbpn.link/export/pub.js"; widget.create({ selector: "idpena", quizId: ${quiz.qid} }) </script>`}
sx={{
"& .MuiInputBase-root": {
maxWidth: "520px",

@ -140,7 +140,7 @@ function TariffPage() {
link.href = `https://${isTestServer ? "s" : ""}hub.pena.digital/quizpayment?action=squizpay&dif=${cashDif}&data=${token}&userid=${userId}`;
document.body.appendChild(link);
link.click();
return
return;
}
//другая ошибка
enqueueSnackbar("Произошла ошибка. Попробуйте позже");
@ -171,18 +171,18 @@ function TariffPage() {
return tariff.privileges[0].privilegeId !== "squizHideBadge";
});
function handleApplyPromocode () {
function handleApplyPromocode() {
if (!promocodeField) return;
activatePromocode(promocodeField)
.then(async (greetings) => {
enqueueSnackbar(greetings)
enqueueSnackbar(greetings);
const discounts = await makeRequest({
method: "GET",
url: `${process.env.REACT_APP_DOMAIN}/price/discount/user/${userId}`,
});
setDiscounts(discounts.Discounts);
const discounts = await makeRequest({
method: "GET",
url: `${process.env.REACT_APP_DOMAIN}/price/discount/user/${userId}`,
});
setDiscounts(discounts.Discounts);
})
.catch(enqueueSnackbar);
}

@ -1,4 +1,4 @@
import React, { FC, useEffect, useRef, useState } from "react";
import React, { ChangeEvent, FC, useEffect, useRef, useState } from "react";
import {
Box,
FormControl,
@ -49,7 +49,7 @@ export const SidebarMobile: FC<Iprops> = ({
const [inputOpen, setInputOpen] = useState<boolean>(false);
const quiz = useCurrentQuiz();
const [inputValue, setInputValue] = useState(quiz.name);
const ref = useRef(null);
const ref = useRef<HTMLInputElement | null>(null);
const heightSidebar = useRef(null);
const navigate = useNavigate();
const { pathname } = useLocation();
@ -62,15 +62,19 @@ export const SidebarMobile: FC<Iprops> = ({
);
useEffect(() => {
observer.current.observe(heightSidebar.current);
if (heightSidebar.current) {
observer.current.observe(heightSidebar.current);
}
}, [heightSidebar, observer]);
const handleClick = (event) => {
const handleClick = (event: ChangeEvent<HTMLDivElement>) => {
setAnchorEl(anchorEl ? null : event.currentTarget);
};
const clickInput = (event) => {
if (ref.current && !ref.current.contains(event.target)) setInputOpen(false);
const clickInput = (event: MouseEvent) => {
debugger;
if (ref.current && !ref.current?.contains(event.target as Node))
setInputOpen(false);
};
useEffect(() => {
document.addEventListener("mousedown", clickInput);
@ -88,7 +92,7 @@ export const SidebarMobile: FC<Iprops> = ({
changePage(index);
};
const openPopper = Boolean(anchorEl);
const id = openPopper ? "simple-popper" : undefined;
const id = openPopper ? "simple-popper" : "";
return (
<Box
ref={heightSidebar}

@ -6,7 +6,7 @@ type SidebarModalProps = {
open: boolean;
handleClick: () => void;
changePage: (step: number) => void;
anchorEl: HTMLElement;
anchorEl: HTMLElement | null;
id: string;
};
export const SidebarModal = ({

@ -10,7 +10,7 @@ import {
import { Quiz } from "@model/quiz/quiz";
export const checkQuestionHint = (
questions: AnyTypedQuizQuestion,
questions: AnyTypedQuizQuestion[],
quiz: Quiz,
): Record<string, WhyCantCreatePublic> => {
const problems: any = {};
@ -77,12 +77,17 @@ export const checkQuestionHint = (
(condition: QuestionBranchingRuleMain) => {
buffer.forEach((oldCondition: QuestionBranchingRuleMain) => {
if (areRulesEqual(condition.rules, oldCondition.rules)) {
const q = getQuestionByContentId(condition.next);
const oldq = getQuestionByContentId(oldCondition.next);
const currentQuestion = getQuestionByContentId(condition.next);
const oldQuestions = getQuestionByContentId(oldCondition.next);
if (!currentQuestion?.type || !oldQuestions?.type) {
return;
}
pushProblem(
question.content.id,
`У вопроса "${q?.title || "noname №" + q?.page}" и "${
oldq?.title || "noname №" + oldq?.page
`У вопроса "${currentQuestion.title || "noname №" + currentQuestion.page}" и "${
oldQuestions.title || "noname №" + oldQuestions.page
}" одинаковые условия ветвления`,
question.title,
);

@ -1,4 +1,3 @@
import { AnyTypedQuizQuestion } from "@model/questionTypes/shared";
import {
clearRuleForAll,
createResult,
@ -10,6 +9,13 @@ import { useQuestionsStore } from "@root/questions/store";
import { updateRootContentId } from "@root/quizes/actions";
import { getCurrentQuiz } from "@root/quizes/hooks";
import type {
AnyTypedQuizQuestion,
QuestionBranchingRule,
QuestionBranchingRuleMain,
} from "@model/questionTypes/shared";
import { QuizQuestionResult } from "@model/questionTypes/result";
//Всё здесь нужно сделать последовательно. И пусть весь мир ждёт.
export const DeleteFunction = async (questionId: string) => {
@ -33,7 +39,9 @@ export const DeleteFunction = async (questionId: string) => {
const parentQuestion = getQuestionByContentId(
question.content.rule.parentId,
);
let startCountParentChildren = parentQuestion.content.rule.children;
let startCountParentChildren = parentQuestion?.type
? parentQuestion.content.rule.children
: null;
//записываем потомков , а их результаты удаляем
const getChildren = (parentQuestion: AnyTypedQuizQuestion) => {
@ -67,8 +75,10 @@ export const DeleteFunction = async (questionId: string) => {
}),
);
//чистим rule родителя
const newRule = {};
if (!parentQuestion?.type) {
return;
}
const parentChildren = [...parentQuestion.content.rule.children];
if (parentChildren.includes(question.content.id))
@ -77,15 +87,21 @@ export const DeleteFunction = async (questionId: string) => {
1,
);
newRule.main = parentQuestion.content.rule.main.filter(
(data) => data.next !== question.content.id,
const main = parentQuestion.content.rule.main.filter(
(data: QuestionBranchingRuleMain) => data.next !== question.content.id,
); //удаляем условия перехода от родителя к этому вопросу
newRule.parentId = parentQuestion.content.rule.parentId;
newRule.default =
const defaultValue =
parentQuestion.content.rule.parentId === question.content.id
? ""
: parentQuestion.content.rule.parentId;
newRule.children = parentChildren;
//чистим rule родителя
const newRule: QuestionBranchingRule = {
main,
default: defaultValue,
children: parentChildren,
parentId: parentQuestion.content.rule.parentId,
};
await updateQuestion(question.content.rule.parentId, (PQ) => {
PQ.content.rule = newRule;
@ -101,10 +117,13 @@ export const DeleteFunction = async (questionId: string) => {
//сделать результ родителя видимым если у него не осталось потомков
if (startCountParentChildren.length === 1) {
if (parentResult) {
await updateQuestion(parentResult.content.id, (q) => {
q.content.usage = true;
});
if (parentResult?.type) {
await updateQuestion<QuizQuestionResult>(
parentResult.content.id,
(item) => {
item.content.usage = true;
},
);
} else {
//почему-то не существует результа у родителя. Создаём. Новосозданные результы видны сразу
await createResult(quiz.backendId, parentQuestion.content.id);