A lead can carry the same honorific as a person, and a form that asks gender writes it onto the lead. Co-authored-by: Cursor <cursoragent@cursor.com>
135 lines
4.6 KiB
TypeScript
135 lines
4.6 KiB
TypeScript
/** مرد، زن، یا هیچکدام (مثلاً فروشگاه) */
|
|
export type PersonGender = "male" | "female" | "none";
|
|
|
|
export const PERSON_GENDERS: PersonGender[] = ["male", "female", "none"];
|
|
|
|
export const PERSON_GENDER_LABELS: Record<PersonGender, string> = {
|
|
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<PersonGender, "none"> | null;
|
|
} {
|
|
let full = normalizeFa(`${firstName} ${lastName}`);
|
|
let gender: Exclude<PersonGender, "none"> | 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,
|
|
};
|
|
}
|