Обработка ошибки, когда вопросы для аудитории ещё не созданы
Some checks failed
Deploy / DeployService (push) Failing after 28s
Deploy / CreateImage (push) Has been cancelled

This commit is contained in:
Nastya 2025-06-07 06:26:56 +03:00
parent c186a04fa5
commit 12a1aab506
8 changed files with 357 additions and 279 deletions

@ -93,18 +93,21 @@ export async function getData({ quizId }: { quizId: string }): Promise<{
if (paudParam) body.auditory = Number(paudParam); if (paudParam) body.auditory = Number(paudParam);
try { try {
const { data, headers } = await axios<GetQuizDataResponse>(domain + `/answer/v1.0.0/settings`, { const { data, headers } = await axios<GetQuizDataResponse>(
method: "POST", domain + `/answer/v1.0.0/settings${window.location.search}`,
headers: { {
"X-Sessionkey": SESSIONS, method: "POST",
"Content-Type": "application/json", headers: {
DeviceType: DeviceType, "X-Sessionkey": SESSIONS,
Device: Device, "Content-Type": "application/json",
OS: OSDevice, DeviceType: DeviceType,
Browser: userAgent, Device: Device,
}, OS: OSDevice,
data: body, Browser: userAgent,
}); },
data: body,
}
);
const sessions = JSON.parse(localStorage.getItem("sessions") || "{}"); const sessions = JSON.parse(localStorage.getItem("sessions") || "{}");
//Тут ещё проверка на антифрод без парса конфига. Нам не интересно время если не нужно запрещать проходить чаще чем в сутки //Тут ещё проверка на антифрод без парса конфига. Нам не интересно время если не нужно запрещать проходить чаще чем в сутки
@ -133,23 +136,26 @@ export async function getDataSingle({ quizId, page }: { quizId: string; page?: n
try { try {
// Первый запрос: 1 вопрос + конфиг // Первый запрос: 1 вопрос + конфиг
if (isFirstRequest) { if (isFirstRequest) {
const { data, headers } = await axios<GetQuizDataResponse>(domain + `/answer/v1.0.0/settings`, { const { data, headers } = await axios<GetQuizDataResponse>(
method: "POST", domain + `/answer/v1.0.0/settings${window.location.search}`,
headers: { {
"X-Sessionkey": SESSIONS, method: "POST",
"Content-Type": "application/json", headers: {
DeviceType: DeviceType, "X-Sessionkey": SESSIONS,
Device: Device, "Content-Type": "application/json",
OS: OSDevice, DeviceType: DeviceType,
Browser: userAgent, Device: Device,
}, OS: OSDevice,
data: { Browser: userAgent,
quiz_id: quizId, },
limit: 1, data: {
page: 0, quiz_id: quizId,
need_config: true, limit: 1,
}, page: 0,
}); need_config: true,
},
}
);
globalStatus = data.settings.status; globalStatus = data.settings.status;
isFirstRequest = false; isFirstRequest = false;
@ -165,23 +171,26 @@ export async function getDataSingle({ quizId, page }: { quizId: string; page?: n
// Если статус не AI - сразу делаем запрос за всеми вопросами // Если статус не AI - сразу делаем запрос за всеми вопросами
if (globalStatus !== "ai") { if (globalStatus !== "ai") {
const secondResponse = await axios<GetQuizDataResponse>(domain + `/answer/v1.0.0/settings`, { const secondResponse = await axios<GetQuizDataResponse>(
method: "POST", domain + `/answer/v1.0.0/settings${window.location.search}`,
headers: { {
"X-Sessionkey": SESSIONS, method: "POST",
"Content-Type": "application/json", headers: {
DeviceType: DeviceType, "X-Sessionkey": SESSIONS,
Device: Device, "Content-Type": "application/json",
OS: OSDevice, DeviceType: DeviceType,
Browser: userAgent, Device: Device,
}, OS: OSDevice,
data: { Browser: userAgent,
quiz_id: quizId, },
limit: 100, data: {
page: 0, quiz_id: quizId,
need_config: false, limit: 100,
}, page: 0,
}); need_config: false,
},
}
);
return { return {
data: { ...data, items: secondResponse.data.items }, data: { ...data, items: secondResponse.data.items },
isRecentlyCompleted: false, isRecentlyCompleted: false,
@ -192,7 +201,7 @@ export async function getDataSingle({ quizId, page }: { quizId: string; page?: n
} }
// Последующие запросы // Последующие запросы
const response = await axios<GetQuizDataResponse>(domain + `/answer/v1.0.0/settings`, { const response = await axios<GetQuizDataResponse>(domain + `/answer/v1.0.0/settings${window.location.search}`, {
method: "POST", method: "POST",
headers: { headers: {
"X-Sessionkey": SESSIONS, "X-Sessionkey": SESSIONS,
@ -418,7 +427,7 @@ type Answer = {
}; };
export function sendFile({ questionId, body, qid }: SendFileParams) { export function sendFile({ questionId, body, qid }: SendFileParams) {
if (body.preview) return; if (body.preview) return Promise.resolve();
const formData = new FormData(); const formData = new FormData();
const file = new File([body.file], body.file.name.replace(/\s/g, "_")); const file = new File([body.file], body.file.name.replace(/\s/g, "_"));
@ -439,7 +448,10 @@ export function sendFile({ questionId, body, qid }: SendFileParams) {
url: domain + `/answer/v1.0.0/answer`, url: domain + `/answer/v1.0.0/answer`,
body: formData, body: formData,
method: "POST", method: "POST",
}); }).then((response) => ({
fileName: nameImage,
response,
}));
} }
//форма контактов //форма контактов

@ -22,12 +22,10 @@ import { useQuizStore } from "@/stores/useQuizStore";
export default function ViewPublicationPage() { export default function ViewPublicationPage() {
const { settings, recentlyCompleted, quizId, preview, changeFaviconAndTitle } = useQuizStore(); const { settings, recentlyCompleted, quizId, preview, changeFaviconAndTitle } = useQuizStore();
const answers = useQuizViewStore((state) => state.answers); const { currentQuestion, currentQuestionStepNumber, currentQuizStep, answers, ownVariants } = useQuizViewStore();
const ownVariants = useQuizViewStore((state) => state.ownVariants); const uploadingFiles = useQuizViewStore((state) => state.uploadingFiles);
let currentQuizStep = useQuizViewStore((state) => state.currentQuizStep); const isFileUploading = Object.values(uploadingFiles).some((isUploading) => isUploading);
const { const {
currentQuestion,
currentQuestionStepNumber,
nextQuestion, nextQuestion,
isNextButtonEnabled, isNextButtonEnabled,
isPreviousButtonEnabled, isPreviousButtonEnabled,
@ -106,7 +104,7 @@ export default function ViewPublicationPage() {
} }
nextButton={ nextButton={
<NextButton <NextButton
isNextButtonEnabled={settings.status === "ai" || isNextButtonEnabled} isNextButtonEnabled={settings.status === "ai" || (isNextButtonEnabled && !isFileUploading)}
moveToNextQuestion={async () => { moveToNextQuestion={async () => {
if (!preview) { if (!preview) {
await sendQuestionAnswer(quizId, currentQuestion, currentAnswer, ownVariants)?.catch((e) => { await sendQuestionAnswer(quizId, currentQuestion, currentAnswer, ownVariants)?.catch((e) => {

@ -1,4 +1,4 @@
import { useState } from "react"; import { useState, Dispatch, SetStateAction } from "react";
import { Box, ButtonBase, Skeleton, Typography, useTheme } from "@mui/material"; import { Box, ButtonBase, Skeleton, Typography, useTheme } from "@mui/material";
import { enqueueSnackbar } from "notistack"; import { enqueueSnackbar } from "notistack";
@ -16,18 +16,28 @@ import Info from "@icons/Info";
import UploadIcon from "@icons/UploadIcon"; import UploadIcon from "@icons/UploadIcon";
import type { QuizQuestionFile } from "@model/questionTypes/file"; import type { QuizQuestionFile } from "@model/questionTypes/file";
import type { ModalWarningType } from "./index";
import { useQuizStore } from "@/stores/useQuizStore"; import { useQuizStore } from "@/stores/useQuizStore";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
export type ModalWarningType = "errorType" | "errorSize" | "picture" | "video" | "audio" | "document" | null;
type UploadFileProps = { type UploadFileProps = {
currentQuestion: QuizQuestionFile; currentQuestion: QuizQuestionFile;
setModalWarningType: (modalType: ModalWarningType) => void; setModalWarningType: Dispatch<SetStateAction<ModalWarningType>>;
isSending: boolean; isSending: boolean;
setIsSending: (isSending: boolean) => void; setIsSending: Dispatch<SetStateAction<boolean>>;
onFileUpload: (file: File) => Promise<void>;
isUploading: boolean;
}; };
export const UploadFile = ({ currentQuestion, setModalWarningType, isSending, setIsSending }: UploadFileProps) => { export const UploadFile = ({
currentQuestion,
setModalWarningType,
isSending,
setIsSending,
onFileUpload,
isUploading,
}: UploadFileProps) => {
const { quizId, preview } = useQuizStore(); const { quizId, preview } = useQuizStore();
const [isDropzoneHighlighted, setIsDropzoneHighlighted] = useState<boolean>(false); const [isDropzoneHighlighted, setIsDropzoneHighlighted] = useState<boolean>(false);
const theme = useTheme(); const theme = useTheme();
@ -38,51 +48,57 @@ export const UploadFile = ({ currentQuestion, setModalWarningType, isSending, se
const answer = answers.find(({ questionId }) => questionId === currentQuestion.id)?.answer as string; const answer = answers.find(({ questionId }) => questionId === currentQuestion.id)?.answer as string;
const uploadFile = async (file: File | undefined) => { const handleFileChange = async (event: React.ChangeEvent<HTMLInputElement>) => {
if (isSending) return; const file = event.target.files?.[0];
if (!file) return; if (!file) return;
if (file.size > MAX_FILE_SIZE) return setModalWarningType("errorSize");
const isFileTypeAccepted = ACCEPT_SEND_FILE_TYPES_MAP[currentQuestion.content.type].some((fileType) => const fileType = file.type.split("/")[0];
file.name.toLowerCase().endsWith(fileType) const fileExtension = file.name.split(".").pop()?.toLowerCase();
);
if (!isFileTypeAccepted) return setModalWarningType("errorType"); if (!ACCEPT_SEND_FILE_TYPES_MAP[currentQuestion.content.type].includes(`.${fileExtension}`)) {
setModalWarningType("errorType");
return;
}
if (file.size > 50 * 1024 * 1024) {
setModalWarningType("errorSize");
return;
}
setIsSending(true); setIsSending(true);
try { try {
const data = await sendFile({ await onFileUpload(file);
questionId: currentQuestion.id, } finally {
body: { setIsSending(false);
file: file,
name: file.name,
preview,
},
qid: quizId,
});
await sendAnswer({
questionId: currentQuestion.id,
body: `${data!.data.fileIDMap[currentQuestion.id]}`,
qid: quizId,
preview,
});
updateAnswer(currentQuestion.id, `${file.name}|${URL.createObjectURL(file)}`, 0);
} catch (error) {
console.error(error);
enqueueSnackbar(t("The answer was not counted"));
} }
setIsSending(false);
}; };
const onDrop = (event: React.DragEvent<HTMLDivElement>) => { const onDrop = async (event: React.DragEvent<HTMLLabelElement>) => {
event.preventDefault(); event.preventDefault();
setIsDropzoneHighlighted(false); setIsDropzoneHighlighted(false);
const file = event.dataTransfer.files[0]; const file = event.dataTransfer.files[0];
if (!file) return;
uploadFile(file); const fileType = file.type.split("/")[0];
const fileExtension = file.name.split(".").pop()?.toLowerCase();
if (!ACCEPT_SEND_FILE_TYPES_MAP[currentQuestion.content.type].includes(`.${fileExtension}`)) {
setModalWarningType("errorType");
return;
}
if (file.size > 50 * 1024 * 1024) {
setModalWarningType("errorSize");
return;
}
setIsSending(true);
try {
await onFileUpload(file);
} finally {
setIsSending(false);
}
}; };
return ( return (
@ -93,52 +109,48 @@ export const UploadFile = ({ currentQuestion, setModalWarningType, isSending, se
sx={{ width: "100%", height: "120px", maxWidth: "560px" }} sx={{ width: "100%", height: "120px", maxWidth: "560px" }}
/> />
) : ( ) : (
<ButtonBase <Box
component="label" component="label"
sx={{ justifyContent: "flex-start", width: "100%" }} sx={{
width: "100%",
height: "300px",
border: "2px dashed",
borderColor: isDropzoneHighlighted ? "primary.main" : "grey.300",
borderRadius: "12px",
display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
cursor: "pointer",
transition: "all 0.2s",
backgroundColor: isDropzoneHighlighted ? "action.hover" : "background.paper",
opacity: isSending || isUploading ? 0.7 : 1,
pointerEvents: isSending || isUploading ? "none" : "auto",
"&:hover": {
borderColor: "primary.main",
backgroundColor: "action.hover",
},
}}
onDragEnter={() => setIsDropzoneHighlighted(true)}
onDragLeave={() => setIsDropzoneHighlighted(false)}
onDragOver={(event) => event.preventDefault()}
onDrop={onDrop}
> >
<input <input
onChange={({ target }) => uploadFile(target.files?.[0])} onChange={handleFileChange}
hidden hidden
accept={ACCEPT_SEND_FILE_TYPES_MAP[currentQuestion.content.type].join(",")} accept={ACCEPT_SEND_FILE_TYPES_MAP[currentQuestion.content.type].join(",")}
multiple
type="file" type="file"
/> />
<Box <UploadIcon color={isDropzoneHighlighted ? "primary" : "grey"} />
onDragEnter={() => !answer?.split("|")[0] && setIsDropzoneHighlighted(true)} <Typography
onDragLeave={() => setIsDropzoneHighlighted(false)} variant="body1"
onDragOver={(event) => event.preventDefault()} color={isDropzoneHighlighted ? "primary" : "text.secondary"}
onDrop={onDrop} sx={{ mt: 2 }}
sx={{
width: "100%",
height: isMobile ? undefined : "120px",
display: "flex",
gap: "50px",
justifyContent: "flex-start",
alignItems: "center",
padding: "33px 44px 33px 55px",
backgroundColor: "#F2F3F7",
border: `1px solid ${isDropzoneHighlighted ? "red" : "#9A9AAF"}`,
borderRadius: "8px",
}}
> >
<UploadIcon /> {isUploading ? t("Uploading...") : t("Drop file here or click to upload")}
<Box> </Typography>
<Typography sx={{ color: "#9A9AAF", fontWeight: 500 }}> </Box>
{t(UPLOAD_FILE_DESCRIPTIONS_MAP[currentQuestion.content.type].title)}
</Typography>
<Typography
sx={{
color: "#9A9AAF",
fontSize: "16px",
lineHeight: "19px",
}}
>
{t(UPLOAD_FILE_DESCRIPTIONS_MAP[currentQuestion.content.type].description)}
</Typography>
</Box>
</Box>
</ButtonBase>
)} )}
<Info <Info
sx={{ width: "40px", height: "40px" }} sx={{ width: "40px", height: "40px" }}

@ -11,6 +11,8 @@ import { ACCEPT_SEND_FILE_TYPES_MAP } from "@/components/ViewPublicationPage/too
import type { QuizQuestionFile } from "@model/questionTypes/file"; import type { QuizQuestionFile } from "@model/questionTypes/file";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { sendFile } from "@api/quizRelase";
import { enqueueSnackbar } from "notistack";
export type ModalWarningType = "errorType" | "errorSize" | "picture" | "video" | "audio" | "document" | null; export type ModalWarningType = "errorType" | "errorSize" | "picture" | "video" | "audio" | "document" | null;
@ -20,13 +22,46 @@ type FileProps = {
export const File = ({ currentQuestion }: FileProps) => { export const File = ({ currentQuestion }: FileProps) => {
const theme = useTheme(); const theme = useTheme();
const { t } = useTranslation();
const answers = useQuizViewStore((state) => state.answers); const answers = useQuizViewStore((state) => state.answers);
const updateAnswer = useQuizViewStore((state) => state.updateAnswer);
const setFileUploading = useQuizViewStore((state) => state.setFileUploading);
const [modalWarningType, setModalWarningType] = useState<ModalWarningType>(null); const [modalWarningType, setModalWarningType] = useState<ModalWarningType>(null);
const [isSending, setIsSending] = useState<boolean>(false); const [isSending, setIsSending] = useState<boolean>(false);
const [isUploading, setIsUploading] = useState<boolean>(false);
const isMobile = useRootContainerSize() < 500; const isMobile = useRootContainerSize() < 500;
const answer = answers.find(({ questionId }) => questionId === currentQuestion.id)?.answer as string; const answer = answers.find(({ questionId }) => questionId === currentQuestion.id)?.answer as string;
const handleFileUpload = async (file: File) => {
if (!file) return;
try {
setIsUploading(true);
setFileUploading(currentQuestion.id, true);
const result = await sendFile({
questionId: currentQuestion.id,
body: {
name: file.name,
file,
preview: false,
},
qid: window.location.pathname.split("/").pop() || "",
});
if (result) {
updateAnswer(currentQuestion.id, result.fileName, 0);
enqueueSnackbar(t("File uploaded successfully"));
}
} catch (error) {
console.error(error);
enqueueSnackbar(t("Failed to upload file"));
} finally {
setIsUploading(false);
setFileUploading(currentQuestion.id, false);
}
};
return ( return (
<Box> <Box>
<Typography <Typography
@ -56,6 +91,8 @@ export const File = ({ currentQuestion }: FileProps) => {
setModalWarningType={setModalWarningType} setModalWarningType={setModalWarningType}
isSending={isSending} isSending={isSending}
setIsSending={setIsSending} setIsSending={setIsSending}
onFileUpload={handleFileUpload}
isUploading={isUploading}
/> />
)} )}
{answer && currentQuestion.content.type === "picture" && ( {answer && currentQuestion.content.type === "picture" && (

@ -1,147 +1,163 @@
import { QuestionVariant } from "@model/questionTypes/shared"; import { QuestionVariant } from "@model/questionTypes/shared";
import { QuizStep } from "@model/settingsData"; import { QuizStep } from "@model/settingsData";
import type { Moment } from "moment"; import type { Moment } from "moment";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import { createContext, useContext } from "react"; import { createContext, useContext } from "react";
import { createStore, useStore } from "zustand"; import { createStore, useStore } from "zustand";
import { immer } from "zustand/middleware/immer"; import { immer } from "zustand/middleware/immer";
import { devtools } from "zustand/middleware"; import { devtools } from "zustand/middleware";
export type Answer = string | string[] | Moment; export type Answer = string | string[] | Moment;
export type QuestionAnswer = { export type QuestionAnswer = {
questionId: string; questionId: string;
answer: Answer; answer: Answer;
}; };
export type OwnVariant = { export type OwnVariant = {
id: string; id: string;
variant: QuestionVariant; variant: QuestionVariant;
}; };
interface QuizViewStore { interface QuizViewStore {
answers: QuestionAnswer[]; answers: QuestionAnswer[];
ownVariants: OwnVariant[]; ownVariants: OwnVariant[];
pointsSum: number; pointsSum: number;
points: Record<string, number>; points: Record<string, number>;
currentQuizStep: QuizStep; currentQuizStep: QuizStep;
} uploadingFiles: Record<string, boolean>;
}
interface QuizViewActions {
updateAnswer: (questionId: string, answer: string | string[] | Moment, points: number) => void; interface QuizViewActions {
deleteAnswer: (questionId: string) => void; updateAnswer: (questionId: string, answer: string | string[] | Moment, points: number) => void;
updateOwnVariant: (id: string, answer: string) => void; deleteAnswer: (questionId: string) => void;
deleteOwnVariant: (id: string) => void; updateOwnVariant: (id: string, answer: string) => void;
setCurrentQuizStep: (step: QuizStep) => void; deleteOwnVariant: (id: string) => void;
} setCurrentQuizStep: (step: QuizStep) => void;
setFileUploading: (questionId: string, isUploading: boolean) => void;
export const QuizViewContext = createContext<ReturnType<typeof createQuizViewStore> | null>(null); }
export function useQuizViewStore<U>(selector: (state: QuizViewStore & QuizViewActions) => U): U { export const QuizViewContext = createContext<ReturnType<typeof createQuizViewStore> | null>(null);
const store = useContext(QuizViewContext);
if (!store) throw new Error("QuizViewStore context is null"); export function useQuizViewStore<U>(selector: (state: QuizViewStore & QuizViewActions) => U): U {
const store = useContext(QuizViewContext);
return useStore(store, selector); if (!store) throw new Error("QuizViewStore context is null");
}
return useStore(store, selector);
export const createQuizViewStore = () => }
createStore<QuizViewStore & QuizViewActions>()(
immer( export const createQuizViewStore = () =>
devtools( createStore<QuizViewStore & QuizViewActions>()(
(set, get) => ({ immer(
answers: [], devtools(
ownVariants: [], (set, get) => ({
points: {}, answers: [],
pointsSum: 0, ownVariants: [],
currentQuizStep: "startpage", points: {},
updateAnswer(questionId, answer, points) { pointsSum: 0,
set( currentQuizStep: "startpage",
(state) => { uploadingFiles: {},
const index = state.answers.findIndex((answer) => questionId === answer.questionId); updateAnswer(questionId, answer, points) {
set(
if (index < 0) { (state) => {
state.answers.push({ questionId, answer }); const index = state.answers.findIndex((answer) => questionId === answer.questionId);
} else {
state.answers[index] = { questionId, answer }; if (index < 0) {
} state.answers.push({ questionId, answer });
} else {
state.points = { ...state.points, ...{ [questionId]: points } }; state.answers[index] = { questionId, answer };
}
state.pointsSum = Object.values(state.points).reduce((sum, value) => sum + value);
}, state.points = { ...state.points, ...{ [questionId]: points } };
false,
{ state.pointsSum = Object.values(state.points).reduce((sum, value) => sum + value);
type: "updateAnswer", },
questionId, false,
answer, {
points, type: "updateAnswer",
} questionId,
); answer,
}, points,
deleteAnswer(questionId) { }
set( );
(state) => { },
state.answers = state.answers.filter((answer) => questionId !== answer.questionId); deleteAnswer(questionId) {
}, set(
false, (state) => {
{ state.answers = state.answers.filter((answer) => questionId !== answer.questionId);
type: "deleteAnswer", },
questionId, false,
} {
); type: "deleteAnswer",
}, questionId,
updateOwnVariant(id, answer) { }
set( );
(state) => { },
const index = state.ownVariants.findIndex((variant) => variant.id === id); updateOwnVariant(id, answer) {
set(
if (index < 0) { (state) => {
state.ownVariants.push({ const index = state.ownVariants.findIndex((variant) => variant.id === id);
id,
variant: { if (index < 0) {
id: id, state.ownVariants.push({
answer, id,
extendedText: "", variant: {
hints: "", id: id,
originalImageUrl: "", answer,
}, extendedText: "",
}); hints: "",
} else { originalImageUrl: "",
state.ownVariants[index].variant.answer = answer; },
} });
}, } else {
false, state.ownVariants[index].variant.answer = answer;
{ }
type: "updateOwnVariant", },
id, false,
answer, {
} type: "updateOwnVariant",
); id,
}, answer,
deleteOwnVariant(id) { }
set( );
(state) => { },
state.ownVariants = state.ownVariants.filter((variant) => variant.id !== id); deleteOwnVariant(id) {
}, set(
false, (state) => {
{ state.ownVariants = state.ownVariants.filter((variant) => variant.id !== id);
type: "deleteOwnVariant", },
id, false,
} {
); type: "deleteOwnVariant",
}, id,
setCurrentQuizStep(step) { }
set({ currentQuizStep: step }, false, { );
type: "setCurrentQuizStep", },
step, setCurrentQuizStep(step) {
}); set({ currentQuizStep: step }, false, {
}, type: "setCurrentQuizStep",
}), step,
{ });
name: "QuizViewStore-" + nanoid(4), },
enabled: import.meta.env.DEV, setFileUploading(questionId, isUploading) {
trace: import.meta.env.DEV, set(
} (state) => {
) state.uploadingFiles[questionId] = isUploading;
) },
); false,
{
type: "setFileUploading",
questionId,
isUploading,
}
);
},
}),
{
name: "QuizViewStore-" + nanoid(4),
enabled: import.meta.env.DEV,
trace: import.meta.env.DEV,
}
)
)
);

@ -53,5 +53,6 @@
"and": "and", "and": "and",
"Get results": "Get results", "Get results": "Get results",
"Data sent successfully": "Data sent successfully", "Data sent successfully": "Data sent successfully",
"Step": "Step" "Step": "Step",
"questions are not ready yet": "There are no questions for the audience yet. Please wait"
} }

@ -53,5 +53,6 @@
"and": "и", "and": "и",
"Get results": "Получить результаты", "Get results": "Получить результаты",
"Data sent successfully": "Данные успешно отправлены", "Data sent successfully": "Данные успешно отправлены",
"Step": "Шаг" "Step": "Шаг",
"questions are not ready yet": "Вопросы для аудитории ещё не созданы. Пожалуйста, подождите"
} }

@ -53,5 +53,6 @@
"and": "va", "and": "va",
"Get results": "Natijalarni olish", "Get results": "Natijalarni olish",
"Data sent successfully": "Ma'lumotlar muvaffaqiyatli yuborildi", "Data sent successfully": "Ma'lumotlar muvaffaqiyatli yuborildi",
"Step": "Qadam" "Step": "Qadam",
"questions are not ready yet": "Tomoshabinlar uchun hozircha savollar yo'q. Iltimos kuting"
} }