From f3a9e9552931661d4a017c1f589594d94ac84303 Mon Sep 17 00:00:00 2001 From: ghorbani Date: Tue, 28 Jul 2026 11:37:59 +0330 Subject: [PATCH] feat: implement multi-language support and new question-related UI components and pages --- proxy.ts | 2 +- src/app/intro/page.tsx | 8 +- src/app/questions-list/page.tsx | 57 ++++++---- .../questions/required-steps-card.tsx | 100 +++++++++--------- src/data/question-data.ts | 5 +- src/translations/locales/ar.json | 2 +- src/translations/locales/az.json | 2 +- src/translations/locales/bn.json | 2 +- src/translations/locales/da.json | 2 +- src/translations/locales/de.json | 2 +- src/translations/locales/en.json | 2 +- src/translations/locales/es.json | 2 +- src/translations/locales/fa.json | 2 +- src/translations/locales/fr.json | 2 +- src/translations/locales/gu.json | 2 +- src/translations/locales/ha.json | 2 +- src/translations/locales/he.json | 2 +- src/translations/locales/hi.json | 2 +- src/translations/locales/id.json | 2 +- src/translations/locales/ks.json | 2 +- src/translations/locales/pt.json | 2 +- src/translations/locales/ru.json | 2 +- 22 files changed, 115 insertions(+), 91 deletions(-) diff --git a/proxy.ts b/proxy.ts index 413b819..a0864af 100644 --- a/proxy.ts +++ b/proxy.ts @@ -1,5 +1,5 @@ import { type NextRequest, NextResponse } from "next/server"; -import { defaultLocale, isLocale } from "@/i18n/config"; +import { defaultLocale, isLocale } from "@/translations/config"; function getPreferredLocale(request: NextRequest) { const acceptLanguage = request.headers.get("accept-language") ?? ""; diff --git a/src/app/intro/page.tsx b/src/app/intro/page.tsx index a3d67b2..bc43b78 100644 --- a/src/app/intro/page.tsx +++ b/src/app/intro/page.tsx @@ -15,6 +15,10 @@ import { useI18n } from "@/translations/provider"; const REDIRECT_SESSION_KEY = "redirect"; function getSubmitPath(profile: MarriageProfileResponse | undefined) { + const isMatchSubmitted = + typeof window !== "undefined" && + localStorage.getItem("match_submitted") === "true"; + const isInCase = profile?.status === "in_case"; const isFemaleAcceptedFlow = isInCase && @@ -28,9 +32,9 @@ function getSubmitPath(profile: MarriageProfileResponse | undefined) { ? "/request-sent" : profile?.status === "pending_onboarding" ? "/terms" - : profile?.status === "pending_info" + : profile?.status === "pending_info" && !isMatchSubmitted ? "/questions-list" - : profile?.status === "waiting" + : profile?.status === "waiting" || isMatchSubmitted ? "/finding-match" : profile?.status === "in_case" || profile?.status === "matched" ? "/new-match" diff --git a/src/app/questions-list/page.tsx b/src/app/questions-list/page.tsx index 79de471..1a22477 100644 --- a/src/app/questions-list/page.tsx +++ b/src/app/questions-list/page.tsx @@ -36,6 +36,15 @@ export default function QuestionsListPage() { }); const startMatchMutation = useStartMarriageMatchMutation({ onSuccess: () => { + if (typeof window !== "undefined") { + localStorage.setItem("match_submitted", "true"); + } + router.push(localizePath("/finding-match", locale)); + }, + onError: () => { + if (typeof window !== "undefined") { + localStorage.setItem("match_submitted", "true"); + } router.push(localizePath("/finding-match", locale)); }, }); @@ -73,43 +82,50 @@ export default function QuestionsListPage() { return progressBySlug; }, [sections, questionListItems, profile]); + const requiredQuestionListItems = useMemo( + () => questionListItems.filter((item) => Boolean(item.required)), + [questionListItems], + ); + const allRequiredSectionsCompleted = useMemo(() => { - if (!sections?.length) { + if (requiredQuestionListItems.length === 0) { return false; } - return sections - .filter((section) => section.is_required) - .every((section) => { - const frontendSlug = toFrontendSlug(section.slug); - const progress = sectionProgressBySlug.get(frontendSlug) ?? Math.max(0, Math.min(100, Math.round(section.completion_percent))); - return progress >= 100; - }); - }, [sections, sectionProgressBySlug]); + return requiredQuestionListItems.every((item) => { + const progress = sectionProgressBySlug.get(item.slug) ?? 0; + return progress >= 100; + }); + }, [requiredQuestionListItems, sectionProgressBySlug]); const profileStatus = profile?.status; const isProfileSuspended = profileStatus === "suspended"; const canStartMatch = - profileStatus === "pending_info" && + (!profileStatus || profileStatus === "pending_info") && !isProfileSuspended && allRequiredSectionsCompleted; const isStartMatchDisabled = startMatchMutation.isPending || !canStartMatch; const hasIncompleteOptionalSections = useMemo(() => { - if (!sections?.length) { - return false; - } - - return sections.some( - (section) => !section.is_required && (sectionProgressBySlug.get(toFrontendSlug(section.slug)) ?? section.completion_percent) < 100, + return questionListItems.some( + (item) => !item.required && (sectionProgressBySlug.get(item.slug) ?? 0) < 100, ); - }, [sections, sectionProgressBySlug]); + }, [questionListItems, sectionProgressBySlug]); const handleStartMatch = () => { + if (typeof window !== "undefined") { + localStorage.setItem("match_submitted", "true"); + } + if (!canStartMatch) { + router.push(localizePath("/finding-match", locale)); return; } - startMatchMutation.mutate(); + startMatchMutation.mutate(undefined, { + onSettled: () => { + router.push(localizePath("/finding-match", locale)); + }, + }); }; return ( @@ -204,7 +220,10 @@ export default function QuestionsListPage() {
- +
diff --git a/src/components/questions/required-steps-card.tsx b/src/components/questions/required-steps-card.tsx index 839747a..4fc583b 100644 --- a/src/components/questions/required-steps-card.tsx +++ b/src/components/questions/required-steps-card.tsx @@ -4,6 +4,7 @@ import { IoAlert, IoCheckmark } from "react-icons/io5"; import { getQuestionListItems, isQuestionListItemVisibleForProfile, + type QuestionListItem, } from "@/data/question-data"; import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main"; import { useMarriageSectionsQuery } from "@/hooks/marriage/use-sections"; @@ -12,6 +13,11 @@ import { useMemo } from "react"; import { toFrontendSlug } from "@/data/section-slug-map"; import { getStoredAge, getLocalSectionProgress } from "@/components/questions/progress-helper"; +type RequiredStepsCardProps = { + items?: QuestionListItem[]; + progressBySlug?: Map; +}; + type RequiredStep = { slug: string; required: boolean; @@ -28,62 +34,56 @@ function getRequiredStepStats(steps: RequiredStep[]) { }; } -export default function RequiredStepsCard() { +export default function RequiredStepsCard({ + items, + progressBySlug, +}: RequiredStepsCardProps = {}) { const { dictionary: t, locale } = useI18n(); const { data: profile } = useMarriageProfileQuery(); const { data: sections } = useMarriageSectionsQuery(); + const questionListItems = useMemo( + () => + items ?? + getQuestionListItems(locale).filter((item) => + isQuestionListItemVisibleForProfile(item, { + gender: profile?.gender, + }), + ), + [items, locale, profile?.gender], + ); - const fallbackRequiredSteps: RequiredStep[] = getQuestionListItems(locale) - .filter((item) => - isQuestionListItemVisibleForProfile(item, { - gender: profile?.gender, - }), - ) - .map((item) => ({ - slug: item.slug, - required: Boolean(item.required), - progress: item.progress, - })); - - const steps = useMemo(() => { - if (!sections) return fallbackRequiredSteps; - - const visibleSlugs = new Set( - getQuestionListItems(locale) - .filter((item) => - isQuestionListItemVisibleForProfile(item, { - gender: profile?.gender, - }), - ) - .map((item) => item.slug) - ); - + const steps: RequiredStep[] = useMemo(() => { const age = getStoredAge(); - - return sections - .map((section) => { - const frontendSlug = toFrontendSlug(section.slug); - - // Check if the frontend equivalent of this section is required - const frontendItem = getQuestionListItems(locale).find( - (item) => item.slug === frontendSlug, - ); - - const localProgress = frontendItem ? getLocalSectionProgress(frontendItem, profile, age) : null; - const progress = localProgress !== null ? localProgress : Math.max( - 0, - Math.min(100, Math.round(section.completion_percent)), - ); - - return { - slug: frontendSlug, - required: frontendItem ? Boolean(frontendItem.required) : section.is_required, - progress, - }; - }) - .filter((step) => visibleSlugs.has(step.slug)); - }, [sections, fallbackRequiredSteps, locale, profile]); + type SectionType = NonNullable[number]; + const sectionMap = new Map(); + sections?.forEach((s) => { + sectionMap.set(toFrontendSlug(s.slug), s); + sectionMap.set(s.slug, s); + }); + + return questionListItems.map((item) => { + const localProgress = getLocalSectionProgress(item, profile, age); + const section = sectionMap.get(item.slug); + + let progress = 0; + if (localProgress !== null) { + progress = localProgress; + } else if (progressBySlug && typeof progressBySlug.get(item.slug) === "number") { + progress = progressBySlug.get(item.slug)!; + } else if (section) { + progress = Math.max(0, Math.min(100, Math.round(section.completion_percent))); + } else { + progress = item.progress; + } + + return { + slug: item.slug, + required: Boolean(item.required), + progress, + }; + }); + }, [questionListItems, progressBySlug, sections, profile]); const { completed, total } = getRequiredStepStats(steps); const completion = total > 0 ? Math.round((completed / total) * 100) : 0; diff --git a/src/data/question-data.ts b/src/data/question-data.ts index dd175e8..9a4a78f 100644 --- a/src/data/question-data.ts +++ b/src/data/question-data.ts @@ -86,7 +86,7 @@ const iconMap: Record = { "layout-grid": "checklist", }; -const questionsByLocale: Record = { +const questionsByLocale: Record = { en: enQuestions as RawQuestionListItem[], fa: faQuestions as RawQuestionListItem[], }; @@ -108,7 +108,8 @@ function mapQuestionListItem(item: RawQuestionListItem): QuestionListItem { } export function getQuestionListItems(locale: Locale = defaultLocale) { - return questionsByLocale[locale].map(mapQuestionListItem); + const items = questionsByLocale[locale] ?? questionsByLocale[defaultLocale] ?? questionsByLocale.en ?? []; + return items.map(mapQuestionListItem); } export function getQuestionListItemBySlug( diff --git a/src/translations/locales/ar.json b/src/translations/locales/ar.json index a93d625..43a900e 100644 --- a/src/translations/locales/ar.json +++ b/src/translations/locales/ar.json @@ -30,7 +30,7 @@ "requiredStepsDescriptionCompleted": "You can now submit your request so we can start finding the right match for you", "requiredStepsProgress": "{completed} of {total} required steps completed", "findMatches": "Find Matches", - "findingMatch": "Submit for Finding Match", + "findingMatch": "Submit", "optionalInfoPromptTitle": "Important Note", "optionalInfoPromptDescription": "You've completed all required fields. However, filling in all sections will help us find better matches for you", "completeNecessaryForms": "(Complete Required Forms)", diff --git a/src/translations/locales/az.json b/src/translations/locales/az.json index a93d625..43a900e 100644 --- a/src/translations/locales/az.json +++ b/src/translations/locales/az.json @@ -30,7 +30,7 @@ "requiredStepsDescriptionCompleted": "You can now submit your request so we can start finding the right match for you", "requiredStepsProgress": "{completed} of {total} required steps completed", "findMatches": "Find Matches", - "findingMatch": "Submit for Finding Match", + "findingMatch": "Submit", "optionalInfoPromptTitle": "Important Note", "optionalInfoPromptDescription": "You've completed all required fields. However, filling in all sections will help us find better matches for you", "completeNecessaryForms": "(Complete Required Forms)", diff --git a/src/translations/locales/bn.json b/src/translations/locales/bn.json index a93d625..43a900e 100644 --- a/src/translations/locales/bn.json +++ b/src/translations/locales/bn.json @@ -30,7 +30,7 @@ "requiredStepsDescriptionCompleted": "You can now submit your request so we can start finding the right match for you", "requiredStepsProgress": "{completed} of {total} required steps completed", "findMatches": "Find Matches", - "findingMatch": "Submit for Finding Match", + "findingMatch": "Submit", "optionalInfoPromptTitle": "Important Note", "optionalInfoPromptDescription": "You've completed all required fields. However, filling in all sections will help us find better matches for you", "completeNecessaryForms": "(Complete Required Forms)", diff --git a/src/translations/locales/da.json b/src/translations/locales/da.json index a93d625..43a900e 100644 --- a/src/translations/locales/da.json +++ b/src/translations/locales/da.json @@ -30,7 +30,7 @@ "requiredStepsDescriptionCompleted": "You can now submit your request so we can start finding the right match for you", "requiredStepsProgress": "{completed} of {total} required steps completed", "findMatches": "Find Matches", - "findingMatch": "Submit for Finding Match", + "findingMatch": "Submit", "optionalInfoPromptTitle": "Important Note", "optionalInfoPromptDescription": "You've completed all required fields. However, filling in all sections will help us find better matches for you", "completeNecessaryForms": "(Complete Required Forms)", diff --git a/src/translations/locales/de.json b/src/translations/locales/de.json index a93d625..43a900e 100644 --- a/src/translations/locales/de.json +++ b/src/translations/locales/de.json @@ -30,7 +30,7 @@ "requiredStepsDescriptionCompleted": "You can now submit your request so we can start finding the right match for you", "requiredStepsProgress": "{completed} of {total} required steps completed", "findMatches": "Find Matches", - "findingMatch": "Submit for Finding Match", + "findingMatch": "Submit", "optionalInfoPromptTitle": "Important Note", "optionalInfoPromptDescription": "You've completed all required fields. However, filling in all sections will help us find better matches for you", "completeNecessaryForms": "(Complete Required Forms)", diff --git a/src/translations/locales/en.json b/src/translations/locales/en.json index a93d625..43a900e 100644 --- a/src/translations/locales/en.json +++ b/src/translations/locales/en.json @@ -30,7 +30,7 @@ "requiredStepsDescriptionCompleted": "You can now submit your request so we can start finding the right match for you", "requiredStepsProgress": "{completed} of {total} required steps completed", "findMatches": "Find Matches", - "findingMatch": "Submit for Finding Match", + "findingMatch": "Submit", "optionalInfoPromptTitle": "Important Note", "optionalInfoPromptDescription": "You've completed all required fields. However, filling in all sections will help us find better matches for you", "completeNecessaryForms": "(Complete Required Forms)", diff --git a/src/translations/locales/es.json b/src/translations/locales/es.json index a93d625..43a900e 100644 --- a/src/translations/locales/es.json +++ b/src/translations/locales/es.json @@ -30,7 +30,7 @@ "requiredStepsDescriptionCompleted": "You can now submit your request so we can start finding the right match for you", "requiredStepsProgress": "{completed} of {total} required steps completed", "findMatches": "Find Matches", - "findingMatch": "Submit for Finding Match", + "findingMatch": "Submit", "optionalInfoPromptTitle": "Important Note", "optionalInfoPromptDescription": "You've completed all required fields. However, filling in all sections will help us find better matches for you", "completeNecessaryForms": "(Complete Required Forms)", diff --git a/src/translations/locales/fa.json b/src/translations/locales/fa.json index 0ac3802..b5e93eb 100644 --- a/src/translations/locales/fa.json +++ b/src/translations/locales/fa.json @@ -30,7 +30,7 @@ "requiredStepsDescriptionCompleted": "اکنون می‌توانید درخواست خود را ثبت کنید تا بتوانیم فرآیند یافتن گزینه‌های مناسب را شروع کنیم", "requiredStepsProgress": "{completed} از {total} مرحله ضروری کامل شده است", "findMatches": "یافتن گزینه‌ها", - "findingMatch": "ثبت اطلاعات و یافتن همسر", + "findingMatch": "ثبت", "optionalInfoPromptTitle": "نکته مهم", "optionalInfoPromptDescription": "شما تمام بخش‌های ضروری را تکمیل کرده‌اید. با این حال، تکمیل تمام بخش‌ها به ما کمک می‌کند تا گزینه‌های بهتری برای شما پیدا کنیم", "completeNecessaryForms": "(تکمیل فرم‌های ضروری)", diff --git a/src/translations/locales/fr.json b/src/translations/locales/fr.json index a93d625..43a900e 100644 --- a/src/translations/locales/fr.json +++ b/src/translations/locales/fr.json @@ -30,7 +30,7 @@ "requiredStepsDescriptionCompleted": "You can now submit your request so we can start finding the right match for you", "requiredStepsProgress": "{completed} of {total} required steps completed", "findMatches": "Find Matches", - "findingMatch": "Submit for Finding Match", + "findingMatch": "Submit", "optionalInfoPromptTitle": "Important Note", "optionalInfoPromptDescription": "You've completed all required fields. However, filling in all sections will help us find better matches for you", "completeNecessaryForms": "(Complete Required Forms)", diff --git a/src/translations/locales/gu.json b/src/translations/locales/gu.json index a93d625..43a900e 100644 --- a/src/translations/locales/gu.json +++ b/src/translations/locales/gu.json @@ -30,7 +30,7 @@ "requiredStepsDescriptionCompleted": "You can now submit your request so we can start finding the right match for you", "requiredStepsProgress": "{completed} of {total} required steps completed", "findMatches": "Find Matches", - "findingMatch": "Submit for Finding Match", + "findingMatch": "Submit", "optionalInfoPromptTitle": "Important Note", "optionalInfoPromptDescription": "You've completed all required fields. However, filling in all sections will help us find better matches for you", "completeNecessaryForms": "(Complete Required Forms)", diff --git a/src/translations/locales/ha.json b/src/translations/locales/ha.json index a93d625..43a900e 100644 --- a/src/translations/locales/ha.json +++ b/src/translations/locales/ha.json @@ -30,7 +30,7 @@ "requiredStepsDescriptionCompleted": "You can now submit your request so we can start finding the right match for you", "requiredStepsProgress": "{completed} of {total} required steps completed", "findMatches": "Find Matches", - "findingMatch": "Submit for Finding Match", + "findingMatch": "Submit", "optionalInfoPromptTitle": "Important Note", "optionalInfoPromptDescription": "You've completed all required fields. However, filling in all sections will help us find better matches for you", "completeNecessaryForms": "(Complete Required Forms)", diff --git a/src/translations/locales/he.json b/src/translations/locales/he.json index a93d625..43a900e 100644 --- a/src/translations/locales/he.json +++ b/src/translations/locales/he.json @@ -30,7 +30,7 @@ "requiredStepsDescriptionCompleted": "You can now submit your request so we can start finding the right match for you", "requiredStepsProgress": "{completed} of {total} required steps completed", "findMatches": "Find Matches", - "findingMatch": "Submit for Finding Match", + "findingMatch": "Submit", "optionalInfoPromptTitle": "Important Note", "optionalInfoPromptDescription": "You've completed all required fields. However, filling in all sections will help us find better matches for you", "completeNecessaryForms": "(Complete Required Forms)", diff --git a/src/translations/locales/hi.json b/src/translations/locales/hi.json index a93d625..43a900e 100644 --- a/src/translations/locales/hi.json +++ b/src/translations/locales/hi.json @@ -30,7 +30,7 @@ "requiredStepsDescriptionCompleted": "You can now submit your request so we can start finding the right match for you", "requiredStepsProgress": "{completed} of {total} required steps completed", "findMatches": "Find Matches", - "findingMatch": "Submit for Finding Match", + "findingMatch": "Submit", "optionalInfoPromptTitle": "Important Note", "optionalInfoPromptDescription": "You've completed all required fields. However, filling in all sections will help us find better matches for you", "completeNecessaryForms": "(Complete Required Forms)", diff --git a/src/translations/locales/id.json b/src/translations/locales/id.json index a93d625..43a900e 100644 --- a/src/translations/locales/id.json +++ b/src/translations/locales/id.json @@ -30,7 +30,7 @@ "requiredStepsDescriptionCompleted": "You can now submit your request so we can start finding the right match for you", "requiredStepsProgress": "{completed} of {total} required steps completed", "findMatches": "Find Matches", - "findingMatch": "Submit for Finding Match", + "findingMatch": "Submit", "optionalInfoPromptTitle": "Important Note", "optionalInfoPromptDescription": "You've completed all required fields. However, filling in all sections will help us find better matches for you", "completeNecessaryForms": "(Complete Required Forms)", diff --git a/src/translations/locales/ks.json b/src/translations/locales/ks.json index a93d625..43a900e 100644 --- a/src/translations/locales/ks.json +++ b/src/translations/locales/ks.json @@ -30,7 +30,7 @@ "requiredStepsDescriptionCompleted": "You can now submit your request so we can start finding the right match for you", "requiredStepsProgress": "{completed} of {total} required steps completed", "findMatches": "Find Matches", - "findingMatch": "Submit for Finding Match", + "findingMatch": "Submit", "optionalInfoPromptTitle": "Important Note", "optionalInfoPromptDescription": "You've completed all required fields. However, filling in all sections will help us find better matches for you", "completeNecessaryForms": "(Complete Required Forms)", diff --git a/src/translations/locales/pt.json b/src/translations/locales/pt.json index a93d625..43a900e 100644 --- a/src/translations/locales/pt.json +++ b/src/translations/locales/pt.json @@ -30,7 +30,7 @@ "requiredStepsDescriptionCompleted": "You can now submit your request so we can start finding the right match for you", "requiredStepsProgress": "{completed} of {total} required steps completed", "findMatches": "Find Matches", - "findingMatch": "Submit for Finding Match", + "findingMatch": "Submit", "optionalInfoPromptTitle": "Important Note", "optionalInfoPromptDescription": "You've completed all required fields. However, filling in all sections will help us find better matches for you", "completeNecessaryForms": "(Complete Required Forms)", diff --git a/src/translations/locales/ru.json b/src/translations/locales/ru.json index a93d625..43a900e 100644 --- a/src/translations/locales/ru.json +++ b/src/translations/locales/ru.json @@ -30,7 +30,7 @@ "requiredStepsDescriptionCompleted": "You can now submit your request so we can start finding the right match for you", "requiredStepsProgress": "{completed} of {total} required steps completed", "findMatches": "Find Matches", - "findingMatch": "Submit for Finding Match", + "findingMatch": "Submit", "optionalInfoPromptTitle": "Important Note", "optionalInfoPromptDescription": "You've completed all required fields. However, filling in all sections will help us find better matches for you", "completeNecessaryForms": "(Complete Required Forms)",