diff --git a/src/components/Componentes/question-sheet.tsx b/src/components/Componentes/question-sheet.tsx index 6823b9d..99db9fa 100644 --- a/src/components/Componentes/question-sheet.tsx +++ b/src/components/Componentes/question-sheet.tsx @@ -2,7 +2,8 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { createPortal } from "react-dom"; -import { getLanguageList, LANGUAGES_EN, LANGUAGES_FA } from "@/data/languages"; +import { COUNTRIES_EN, COUNTRIES_FA } from "@/data/countries"; +import { LANGUAGES_EN, LANGUAGES_FA } from "@/data/languages"; import type { QuestionField } from "@/lib/schema-adapter"; import { useI18n } from "@/translations/provider"; import { Button } from "./button"; @@ -81,63 +82,133 @@ export function QuestionSheet({ question, disabled }: QuestionSheetProps) { question.title?.includes("زبان") || question.ui_config?.dataset === "languages"; + const isCountryQuestion = + question.id?.toLowerCase().includes("nationality") || + question.id?.toLowerCase().includes("citizenship") || + question.id?.toLowerCase().includes("country") || + question.id?.toLowerCase().includes("birthplace") || + question.id?.toLowerCase().includes("residence") || + question.title?.toLowerCase().includes("nationality") || + question.title?.toLowerCase().includes("citizenship") || + question.title?.toLowerCase().includes("country") || + question.title?.includes("ملیت") || + question.title?.includes("تابعیت") || + question.title?.includes("کشور") || + question.title?.includes("سکونت") || + question.ui_config?.dataset === "countries" || + question.ui_config?.dataset === "nationalities"; + const options = useMemo(() => { const rawOptions = question.options || []; - if (!isLanguageQuestion) { - return rawOptions; + + if (isLanguageQuestion) { + const existingByLabel = new Map( + rawOptions.map((opt) => [opt.label.toLowerCase().trim(), opt]), + ); + const existingByVal = new Map( + rawOptions.map((opt) => [String(opt.value).toLowerCase().trim(), opt]), + ); + const existingById = new Map( + rawOptions.map((opt) => [opt.id.toLowerCase().trim(), opt]), + ); + + const mergedOptions: typeof rawOptions = []; + const seenIds = new Set(); + + LANGUAGES_EN.forEach((enLang, idx) => { + const localizedLabel = + (locale === "fa" || locale === "fa-ir" + ? LANGUAGES_FA[idx] + : (t as any)[enLang]) || enLang; + const cleanSlug = enLang + .toLowerCase() + .replace(/[^a-z0-9]+/g, "_") + .replace(/^_+|_+$/g, ""); + const generatedId = `${question.id}.${cleanSlug}`; + + const existing = + existingByLabel.get(enLang.toLowerCase()) || + existingByLabel.get(localizedLabel.toLowerCase()) || + existingByVal.get(cleanSlug) || + existingById.get(generatedId.toLowerCase()); + + const finalId = existing?.id || generatedId; + if (seenIds.has(finalId)) return; + seenIds.add(finalId); + + mergedOptions.push({ + id: finalId, + value: existing?.value ?? cleanSlug, + label: existing?.label ?? localizedLabel, + order: existing?.order ?? idx, + }); + }); + + rawOptions.forEach((opt) => { + if (!seenIds.has(opt.id)) { + seenIds.add(opt.id); + mergedOptions.push(opt); + } + }); + + return mergedOptions; } - const existingByLabel = new Map( - rawOptions.map((opt) => [opt.label.toLowerCase().trim(), opt]), - ); - const existingByVal = new Map( - rawOptions.map((opt) => [String(opt.value).toLowerCase().trim(), opt]), - ); - const existingById = new Map( - rawOptions.map((opt) => [opt.id.toLowerCase().trim(), opt]), - ); + if (isCountryQuestion) { + const existingByLabel = new Map( + rawOptions.map((opt) => [opt.label.toLowerCase().trim(), opt]), + ); + const existingByVal = new Map( + rawOptions.map((opt) => [String(opt.value).toLowerCase().trim(), opt]), + ); + const existingById = new Map( + rawOptions.map((opt) => [opt.id.toLowerCase().trim(), opt]), + ); + + const mergedOptions: typeof rawOptions = []; + const seenIds = new Set(); + + COUNTRIES_EN.forEach((enCountry, idx) => { + const localizedLabel = + (locale === "fa" || locale === "fa-ir" + ? COUNTRIES_FA[idx] + : (t as any)[enCountry]) || enCountry; + const cleanSlug = enCountry + .toLowerCase() + .replace(/[^a-z0-9]+/g, "_") + .replace(/^_+|_+$/g, ""); + const generatedId = `${question.id}.${cleanSlug}`; + + const existing = + existingByLabel.get(enCountry.toLowerCase()) || + existingByLabel.get(localizedLabel.toLowerCase()) || + existingByVal.get(cleanSlug) || + existingById.get(generatedId.toLowerCase()); + + const finalId = existing?.id || generatedId; + if (seenIds.has(finalId)) return; + seenIds.add(finalId); + + mergedOptions.push({ + id: finalId, + value: existing?.value ?? cleanSlug, + label: existing?.label ?? localizedLabel, + order: existing?.order ?? idx, + }); + }); - const mergedOptions: typeof rawOptions = []; - const seenIds = new Set(); - - LANGUAGES_EN.forEach((enLang, idx) => { - const localizedLabel = - (locale === "fa" || locale === "fa-ir" - ? LANGUAGES_FA[idx] - : (t as any)[enLang]) || enLang; - const cleanSlug = enLang - .toLowerCase() - .replace(/[^a-z0-9]+/g, "_") - .replace(/^_+|_+$/g, ""); - const generatedId = `${question.id}.${cleanSlug}`; - - const existing = - existingByLabel.get(enLang.toLowerCase()) || - existingByLabel.get(localizedLabel.toLowerCase()) || - existingByVal.get(cleanSlug) || - existingById.get(generatedId.toLowerCase()); - - const finalId = existing?.id || generatedId; - if (seenIds.has(finalId)) return; - seenIds.add(finalId); - - mergedOptions.push({ - id: finalId, - value: existing?.value ?? cleanSlug, - label: existing?.label ?? localizedLabel, - order: existing?.order ?? idx, + rawOptions.forEach((opt) => { + if (!seenIds.has(opt.id)) { + seenIds.add(opt.id); + mergedOptions.push(opt); + } }); - }); - rawOptions.forEach((opt) => { - if (!seenIds.has(opt.id)) { - seenIds.add(opt.id); - mergedOptions.push(opt); - } - }); + return mergedOptions; + } - return mergedOptions; - }, [question, isLanguageQuestion, locale, t]); + return rawOptions; + }, [question, isLanguageQuestion, isCountryQuestion, locale, t]); const COMPACT_OPTIONS_MAX = 6; @@ -166,7 +237,14 @@ export function QuestionSheet({ question, disabled }: QuestionSheetProps) { ); const getCleanLabel = (optId: string) => { - const opt = options.find((o) => o.id === optId); + const normalized = String(optId || "").toLowerCase().trim(); + const opt = options.find( + (o) => + o.id === optId || + o.id.toLowerCase() === normalized || + String(o.value).toLowerCase() === normalized || + o.id.endsWith(`.${normalized}`), + ); if (!opt) return optId; return opt.label.split(" - ")[0]; };