/** مرد، زن، یا هیچکدام (مثلاً فروشگاه) */ export type PersonGender = "male" | "female" | "none"; export const PERSON_GENDERS: PersonGender[] = ["male", "female", "none"]; export const PERSON_GENDER_LABELS: Record = { male: "مرد", female: "زن", none: "هیچکدام", }; const MALE_TITLE = "جناب آقای"; const FEMALE_TITLE = "سرکار خانم"; function normalizeFa(value: string) { return value.replace(/ي/g, "ی").replace(/ك/g, "ک").replace(/\s+/g, " ").trim(); } function splitWords(value: string) { const parts = normalizeFa(value).split(" ").filter(Boolean); return { firstName: parts[0] ?? "", lastName: parts.slice(1).join(" "), }; } /** عنوان ابتدای نام را برمی‌دارد تا فیلدهای نام، عنوان را دوباره ذخیره نکنند. */ export function splitPersonHonorific(firstName: string, lastName: string): { firstName: string; lastName: string; gender: Exclude | null; } { let full = normalizeFa(`${firstName} ${lastName}`); let gender: Exclude | null = null; // فاصله اختیاری است: «سرکارخانم» و «جناب آقاي» هر دو عنوان‌اند. for (;;) { const male = full.match(/^جناب\s*آقای(?=\s|$)/); const female = full.match(/^سرکار\s*خانم(?=\s|$)/); const hit = male?.[0] ?? female?.[0]; if (!hit) break; gender = male ? "male" : "female"; full = normalizeFa(full.slice(hit.length)); } const plain = splitWords(full); return { ...plain, gender }; } /** * نام ذخیره‌شده را با عنوان می‌سازد. * اگر جنسیت یا تیک نفرستاده شود و خود نام عنوان داشته باشد، همان برداشت می‌شود. */ export function applyPersonGenderTitle(input: { firstName: string; lastName: string; gender?: PersonGender | null; showGenderInName?: boolean | null; }): { firstName: string; lastName: string; gender: PersonGender; showGenderInName: boolean; } { const bare = splitPersonHonorific(input.firstName, input.lastName); const gender: PersonGender = input.gender === "male" || input.gender === "female" || input.gender === "none" ? input.gender : (bare.gender ?? "none"); let show = input.showGenderInName; if (typeof show !== "boolean") show = bare.gender != null && gender !== "none"; if (gender === "none") show = false; const body = [bare.firstName, bare.lastName].filter(Boolean).join(" "); const titled = show && gender === "male" ? `${MALE_TITLE} ${body}`.trim() : show && gender === "female" ? `${FEMALE_TITLE} ${body}`.trim() : body; const parts = splitWords(titled); return { firstName: parts.firstName || "—", lastName: parts.lastName || "—", gender, showGenderInName: show, }; } /** پاسخ فیلد جنسیت فرم: مرد، زن، یا هیچکدام. مقدار ناشناس null است. */ export function parsePersonGenderAnswer(raw: string): PersonGender | null { const text = normalizeFa(raw).toLowerCase(); if (!text) return null; const has = (words: string[]) => words.some((word) => text === word || text.includes(word)); if (has(["هیچکدام", "none", "نامشخص"])) return "none"; if (has(["female", "woman", "خانم", "زن"])) return "female"; if (has(["male", "man", "آقا", "مرد"])) return "male"; return null; } export function isGenderQuestionLabel(label: string): boolean { return normalizeFa(label).replace(/\s+/g, "").includes("جنسیت"); } /** * عنوان روی نام مخاطب لید می‌نشیند. * اگر نام مخاطب خالی باشد، عنوان لید همان نام شخص است. */ export function applyLeadGenderName(input: { title: string; contactName?: string | null; gender?: PersonGender | null; showGenderInName?: boolean | null; }): { title: string; contactName: string | null; gender: PersonGender; showGenderInName: boolean; } { const contact = (input.contactName ?? "").trim(); const title = input.title.trim(); const onContact = contact.length > 0; const named = applyPersonGenderTitle({ firstName: onContact ? contact : title, lastName: "", gender: input.gender, showGenderInName: input.showGenderInName, }); const full = [named.firstName, named.lastName].filter((part) => part && part !== "—").join(" "); return { title: onContact ? title : full || title, contactName: onContact ? full || null : null, gender: named.gender, showGenderInName: named.showGenderInName, }; }