|
|
|
@ -9,7 +9,12 @@ import { |
|
|
|
normalizeSearchString, |
|
|
|
getCountrySearchKeywords, |
|
|
|
} from "@/data/countries"; |
|
|
|
import { LANGUAGES_EN, LANGUAGES_FA, resolveLanguageName } from "@/data/languages"; |
|
|
|
import { |
|
|
|
LANGUAGES_EN, |
|
|
|
LANGUAGES_FA, |
|
|
|
resolveLanguageName, |
|
|
|
getLanguageSearchKeywords, |
|
|
|
} from "@/data/languages"; |
|
|
|
import type { QuestionField } from "@/lib/schema-adapter"; |
|
|
|
import { useI18n } from "@/translations/provider"; |
|
|
|
import { Button } from "./button"; |
|
|
|
@ -21,6 +26,7 @@ import { useSheetScrollLock } from "./use-sheet-scroll-lock"; |
|
|
|
|
|
|
|
const EXIT_ANIMATION_MS = 200; |
|
|
|
const EMPTY_ARRAY: string[] = []; |
|
|
|
const COMPACT_OPTIONS_MAX = 6; |
|
|
|
|
|
|
|
export type QuestionSheetProps = { |
|
|
|
question: QuestionField; |
|
|
|
@ -122,32 +128,47 @@ export function QuestionSheet({ question, disabled }: QuestionSheetProps) { |
|
|
|
question.id?.toLowerCase().includes("residence_status") || |
|
|
|
question.id?.toLowerCase().includes("status") || |
|
|
|
question.id?.toLowerCase().includes("responsibility") || |
|
|
|
question.extras?.noSearch === true || |
|
|
|
question.ui_config?.noSearch === true; |
|
|
|
question.id?.toLowerCase().includes("difference") || |
|
|
|
question.id?.toLowerCase().includes("impact"); |
|
|
|
|
|
|
|
const isLanguageQuestion = Boolean( |
|
|
|
question.ui_config?.dataset === "languages" || |
|
|
|
question.id?.endsWith(".mother_tongue") || |
|
|
|
question.id?.endsWith(".native_language") || |
|
|
|
question.id?.endsWith(".other_languages") || |
|
|
|
question.id?.endsWith(".languages") || |
|
|
|
question.id?.endsWith(".language") || |
|
|
|
(!question.id?.includes("impact") && |
|
|
|
!question.id?.includes("difference") && |
|
|
|
Boolean( |
|
|
|
question.title?.toLowerCase().includes("mother tongue") || |
|
|
|
question.title?.toLowerCase().includes("native language") || |
|
|
|
question.title?.toLowerCase() === "language" || |
|
|
|
question.title?.toLowerCase() === "languages" || |
|
|
|
question.title?.includes("زبان مادری") || |
|
|
|
question.title?.includes("زبانهای مسلط") || |
|
|
|
question.title?.includes("زبان های مسلط") |
|
|
|
)) |
|
|
|
); |
|
|
|
|
|
|
|
const isLanguageQuestion = |
|
|
|
!isExcludedFromAutoDatasets && |
|
|
|
(question.ui_config?.dataset === "languages" || |
|
|
|
question.id?.endsWith(".mother_tongue") || |
|
|
|
question.id?.endsWith(".native_language") || |
|
|
|
question.id?.endsWith(".other_languages") || |
|
|
|
(Boolean(question.title?.toLowerCase().includes("language")) && |
|
|
|
!question.options?.length)); |
|
|
|
|
|
|
|
const isCountryQuestion = |
|
|
|
const isCountryQuestion = Boolean( |
|
|
|
!isExcludedFromAutoDatasets && |
|
|
|
(question.ui_config?.dataset === "countries" || |
|
|
|
question.ui_config?.dataset === "nationalities" || |
|
|
|
question.id?.endsWith(".nationality") || |
|
|
|
question.id?.endsWith(".citizenship") || |
|
|
|
question.id?.endsWith(".second_nationality") || |
|
|
|
(Boolean( |
|
|
|
question.id?.endsWith(".birthplace") || |
|
|
|
question.id?.endsWith(".current_residence") || |
|
|
|
question.title?.toLowerCase().includes("nationality") || |
|
|
|
question.title?.toLowerCase().includes("citizenship"), |
|
|
|
) && |
|
|
|
!question.options?.length)); |
|
|
|
question.id?.includes("nationality") || |
|
|
|
question.id?.includes("citizenship") || |
|
|
|
question.id?.endsWith(".birthplace") || |
|
|
|
question.id?.endsWith(".current_residence") || |
|
|
|
question.id?.endsWith(".country") || |
|
|
|
Boolean( |
|
|
|
question.title?.toLowerCase().includes("nationality") || |
|
|
|
question.title?.toLowerCase().includes("citizenship") || |
|
|
|
question.title?.toLowerCase().includes("country") || |
|
|
|
question.title?.includes("تابعیت") || |
|
|
|
question.title?.includes("ملیت") || |
|
|
|
question.title?.includes("کشور") |
|
|
|
)) |
|
|
|
); |
|
|
|
|
|
|
|
const options = useMemo(() => { |
|
|
|
const rawOptions = question.options || []; |
|
|
|
@ -165,6 +186,7 @@ export function QuestionSheet({ question, disabled }: QuestionSheetProps) { |
|
|
|
|
|
|
|
const mergedOptions: typeof rawOptions = []; |
|
|
|
const seenIds = new Set<string>(); |
|
|
|
const seenLabels = new Set<string>(); |
|
|
|
|
|
|
|
LANGUAGES_EN.forEach((enLang, idx) => { |
|
|
|
const displayLabel = resolveLanguageName(enLang, locale); |
|
|
|
@ -181,8 +203,10 @@ export function QuestionSheet({ question, disabled }: QuestionSheetProps) { |
|
|
|
existingById.get(generatedId.toLowerCase()); |
|
|
|
|
|
|
|
const finalId = existing?.id || generatedId; |
|
|
|
if (seenIds.has(finalId)) return; |
|
|
|
const normLabel = displayLabel.toLowerCase().trim(); |
|
|
|
if (seenIds.has(finalId) || seenLabels.has(normLabel)) return; |
|
|
|
seenIds.add(finalId); |
|
|
|
seenLabels.add(normLabel); |
|
|
|
|
|
|
|
mergedOptions.push({ |
|
|
|
id: finalId, |
|
|
|
@ -193,12 +217,27 @@ export function QuestionSheet({ question, disabled }: QuestionSheetProps) { |
|
|
|
}); |
|
|
|
|
|
|
|
rawOptions.forEach((opt) => { |
|
|
|
if (!seenIds.has(opt.id)) { |
|
|
|
const normLabel = opt.label.toLowerCase().trim(); |
|
|
|
if (!seenIds.has(opt.id) && !seenLabels.has(normLabel)) { |
|
|
|
seenIds.add(opt.id); |
|
|
|
seenLabels.add(normLabel); |
|
|
|
mergedOptions.push(opt); |
|
|
|
} |
|
|
|
}); |
|
|
|
|
|
|
|
// Keep "Other" / "سایر" at the very end
|
|
|
|
const otherIndex = mergedOptions.findIndex( |
|
|
|
(opt) => |
|
|
|
opt.value === "other" || |
|
|
|
opt.id.endsWith(".other") || |
|
|
|
opt.label.toLowerCase() === "other" || |
|
|
|
opt.label === "سایر", |
|
|
|
); |
|
|
|
if (otherIndex !== -1 && otherIndex !== mergedOptions.length - 1) { |
|
|
|
const [otherOpt] = mergedOptions.splice(otherIndex, 1); |
|
|
|
mergedOptions.push(otherOpt); |
|
|
|
} |
|
|
|
|
|
|
|
return mergedOptions; |
|
|
|
} |
|
|
|
|
|
|
|
@ -259,13 +298,16 @@ export function QuestionSheet({ question, disabled }: QuestionSheetProps) { |
|
|
|
}, [question, isLanguageQuestion, isCountryQuestion, locale, t]); |
|
|
|
|
|
|
|
const noSearch = Boolean( |
|
|
|
question.extras?.noSearch === true || |
|
|
|
question.ui_config?.noSearch === true || |
|
|
|
question.id?.toLowerCase().includes("responsibility"), |
|
|
|
!isLanguageQuestion && |
|
|
|
!isCountryQuestion && |
|
|
|
options.length <= COMPACT_OPTIONS_MAX && |
|
|
|
( |
|
|
|
question.extras?.noSearch === true || |
|
|
|
question.ui_config?.noSearch === true || |
|
|
|
question.id?.toLowerCase().includes("responsibility") |
|
|
|
) |
|
|
|
); |
|
|
|
|
|
|
|
const COMPACT_OPTIONS_MAX = 6; |
|
|
|
|
|
|
|
const isCompact = options.length <= COMPACT_OPTIONS_MAX || noSearch; |
|
|
|
const showSearch = !noSearch && options.length > COMPACT_OPTIONS_MAX; |
|
|
|
|
|
|
|
@ -299,6 +341,13 @@ export function QuestionSheet({ question, disabled }: QuestionSheetProps) { |
|
|
|
) { |
|
|
|
return true; |
|
|
|
} |
|
|
|
if (isLanguageQuestion) { |
|
|
|
const labelKeywords = getLanguageSearchKeywords(option.label, locale); |
|
|
|
if (labelKeywords.some((k) => k.includes(normQ))) return true; |
|
|
|
|
|
|
|
const valKeywords = getLanguageSearchKeywords(String(option.value), locale); |
|
|
|
if (valKeywords.some((k) => k.includes(normQ))) return true; |
|
|
|
} |
|
|
|
if (isCountryQuestion) { |
|
|
|
const labelKeywords = getCountrySearchKeywords(option.label, locale); |
|
|
|
if (labelKeywords.some((k) => k.includes(normQ))) return true; |
|
|
|
@ -308,7 +357,7 @@ export function QuestionSheet({ question, disabled }: QuestionSheetProps) { |
|
|
|
} |
|
|
|
return false; |
|
|
|
}); |
|
|
|
}, [options, searchQuery, isCountryQuestion, locale]); |
|
|
|
}, [options, searchQuery, isLanguageQuestion, isCountryQuestion, locale]); |
|
|
|
|
|
|
|
const getCleanLabel = (optId: string) => { |
|
|
|
const normalized = String(optId || "").toLowerCase().trim(); |
|
|
|
@ -317,9 +366,16 @@ export function QuestionSheet({ question, disabled }: QuestionSheetProps) { |
|
|
|
o.id === optId || |
|
|
|
o.id.toLowerCase() === normalized || |
|
|
|
String(o.value).toLowerCase() === normalized || |
|
|
|
o.label.toLowerCase().trim() === normalized || |
|
|
|
o.id.endsWith(`.${normalized}`), |
|
|
|
); |
|
|
|
if (!opt) return optId; |
|
|
|
if (!opt) { |
|
|
|
if (isLanguageQuestion) { |
|
|
|
const resolved = resolveLanguageName(optId, locale); |
|
|
|
if (resolved && resolved !== optId) return resolved; |
|
|
|
} |
|
|
|
return optId; |
|
|
|
} |
|
|
|
return opt.label.split(" - ")[0]; |
|
|
|
}; |
|
|
|
|
|
|
|
@ -593,8 +649,13 @@ export function QuestionSheet({ question, disabled }: QuestionSheetProps) { |
|
|
|
{filteredOptions.length > 0 ? ( |
|
|
|
filteredOptions.map((option) => { |
|
|
|
const isSelected = isMulti |
|
|
|
? localSelectedList.includes(option.id) |
|
|
|
: singleValue === option.id; |
|
|
|
? localSelectedList.includes(option.id) || |
|
|
|
localSelectedList.includes(option.value) || |
|
|
|
localSelectedList.includes(option.id.split(".").pop() || "") |
|
|
|
: singleValue === option.id || |
|
|
|
singleValue === option.value || |
|
|
|
singleValue === option.id.split(".").pop() || |
|
|
|
option.id.endsWith(`.${singleValue}`); |
|
|
|
|
|
|
|
const isOptionDisabled = |
|
|
|
isMulti && |
|
|
|
|