"use client"; import { useRouter } from "next/navigation"; import { useEffect, useState } from "react"; import { GoArrowLeft, GoArrowRight } from "react-icons/go"; import { useI18n } from "@/translations/provider"; import Button from "./button"; import { ExplanationUiFont } from "./explanation-ui-font"; import NavigationButton from "./navigation-button"; import { PageBackground } from "./page-background"; import StickyHeader from "./sticky-header"; import TestLoadingScreen from "./test-loading-screen"; export type QuestionOption = { id: string; label: string; value: string | number; }; export type TestQuestion = { id: number; text: string; info?: string; options: QuestionOption[]; }; type TestQuestionsFlowProps = { title: string; questions: TestQuestion[]; closeLabel: string; informationLabel: string; previousLabel?: string; finishLabel?: string; stepsLabel?: string; onFinish?: (answers: Record) => void; onClose?: () => void; draftStorageKey?: string | null; }; type StoredTestDraft = { answers?: Record; currentIndex?: number; totalQuestions?: number; }; function getStoredDraft( storageKey: string | null | undefined, totalQuestions: number, ) { if (!storageKey || typeof window === "undefined") return { answers: {}, currentIndex: 0 }; try { const rawDraft = window.localStorage.getItem(storageKey); if (!rawDraft) return { answers: {}, currentIndex: 0 }; const draft = JSON.parse(rawDraft) as StoredTestDraft; return { answers: draft.answers && typeof draft.answers === "object" ? draft.answers : {}, currentIndex: Number.isInteger(draft.currentIndex) ? Math.min( Math.max(draft.currentIndex ?? 0, 0), Math.max(totalQuestions - 1, 0), ) : 0, }; } catch { return { answers: {}, currentIndex: 0 }; } } function formatOptionLabel(str: string): string { if (!str) return str; let result = str.trim(); // Remove trailing numbers in parentheses, e.g., (1) or (۱) result = result.replace(/\s*\([\d۱۲۳۴۵۶۷۸۹۰]+\)\s*$/, ""); if (result.startsWith(")")) { result = result.slice(1).trim(); } if (result.endsWith("(")) { result = result.slice(0, -1).trim(); } let openCount = 0; let closeCount = 0; for (let i = 0; i < result.length; i++) { if (result[i] === "(") openCount++; else if (result[i] === ")") closeCount++; } if (openCount > closeCount) { result = result + ")".repeat(openCount - closeCount); } else if (closeCount > openCount) { result = "(".repeat(closeCount - openCount) + result; } if (result.length > 0) { result = result.charAt(0).toUpperCase() + result.slice(1); } return result; } export default function TestQuestionsFlow({ title, questions, closeLabel, informationLabel, previousLabel = "Previous", finishLabel = "Finish", stepsLabel = "Steps", onFinish, onClose, draftStorageKey, }: TestQuestionsFlowProps) { const router = useRouter(); const { locale } = useI18n(); const [currentIndex, setCurrentIndex] = useState( () => getStoredDraft(draftStorageKey, questions.length).currentIndex, ); const [answers, setAnswers] = useState>( () => getStoredDraft(draftStorageKey, questions.length).answers, ); const [isSubmitting, setIsSubmitting] = useState(false); const isTargetTest = !!draftStorageKey && (draftStorageKey.includes("personality_test") || draftStorageKey.includes("glasser_5_needs_test")); const [maxVisitedIndex, setMaxVisitedIndex] = useState(() => { const initialIndex = getStoredDraft( draftStorageKey, questions.length, ).currentIndex; const initialAnswers = getStoredDraft( draftStorageKey, questions.length, ).answers; let highestAnswered = -1; for (let i = 0; i < questions.length; i++) { if (initialAnswers[questions[i].id] !== undefined) { highestAnswered = i; } } const furthestReached = highestAnswered !== -1 ? Math.min(highestAnswered + 1, questions.length - 1) : 0; return Math.max(initialIndex, furthestReached); }); useEffect(() => { if (Object.keys(answers).length === 0) { setMaxVisitedIndex(currentIndex); } else if (currentIndex > maxVisitedIndex) { setMaxVisitedIndex(currentIndex); } }, [currentIndex, answers, maxVisitedIndex]); const currentQuestion = questions[currentIndex] ?? questions[0]; const totalQuestions = questions.length; const isLastQuestion = currentIndex === totalQuestions - 1; const selectedValue = currentQuestion ? answers[currentQuestion.id] : undefined; useEffect(() => { if (!draftStorageKey) return; try { const match = draftStorageKey.match( /^marriage:user:(\d+):tests:([^:]+):draft:v(\d+)$/, ); const ownerProfileId = match ? Number(match[1]) : undefined; const version = match ? Number(match[3]) : undefined; const slug = match ? match[2] : undefined; window.localStorage.setItem( draftStorageKey, JSON.stringify({ answers, currentIndex, totalQuestions, ...(ownerProfileId !== undefined ? { ownerProfileId } : {}), ...(version !== undefined ? { version } : {}), ...(slug !== undefined ? { slug } : {}), }), ); } catch {} }, [answers, currentIndex, draftStorageKey, totalQuestions]); const handleOptionSelect = (value: string | number) => { if (!currentQuestion) return; setAnswers((prev) => ({ ...prev, [currentQuestion.id]: value, })); // Auto advance to next question if not on last question with slide animation if (!isLastQuestion) { setTimeout(() => { setCurrentIndex((prev) => Math.min(prev + 1, totalQuestions - 1)); }, 200); } }; const handlePrev = () => { setCurrentIndex((prev) => Math.max(prev - 1, 0)); }; const handleNext = () => { setCurrentIndex((prev) => Math.min(prev + 1, totalQuestions - 1)); }; const handleFinishSubmit = async () => { if (isSubmitting) return; setIsSubmitting(true); try { if (onFinish) { await onFinish(answers); } if (draftStorageKey) window.localStorage.removeItem(draftStorageKey); router.back(); } catch { // ignore } finally { setIsSubmitting(false); } }; if (isSubmitting) { return ( ); } if (!currentQuestion) { return null; } const progressPercent = (((isTargetTest ? maxVisitedIndex : currentIndex) + 1) / totalQuestions) * 100; const areAllQuestionsAnswered = questions.every( (question) => answers[question.id] !== undefined, ); return ( <>
{/* Header */}

{title}

{/* Content Area */}
{/* Progress Section */}
{stepsLabel} {currentIndex + 1} /{totalQuestions}
{/* Progress Track & Fill */}
{/* Question Slider Viewport */}
{questions.map((q, index) => { const offset = index - currentIndex; const isNearby = Math.abs(offset) <= 1; if (!isNearby) return null; const qSelectedValue = answers[q.id]; const options = q.options || []; return (
{/* Question Title */}

{q.text}

{/* Answer Options Stack */}
{options.map((option) => { const isSelected = qSelectedValue === option.value; return ( ); })}
); })}
{/* Bottom Actions Bar */}
{/* Previous Button */} {/* Next Button */} {isTargetTest && !isLastQuestion && selectedValue !== undefined && currentIndex < maxVisitedIndex && ( )} {/* Finish Button on Last Question */} {isLastQuestion ? ( ) : null}
); }