staging #7

Merged
skeris merged 2 commits from staging into main 2025-08-12 16:37:55 +00:00
10 changed files with 460 additions and 256 deletions
Showing only changes of commit 0c9b6e5b7a - Show all commits

@ -22,6 +22,9 @@ import { HelmetProvider } from "react-helmet-async";
import "moment/dist/locale/ru";
import { useQuizStore, setQuizData, addquizid } from "@/stores/useQuizStore";
import { initDataManager, statusOfQuiz } from "@/utils/hooks/useQuestionFlowControl";
console.log("🚀 QuizAnswerer: Component module loaded");
moment.locale("ru");
const localeText = ruRU.components.MuiLocalizationProvider.defaultProps.localeText;
@ -42,6 +45,13 @@ function QuizAnswererInner({
className,
disableGlobalCss = false,
}: Props) {
console.log("🔧 QuizAnswererInner: Component function called with props:", {
quizId,
preview,
changeFaviconAndTitle,
disableGlobalCss,
});
const [quizViewStore] = useState(createQuizViewStore);
const [rootContainerWidth, setRootContainerWidth] = useState<number>(() => window.innerWidth);
const rootContainerRef = useRef<HTMLDivElement>(null);
@ -51,18 +61,21 @@ function QuizAnswererInner({
const r = useQuizStore();
const { settings, questions } = useQuizStore();
console.log("🔍 QuizAnswererInner: Component state initialized");
useEffect(() => {
console.log("🔧 QuizAnswererInner: addquizid effect triggered");
addquizid(quizId);
}, []);
useEffect(() => {
console.log(settings);
console.log(questions);
console.log("r");
console.log(r);
console.log("🔍 QuizAnswererInner: Debug effect - settings:", settings);
console.log("🔍 QuizAnswererInner: Debug effect - questions:", questions);
console.log("🔍 QuizAnswererInner: Debug effect - r:", r);
}, [questions, settings]);
useEffect(() => {
console.log("🔧 QuizAnswererInner: Metrics effect triggered");
setTimeout(() => {
vkMetrics.quizOpened();
yandexMetrics.quizOpened();
@ -72,7 +85,7 @@ function QuizAnswererInner({
useEffect(() => {
//Хук на случай если данные переданы нам сразу, а не "нам нужно их запросить"
if (quizSettings !== undefined) {
console.log("QuizAnswerer: calling setQuizData with quizSettings");
console.log("🔧 QuizAnswererInner: setQuizData effect triggered with quizSettings");
setQuizData(quizSettings);
initDataManager({
status: quizSettings.settings.status,
@ -82,6 +95,7 @@ function QuizAnswererInner({
}, [quizSettings]);
useLayoutEffect(() => {
console.log("🔧 QuizAnswererInner: Layout effect triggered");
if (rootContainerRef.current) setRootContainerWidth(rootContainerRef.current.clientWidth);
}, []);
@ -98,19 +112,44 @@ function QuizAnswererInner({
};
}, []);
console.log("🔍 QuizAnswererInner: About to render, current state:");
console.log("🔍 QuizAnswererInner: isLoading:", isLoading);
console.log("🔍 QuizAnswererInner: error:", error);
console.log("🔍 QuizAnswererInner: settings keys count:", Object.keys(settings).length);
console.log("🔍 QuizAnswererInner: questions length:", questions.length);
console.log("settings");
console.log(settings);
if (isLoading && !questions.length) return <LoadingSkeleton />;
if (isLoading && !questions.length) {
console.log("🔍 QuizAnswererInner: Rendering LoadingSkeleton");
return <LoadingSkeleton />;
}
console.log("error");
console.log(error);
if (error) return <ApologyPage error={error} />;
if (error) {
console.log("🔍 QuizAnswererInner: Rendering ApologyPage due to error");
return <ApologyPage error={error} />;
}
if (Object.keys(settings).length == 0) return <ApologyPage error={new Error("quiz data is null")} />;
if (questions.length === 0) return <ApologyPage error={new Error("No questions found")} />;
if (Object.keys(settings).length == 0) {
console.log("🔍 QuizAnswererInner: Rendering ApologyPage due to empty settings");
return <ApologyPage error={new Error("quiz data is null")} />;
}
if (questions.length === 0) {
console.log("🔍 QuizAnswererInner: Rendering ApologyPage due to no questions");
return <ApologyPage error={new Error("No questions found")} />;
}
if (questions.length === 1 && settings.cfg.noStartPage && statusOfQuiz != "ai")
if (questions.length === 1 && settings.cfg.noStartPage && statusOfQuiz != "ai") {
console.log("🔍 QuizAnswererInner: Rendering ApologyPage due to quiz is empty");
return <ApologyPage error={new Error("quiz is empty")} />;
if (!quizId) return <ApologyPage error={new Error("no quiz id")} />;
}
if (!quizId) {
console.log("🔍 QuizAnswererInner: Rendering ApologyPage due to no quiz id");
return <ApologyPage error={new Error("no quiz id")} />;
}
console.log("🔍 QuizAnswererInner: Rendering main quiz container");
const quizContainer = (
<Box
@ -152,6 +191,8 @@ function QuizAnswererInner({
);
}
export default function QuizAnswerer(props: Props) {
console.log("🔧 QuizAnswerer: Main component function called with props:", props);
return (
<HelmetProvider>
<LocalizationProvider

@ -15,7 +15,18 @@ export const Footer = ({ stepNumber, nextButton, prevButton }: FooterProps) => {
const theme = useTheme();
const { questions, settings } = useQuizStore();
const questionsAmount = questions.filter(({ type }) => type !== "result").length;
const { t } = useTranslation();
const { t, i18n } = useTranslation();
// Диагностика i18n в Footer
console.log("🔍 Footer: useTranslation called");
console.log("🔍 Footer: i18n instance:", i18n);
console.log("🔍 Footer: i18n.isInitialized:", i18n.isInitialized);
console.log("🔍 Footer: i18n.language:", i18n.language);
console.log("🔍 Footer: Testing translations:");
console.log("🔍 Footer: t('Step'):", t("Step"));
console.log("🔍 Footer: t('of'):", t("of"));
console.log("🔍 Footer: t('Prev'):", t("Prev"));
console.log("🔍 Footer: t('Next'):", t("Next"));
return (
<Box

@ -21,9 +21,13 @@ import unscreen from "@/ui_kit/unscreen";
import { changeNextLoading, useQuizStore } from "@/stores/useQuizStore";
import { polyfillCountryFlagEmojis } from "country-flag-emoji-polyfill";
console.log("🚀 ViewPublicationPage: Component module loaded");
polyfillCountryFlagEmojis();
export default function ViewPublicationPage() {
console.log("🔧 ViewPublicationPage: Component function called");
const { settings, recentlyCompleted, quizId, preview, changeFaviconAndTitle } = useQuizStore();
const answers = useQuizViewStore((state) => state.answers);
const ownVariants = useQuizViewStore((state) => state.ownVariants);
@ -42,7 +46,12 @@ export default function ViewPublicationPage() {
useYandexMetrics(settings?.cfg?.yandexMetricsNumber, preview);
useVKMetrics(settings?.cfg?.vkMetricsNumber, preview);
console.log("🔍 ViewPublicationPage: Component state initialized");
console.log("🔍 ViewPublicationPage: currentQuizStep:", currentQuizStep);
console.log("🔍 ViewPublicationPage: currentQuestion:", currentQuestion);
useEffect(() => {
console.log("🔧 ViewPublicationPage: unscreen effect triggered");
const root = document.getElementById("root");
const overlay = document.getElementById("hideoverlay");
@ -70,7 +79,8 @@ export default function ViewPublicationPage() {
if (settings.cfg.antifraud && recentlyCompleted) throw new Error("Quiz already completed");
if (currentQuizStep === "startpage" && settings.cfg.noStartPage) currentQuizStep = "question";
if (!currentQuestion)
if (!currentQuestion) {
console.log("🔍 ViewPublicationPage: No current question, rendering error");
return (
<ThemeProvider theme={quizThemes[settings.cfg.theme || "StandardTheme"].theme}>
<Typography
@ -81,21 +91,27 @@ export default function ViewPublicationPage() {
</Typography>
</ThemeProvider>
);
}
const currentAnswer = answers.find(({ questionId }) => questionId === currentQuestion.id);
console.log("🔍 ViewPublicationPage: About to render quiz step:", currentQuizStep);
let quizStepElement: ReactElement;
switch (currentQuizStep) {
case "startpage": {
console.log("🔍 ViewPublicationPage: Rendering startpage");
quizStepElement = <StartPageViewPublication />;
break;
}
case "question": {
if (currentQuestion.type === "result") {
console.log("🔍 ViewPublicationPage: Rendering result form");
quizStepElement = <ResultForm resultQuestion={currentQuestion} />;
break;
}
console.log("🔍 ViewPublicationPage: Rendering question");
quizStepElement = (
<Question
key={currentQuestion.id}

@ -10,7 +10,15 @@ interface Props {
export default function NextButton({ isNextButtonEnabled, moveToNextQuestion }: Props) {
const { settings, nextLoading } = useQuizStore();
const { t } = useTranslation();
const { t, i18n } = useTranslation();
// Диагностика i18n в NextButton
console.log("🔍 NextButton: useTranslation called");
console.log("🔍 NextButton: i18n instance:", i18n);
console.log("🔍 NextButton: i18n.isInitialized:", i18n.isInitialized);
console.log("🔍 NextButton: i18n.language:", i18n.language);
console.log("🔍 NextButton: Testing translation:");
console.log("🔍 NextButton: t('Next'):", t("Next"));
return nextLoading ? (
<Skeleton

@ -13,7 +13,16 @@ export default function PrevButton({ isPreviousButtonEnabled, moveToPrevQuestion
const theme = useTheme();
const { settings } = useQuizStore();
const isMobileMini = useRootContainerSize() < 382;
const { t } = useTranslation();
const { t, i18n } = useTranslation();
// Диагностика i18n в PrevButton
console.log("🔍 PrevButton: useTranslation called");
console.log("🔍 PrevButton: i18n instance:", i18n);
console.log("🔍 PrevButton: i18n.isInitialized:", i18n.isInitialized);
console.log("🔍 PrevButton: i18n.language:", i18n.language);
console.log("🔍 PrevButton: Testing translation:");
console.log("🔍 PrevButton: t('Prev'):", t("Prev"));
return (
<Button
disabled={!isPreviousButtonEnabled}

@ -2,15 +2,23 @@ import { Box } from "@mui/material";
import { useParams } from "react-router-dom";
import QuizAnswerer from "../lib/components/QuizAnswerer";
console.log("🚀 App: Component module loaded");
// const defaultQuizId = "45ef7f9c-784d-4e58-badb-f6b337f08ba0"; // branching
// const defaultQuizId = "b7dafa46-a535-4beb-b289-65253d14a951"; //looooong header
const defaultQuizId = "3c49550d-8c77-4788-bc2d-42586a261514"; //тест визуала
// const defaultQuizId = "ad7f5a87-b833-4f5b-854e-453706ed655c"; // linear
export default function App() {
console.log("🔧 App: Component function called");
const quizId = useParams().quizId || "";
const currentUrl = window.location.pathname.slice(1);
console.log("🔍 App: quizId from params:", quizId);
console.log("🔍 App: currentUrl:", currentUrl);
console.log("🔍 App: final quizId:", quizId || currentUrl || "");
return (
<Box
sx={{

@ -2,6 +2,10 @@ import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import Backend from "i18next-http-backend";
console.log("🚀 Main i18n: Starting main i18n initialization...");
console.log("🔍 Main i18n: i18next instance before init:", i18n);
console.log("🔍 Main i18n: i18next isInitialized before init:", i18n.isInitialized);
// 1. Функция для принудительного определения языка из URL
const getLanguageFromURL = (): string => {
const path = window.location.pathname;
@ -10,11 +14,12 @@ const getLanguageFromURL = (): string => {
const langMatch = path.match(/^\/(en|ru|uz)(\/|$)/i);
if (langMatch) {
//console.log("Язык из URL:", langMatch[1]);
return langMatch[1].toLowerCase();
const detectedLang = langMatch[1].toLowerCase();
console.log("🔍 Main i18n: Detected language from URL:", detectedLang, "path:", path);
return detectedLang;
}
//console.log('Язык не указан в URL, используем "ru"');
console.log('🔍 Main i18n: Language not specified in URL, using "ru"');
return "ru"; // Жёсткий фолбэк
};
@ -46,11 +51,11 @@ i18n
caches: [], // Не использовать localStorage
},
parseMissingKeyHandler: (key) => {
console.warn("Missing translation:", key);
console.warn("⚠️ Main i18n: Missing translation:", key);
return key; // Вернёт ключ вместо ошибки
},
missingKeyHandler: (lngs, ns, key) => {
console.error("🚨 Missing i18n key:", {
console.error("🚨 Main i18n: Missing i18n key:", {
key,
languages: lngs,
namespace: ns,
@ -59,34 +64,41 @@ i18n
},
})
.then(() => {
console.log("i18n initialized. Current language:", i18n.language);
console.log("Loading translations from:", `/locales/${i18n.language}.json`);
console.log("✅ Main i18n: Initialization completed successfully");
console.log("🔍 Main i18n: Current language:", i18n.language);
console.log("🔍 Main i18n: Loading translations from:", `/locales/${i18n.language}.json`);
console.log("🔍 Main i18n: i18n.isInitialized:", i18n.isInitialized);
console.log("🔍 Main i18n: i18n.store.data:", i18n.store.data);
})
.catch((err) => {
console.error("Ошибка i18n:", err);
console.error("❌ Main i18n: Initialization failed:", err);
});
// 3. Логирование всех событий
i18n.on("languageChanged", (lng) => {
console.log("Язык изменён на:", lng);
console.log("🔄 Main i18n: Language changed to:", lng);
});
i18n.on("initialized", (options) => {
console.log("🎯 Main i18n: Initialized event fired with options:", options);
});
i18n.on("failedLoading", (lng, ns, msg) => {
console.error(`Ошибка загрузки ${lng}.json:`, msg);
console.error(`💥 Main i18n: Failed loading ${lng}.json:`, msg);
// Если не удалось загрузить русский, пробуем английский
if (lng === "ru") {
console.log("Пробуем загрузить английский язык как fallback");
console.log("🔄 Main i18n: Trying to load English as fallback");
i18n.changeLanguage("en");
}
});
i18n.on("loaded", (loaded) => {
console.log("Переводы загружены:", loaded);
console.log("📦 Main i18n: Translations loaded:", loaded);
});
i18n.on("missingKey", (lngs, namespace, key, res) => {
console.warn("Отсутствует ключ перевода:", { lngs, namespace, key, res });
console.warn("⚠️ Main i18n: Missing key event:", { lngs, namespace, key, res });
});
export default i18n;

@ -5,13 +5,16 @@ import { initReactI18next } from "react-i18next";
const getLanguageFromURL = (): string => {
const path = window.location.pathname;
const langMatch = path.match(/^\/(en|ru|uz)(\/|$)/i);
return langMatch ? langMatch[1].toLowerCase() : "ru"; // Фолбэк на 'ru'
const detectedLang = langMatch ? langMatch[1].toLowerCase() : "ru"; // Фолбэк на 'ru'
console.log("🔍 Widget i18n: Detected language from URL:", detectedLang, "path:", path);
return detectedLang;
};
// 2. Локали, встроенные прямо в конфиг
const r = {
ru: {
translation: {
"quiz is inactive": "Квиз не активирован",
"no questions found": "Нет созданных вопросов",
"quiz is empty": "Квиз пуст",
@ -73,7 +76,9 @@ const r = {
"Please complete the phone number": "Пожалуйста, заполните номер телефона до конца",
"": "", // Пустой ключ для fallback
},
},
en: {
translation: {
"quiz is inactive": "Quiz is inactive",
"no questions found": "No questions found",
"quiz is empty": "Quiz is empty",
@ -135,7 +140,9 @@ const r = {
"Please complete the phone number": "Please complete the phone number",
"": "", // Пустой ключ для fallback
},
},
uz: {
translation: {
"quiz is inactive": "Test faol emas",
"no questions found": "Savollar topilmadi",
"quiz is empty": "Test boʻsh",
@ -197,10 +204,43 @@ const r = {
"Please complete the phone number": "Iltimos, telefon raqamini to'liq kiriting",
"": "", // Пустой ключ для fallback
},
},
};
// 3. Конфигурация i18n без Backend
i18n
console.log("🚀 Widget i18n: Starting initialization...");
console.log("🔍 Widget i18n: i18next instance before init:", i18n);
console.log("🔍 Widget i18n: i18next isInitialized before init:", i18n.isInitialized);
// Проверяем, не инициализирован ли уже i18n
if (i18n.isInitialized) {
console.log("⚠️ Widget i18n: i18n already initialized, adding resources to existing instance");
// Добавляем ресурсы к существующему экземпляру
(Object.keys(r) as Array<"ru" | "en" | "uz">).forEach((lng) => {
if (i18n.store.data[lng] && i18n.store.data[lng].translation) {
// Объединяем с существующими переводами
i18n.store.data[lng].translation = {
...(i18n.store.data[lng].translation as Record<string, string>),
...r[lng].translation,
};
} else {
// Добавляем новые переводы
i18n.store.data[lng] = {
...(i18n.store.data[lng] as Record<string, any>),
translation: r[lng].translation,
};
}
});
console.log("✅ Widget i18n: Resources added to existing i18n instance");
console.log("🔍 Widget i18n: Testing translations after adding resources:");
console.log("🔍 Widget i18n: t('Step'):", i18n.t("Step"));
console.log("🔍 Widget i18n: t('of'):", i18n.t("of"));
console.log("🔍 Widget i18n: t('Prev'):", i18n.t("Prev"));
console.log("🔍 Widget i18n: t('Next'):", i18n.t("Next"));
} else {
// 3. Конфигурация i18n без Backend
i18n
.use(initReactI18next)
.init({
resources: r, // Используем встроенные переводы
@ -220,11 +260,11 @@ i18n
caches: [],
},
parseMissingKeyHandler: (key) => {
console.warn("Missing translation:", key);
console.warn("⚠️ Widget i18n: Missing translation key:", key);
return key;
},
missingKeyHandler: (lngs, ns, key) => {
console.error("🚨 Missing i18n key:", {
console.error("🚨 Widget i18n: Missing i18n key:", {
key,
languages: lngs,
namespace: ns,
@ -233,21 +273,54 @@ i18n
},
})
.then(() => {
console.log("i18n initialized. Current language:", i18n.language);
console.log("Available languages:", i18n.languages);
console.log("Available keys for ru:", Object.keys(r.ru));
console.log("Available keys for en:", Object.keys(r.en));
console.log("Available keys for uz:", Object.keys(r.uz));
console.log("Looking for keys: Step, of, Prev, Next");
console.log("Step in ru:", r.ru.Step);
console.log("of in ru:", r.ru.of);
console.log("Prev in ru:", r.ru.Prev);
console.log("Next in ru:", r.ru.Next);
console.log("✅ Widget i18n: Initialization completed successfully");
console.log("🔍 Widget i18n: Current language:", i18n.language);
console.log("🔍 Widget i18n: Available languages:", i18n.languages);
console.log("🔍 Widget i18n: Available keys for ru:", Object.keys(r.ru.translation));
console.log("🔍 Widget i18n: Available keys for en:", Object.keys(r.en.translation));
console.log("🔍 Widget i18n: Available keys for uz:", Object.keys(r.uz.translation));
console.log("🔍 Widget i18n: Looking for keys: Step, of, Prev, Next");
console.log("🔍 Widget i18n: Step in ru:", r.ru.translation.Step);
console.log("🔍 Widget i18n: of in ru:", r.ru.translation.of);
console.log("🔍 Widget i18n: Prev in ru:", r.ru.translation.Prev);
console.log("🔍 Widget i18n: Next in ru:", r.ru.translation.Next);
// Проверяем, что ключи доступны через i18n
console.log("🔍 Widget i18n: Testing translations through i18n:");
console.log("🔍 Widget i18n: t('Step'):", i18n.t("Step"));
console.log("🔍 Widget i18n: t('of'):", i18n.t("of"));
console.log("🔍 Widget i18n: t('Prev'):", i18n.t("Prev"));
console.log("🔍 Widget i18n: t('Next'):", i18n.t("Next"));
// Проверяем состояние i18n
console.log("🔍 Widget i18n: i18n.isInitialized:", i18n.isInitialized);
console.log("🔍 Widget i18n: i18n.store.data:", i18n.store.data);
console.log("🔍 Widget i18n: i18n.store.options:", i18n.store.options);
})
.catch((error) => {
console.error("❌ Widget i18n: Initialization failed:", error);
});
}
// 4. Логирование событий
i18n.on("languageChanged", (lng) => {
console.log("Language changed to:", lng);
console.log("🔄 Widget i18n: Language changed to:", lng);
});
i18n.on("initialized", (options) => {
console.log("🎯 Widget i18n: Initialized event fired with options:", options);
});
i18n.on("loaded", (loaded) => {
console.log("📦 Widget i18n: Loaded event fired:", loaded);
});
i18n.on("failedLoading", (lng, ns, msg) => {
console.error("💥 Widget i18n: Failed loading:", { lng, ns, msg });
});
i18n.on("missingKey", (lngs, namespace, key, res) => {
console.warn("⚠️ Widget i18n: Missing key event:", { lngs, namespace, key, res });
});
export default i18n;

@ -2,8 +2,14 @@ import { createRoot } from "react-dom/client";
import { RouteObject, RouterProvider, createBrowserRouter } from "react-router-dom";
import App from "./App";
import { StrictMode, lazy } from "react";
console.log("🚀 Main: Starting main application...");
console.log("🔍 Main: About to import main i18n...");
import "./i18n/i18n";
console.log("✅ Main: Main i18n imported successfully");
const routes: RouteObject[] = [
{
path: "/",
@ -38,8 +44,12 @@ const router = createBrowserRouter(routes);
const root = createRoot(document.getElementById("root")!);
console.log("🔧 Main: About to render application...");
root.render(
<StrictMode>
<RouterProvider router={router} />
</StrictMode>
);
console.log("✅ Main: Application rendered successfully");

@ -2,8 +2,14 @@ import QuizAnswerer from "@/components/QuizAnswerer";
import { createRoot } from "react-dom/client";
// eslint-disable-next-line react-refresh/only-export-components
export * from "./widgets";
console.log("🚀 Widget: Starting widget initialization...");
console.log("🔍 Widget: About to import i18nWidget...");
import "./i18n/i18nWidget";
console.log("✅ Widget: i18nWidget imported successfully");
// old widget
const widget = {
create({
@ -15,11 +21,18 @@ const widget = {
quizId: string;
changeFaviconAndTitle: boolean;
}) {
const element = document.getElementById(selector);
if (!element) throw new Error("Element for widget doesn't exist");
console.log("🔧 Widget: create() called with:", { selector, quizId, changeFaviconAndTitle });
const element = document.getElementById(selector);
if (!element) {
console.error("❌ Widget: Element for widget doesn't exist:", selector);
throw new Error("Element for widget doesn't exist");
}
console.log("✅ Widget: Element found, creating root...");
const root = createRoot(element);
console.log("🔧 Widget: Rendering QuizAnswerer component...");
root.render(
<QuizAnswerer
quizId={quizId}
@ -27,7 +40,10 @@ const widget = {
disableGlobalCss
/>
);
console.log("✅ Widget: QuizAnswerer component rendered successfully");
},
};
console.log("✅ Widget: Widget object created and exported");
export default widget;