Browse Source
feat: implement core marriage matching application architecture including UI components, question workflows, and multi-language support.
front-test-2
feat: implement core marriage matching application architecture including UI components, question workflows, and multi-language support.
front-test-2
102 changed files with 26606 additions and 2882 deletions
-
8next.config.ts
-
22668public/openapi.json
-
4src/app/[lang]/layout.tsx
-
2src/app/[lang]/questions-list/[slug]/page.tsx
-
14src/app/[lang]/test-webview/page.tsx
-
7src/app/api/dev-storage/route.ts
-
10src/app/api/open-in-ide/route.ts
-
2src/app/api/proxy/route.ts
-
2src/app/api/remote-logs/route.ts
-
29src/app/api/remote-network/route.ts
-
6src/app/finding-match/page.tsx
-
21src/app/globals.css
-
19src/app/intro/page.tsx
-
5src/app/layout.tsx
-
45src/app/new-match/page.tsx
-
10src/app/new-match/profile/page.tsx
-
12src/app/providers.tsx
-
5src/app/questions-list/[slug]/answer-pace-sheet.tsx
-
54src/app/questions-list/[slug]/question-detail-client.tsx
-
2src/app/questions-list/[slug]/test-intro-client.tsx
-
29src/app/questions-list/page.tsx
-
53src/app/request-accepted/page.tsx
-
19src/app/request-sent/page.tsx
-
159src/components/dev/dev-click-to-component.tsx
-
72src/components/questions/progress-helper.ts
-
24src/components/questions/question-answer-storage.tsx
-
716src/components/questions/question-birthplace.tsx
-
2src/components/questions/question-card.tsx
-
25src/components/questions/question-date.tsx
-
69src/components/questions/question-dropdown.tsx
-
2src/components/questions/question-exit-navigation-button.tsx
-
18src/components/questions/question-file.tsx
-
29src/components/questions/question-number.tsx
-
350src/components/questions/question-phone.tsx
-
22src/components/questions/question-photo.tsx
-
2src/components/questions/question-progress-tracker.tsx
-
11src/components/questions/question-section-flow.tsx
-
8src/components/questions/question-slider.tsx
-
37src/components/questions/question-snap-list.tsx
-
11src/components/questions/question-text.tsx
-
11src/components/questions/question-title.tsx
-
19src/components/questions/required-steps-card.tsx
-
36src/components/questions/test-loading-screen.tsx
-
16src/components/questions/test-questions-flow.tsx
-
8src/components/sliders/slider-slide-four.tsx
-
24src/components/sliders/slider-slide-one.tsx
-
4src/components/sliders/slider-slide-two.tsx
-
4src/components/ui/button.tsx
-
9src/components/ui/female-consent-sheet.tsx
-
15src/components/ui/help-modal.tsx
-
8src/components/ui/navigation-button.tsx
-
45src/components/ui/report-actions-sheet.tsx
-
2src/components/ui/token-switcher.tsx
-
5src/components/utils/dev-click-to-component.tsx
-
5src/components/utils/fixed-bottom.tsx
-
2024src/data/cattell-fallback.ts
-
401src/data/countries.ts
-
72src/data/glasser-fallback.ts
-
157src/data/languages.ts
-
283src/data/nationalities.ts
-
10src/data/question-data.ts
-
658src/data/questions/en.json
-
658src/data/questions/fa.json
-
14src/hooks/marriage/query-keys.ts
-
10src/hooks/marriage/types.ts
-
3src/hooks/marriage/use-section-data.ts
-
6src/hooks/use-close-service-on-back.ts
-
104src/hooks/useFlutterBridge.ts
-
11src/lib/auth-bridge.ts
-
6src/lib/http.ts
-
18src/lib/performance.ts
-
18src/lib/view-paddings.ts
-
4src/plugins/jsx-locator-loader.cjs
-
5src/translations/dictionaries.ts
-
2src/translations/locales/ar.json
-
2src/translations/locales/az.json
-
2src/translations/locales/bn.json
-
2src/translations/locales/da.json
-
2src/translations/locales/de.json
-
2src/translations/locales/en.json
-
2src/translations/locales/es.json
-
2src/translations/locales/fa.json
-
2src/translations/locales/fr.json
-
2src/translations/locales/gu.json
-
2src/translations/locales/ha.json
-
2src/translations/locales/he.json
-
2src/translations/locales/hi.json
-
2src/translations/locales/id.json
-
2src/translations/locales/ks.json
-
2src/translations/locales/pt.json
-
2src/translations/locales/ru.json
-
2src/translations/locales/sw.json
-
2src/translations/locales/tg.json
-
2src/translations/locales/tr.json
-
2src/translations/locales/ul.json
-
2src/translations/locales/ur.json
-
2src/translations/locales/uz.json
-
2src/translations/locales/zh.json
-
6src/translations/provider.tsx
-
2src/view-paddings.ts
22668
public/openapi.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,716 @@ |
|||||
|
"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<HTMLDivElement>(null); |
||||
|
const listRef = useRef<HTMLDivElement>(null); |
||||
|
const searchInputRef = useRef<HTMLInputElement>(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<HTMLDivElement>) => { |
||||
|
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<HTMLInputElement>) => { |
||||
|
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 ( |
||||
|
<div |
||||
|
ref={containerRef} |
||||
|
className={[ |
||||
|
"relative flex w-full flex-col gap-3 transition-opacity duration-200", |
||||
|
disabled ? "pointer-events-none opacity-30" : "", |
||||
|
].join(" ")} |
||||
|
> |
||||
|
<QuestionTitle question={question} /> |
||||
|
|
||||
|
{isResidence ? ( |
||||
|
<> |
||||
|
{/* GeoIP Auto/Manual selector UI */} |
||||
|
<div className="flex flex-col gap-3.5 w-full py-1"> |
||||
|
{/* Left Side: Location Info */} |
||||
|
<div className="flex items-center gap-2.5 min-w-0"> |
||||
|
<svg |
||||
|
width="16" |
||||
|
height="20" |
||||
|
viewBox="0 0 16 20" |
||||
|
fill="none" |
||||
|
className="shrink-0 text-[#181818]" |
||||
|
> |
||||
|
<path |
||||
|
d="M8 0C3.58 0 0 3.58 0 8C0 13.54 8 20 8 20C8 20 16 13.54 16 8C16 3.58 12.42 0 8 0ZM8 11C6.34 11 5 9.66 5 8C5 6.34 6.34 5 8 5C9.66 5 11 6.34 11 8C11 9.66 9.66 11 8 11Z" |
||||
|
fill="currentColor" |
||||
|
/> |
||||
|
</svg> |
||||
|
<span className="text-[16px] font-bold text-[#181818]"> |
||||
|
{isDetecting |
||||
|
? locale === "fa" |
||||
|
? "در حال شناسایی..." |
||||
|
: "Detecting..." |
||||
|
: detectedLocation || |
||||
|
(locale === "fa" ? "نامشخص" : "Unknown")} |
||||
|
</span> |
||||
|
</div> |
||||
|
|
||||
|
{/* Right Side: Toggle Buttons */} |
||||
|
<div className="flex items-center gap-2 w-full"> |
||||
|
{/* Auto Button */} |
||||
|
<button |
||||
|
type="button" |
||||
|
onClick={handleAutoClick} |
||||
|
className={[ |
||||
|
"flex flex-1 items-center justify-center gap-1.5 h-[46px] rounded-xl text-[14px] font-bold cursor-pointer transition-all shadow-sm", |
||||
|
mode === "auto" |
||||
|
? "bg-[#F2465F] text-white" |
||||
|
: "bg-white border border-[#D0D5DD] text-[#344054] hover:bg-gray-50", |
||||
|
].join(" ")} |
||||
|
> |
||||
|
<svg |
||||
|
width="16" |
||||
|
height="16" |
||||
|
viewBox="0 0 16 16" |
||||
|
fill="none" |
||||
|
className="shrink-0" |
||||
|
> |
||||
|
<circle |
||||
|
cx="8" |
||||
|
cy="8" |
||||
|
r="6" |
||||
|
stroke="currentColor" |
||||
|
strokeWidth="2" |
||||
|
/> |
||||
|
<circle cx="8" cy="8" r="2" fill="currentColor" /> |
||||
|
<path |
||||
|
d="M8 0V3M8 13V16M0 8H3M13 8H16" |
||||
|
stroke="currentColor" |
||||
|
strokeWidth="2" |
||||
|
strokeLinecap="round" |
||||
|
/> |
||||
|
</svg> |
||||
|
<span>{locale === "fa" ? "خودکار" : "Auto"}</span> |
||||
|
</button> |
||||
|
|
||||
|
{/* Manual Button */} |
||||
|
<button |
||||
|
type="button" |
||||
|
onClick={handleManualClick} |
||||
|
className={[ |
||||
|
"flex flex-1 items-center justify-center gap-1.5 h-[46px] rounded-xl text-[14px] font-bold cursor-pointer transition-all shadow-sm", |
||||
|
mode === "manual" |
||||
|
? "bg-[#F2465F] text-white" |
||||
|
: "bg-white border border-[#D0D5DD] text-[#344054] hover:bg-gray-50", |
||||
|
].join(" ")} |
||||
|
> |
||||
|
<svg |
||||
|
width="16" |
||||
|
height="16" |
||||
|
viewBox="0 0 16 16" |
||||
|
fill="none" |
||||
|
className="shrink-0" |
||||
|
> |
||||
|
<path |
||||
|
d="M1 3.5L5 1.5L11 4.5L15 2.5V12.5L11 14.5L5 11.5L1 13.5V3.5Z" |
||||
|
stroke="currentColor" |
||||
|
strokeWidth="2" |
||||
|
strokeLinecap="round" |
||||
|
strokeLinejoin="round" |
||||
|
/> |
||||
|
<path |
||||
|
d="M5 1.5V11.5M11 4.5V14.5" |
||||
|
stroke="currentColor" |
||||
|
strokeWidth="2" |
||||
|
/> |
||||
|
</svg> |
||||
|
<span>{locale === "fa" ? "دستی" : "Manual"}</span> |
||||
|
</button> |
||||
|
</div> |
||||
|
</div> |
||||
|
|
||||
|
{mode === "manual" && ( |
||||
|
<div className="flex flex-col gap-3 w-full animate-in fade-in slide-in-from-top-2 duration-200"> |
||||
|
{/* Country Selection Dropdown */} |
||||
|
<div className="relative w-full"> |
||||
|
<button |
||||
|
type="button" |
||||
|
disabled={disabled} |
||||
|
onClick={() => setIsOpen(!isOpen)} |
||||
|
className={[ |
||||
|
"flex h-[54px] w-full items-center justify-between rounded-[16px] border bg-white px-4.5 text-start transition-all cursor-pointer outline-none", |
||||
|
isOpen |
||||
|
? "border-[#6F6F6F] ring-1 ring-[#6F6F6F]" |
||||
|
: "border-[#D0D5DD] hover:border-[#98A2B3]", |
||||
|
].join(" ")} |
||||
|
> |
||||
|
<span |
||||
|
className={[ |
||||
|
"text-[15px] font-medium truncate", |
||||
|
selectedCountry ? "text-[#181818]" : "text-[#667085]", |
||||
|
].join(" ")} |
||||
|
> |
||||
|
{selectedCountry || selectCountryPlaceholder} |
||||
|
</span> |
||||
|
<svg |
||||
|
width="16" |
||||
|
height="10" |
||||
|
viewBox="0 0 16 10" |
||||
|
fill="none" |
||||
|
className={[ |
||||
|
"shrink-0 transition-transform duration-200", |
||||
|
isOpen ? "rotate-180" : "", |
||||
|
].join(" ")} |
||||
|
> |
||||
|
<path |
||||
|
d="M14.75 1.25L7.75 8.25L0.75 1.25" |
||||
|
stroke="#344054" |
||||
|
strokeWidth="1.75" |
||||
|
strokeLinecap="round" |
||||
|
strokeLinejoin="round" |
||||
|
/> |
||||
|
</svg> |
||||
|
</button> |
||||
|
|
||||
|
{isOpen && ( |
||||
|
<div className="absolute top-[calc(100%+8px)] left-0 z-50 flex w-full flex-col gap-3.5 rounded-[20px] bg-white p-4.5 shadow-[0_12px_36px_rgba(0,0,0,0.09)] border border-[#EAECF0] animate-in fade-in zoom-in-95 duration-150"> |
||||
|
{options.length > 3 || searchQuery ? ( |
||||
|
<div className="flex h-[46px] w-full items-center gap-2.5 rounded-[12px] bg-[#EFEFEF] px-3.5 transition-colors focus-within:bg-[#E8E8E8]"> |
||||
|
<svg |
||||
|
width="18" |
||||
|
height="18" |
||||
|
viewBox="0 0 18 18" |
||||
|
fill="none" |
||||
|
className="shrink-0 text-[#667085]" |
||||
|
> |
||||
|
<path |
||||
|
d="M8.25 14.25C11.5637 14.25 14.25 11.5637 14.25 8.25C14.25 4.93629 11.5637 2.25 8.25 2.25C4.93629 2.25 2.25 4.93629 2.25 8.25C2.25 11.5637 4.93629 14.25 8.25 14.25Z" |
||||
|
stroke="#667085" |
||||
|
strokeWidth="1.75" |
||||
|
strokeLinecap="round" |
||||
|
strokeLinejoin="round" |
||||
|
/> |
||||
|
<path |
||||
|
d="M15.75 15.75L12.5 12.5" |
||||
|
stroke="#667085" |
||||
|
strokeWidth="1.75" |
||||
|
strokeLinecap="round" |
||||
|
strokeLinejoin="round" |
||||
|
/> |
||||
|
</svg> |
||||
|
<input |
||||
|
ref={searchInputRef} |
||||
|
type="text" |
||||
|
value={searchQuery} |
||||
|
onChange={(e) => setSearchQuery(e.target.value)} |
||||
|
placeholder="Search..." |
||||
|
className="flex-1 bg-transparent text-[14px] font-medium text-[#181818] outline-none placeholder:text-[#98A2B3]" |
||||
|
/> |
||||
|
{searchQuery ? ( |
||||
|
<button |
||||
|
type="button" |
||||
|
onClick={() => setSearchQuery("")} |
||||
|
className="text-[#667085] hover:text-[#181818] text-xs font-semibold" |
||||
|
> |
||||
|
✕ |
||||
|
</button> |
||||
|
) : null} |
||||
|
</div> |
||||
|
) : null} |
||||
|
|
||||
|
<div |
||||
|
ref={listRef} |
||||
|
onWheel={handleListWheel} |
||||
|
onTouchStart={(e) => 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 ( |
||||
|
<button |
||||
|
key={option} |
||||
|
type="button" |
||||
|
onClick={() => handleSelectCountry(option)} |
||||
|
className="flex w-full items-center gap-3 text-start cursor-pointer group/opt py-0.5" |
||||
|
> |
||||
|
<div |
||||
|
className={[ |
||||
|
"size-[20px] shrink-0 rounded-full transition-all duration-150", |
||||
|
isSelected |
||||
|
? "bg-[#F2465F] shadow-xs" |
||||
|
: "border-[2px] border-[#344054] bg-transparent group-hover/opt:border-[#181818]", |
||||
|
].join(" ")} |
||||
|
/> |
||||
|
<span className="text-[15px] font-bold text-[#181818]"> |
||||
|
{option} |
||||
|
</span> |
||||
|
</button> |
||||
|
); |
||||
|
}) |
||||
|
) : ( |
||||
|
<span className="py-2 text-[13px] text-[#667085]"> |
||||
|
No options found |
||||
|
</span> |
||||
|
)} |
||||
|
</div> |
||||
|
</div> |
||||
|
)} |
||||
|
</div> |
||||
|
|
||||
|
{/* City Text Input */} |
||||
|
<div className="w-full"> |
||||
|
<input |
||||
|
type="text" |
||||
|
disabled={disabled} |
||||
|
value={cityInput} |
||||
|
onChange={handleCityChange} |
||||
|
placeholder={cityPlaceholder} |
||||
|
className="h-[54px] w-full rounded-[16px] border border-[#D0D5DD] bg-white px-4.5 text-[15px] font-medium text-[#181818] placeholder:text-[#98A2B3] hover:border-[#98A2B3] focus:border-[#6F6F6F] focus:ring-1 focus:ring-[#6F6F6F] outline-none transition-all" |
||||
|
/> |
||||
|
</div> |
||||
|
</div> |
||||
|
)} |
||||
|
</> |
||||
|
) : ( |
||||
|
<> |
||||
|
{/* 1. Country Selection Dropdown */} |
||||
|
<div className="relative w-full"> |
||||
|
<button |
||||
|
type="button" |
||||
|
disabled={disabled} |
||||
|
onClick={() => setIsOpen(!isOpen)} |
||||
|
className={[ |
||||
|
"flex h-[54px] w-full items-center justify-between rounded-[16px] border bg-white px-4.5 text-start transition-all cursor-pointer outline-none", |
||||
|
isOpen |
||||
|
? "border-[#6F6F6F] ring-1 ring-[#6F6F6F]" |
||||
|
: "border-[#D0D5DD] hover:border-[#98A2B3]", |
||||
|
].join(" ")} |
||||
|
> |
||||
|
<span |
||||
|
className={[ |
||||
|
"text-[15px] font-medium truncate", |
||||
|
selectedCountry ? "text-[#181818]" : "text-[#667085]", |
||||
|
].join(" ")} |
||||
|
> |
||||
|
{selectedCountry || selectCountryPlaceholder} |
||||
|
</span> |
||||
|
<svg |
||||
|
width="16" |
||||
|
height="10" |
||||
|
viewBox="0 0 16 10" |
||||
|
fill="none" |
||||
|
className={[ |
||||
|
"shrink-0 transition-transform duration-200", |
||||
|
isOpen ? "rotate-180" : "", |
||||
|
].join(" ")} |
||||
|
> |
||||
|
<path |
||||
|
d="M14.75 1.25L7.75 8.25L0.75 1.25" |
||||
|
stroke="#344054" |
||||
|
strokeWidth="1.75" |
||||
|
strokeLinecap="round" |
||||
|
strokeLinejoin="round" |
||||
|
/> |
||||
|
</svg> |
||||
|
</button> |
||||
|
|
||||
|
{isOpen && ( |
||||
|
<div className="absolute top-[calc(100%+8px)] left-0 z-50 flex w-full flex-col gap-3.5 rounded-[20px] bg-white p-4.5 shadow-[0_12px_36px_rgba(0,0,0,0.09)] border border-[#EAECF0] animate-in fade-in zoom-in-95 duration-150"> |
||||
|
{options.length > 3 || searchQuery ? ( |
||||
|
<div className="flex h-[46px] w-full items-center gap-2.5 rounded-[12px] bg-[#EFEFEF] px-3.5 transition-colors focus-within:bg-[#E8E8E8]"> |
||||
|
<svg |
||||
|
width="18" |
||||
|
height="18" |
||||
|
viewBox="0 0 18 18" |
||||
|
fill="none" |
||||
|
className="shrink-0 text-[#667085]" |
||||
|
> |
||||
|
<path |
||||
|
d="M8.25 14.25C11.5637 14.25 14.25 11.5637 14.25 8.25C14.25 4.93629 11.5637 2.25 8.25 2.25C4.93629 2.25 2.25 4.93629 2.25 8.25C2.25 11.5637 4.93629 14.25 8.25 14.25Z" |
||||
|
stroke="#667085" |
||||
|
strokeWidth="1.75" |
||||
|
strokeLinecap="round" |
||||
|
strokeLinejoin="round" |
||||
|
/> |
||||
|
<path |
||||
|
d="M15.75 15.75L12.5 12.5" |
||||
|
stroke="#667085" |
||||
|
strokeWidth="1.75" |
||||
|
strokeLinecap="round" |
||||
|
strokeLinejoin="round" |
||||
|
/> |
||||
|
</svg> |
||||
|
<input |
||||
|
ref={searchInputRef} |
||||
|
type="text" |
||||
|
value={searchQuery} |
||||
|
onChange={(e) => setSearchQuery(e.target.value)} |
||||
|
placeholder="Search..." |
||||
|
className="flex-1 bg-transparent text-[14px] font-medium text-[#181818] outline-none placeholder:text-[#98A2B3]" |
||||
|
/> |
||||
|
{searchQuery ? ( |
||||
|
<button |
||||
|
type="button" |
||||
|
onClick={() => setSearchQuery("")} |
||||
|
className="text-[#667085] hover:text-[#181818] text-xs font-semibold" |
||||
|
> |
||||
|
✕ |
||||
|
</button> |
||||
|
) : null} |
||||
|
</div> |
||||
|
) : null} |
||||
|
|
||||
|
<div |
||||
|
ref={listRef} |
||||
|
onWheel={handleListWheel} |
||||
|
onTouchStart={(e) => 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 ( |
||||
|
<button |
||||
|
key={option} |
||||
|
type="button" |
||||
|
onClick={() => handleSelectCountry(option)} |
||||
|
className="flex w-full items-center gap-3 text-start cursor-pointer group/opt py-0.5" |
||||
|
> |
||||
|
<div |
||||
|
className={[ |
||||
|
"size-[20px] shrink-0 rounded-full transition-all duration-150", |
||||
|
isSelected |
||||
|
? "bg-[#F2465F] shadow-xs" |
||||
|
: "border-[2px] border-[#344054] bg-transparent group-hover/opt:border-[#181818]", |
||||
|
].join(" ")} |
||||
|
/> |
||||
|
<span className="text-[15px] font-bold text-[#181818]"> |
||||
|
{option} |
||||
|
</span> |
||||
|
</button> |
||||
|
); |
||||
|
}) |
||||
|
) : ( |
||||
|
<span className="py-2 text-[13px] text-[#667085]"> |
||||
|
No options found |
||||
|
</span> |
||||
|
)} |
||||
|
</div> |
||||
|
</div> |
||||
|
)} |
||||
|
</div> |
||||
|
|
||||
|
{/* 2. City Text Input */} |
||||
|
<div className="w-full"> |
||||
|
<input |
||||
|
type="text" |
||||
|
disabled={disabled} |
||||
|
value={cityInput} |
||||
|
onChange={handleCityChange} |
||||
|
placeholder={cityPlaceholder} |
||||
|
className="h-[54px] w-full rounded-[16px] border border-[#D0D5DD] bg-white px-4.5 text-[15px] font-medium text-[#181818] placeholder:text-[#98A2B3] hover:border-[#98A2B3] focus:border-[#6F6F6F] focus:ring-1 focus:ring-[#6F6F6F] outline-none transition-all" |
||||
|
/> |
||||
|
</div> |
||||
|
</> |
||||
|
)} |
||||
|
</div> |
||||
|
); |
||||
|
} |
||||
|
|
||||
|
export default QuestionBirthplace; |
||||
@ -1 +1,4 @@ |
|||||
export { DevClickToComponent, default } from "@/components/dev/dev-click-to-component"; |
|
||||
|
export { |
||||
|
DevClickToComponent, |
||||
|
default, |
||||
|
} from "@/components/dev/dev-click-to-component"; |
||||
2024
src/data/cattell-fallback.ts
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1,401 @@ |
|||||
|
export const COUNTRIES_EN = [ |
||||
|
"Afghanistan", |
||||
|
"Albania", |
||||
|
"Algeria", |
||||
|
"Andorra", |
||||
|
"Angola", |
||||
|
"Antigua and Barbuda", |
||||
|
"Argentina", |
||||
|
"Armenia", |
||||
|
"Australia", |
||||
|
"Austria", |
||||
|
"Azerbaijan", |
||||
|
"Bahamas", |
||||
|
"Bahrain", |
||||
|
"Bangladesh", |
||||
|
"Barbados", |
||||
|
"Belarus", |
||||
|
"Belgium", |
||||
|
"Belize", |
||||
|
"Benin", |
||||
|
"Bhutan", |
||||
|
"Bolivia", |
||||
|
"Bosnia and Herzegovina", |
||||
|
"Botswana", |
||||
|
"Brazil", |
||||
|
"Brunei", |
||||
|
"Bulgaria", |
||||
|
"Burkina Faso", |
||||
|
"Burundi", |
||||
|
"Cabo Verde", |
||||
|
"Cambodia", |
||||
|
"Cameroon", |
||||
|
"Canada", |
||||
|
"Central African Republic", |
||||
|
"Chad", |
||||
|
"Chile", |
||||
|
"China", |
||||
|
"Colombia", |
||||
|
"Comoros", |
||||
|
"Congo", |
||||
|
"Costa Rica", |
||||
|
"Croatia", |
||||
|
"Cuba", |
||||
|
"Cyprus", |
||||
|
"Czechia", |
||||
|
"Democratic Republic of the Congo", |
||||
|
"Denmark", |
||||
|
"Djibouti", |
||||
|
"Dominica", |
||||
|
"Dominican Republic", |
||||
|
"Ecuador", |
||||
|
"Egypt", |
||||
|
"El Salvador", |
||||
|
"Equatorial Guinea", |
||||
|
"Eritrea", |
||||
|
"Estonia", |
||||
|
"Eswatini", |
||||
|
"Ethiopia", |
||||
|
"Fiji", |
||||
|
"Finland", |
||||
|
"France", |
||||
|
"Gabon", |
||||
|
"Gambia", |
||||
|
"Georgia", |
||||
|
"Germany", |
||||
|
"Ghana", |
||||
|
"Greece", |
||||
|
"Grenada", |
||||
|
"Guatemala", |
||||
|
"Guinea", |
||||
|
"Guinea-Bissau", |
||||
|
"Guyana", |
||||
|
"Haiti", |
||||
|
"Holy See", |
||||
|
"Honduras", |
||||
|
"Hungary", |
||||
|
"Iceland", |
||||
|
"India", |
||||
|
"Indonesia", |
||||
|
"Iran", |
||||
|
"Iraq", |
||||
|
"Ireland", |
||||
|
"Italy", |
||||
|
"Ivory Coast", |
||||
|
"Jamaica", |
||||
|
"Japan", |
||||
|
"Jordan", |
||||
|
"Kazakhstan", |
||||
|
"Kenya", |
||||
|
"Kiribati", |
||||
|
"Kuwait", |
||||
|
"Kyrgyzstan", |
||||
|
"Laos", |
||||
|
"Latvia", |
||||
|
"Lebanon", |
||||
|
"Lesotho", |
||||
|
"Liberia", |
||||
|
"Libya", |
||||
|
"Liechtenstein", |
||||
|
"Lithuania", |
||||
|
"Luxembourg", |
||||
|
"Madagascar", |
||||
|
"Malawi", |
||||
|
"Malaysia", |
||||
|
"Maldives", |
||||
|
"Mali", |
||||
|
"Malta", |
||||
|
"Marshall Islands", |
||||
|
"Mauritania", |
||||
|
"Mauritius", |
||||
|
"Mexico", |
||||
|
"Micronesia", |
||||
|
"Moldova", |
||||
|
"Monaco", |
||||
|
"Mongolia", |
||||
|
"Montenegro", |
||||
|
"Morocco", |
||||
|
"Mozambique", |
||||
|
"Myanmar", |
||||
|
"Namibia", |
||||
|
"Nauru", |
||||
|
"Nepal", |
||||
|
"Netherlands", |
||||
|
"New Zealand", |
||||
|
"Nicaragua", |
||||
|
"Niger", |
||||
|
"Nigeria", |
||||
|
"North Korea", |
||||
|
"North Macedonia", |
||||
|
"Norway", |
||||
|
"Oman", |
||||
|
"Pakistan", |
||||
|
"Palau", |
||||
|
"Palestine State", |
||||
|
"Panama", |
||||
|
"Papua New Guinea", |
||||
|
"Paraguay", |
||||
|
"Peru", |
||||
|
"Philippines", |
||||
|
"Poland", |
||||
|
"Portugal", |
||||
|
"Qatar", |
||||
|
"Romania", |
||||
|
"Russia", |
||||
|
"Rwanda", |
||||
|
"Saint Kitts and Nevis", |
||||
|
"Saint Lucia", |
||||
|
"Saint Vincent and the Grenadines", |
||||
|
"Samoa", |
||||
|
"San Marino", |
||||
|
"Sao Tome and Principe", |
||||
|
"Saudi Arabia", |
||||
|
"Senegal", |
||||
|
"Serbia", |
||||
|
"Seychelles", |
||||
|
"Sierra Leone", |
||||
|
"Singapore", |
||||
|
"Slovakia", |
||||
|
"Slovenia", |
||||
|
"Solomon Islands", |
||||
|
"Somalia", |
||||
|
"South Africa", |
||||
|
"South Korea", |
||||
|
"South Sudan", |
||||
|
"Spain", |
||||
|
"Sri Lanka", |
||||
|
"Sudan", |
||||
|
"Suriname", |
||||
|
"Sweden", |
||||
|
"Switzerland", |
||||
|
"Syria", |
||||
|
"Tajikistan", |
||||
|
"Tanzania", |
||||
|
"Thailand", |
||||
|
"Timor-Leste", |
||||
|
"Togo", |
||||
|
"Tonga", |
||||
|
"Trinidad and Tobago", |
||||
|
"Tunisia", |
||||
|
"Turkey", |
||||
|
"Turkmenistan", |
||||
|
"Tuvalu", |
||||
|
"Uganda", |
||||
|
"Ukraine", |
||||
|
"United Arab Emirates (UAE)", |
||||
|
"United Kingdom (UK)", |
||||
|
"United States (US)", |
||||
|
"Uruguay", |
||||
|
"Uzbekistan", |
||||
|
"Vanuatu", |
||||
|
"Venezuela", |
||||
|
"Vietnam", |
||||
|
"Yemen", |
||||
|
"Zambia", |
||||
|
"Zimbabwe", |
||||
|
]; |
||||
|
|
||||
|
export const COUNTRIES_FA = [ |
||||
|
"افغانستان", |
||||
|
"آلبانی", |
||||
|
"الجزایر", |
||||
|
"آندورا", |
||||
|
"آنگولا", |
||||
|
"آنتیگوا و باربودا", |
||||
|
"آرژانتین", |
||||
|
"ارمنستان", |
||||
|
"استرالیا", |
||||
|
"اتریش", |
||||
|
"آذربایجان", |
||||
|
"باهاما", |
||||
|
"بحرین", |
||||
|
"بنگلادش", |
||||
|
"باربادوس", |
||||
|
"بلاروس", |
||||
|
"بلژیک", |
||||
|
"بلیز", |
||||
|
"بنین", |
||||
|
"بوتان", |
||||
|
"بولیوی", |
||||
|
"بوسنی و هرزگوین", |
||||
|
"بوتسوانا", |
||||
|
"برزیل", |
||||
|
"برونئی", |
||||
|
"بلغارستان", |
||||
|
"بورکینافاسو", |
||||
|
"بوروندی", |
||||
|
"کیپ ورد", |
||||
|
"کامبوج", |
||||
|
"کامرون", |
||||
|
"کانادا", |
||||
|
"جمهوری آفریقای مرکزی", |
||||
|
"چاد", |
||||
|
"شیلی", |
||||
|
"چین", |
||||
|
"کلمبیا", |
||||
|
"کومور", |
||||
|
"کنگو", |
||||
|
"کاستاریکا", |
||||
|
"کرواسی", |
||||
|
"کوبا", |
||||
|
"قبرس", |
||||
|
"جمهوری چک", |
||||
|
"جمهوری دموکراتیک کنگو", |
||||
|
"دانمارک", |
||||
|
"جیبوتی", |
||||
|
"دومینیکا", |
||||
|
"جمهوری دومینیکن", |
||||
|
"اکوادور", |
||||
|
"مصر", |
||||
|
"السالوادور", |
||||
|
"گینه استوایی", |
||||
|
"اریتره", |
||||
|
"استونی", |
||||
|
"اسواتینی", |
||||
|
"اتیوپی", |
||||
|
"فیجی", |
||||
|
"فنلاند", |
||||
|
"فرانسه", |
||||
|
"گابن", |
||||
|
"Gambia", |
||||
|
"گرجستان", |
||||
|
"آلمان", |
||||
|
"غنا", |
||||
|
"یونان", |
||||
|
"گرنادا", |
||||
|
"گواتمالا", |
||||
|
"گینه", |
||||
|
"گینه بیسائو", |
||||
|
"گویان", |
||||
|
"هایتی", |
||||
|
"واتیکان", |
||||
|
"هندوراس", |
||||
|
"مجارستان", |
||||
|
"ایسلند", |
||||
|
"هند", |
||||
|
"اندونزی", |
||||
|
"ایران", |
||||
|
"عراق", |
||||
|
"ایرلند", |
||||
|
"ایتالیا", |
||||
|
"ساحل عاج", |
||||
|
"جامائیکا", |
||||
|
"ژاپن", |
||||
|
"اردن", |
||||
|
"قزاقستان", |
||||
|
"کنیا", |
||||
|
"کیریباتی", |
||||
|
"کویت", |
||||
|
"قرقیزستان", |
||||
|
"لائوس", |
||||
|
"لتونی", |
||||
|
"لبنان", |
||||
|
"لسوتو", |
||||
|
"لیبریا", |
||||
|
"لیبی", |
||||
|
"لیختناشتاین", |
||||
|
"لیتوانی", |
||||
|
"لوکزامبورگ", |
||||
|
"ماداگاسکار", |
||||
|
"مالاوی", |
||||
|
"مالزی", |
||||
|
"مالدیو", |
||||
|
"مالی", |
||||
|
"مالت", |
||||
|
"جزایر مارشال", |
||||
|
"موریتانی", |
||||
|
"موریس", |
||||
|
"مکزیک", |
||||
|
"میکرونزی", |
||||
|
"مولداوی", |
||||
|
"موناکو", |
||||
|
"مغولستان", |
||||
|
"مونتهنگرو", |
||||
|
"مراکش", |
||||
|
"موزامبیک", |
||||
|
"میانمار", |
||||
|
"نامیبیا", |
||||
|
"نائورو", |
||||
|
"نپال", |
||||
|
"هلند", |
||||
|
"نیوزیلند", |
||||
|
"نیکاراگوئه", |
||||
|
"نیجر", |
||||
|
"نیجریه", |
||||
|
"کره شمالی", |
||||
|
"مقدونیه شمالی", |
||||
|
"نروژ", |
||||
|
"عمان", |
||||
|
"پاکستان", |
||||
|
"پالائو", |
||||
|
"فلسطین", |
||||
|
"پاناما", |
||||
|
"پاپوآ گینه نو", |
||||
|
"پاراگوئه", |
||||
|
"پرو", |
||||
|
"فیلیپین", |
||||
|
"لهستان", |
||||
|
"پرتغال", |
||||
|
"قطر", |
||||
|
"رومانی", |
||||
|
"روسیه", |
||||
|
"رواندا", |
||||
|
"سنت کیتس و نویس", |
||||
|
"سنت لوسیا", |
||||
|
"سنت وینسنت و گرنادینها", |
||||
|
"ساموآ", |
||||
|
"سان مارینو", |
||||
|
"سائوتومه و پرنسیپ", |
||||
|
"عربستان سعودی", |
||||
|
"سنگال", |
||||
|
"صربستان", |
||||
|
"سیشل", |
||||
|
"سیرالئون", |
||||
|
"سنگاپور", |
||||
|
"اسلواکی", |
||||
|
"اسلوونی", |
||||
|
"جزایر سلیمان", |
||||
|
"سومالی", |
||||
|
"آفریقای جنوبی", |
||||
|
"کره جنوبی", |
||||
|
"سودان جنوبی", |
||||
|
"اسپانیا", |
||||
|
"سریلانکا", |
||||
|
"سودان", |
||||
|
"سورینام", |
||||
|
"سوئد", |
||||
|
"سوئیس", |
||||
|
"سوریه", |
||||
|
"تاجیکستان", |
||||
|
"تانزانیا", |
||||
|
"تایلند", |
||||
|
"تیمور شرقی", |
||||
|
"توگو", |
||||
|
"تونگا", |
||||
|
"ترینیداد و توباگو", |
||||
|
"تونس", |
||||
|
"ترکیه", |
||||
|
"ترکمنستان", |
||||
|
"تووالو", |
||||
|
"اوگاندا", |
||||
|
"اوکراین", |
||||
|
"امارات متحده عربی (UAE)", |
||||
|
"بریتانیا (UK)", |
||||
|
"ایالات متحده آمریکا (US)", |
||||
|
"اروگوئه", |
||||
|
"ازبکستان", |
||||
|
"وانواتو", |
||||
|
"ونزوئلا", |
||||
|
"ویتنام", |
||||
|
"یمن", |
||||
|
"زامبیا", |
||||
|
"زیمبابوه", |
||||
|
]; |
||||
|
|
||||
|
export function getCountryList(locale: string): string[] { |
||||
|
const normalized = String(locale || "en").toLowerCase(); |
||||
|
if (normalized === "fa" || normalized === "fa-ir") { |
||||
|
return COUNTRIES_FA; |
||||
|
} |
||||
|
return COUNTRIES_EN; |
||||
|
} |
||||
@ -0,0 +1,157 @@ |
|||||
|
export const LANGUAGES_EN = [ |
||||
|
"Afrikaans", |
||||
|
"Albanian", |
||||
|
"Amharic", |
||||
|
"Arabic", |
||||
|
"Armenian", |
||||
|
"Azerbaijani", |
||||
|
"Bengali", |
||||
|
"Bosnian", |
||||
|
"Bulgarian", |
||||
|
"Burmese", |
||||
|
"Catalan", |
||||
|
"Chinese", |
||||
|
"Croatian", |
||||
|
"Czech", |
||||
|
"Danish", |
||||
|
"Dutch", |
||||
|
"English", |
||||
|
"Estonian", |
||||
|
"Finnish", |
||||
|
"French", |
||||
|
"Georgian", |
||||
|
"German", |
||||
|
"Greek", |
||||
|
"Gujarati", |
||||
|
"Hausa", |
||||
|
"Hebrew", |
||||
|
"Hindi", |
||||
|
"Hungarian", |
||||
|
"Icelandic", |
||||
|
"Indonesian", |
||||
|
"Irish", |
||||
|
"Italian", |
||||
|
"Japanese", |
||||
|
"Kashmiri", |
||||
|
"Kazakh", |
||||
|
"Khmer", |
||||
|
"Korean", |
||||
|
"Kyrgyz", |
||||
|
"Latvian", |
||||
|
"Lithuanian", |
||||
|
"Macedonian", |
||||
|
"Malay", |
||||
|
"Maltese", |
||||
|
"Mongolian", |
||||
|
"Nepali", |
||||
|
"Norwegian", |
||||
|
"Pashto", |
||||
|
"Persian (Farsi)", |
||||
|
"Polish", |
||||
|
"Portuguese", |
||||
|
"Punjabi", |
||||
|
"Romanian", |
||||
|
"Russian", |
||||
|
"Serbian", |
||||
|
"Sinhala", |
||||
|
"Slovak", |
||||
|
"Slovenian", |
||||
|
"Somali", |
||||
|
"Spanish", |
||||
|
"Swahili", |
||||
|
"Swedish", |
||||
|
"Tajik", |
||||
|
"Tamil", |
||||
|
"Telugu", |
||||
|
"Thai", |
||||
|
"Turkish", |
||||
|
"Turkmen", |
||||
|
"Ukrainian", |
||||
|
"Urdu", |
||||
|
"Urdu Roman (Latin)", |
||||
|
"Uzbek", |
||||
|
"Vietnamese", |
||||
|
"Other", |
||||
|
]; |
||||
|
|
||||
|
export const LANGUAGES_FA = [ |
||||
|
"آذربایجانی", |
||||
|
"آفریکانس", |
||||
|
"آلمانی", |
||||
|
"آلبانیایی", |
||||
|
"امهری", |
||||
|
"اردو", |
||||
|
"اردو (لاتین)", |
||||
|
"ارمنی", |
||||
|
"ازبکی", |
||||
|
"اسپانیایی", |
||||
|
"استونیایی", |
||||
|
"اسلواکی", |
||||
|
"اسلوونیایی", |
||||
|
"انگلیسی", |
||||
|
"اندونزیایی", |
||||
|
"اوکراینی", |
||||
|
"ایتالیایی", |
||||
|
"ایرلندی", |
||||
|
"ایسلندی", |
||||
|
"بنگالی", |
||||
|
"بوسنیایی", |
||||
|
"بلغاری", |
||||
|
"پشتو", |
||||
|
"پنجابی", |
||||
|
"پرتغالی", |
||||
|
"تاجیکی", |
||||
|
"تامیلی", |
||||
|
"تایلندی", |
||||
|
"تلوگو", |
||||
|
"ترکی", |
||||
|
"ترکمنی", |
||||
|
"چکی", |
||||
|
"چینی", |
||||
|
"دانمارکی", |
||||
|
"روسی", |
||||
|
"رومانیایی", |
||||
|
"ژاپنی", |
||||
|
"سواحیلی", |
||||
|
"سوئدی", |
||||
|
"سینهالی", |
||||
|
"صربی", |
||||
|
"سومالیایی", |
||||
|
"عربی", |
||||
|
"عبری", |
||||
|
"فارسی", |
||||
|
"فرانسوی", |
||||
|
"فنلاندی", |
||||
|
"قزاقی", |
||||
|
"قرقیزی", |
||||
|
"کاتالان", |
||||
|
"کرهای", |
||||
|
"کرواتی", |
||||
|
"کشمیری", |
||||
|
"گجراتی", |
||||
|
"گرجی", |
||||
|
"لتونیایی", |
||||
|
"لیتوانیایی", |
||||
|
"لهستانی", |
||||
|
"مالایی", |
||||
|
"مالتی", |
||||
|
"مجاری", |
||||
|
"مقدونیهای", |
||||
|
"مغولی", |
||||
|
"میانماری (برمهای)", |
||||
|
"نپالی", |
||||
|
"نروژی", |
||||
|
"هلندی", |
||||
|
"ویتنامی", |
||||
|
"هندی", |
||||
|
"هوسا", |
||||
|
"سایر", |
||||
|
]; |
||||
|
|
||||
|
export function getLanguageList(locale: string): string[] { |
||||
|
const normalized = String(locale || "en").toLowerCase(); |
||||
|
if (normalized === "fa" || normalized === "fa-ir") { |
||||
|
return LANGUAGES_FA; |
||||
|
} |
||||
|
return LANGUAGES_EN; |
||||
|
} |
||||
@ -0,0 +1,283 @@ |
|||||
|
export const NATIONALITIES_EN = [ |
||||
|
"Afghan", |
||||
|
"Albanian", |
||||
|
"Algerian", |
||||
|
"American (US)", |
||||
|
"Andorran", |
||||
|
"Angolan", |
||||
|
"Antiguan", |
||||
|
"Argentinian", |
||||
|
"Armenian", |
||||
|
"Australian", |
||||
|
"Austrian", |
||||
|
"Azerbaijani", |
||||
|
"Bahamian", |
||||
|
"Bahraini", |
||||
|
"Bangladeshi", |
||||
|
"Barbadian", |
||||
|
"Belarusian", |
||||
|
"Belgian", |
||||
|
"Belizean", |
||||
|
"Beninese", |
||||
|
"Bhutanese", |
||||
|
"Bolivian", |
||||
|
"Bosnian", |
||||
|
"Botswanan", |
||||
|
"Brazilian", |
||||
|
"British (UK)", |
||||
|
"Bruneian", |
||||
|
"Bulgarian", |
||||
|
"Burkinabè", |
||||
|
"Burmese", |
||||
|
"Burundian", |
||||
|
"Cabo Verdean", |
||||
|
"Cambodian", |
||||
|
"Cameroonian", |
||||
|
"Canadian", |
||||
|
"Central African", |
||||
|
"Chadian", |
||||
|
"Chilean", |
||||
|
"Chinese", |
||||
|
"Colombian", |
||||
|
"Comorian", |
||||
|
"Congolese", |
||||
|
"Costa Rican", |
||||
|
"Croatian", |
||||
|
"Cuban", |
||||
|
"Cypriot", |
||||
|
"Czech", |
||||
|
"Danish", |
||||
|
"Djiboutian", |
||||
|
"Dominican", |
||||
|
"Dutch", |
||||
|
"Ecuadorian", |
||||
|
"Egyptian", |
||||
|
"Emirati (UAE)", |
||||
|
"Equatorial Guinean", |
||||
|
"Eritrean", |
||||
|
"Estonian", |
||||
|
"Ethiopian", |
||||
|
"Fijian", |
||||
|
"Filipino", |
||||
|
"Finnish", |
||||
|
"French", |
||||
|
"Gabonese", |
||||
|
"Gambian", |
||||
|
"Georgian", |
||||
|
"German", |
||||
|
"Ghanaian", |
||||
|
"Greek", |
||||
|
"Grenadian", |
||||
|
"Guatemalan", |
||||
|
"Guinean", |
||||
|
"Guyanese", |
||||
|
"Haitian", |
||||
|
"Honduran", |
||||
|
"Hungarian", |
||||
|
"Icelandic", |
||||
|
"Indian", |
||||
|
"Indonesian", |
||||
|
"Iranian", |
||||
|
"Iraqi", |
||||
|
"Irish", |
||||
|
"Italian", |
||||
|
"Ivorian", |
||||
|
"Jamaican", |
||||
|
"Japanese", |
||||
|
"Jordanian", |
||||
|
"Kazakhstani", |
||||
|
"Kenyan", |
||||
|
"Kuwaiti", |
||||
|
"Kyrgyzstani", |
||||
|
"Laotian", |
||||
|
"Latvian", |
||||
|
"Lebanese", |
||||
|
"Liberian", |
||||
|
"Libyan", |
||||
|
"Liechtensteiner", |
||||
|
"Lithuanian", |
||||
|
"Luxembourger", |
||||
|
"Macedonian", |
||||
|
"Malagasy", |
||||
|
"Malawian", |
||||
|
"Malaysian", |
||||
|
"Maldivian", |
||||
|
"Malian", |
||||
|
"Maltese", |
||||
|
"Mauritanian", |
||||
|
"Mauritian", |
||||
|
"Mexican", |
||||
|
"Micronesian", |
||||
|
"Moldovan", |
||||
|
"Monégasque", |
||||
|
"Mongolian", |
||||
|
"Montenegrin", |
||||
|
"Moroccan", |
||||
|
"Mozambican", |
||||
|
"Namibian", |
||||
|
"Nauruan", |
||||
|
"Nepalese", |
||||
|
"New Zealander", |
||||
|
"Nicaraguan", |
||||
|
"Nigerien", |
||||
|
"Nigerian", |
||||
|
"North Korean", |
||||
|
"Norwegian", |
||||
|
"Omani", |
||||
|
"Pakistani", |
||||
|
"Palestinian", |
||||
|
"Panamanian", |
||||
|
"Papua New Guinean", |
||||
|
"Paraguayan", |
||||
|
"Peruvian", |
||||
|
"Polish", |
||||
|
"Portuguese", |
||||
|
"Qatari", |
||||
|
"Romanian", |
||||
|
"Russian", |
||||
|
"Rwandan", |
||||
|
"Saudi", |
||||
|
"Senegalese", |
||||
|
"Serbian", |
||||
|
"Seychellois", |
||||
|
"Sierra Leonean", |
||||
|
"Singaporean", |
||||
|
"Slovak", |
||||
|
"Slovenian", |
||||
|
"Somali", |
||||
|
"South African", |
||||
|
"South Korean", |
||||
|
"Spanish", |
||||
|
"Sri Lankan", |
||||
|
"Sudanese", |
||||
|
"Surinamese", |
||||
|
"Swazi", |
||||
|
"Swedish", |
||||
|
"Swiss", |
||||
|
"Syrian", |
||||
|
"Tajikistani", |
||||
|
"Tanzanian", |
||||
|
"Thai", |
||||
|
"Togolese", |
||||
|
"Tongan", |
||||
|
"Tunisian", |
||||
|
"Turkish", |
||||
|
"Turkmen", |
||||
|
"Ugandan", |
||||
|
"Ukrainian", |
||||
|
"Uruguayan", |
||||
|
"Uzbekistani", |
||||
|
"Venezuelan", |
||||
|
"Vietnamese", |
||||
|
"Yemeni", |
||||
|
"Zambian", |
||||
|
"Zimbabwean", |
||||
|
"Other", |
||||
|
]; |
||||
|
|
||||
|
export const NATIONALITIES_FA = [ |
||||
|
"آذربایجانی", |
||||
|
"آرژانتینی", |
||||
|
"آلمانی", |
||||
|
"آلبانیایی", |
||||
|
"آمریکایی (US)", |
||||
|
"آندورایی", |
||||
|
"آنگولایی", |
||||
|
"اتریشی", |
||||
|
"اتیوپیایی", |
||||
|
"ارمنی", |
||||
|
"اردنی", |
||||
|
"اروگوئهای", |
||||
|
"ازبکستانی", |
||||
|
"اسپانیایی", |
||||
|
"استرالیایی", |
||||
|
"استونیایی", |
||||
|
"اسلواکی", |
||||
|
"اسلوونیایی", |
||||
|
"افغانستانی", |
||||
|
"الجزایری", |
||||
|
"اماراتی (UAE)", |
||||
|
"اندونزیایی", |
||||
|
"انگلیسی", |
||||
|
"ایتالیایی", |
||||
|
"ایرانی", |
||||
|
"ایرلندی", |
||||
|
"بحرینی", |
||||
|
"برزیلی", |
||||
|
"بریتانیایی (UK)", |
||||
|
"بلغارستانی", |
||||
|
"بنگلادشی", |
||||
|
"بلژیکی", |
||||
|
"بلاروسی", |
||||
|
"بوسنیایی", |
||||
|
"پاکستانی", |
||||
|
"پرتغالی", |
||||
|
"پروایی", |
||||
|
"تاجیکستانی", |
||||
|
"تایلندی", |
||||
|
"ترکیهای", |
||||
|
"تونسی", |
||||
|
"چکی", |
||||
|
"چینی", |
||||
|
"دانمارکی", |
||||
|
"روسی", |
||||
|
"رومانیایی", |
||||
|
"ژاپنی", |
||||
|
"سریلانکایی", |
||||
|
"سعودی", |
||||
|
"سنگاپوری", |
||||
|
"سوری", |
||||
|
"سومالیایی", |
||||
|
"سوئدی", |
||||
|
"سوئیسی", |
||||
|
"شیلیایی", |
||||
|
"عراقی", |
||||
|
"عربستانی", |
||||
|
"عمانی", |
||||
|
"فرانسوی", |
||||
|
"فلسطینی", |
||||
|
"فیلیپینی", |
||||
|
"فنلاندی", |
||||
|
"قزاقستانی", |
||||
|
"قطری", |
||||
|
"قرقیزستانی", |
||||
|
"کره جنوبی", |
||||
|
"کره شمالی", |
||||
|
"کلمبیایی", |
||||
|
"کامرونی", |
||||
|
"کانادایی", |
||||
|
"کنگویی", |
||||
|
"کوبایی", |
||||
|
"کویتی", |
||||
|
"کنیایی", |
||||
|
"گرجستانی", |
||||
|
"لبنانی", |
||||
|
"لهستانی", |
||||
|
"لیبیایی", |
||||
|
"لیتوانیایی", |
||||
|
"مالزیایی", |
||||
|
"مالدیوی", |
||||
|
"مراکشی", |
||||
|
"مصر", |
||||
|
"مکزیکی", |
||||
|
"مولداویایی", |
||||
|
"مغولی", |
||||
|
"نامیبیاایی", |
||||
|
"نپالی", |
||||
|
"نروژی", |
||||
|
"نیوزیلندی", |
||||
|
"نیجریهای", |
||||
|
"هلندی", |
||||
|
"ونزوئلایی", |
||||
|
"ویتنامی", |
||||
|
"یمنی", |
||||
|
"سایر", |
||||
|
]; |
||||
|
|
||||
|
export function getNationalityList(locale: string): string[] { |
||||
|
const normalized = String(locale || "en").toLowerCase(); |
||||
|
if (normalized === "fa" || normalized === "fa-ir") { |
||||
|
return NATIONALITIES_FA; |
||||
|
} |
||||
|
return NATIONALITIES_EN; |
||||
|
} |
||||
658
src/data/questions/en.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
658
src/data/questions/fa.json
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -1,7 +1,7 @@ |
|||||
|
export { FixedBottom } from "./components/utils/fixed-bottom"; |
||||
export { useFlutterConfig, useViewPaddings } from "./hooks/use-view-paddings"; |
export { useFlutterConfig, useViewPaddings } from "./hooks/use-view-paddings"; |
||||
export { |
export { |
||||
type InitialConfig, |
type InitialConfig, |
||||
type ViewPaddings, |
type ViewPaddings, |
||||
viewPaddingsBridge, |
viewPaddingsBridge, |
||||
} from "./lib/view-paddings"; |
} from "./lib/view-paddings"; |
||||
export { FixedBottom } from "./components/utils/fixed-bottom"; |
|
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue