Merge branch 'dev' of penahub.gitlab.yandexcloud.net:frontend/squiz into dev

This commit is contained in:
ArtChaos189 2024-01-24 16:06:17 +03:00
commit 1ec03d04d7
26 changed files with 499 additions and 113 deletions

@ -1,2 +1 @@
REACT_APP_DOMAIN="https://squiz.pena.digital"
REACT_APP_HUB_DOMAIN="https://shub.pena.digital"

@ -1,2 +1 @@
REACT_APP_DOMAIN="https://squiz.pena.digital"
REACT_APP_HUB_DOMAIN="https://shub.pena.digital"
REACT_APP_DOMAIN="https://squiz.pena.digital"

@ -6,7 +6,7 @@
"@craco/craco": "^7.0.0",
"@emotion/react": "^11.10.5",
"@emotion/styled": "^11.10.5",
"@frontend/kitui": "^1.0.55",
"@frontend/kitui": "^1.0.62",
"@mui/icons-material": "^5.10.14",
"@mui/material": "^5.10.14",
"@mui/x-date-pickers": "^6.16.1",

@ -43,10 +43,12 @@ import {
import { enqueueSnackbar } from "notistack";
import PrivateRoute from "@ui_kit/PrivateRoute";
import { Restore } from "./pages/startPage/Restore";
import { Restore } from "./pages/auth/Restore";
import { isAxiosError } from "axios";
import { useEffect, useLayoutEffect, useRef } from "react";
import RecoverPassword from "./pages/auth/RecoverPassword";
import OutdatedLink from "./pages/auth/OutdatedLink";
export function useUserAccountFetcher({
onError,
onNewUserAccount,
@ -183,7 +185,9 @@ export default function App() {
<Routes>
<Route path="/signin" element={<SigninDialog />} />
<Route path="/signup" element={<SignupDialog />} />
<Route path="/restore" element={<Restore />} />
<Route path="//recover" element={<Restore />} />
<Route path="/changepwd" element={<RecoverPassword />} />
<Route path="/changepwd/expired" element={<OutdatedLink />} />
</Routes>
)}
<Routes location={location.state?.backgroundLocation || location}>
@ -201,9 +205,31 @@ export default function App() {
}
/>
<Route
path="/restore"
path="/recover"
element={
<Navigate to="/" replace state={{ redirectTo: "/restore" }} />
<Navigate to="/" replace state={{ redirectTo: "/recover" }} />
}
/>
<Route
path="/changepwd"
element={
<Navigate
to="/"
replace
state={{
redirectTo: window.location.pathname + window.location.search,
}}
/>
}
/>
<Route
path="/changepwd/expired"
element={
<Navigate
to="/"
replace
state={{ redirectTo: "/changepwd/expired" }}
/>
}
/>
<Route path="/list" element={<MyQuizzesFull />} />

@ -8,7 +8,7 @@ import type {
} from "@frontend/kitui";
import { parseAxiosError } from "../utils/parse-error";
const apiUrl =process.env.REACT_APP_DOMAIN + "/auth";
const apiUrl = process.env.REACT_APP_DOMAIN + "/auth";
export async function register(
login: string,
@ -70,3 +70,29 @@ export async function logout(): Promise<[unknown, string?]> {
return [null, `Не удалось выйти. ${error}`];
}
}
export async function recover(
email: string,
): Promise<[unknown | null, string?]> {
try {
const formData = new FormData();
formData.append("email", email);
formData.append(
"RedirectionURL",
process.env.REACT_APP_DOMAIN + "/changepwd",
);
const recoverResponse = await makeRequest<unknown, unknown>({
url: process.env.REACT_APP_DOMAIN + "/codeword/recover",
body: formData,
useToken: false,
withCredentials: true,
});
console.log(recoverResponse);
return [recoverResponse];
} catch (nativeError) {
console.log(nativeError);
const [error] = parseAxiosError(nativeError);
return [null, `Не удалось восстановить пароль. ${error}`];
}
}

@ -1,6 +1,6 @@
import axios from "axios";
const domen = process.env.REACT_APP_HUB_DOMAIN
const domen = process.env.REACT_APP_DOMAIN;
export function sendContactFormRequest(body: {
contact: string;

@ -18,7 +18,7 @@ import {
CopyQuestionResponse,
} from "@model/question/copy";
const baseUrl = process.env.REACT_APP_DOMAIN ;
const baseUrl = process.env.REACT_APP_DOMAIN;
function get(quizId: string) {
return makeRequest<any>({

@ -50,7 +50,7 @@ export interface Quiz {
question_cnt: number;
/** count passings */
passed_count: number;
sessions_count: number;
session_count: number;
/** average time of passing */
average_time: number;
/** set true if squiz realize group functionality */
@ -109,7 +109,7 @@ export interface RawQuiz {
question_cnt: number;
/** count passings */
passed_count: number;
sessions_count: number;
session_count: number;
/** average time of passing */
average_time: number;
/** set true if squiz realize group functionality */

@ -127,13 +127,13 @@ export default function Component() {
>
Пользовательское соглашение
</Button>
<Button
<Button
variant="text"
sx={{ fontSize: "16px", fontWeight: 500, color: "white" }}
href={"https://hub.pena.digital/docs/privacy"}
target="_blank"
>
Политика конфиденциальности
Политика конфиденциальности
</Button>
</Box>
</Box>

@ -67,18 +67,18 @@ export default function SettingOptionsAndPict({
>
Настройки ответов
</Typography>
<CustomCheckbox
sx={{ mr: isMobile ? "0px" : "16px" }}
label={'Вариант "свой ответ"'}
checked={question.content.own}
handleChange={({ target }) =>
updateQuestion(question.id, (question) => {
if (question.type !== "varimg") return;
{/*<CustomCheckbox*/}
{/* sx={{ mr: isMobile ? "0px" : "16px" }}*/}
{/* label={'Вариант "свой ответ"'}*/}
{/* checked={question.content.own}*/}
{/* handleChange={({ target }) =>*/}
{/* updateQuestion(question.id, (question) => {*/}
{/* if (question.type !== "varimg") return;*/}
question.content.own = target.checked;
})
}
/>
{/* question.content.own = target.checked;*/}
{/* })*/}
{/* }*/}
{/*/>*/}
{!isWrappColumn && (
<Box sx={{ mt: isMobile ? "11px" : "6px", width: "100%" }}>
<Typography

@ -90,7 +90,6 @@ export const ResultSettings = () => {
}}
>
<Typography variant="h5">Настройки результатов</Typography>
<Info />
<Button
disableRipple
sx={{

@ -65,7 +65,9 @@ function TariffPage() {
for (let page = 2; page <= totalPages; page += 1) {
const tariffsResult = await makeRequest<never, GetTariffsResponse>({
method: "GET",
url: process.env.REACT_APP_DOMAIN + `/strator/tariff?page=${page}&limit=100`,
url:
process.env.REACT_APP_DOMAIN +
`/strator/tariff?page=${page}&limit=100`,
});
console.log(page);
@ -109,7 +111,7 @@ function TariffPage() {
//Добавляем желаемый тариф в корзину
await makeRequest({
method: "PATCH",
url: process.env.REACT_APP_HUB_DOMAIN + `/customer/cart?id=${id}`,
url: process.env.REACT_APP_DOMAIN + `/customer/cart?id=${id}`,
});
//Если нам хватает денежек - покупаем тариф
if (price <= user.wallet.cash) {
@ -130,9 +132,11 @@ function TariffPage() {
// history.pushState({}, null, "https://hub.pena.digital/wallet?action=squizpay");
var link = document.createElement("a");
link.href = process.env.REACT_APP_HUB_DOMAIN + `/payment?action=squizpay&dif=${
(price - Number(user.wallet.cash)) * 100
}`;
link.href =
process.env.REACT_APP_DOMAIN +
`/payment?action=squizpay&dif=${
(price - Number(user.wallet.cash)) * 100
}`;
document.body.appendChild(link);
// link.click();
}
@ -300,7 +304,7 @@ export const inCart = () => {
try {
await makeRequest({
method: "PATCH",
url: process.env.REACT_APP_HUB_DOMAIN + `/customer/cart?id=${id}`,
url: process.env.REACT_APP_DOMAIN + `/customer/cart?id=${id}`,
});
let index = saveCart.indexOf("green");

@ -0,0 +1,137 @@
import {
Box,
Dialog,
IconButton,
Typography,
useMediaQuery,
useTheme,
Button,
} from "@mui/material";
import CloseIcon from "@mui/icons-material/Close";
import { useLocation, useNavigate } from "react-router-dom";
import PenaLogo from "../Landing/images/icons/QuizLogo";
import { useEffect, useState } from "react";
import { useUserStore } from "../../stores/user";
// import { cardShadow } from "@root/utils/theme";
export default function OutdatedLink() {
const [isDialogOpen, setIsDialogOpen] = useState<boolean>(true);
const user = useUserStore((state) => state.user);
const theme = useTheme();
const upMd = useMediaQuery(theme.breakpoints.up("md"));
const navigate = useNavigate();
const location = useLocation();
useEffect(
function redirectIfSignedIn() {
if (user) navigate("/tariffs", { replace: true });
},
[navigate, user],
);
function handleClose() {
setIsDialogOpen(false);
setTimeout(() => navigate("/"), theme.transitions.duration.leavingScreen);
}
return (
<Dialog
open={isDialogOpen}
onClose={handleClose}
PaperProps={{
sx: {
width: "600px",
maxWidth: "600px",
},
}}
slotProps={{
backdrop: {
style: {
backgroundColor: "rgb(0 0 0 / 0.7)",
},
},
}}
>
<Box
component="form"
sx={{
position: "relative",
backgroundColor: "white",
display: "flex",
alignItems: "center",
flexDirection: "column",
p: upMd ? "50px" : "18px",
pb: upMd ? "40px" : "30px",
gap: "15px",
borderRadius: "12px",
// boxShadow: cardShadow,
"& .MuiFormHelperText-root.Mui-error, & .MuiFormHelperText-root.Mui-error.MuiFormHelperText-filled":
{
position: "absolute",
top: "46px",
margin: "0",
},
}}
>
<IconButton
onClick={handleClose}
sx={{
position: "absolute",
right: "7px",
top: "7px",
}}
>
<CloseIcon sx={{ transform: "scale(1.5)" }} />
</IconButton>
<Box>
<PenaLogo width={upMd ? 233 : 196} color="black" />
</Box>
<Typography
sx={{
color: "#4D4D4D",
mt: "5px",
mb: upMd ? "10px" : "33px",
}}
>
Внимание! Ссылка устарела!
</Typography>
<Typography
sx={{
color: "#4D4D4D",
mt: "5px",
mb: upMd ? "10px" : "33px",
}}
>
Срок действия ссылки истёк, пожалуйста повторите попытку
восстановления пароля
</Typography>
<Button
variant="contained"
fullWidth
onClick={() => navigate("/")}
sx={{
py: "12px",
"&:hover": {
backgroundColor: "#581CA7",
},
"&:active": {
color: "white",
backgroundColor: "black",
},
}}
>
На главную
</Button>
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: "10px",
mt: "auto",
}}
></Box>
</Box>
</Dialog>
);
}

@ -0,0 +1,198 @@
import {
Box,
Dialog,
IconButton,
Link,
Typography,
useMediaQuery,
useTheme,
Button,
} from "@mui/material";
import CloseIcon from "@mui/icons-material/Close";
import { useLocation, useNavigate } from "react-router-dom";
import { useFormik } from "formik";
import InputTextfield from "@ui_kit/InputTextfield";
import PenaLogo from "../Landing/images/icons/QuizLogo";
import { enqueueSnackbar } from "notistack";
import { object, string } from "yup";
import { useEffect, useState } from "react";
import { useUserStore } from "@root/user";
import { getAuthToken, makeRequest, setAuthToken } from "@frontend/kitui";
import { FormContactFieldName } from "@model/quizSettings";
import { parseAxiosError } from "@utils/parse-error";
interface Values {
password: string;
}
const initialValues: Values = {
password: "",
};
const validationSchema = object({
password: string()
.min(8, "Минимум 8 символов")
.matches(/^[.,:;-_+\d\w]+$/, "Некорректные символы")
.required("Поле обязательно"),
});
export default function RecoverPassword() {
const [isDialogOpen, setIsDialogOpen] = useState<boolean>(true);
const [tokenUser, setTokenUser] = useState<string>("");
const user = useUserStore((state) => state.user);
const theme = useTheme();
const upMd = useMediaQuery(theme.breakpoints.up("md"));
const navigate = useNavigate();
const location = useLocation();
const formik = useFormik<Values>({
initialValues,
validationSchema,
onSubmit: async (values, formikHelpers) => {
if (tokenUser) {
setAuthToken(tokenUser || "");
try {
const response = await makeRequest<unknown, unknown>({
url: process.env.REACT_APP_DOMAIN + "/user/",
method: "PATCH",
body: { password: values.password },
});
setIsDialogOpen(false);
navigate("/");
enqueueSnackbar("Пароль успешно сменён");
} catch (nativeError) {
const [error] = parseAxiosError(nativeError);
setAuthToken("");
enqueueSnackbar(
`Извините, произошла ошибка, попробуйте повторить позже. ${error}`,
);
}
} else {
enqueueSnackbar("Неверный url-адрес");
}
},
});
useEffect(() => {
const params = new URLSearchParams(window.location.search);
const authToken = params.get("auth");
setTokenUser(authToken);
history.pushState(null, document.title, "/changepwd");
return () => {
setAuthToken("");
};
}, []);
console.log(tokenUser);
function handleClose() {
setIsDialogOpen(false);
setTimeout(() => navigate("/"), theme.transitions.duration.leavingScreen);
}
return (
<Dialog
open={isDialogOpen}
onClose={handleClose}
PaperProps={{
sx: {
width: "600px",
maxWidth: "600px",
},
}}
slotProps={{
backdrop: {
style: {
backgroundColor: "rgb(0 0 0 / 0.7)",
},
},
}}
>
<Box
component="form"
onSubmit={formik.handleSubmit}
noValidate
sx={{
position: "relative",
backgroundColor: "white",
display: "flex",
alignItems: "center",
flexDirection: "column",
p: upMd ? "50px" : "18px",
pb: upMd ? "40px" : "30px",
gap: "15px",
borderRadius: "12px",
// boxShadow: cardShadow,
"& .MuiFormHelperText-root.Mui-error, & .MuiFormHelperText-root.Mui-error.MuiFormHelperText-filled":
{
position: "absolute",
top: "46px",
margin: "0",
},
}}
>
<IconButton
onClick={handleClose}
sx={{
position: "absolute",
right: "7px",
top: "7px",
}}
>
<CloseIcon sx={{ transform: "scale(1.5)" }} />
</IconButton>
<Box>
<PenaLogo width={upMd ? 233 : 196} color="black" />
</Box>
<Typography
sx={{
color: "#4D4D4D",
mt: "5px",
mb: upMd ? "10px" : "33px",
}}
>
Введите новый пароль
</Typography>
<InputTextfield
TextfieldProps={{
value: formik.values.password,
placeholder: "введите пароль",
onBlur: formik.handleBlur,
error: formik.touched.password && Boolean(formik.errors.password),
helperText: formik.touched.password && formik.errors.password,
}}
onChange={formik.handleChange}
color="#F2F3F7"
id="password"
label="Новый пароль"
gap={upMd ? "10px" : "10px"}
/>
<Button
variant="contained"
fullWidth
type="submit"
disabled={formik.isSubmitting}
sx={{
py: "12px",
"&:hover": {
backgroundColor: "#581CA7",
},
"&:active": {
color: "white",
backgroundColor: "black",
},
}}
>
Восстановить
</Button>
<Box
sx={{
display: "flex",
flexDirection: "column",
alignItems: "center",
gap: "10px",
mt: "auto",
}}
></Box>
</Box>
</Dialog>
);
}

@ -11,35 +11,25 @@ import {
useTheme,
} from "@mui/material";
import InputTextfield from "@ui_kit/InputTextfield";
import PasswordInput from "@ui_kit/passwordInput";
import { useFormik } from "formik";
import { object, ref, string } from "yup";
import Logotip from "../Landing/images/icons/QuizLogo";
import { useNavigate, Link as RouterLink, useLocation } from "react-router-dom";
import { enqueueSnackbar } from "notistack";
import { recover } from "@api/auth";
interface Values {
email: string;
password: string;
repeatPassword: string;
}
const initialValues: Values = {
email: "",
password: "",
repeatPassword: "",
};
const validationSchema = object({
email: string()
.required("Поле обязательно")
.email("Введите корректный email"),
password: string()
.min(8, "Минимум 8 символов")
.matches(/^[.,:;-_+\d\w]+$/, "Некорректные символы")
.required("Поле обязательно"),
repeatPassword: string()
.oneOf([ref("password"), undefined], "Пароли не совпадают")
.required("Повторите пароль"),
});
export const Restore: FC = () => {
@ -52,7 +42,19 @@ export const Restore: FC = () => {
const formik = useFormik<Values>({
initialValues,
validationSchema,
onSubmit: (values) => {},
onSubmit: async (values, formikHelpers) => {
const [recoverResponse, recoverError] = await recover(
values.email.trim(),
);
formikHelpers.setSubmitting(false);
if (recoverError) {
enqueueSnackbar(recoverError);
return;
}
navigate("/");
enqueueSnackbar("Письмо прийдёт Вам на почту");
},
});
function handleClose() {
@ -138,41 +140,6 @@ export const Restore: FC = () => {
label="Email"
gap={upMd ? "10px" : "10px"}
/>
<PasswordInput
TextfieldProps={{
value: formik.values.password,
placeholder: "Не менее 8 символов",
onBlur: formik.handleBlur,
error: formik.touched.password && Boolean(formik.errors.password),
helperText: formik.touched.password && formik.errors.password,
autoComplete: "new-password",
"data-cy": "password",
}}
onChange={formik.handleChange}
color="#F2F3F7"
id="password"
label="Пароль"
gap={upMd ? "10px" : "10px"}
/>
<PasswordInput
TextfieldProps={{
value: formik.values.repeatPassword,
placeholder: "Не менее 8 символов",
onBlur: formik.handleBlur,
error:
formik.touched.repeatPassword &&
Boolean(formik.errors.repeatPassword),
helperText:
formik.touched.repeatPassword && formik.errors.repeatPassword,
autoComplete: "new-password",
"data-cy": "repeat-password",
}}
onChange={formik.handleChange}
color="#F2F3F7"
id="repeatPassword"
label="Повторить пароль"
gap={upMd ? "10px" : "10px"}
/>
<Button
variant="contained"
fullWidth

@ -235,7 +235,7 @@ export default function SigninDialog() {
</Link>
<Link
component={RouterLink}
to="/restore"
to="/recover"
state={{ backgroundLocation: location.state.backgroundLocation }}
sx={{
color: "#7E2AEA",

@ -7,7 +7,7 @@ import {
useMediaQuery,
useTheme,
} from "@mui/material";
import { createQuiz } from "@root/quizes/actions";
import { createQuiz, updateQuiz } from "@root/quizes/actions";
import { useQuizes } from "@root/quizes/hooks";
import SectionWrapper from "@ui_kit/SectionWrapper";
import React from "react";
@ -16,6 +16,7 @@ import { resetEditConfig } from "@root/quizes/actions";
import FirstQuiz from "./FirstQuiz";
import QuizCard from "./QuizCard";
import HeaderFull from "@ui_kit/Header/HeaderFull";
import QuizgenegationName from "@utils/quizgenegationName";
interface Props {
outerContainerSx?: SxProps<Theme>;
@ -76,16 +77,22 @@ export default function MyQuizzesFull({
}}
>
{quizes.map((quiz) => {
if (quiz.name.length === 0 || quiz.name === " ") {
updateQuiz(quiz.id, (quiz) => {
quiz.name = QuizgenegationName({ quiz });
});
}
return (
<QuizCard
key={quiz.id}
quiz={quiz}
openCount={quiz.sessions_count}
openCount={quiz.session_count}
applicationCount={quiz.passed_count}
conversionPercent={
quiz.sessions_count
quiz.session_count
? (
(quiz.passed_count / quiz.sessions_count) *
(quiz.passed_count / quiz.session_count) *
100
).toFixed(1)
: 0

@ -97,11 +97,7 @@ export default function QuizCard({
}}
variant="h5"
>
{quiz.name.length === 0 || quiz.name === " "
? quiz.config.type === "form"
? "Form " + quiz.backendId.toString().slice(-4).replace(/^0/, "1")
: "Quiz " + quiz.backendId.toString().slice(-4).replace(/^0/, "1")
: quiz.name}
{quiz.name}
</Typography>
<Box
sx={{

@ -6,11 +6,8 @@ import ArrowLeft from "@icons/ArrowLeftSP";
import LayoutCenteredIcon from "@icons/LayoutCenteredIcon";
import LayoutExpandedIcon from "@icons/LayoutExpandedIcon";
import LayoutStandartIcon from "@icons/LayoutStandartIcon";
import MobilePhoneIcon from "@icons/MobilePhoneIcon";
import { QuizStartpageType } from "@model/quizSettings";
import InfoIcon from "@icons/InfoIcon";
import UploadBox from "@ui_kit/UploadBox";
import UploadIcon from "../../assets/icons/UploadIcon";
@ -740,7 +737,7 @@ export default function StartPageSettings() {
quiz.config.info.site = e.target.value;
})
}
maxLength={25}
maxLength={100}
/>
<Typography
sx={{
@ -761,7 +758,7 @@ export default function StartPageSettings() {
quiz.config.info.law = e.target.value;
})
}
maxLength={45}
maxLength={1000}
/>
<Extra />
</>

@ -2,12 +2,13 @@ import { Box, Button, useMediaQuery, useTheme } from "@mui/material";
import CreationCard from "@ui_kit/CreationCard";
import quizCreationImage1 from "../../assets/quiz-creation-1.png";
import quizCreationImage2 from "../../assets/quiz-creation-2.png";
import { setQuizType } from "@root/quizes/actions";
import { setQuizType, updateQuiz } from "@root/quizes/actions";
import { useCurrentQuiz } from "@root/quizes/hooks";
import { useEffect, useRef, useState } from "react";
import arrowLeftIcon from "../../assets/icons/arrow_left.svg";
import arrowRightIcon from "../../assets/icons/arrow_right.svg";
import QuizgenegationName from "@utils/quizgenegationName";
export default function StepOne() {
const quiz = useCurrentQuiz();
@ -69,6 +70,9 @@ export default function StepOne() {
data-cy="create-quiz-card"
onClick={() => {
setQuizType(quiz.id, "quiz");
updateQuiz(quiz.id, (quiz) => {
quiz.name = QuizgenegationName({ quiz });
});
}}
>
<CreationCard
@ -84,6 +88,9 @@ export default function StepOne() {
variant="text"
onClick={() => {
setQuizType(quiz.id, "form");
updateQuiz(quiz.id, (quiz) => {
quiz.name = QuizgenegationName({ quiz });
});
}}
>
<CreationCard

@ -413,7 +413,8 @@ export const uploadQuestionImage = async (
}
const imageId = values[0];
const imageUrl = process.env.REACT_APP_DOMAIN + `/squizimages/${quizQid}/${imageId}`;
const imageUrl =
process.env.REACT_APP_DOMAIN + `/squizimages/${quizQid}/${imageId}`;
updateQuestion(questionId, (question) => {
updateFn(question, imageUrl);

@ -129,7 +129,7 @@ export const quizStore = create<QuizStore>()(
config: {
noStartPage: false,
type: "", // quiz или form
logo: process.env.REACT_APP_HUB_DOMAIN + "/img/logo",
logo: process.env.REACT_APP_DOMAIN + "/img/logo",
startpage: {
description: "", // приветственный текст опроса
button: "", // текст на кнопке начала опроса

@ -269,7 +269,7 @@ export const uploadQuizImage = async (
updateQuiz(quizId, (quiz) => {
updateFn(
quiz,
process.env.REACT_APP_DOMAIN + `/squizimages/${quiz.qid}/${imageId}`,
process.env.REACT_APP_DOMAIN + `/squizimages/${quiz.qid}/${imageId}`,
);
});
} catch (error) {

@ -16,20 +16,27 @@ const translateMessage: Record<string, string> = {
"user with this email or login is exist": "Пользователь уже существует",
"user with this login is exist":
"Пользователь с таким логином уже существует",
unauthorized: "Ссылка просрочена",
};
export const parseAxiosError = (nativeError: unknown): [string, number?] => {
console.log(nativeError);
const error = nativeError as AxiosError;
if (
error.response?.data &&
"statusCode" in (error.response.data as ServerError)
) {
console.log(error);
console.log(error.response?.data);
if (error.response?.data) {
const serverError = error.response.data as ServerError;
const translatedMessage = translateMessage[serverError.message];
if (translatedMessage !== undefined)
serverError.message = translatedMessage;
return [serverError.message, serverError.statusCode];
let SEMessage;
if ("statusCode" in (error.response?.data as ServerError)) {
SEMessage = serverError?.message.toLowerCase() || "";
}
if ("error" in (error.response?.data as ServerError)) {
SEMessage = serverError?.error.toLowerCase() || "";
}
console.log(serverError);
const translatedMessage = translateMessage[SEMessage || ""];
if (translatedMessage !== undefined) SEMessage = translatedMessage;
return [SEMessage || "", serverError.statusCode];
}
switch (error.status) {

@ -0,0 +1,16 @@
import { Quiz } from "@model/quiz/quiz";
interface Props {
quiz: Quiz;
}
export default function QuizgenegationName({ quiz }: Props) {
let QuizgenegationName = "";
quiz.config.type === "form"
? (QuizgenegationName =
"Form " + quiz.backendId.toString().slice(-4).replace(/^0/, "1"))
: quiz.config.type === "quiz"
? (QuizgenegationName =
"Quiz " + quiz.backendId.toString().slice(-4).replace(/^0/, "1"))
: (QuizgenegationName =
"No type " + quiz.backendId.toString().slice(-4).replace(/^0/, "1"));
return QuizgenegationName;
}

@ -1407,10 +1407,10 @@
resolved "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz"
integrity sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==
"@frontend/kitui@^1.0.55":
version "1.0.55"
resolved "https://penahub.gitlab.yandexcloud.net/api/v4/projects/21/packages/npm/@frontend/kitui/-/@frontend/kitui-1.0.55.tgz#6e02f80b2c13828142ffeacaf9704e50e54324d0"
integrity sha1-bgL4CywTgoFC/+rK+XBOUOVDJNA=
"@frontend/kitui@^1.0.62":
version "1.0.62"
resolved "https://penahub.gitlab.yandexcloud.net/api/v4/projects/21/packages/npm/@frontend/kitui/-/@frontend/kitui-1.0.62.tgz#212185a0a19a9b9948a85e9d4c71e002ad7f8b27"
integrity sha1-ISGFoKGam5lIqF6dTHHgAq1/iyc=
dependencies:
immer "^10.0.2"
reconnecting-eventsource "^1.6.2"