feat(leads): keep gender on leads and form submissions

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>
This commit is contained in:
2026-10-31 12:49:42 +03:30
co-authored by Cursor
parent cbc2e4ee8a
commit d7b5085096
12 changed files with 334 additions and 19 deletions
+11
View File
@@ -3,7 +3,10 @@ export type { PersonGender } from "./person-name";
export {
PERSON_GENDERS,
PERSON_GENDER_LABELS,
applyLeadGenderName,
applyPersonGenderTitle,
isGenderQuestionLabel,
parsePersonGenderAnswer,
splitPersonHonorific,
} from "./person-name";
@@ -2432,6 +2435,8 @@ export type LeadFormMapTo =
| "lead.trade"
| "lead.notes"
| "lead.source"
| "lead.gender"
| "person.gender"
| "notes"
| "custom";
@@ -5470,6 +5475,8 @@ export interface LeadDto {
id: string;
title: string;
contactName: string | null;
gender: PersonGender;
showGenderInName: boolean;
mobile: string;
phone: string | null;
city: string | null;
@@ -5510,6 +5517,8 @@ export interface CreateLeadRequest {
title: string;
mobile: string;
contactName?: string | null;
gender?: PersonGender;
showGenderInName?: boolean;
phone?: string | null;
city?: string | null;
region?: string | null;
@@ -5531,6 +5540,8 @@ export interface UpdateLeadRequest {
title?: string;
mobile?: string;
contactName?: string | null;
gender?: PersonGender;
showGenderInName?: boolean;
phone?: string | null;
city?: string | null;
region?: string | null;
+48
View File
@@ -84,3 +84,51 @@ export function applyPersonGenderTitle(input: {
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,
};
}