feat(contacts): keep Tahesab honorifics with person gender
Male and female names can store the title when requested, and Tahesab import and export use the same gender. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,3 +1,12 @@
|
||||
import type { PersonGender } from "./person-name";
|
||||
export type { PersonGender } from "./person-name";
|
||||
export {
|
||||
PERSON_GENDERS,
|
||||
PERSON_GENDER_LABELS,
|
||||
applyPersonGenderTitle,
|
||||
splitPersonHonorific,
|
||||
} from "./person-name";
|
||||
|
||||
export type UserRole = "owner" | "admin" | "sales" | "support";
|
||||
|
||||
export const USER_ROLES: UserRole[] = ["owner", "admin", "sales", "support"];
|
||||
@@ -1193,6 +1202,8 @@ export interface PersonDto {
|
||||
previousImportSource: PersonImportSource | string | null;
|
||||
previousSourceRef: string | null;
|
||||
fullName: string;
|
||||
gender: PersonGender;
|
||||
showGenderInName: boolean;
|
||||
email: string | null;
|
||||
phone: string | null;
|
||||
mobile: string | null;
|
||||
@@ -1222,6 +1233,8 @@ export interface PersonDto {
|
||||
export interface CreatePersonRequest {
|
||||
firstName?: string;
|
||||
lastName?: string;
|
||||
gender?: PersonGender;
|
||||
showGenderInName?: boolean;
|
||||
previousFirstName?: string | null;
|
||||
previousLastName?: string | null;
|
||||
importSource?: PersonImportSource | string;
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/** مرد، زن، یا هیچکدام (مثلاً فروشگاه) */
|
||||
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,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user