"use client"; import { useCallback, useEffect, useRef, useState } from "react"; import { getCountryList, COUNTRIES_EN, COUNTRIES_FA } from "@/data/countries"; import type { QuestionField } from "@/data/question-data"; import { useI18n } from "@/translations/provider"; import { useQuestionAnswers } from "./question-answer-storage"; import QuestionTitle from "./question-title"; type QuestionBirthplaceProps = { question: QuestionField; questionIndex: number; disabled?: boolean; }; type BirthplaceValue = { country?: string; city?: string; }; function parseValue(rawValue: unknown): { country: string; city: string } { if (!rawValue) return { country: "", city: "" }; if (typeof rawValue === "object" && rawValue !== null) { const obj = rawValue as BirthplaceValue; return { country: typeof obj.country === "string" ? obj.country : "", city: typeof obj.city === "string" ? obj.city : "", }; } if (typeof rawValue === "string") { let str = rawValue.trim(); // Clean legacy country names with parentheses containing commas str = str.replace( "United Kingdom (UK, England, Wales, Scotland, Northern Ireland)", "United Kingdom (UK)", ); str = str.replace("United States (US, USA, America)", "United States (US)"); str = str.replace( "United Arab Emirates (UAE, Dubai, Abu Dhabi)", "United Arab Emirates (UAE)", ); str = str.replace("Congo (Congo-Brazzaville)", "Congo"); str = str.replace("Czechia (Czech Republic)", "Czechia"); str = str.replace("Myanmar (formerly Burma)", "Myanmar"); str = str.replace( "امارات متحده عربی (دبی، ابوظبی، UAE)", "امارات متحده عربی (UAE)", ); str = str.replace( "بریتانیا (انگلستان، اسکاتلند، ولز، ایرلند شمالی، UK)", "بریتانیا (UK)", ); str = str.replace( "ایالات متحده آمریکا (آمریکا، US، USA)", "ایالات متحده آمریکا (US)", ); if (str.includes(",")) { const parts = str.split(",").map((s) => s.trim()); return { city: parts[0] || "", country: parts[1] || "" }; } if (str.includes(" - ")) { const parts = str.split(" - ").map((s) => s.trim()); return { country: parts[0] || "", city: parts[1] || "" }; } const isCountry = COUNTRIES_EN.includes(str) || COUNTRIES_FA.includes(str); if (isCountry) { return { country: str, city: "" }; } return { country: "", city: str }; } return { country: "", city: "" }; } export function QuestionBirthplace({ question, questionIndex, disabled, }: QuestionBirthplaceProps) { const { dictionary: t, locale } = useI18n(); const { getAnswerValue, setAnswerValue } = useQuestionAnswers(); const rawValue = getAnswerValue(question, questionIndex); const initial = parseValue(rawValue); const [selectedCountry, setSelectedCountry] = useState(initial.country); const [cityInput, setCityInput] = useState(initial.city); const [isOpen, setIsOpen] = useState(false); const [searchQuery, setSearchQuery] = useState(""); const containerRef = useRef(null); const listRef = useRef(null); const searchInputRef = useRef(null); const isResidence = question.title.toLowerCase().includes("residence") || question.title.includes("سکونت"); const [mode, setMode] = useState<"auto" | "manual">("auto"); const [isDetecting, setIsDetecting] = useState(false); const [detectedLocation, setDetectedLocation] = useState(""); const updateAnswers = (country: string, city: string) => { const formatted = city && country ? `${city}, ${country}` : city || country || null; setAnswerValue(question, questionIndex, formatted); }; // GeoIP detection logic const detectLocation = (force = false) => { const alreadyChecked = typeof window !== "undefined" ? localStorage.getItem("hasCheckedGeoIPResidence") : "true"; if (alreadyChecked && !force) { if (rawValue) { const parsed = parseValue(rawValue); if (parsed.country && parsed.city) { setDetectedLocation(`${parsed.city}, ${parsed.country}`); setMode("auto"); } else { setMode("manual"); } } else { setMode("auto"); } return; } // If there is already a saved value and we are not forcing, do not fetch again if (rawValue && !force) { const parsed = parseValue(rawValue); if (parsed.country && parsed.city) { setDetectedLocation(`${parsed.city}, ${parsed.country}`); setMode("auto"); } else { setMode("manual"); } return; } setIsDetecting(true); if (typeof window !== "undefined") { localStorage.setItem("hasCheckedGeoIPResidence", "true"); } fetch("https://ipapi.co/json/") .then((res) => res.json()) .then((data) => { if (data && data.city && data.country_name) { const city = data.city; const country = data.country_name; setSelectedCountry(country); setCityInput(city); setDetectedLocation(`${city}, ${country}`); updateAnswers(country, city); setMode("auto"); } else { throw new Error("Missing data"); } }) .catch(() => { fetch("https://ipwho.is/") .then((res) => res.json()) .then((data) => { if (data && data.city && data.country) { const city = data.city; const country = data.country; setSelectedCountry(country); setCityInput(city); setDetectedLocation(`${city}, ${country}`); updateAnswers(country, city); setMode("auto"); } else { setMode("manual"); } }) .catch(() => { setMode("manual"); }); }) .finally(() => { setIsDetecting(false); }); }; useEffect(() => { if (isResidence) { detectLocation(); } }, [isResidence]); const handleAutoClick = () => { setMode("auto"); detectLocation(true); }; const handleManualClick = () => { if (typeof window !== "undefined") { localStorage.setItem("hasCheckedGeoIPResidence", "true"); } setMode("manual"); setSelectedCountry(""); setCityInput(""); updateAnswers("", ""); }; // Synchronize state if rawValue changes externally useEffect(() => { const updated = parseValue(rawValue); if (updated.country !== selectedCountry) { setSelectedCountry(updated.country); } if (updated.city.trim() !== cityInput.trim()) { setCityInput(updated.city); } if (updated.country && updated.city) { setDetectedLocation(`${updated.city}, ${updated.country}`); } }, [rawValue]); // Lock page scroll when dropdown is open useEffect(() => { if (isOpen) { document.body.classList.add("dropdown-open"); document.body.style.overflow = "hidden"; document.documentElement.style.overflow = "hidden"; } else { document.body.classList.remove("dropdown-open"); document.body.style.overflow = ""; document.documentElement.style.overflow = ""; } return () => { document.body.classList.remove("dropdown-open"); document.body.style.overflow = ""; document.documentElement.style.overflow = ""; }; }, [isOpen]); useEffect(() => { if (isOpen) { const timer = setTimeout(() => { searchInputRef.current?.focus(); }, 50); return () => clearTimeout(timer); } }, [isOpen]); const handleListWheel = useCallback((e: React.WheelEvent) => { e.stopPropagation(); const el = listRef.current; if (!el) return; const atTop = el.scrollTop <= 0 && e.deltaY < 0; const atBottom = el.scrollTop + el.clientHeight >= el.scrollHeight && e.deltaY > 0; if (atTop || atBottom) { e.preventDefault(); } }, []); useEffect(() => { function handleClickOutside(event: MouseEvent) { if ( containerRef.current && !containerRef.current.contains(event.target as Node) ) { setIsOpen(false); } } document.addEventListener("mousedown", handleClickOutside); return () => { document.removeEventListener("mousedown", handleClickOutside); }; }, []); const options = getCountryList(locale); const filteredOptions = options.filter((option) => option.toLowerCase().includes(searchQuery.toLowerCase()), ); const handleSelectCountry = (country: string) => { if (typeof window !== "undefined") { localStorage.setItem("hasCheckedGeoIPResidence", "true"); } setSelectedCountry(country); setCityInput(""); setIsOpen(false); updateAnswers(country, ""); }; const handleCityChange = (e: React.ChangeEvent) => { if (typeof window !== "undefined") { localStorage.setItem("hasCheckedGeoIPResidence", "true"); } const newCity = e.target.value; setCityInput(newCity); updateAnswers(selectedCountry, newCity); }; const isRtl = locale === "fa" || locale === "ar" || locale === "ur"; const selectCountryPlaceholder = isRtl ? "انتخاب کشور" : "Select country"; const cityPlaceholder = question.extras?.placeHolder || (isRtl ? "شهر، منطقه یا محله" : "City, region, or neighborhood"); return (
{isResidence ? ( <> {/* GeoIP Auto/Manual selector UI */}
{/* Left Side: Location Info */}
{isDetecting ? locale === "fa" ? "در حال شناسایی..." : "Detecting..." : detectedLocation || (locale === "fa" ? "نامشخص" : "Unknown")}
{/* Right Side: Toggle Buttons */}
{/* Auto Button */} {/* Manual Button */}
{mode === "manual" && (
{/* Country Selection Dropdown */}
{isOpen && (
{options.length > 3 || searchQuery ? (
setSearchQuery(e.target.value)} placeholder="Search..." className="flex-1 bg-transparent text-[14px] font-medium text-[#181818] outline-none placeholder:text-[#98A2B3]" /> {searchQuery ? ( ) : null}
) : null}
e.stopPropagation()} onTouchMove={(e) => e.stopPropagation()} onTouchEnd={(e) => e.stopPropagation()} className="flex max-h-[220px] flex-col gap-3.5 overflow-y-auto overscroll-contain pr-1" > {filteredOptions.length > 0 ? ( filteredOptions.map((option) => { const isSelected = selectedCountry === option; return ( ); }) ) : ( No options found )}
)}
{/* City Text Input */}
)} ) : ( <> {/* 1. Country Selection Dropdown */}
{isOpen && (
{options.length > 3 || searchQuery ? (
setSearchQuery(e.target.value)} placeholder="Search..." className="flex-1 bg-transparent text-[14px] font-medium text-[#181818] outline-none placeholder:text-[#98A2B3]" /> {searchQuery ? ( ) : null}
) : null}
e.stopPropagation()} onTouchMove={(e) => e.stopPropagation()} onTouchEnd={(e) => e.stopPropagation()} className="flex max-h-[220px] flex-col gap-3.5 overflow-y-auto overscroll-contain pr-1" > {filteredOptions.length > 0 ? ( filteredOptions.map((option) => { const isSelected = selectedCountry === option; return ( ); }) ) : ( No options found )}
)}
{/* 2. City Text Input */}
)}
); } export default QuestionBirthplace;