You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
176 lines
5.1 KiB
176 lines
5.1 KiB
import type {
|
|
MarriageField,
|
|
MarriageFieldValue,
|
|
MarriagePhoneFieldValue,
|
|
MarriageBirthplaceFieldValue,
|
|
} from "@/hooks/marriage/types";
|
|
import { dictionaries } from "@/translations/dictionaries";
|
|
|
|
const persianToEnglishMap: Record<string, string> = {};
|
|
for (const [enKey, faVal] of Object.entries(dictionaries.fa)) {
|
|
if (typeof faVal === "string") {
|
|
persianToEnglishMap[faVal.trim()] = enKey;
|
|
}
|
|
}
|
|
|
|
const normalizedEnglishKeys: Record<string, string> = {};
|
|
for (const enKey of Object.keys(dictionaries.en)) {
|
|
const norm = enKey.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
if (norm) {
|
|
normalizedEnglishKeys[norm] = enKey;
|
|
}
|
|
}
|
|
|
|
export function isMarriagePhoneFieldValue(
|
|
value: unknown,
|
|
): value is MarriagePhoneFieldValue {
|
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
return false;
|
|
}
|
|
|
|
const phoneValue = value as Partial<MarriagePhoneFieldValue>;
|
|
|
|
return (
|
|
typeof phoneValue.countryCode === "string" &&
|
|
typeof phoneValue.phoneNumber === "string"
|
|
);
|
|
}
|
|
|
|
export function isMarriageBirthplaceFieldValue(
|
|
value: unknown,
|
|
): value is MarriageBirthplaceFieldValue {
|
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
return false;
|
|
}
|
|
|
|
const bpValue = value as Partial<MarriageBirthplaceFieldValue>;
|
|
|
|
return (
|
|
typeof bpValue.country === "string" && typeof bpValue.city === "string"
|
|
);
|
|
}
|
|
|
|
export function formatFieldValue(value: MarriageFieldValue): string | null {
|
|
if (value === null || value === undefined || value === "") {
|
|
return null;
|
|
}
|
|
|
|
if (isMarriagePhoneFieldValue(value)) {
|
|
return `+${value.countryCode}${value.phoneNumber}`;
|
|
}
|
|
|
|
if (typeof value === "boolean") {
|
|
return value ? "Yes" : "No";
|
|
}
|
|
|
|
if (typeof value === "object") {
|
|
if ("country" in value || "city" in value || "state" in value) {
|
|
const v = value as { country?: string; state?: string; city?: string };
|
|
const parts = [v.city, v.state, v.country]
|
|
.map((p) => (typeof p === "string" ? p.trim() : ""))
|
|
.filter(Boolean);
|
|
return parts.join(", ");
|
|
}
|
|
}
|
|
|
|
return String(value);
|
|
}
|
|
|
|
export function titleFromKey(key: string): string {
|
|
const leafKey = key.includes(".") ? key.split(".").pop()! : key;
|
|
return leafKey
|
|
.replace(/^q\d+[_-]?/i, "")
|
|
.replace(/[_-]+/g, " ")
|
|
.replace(/\s+/g, " ")
|
|
.trim()
|
|
.replace(/\b\w/g, (letter) => letter.toUpperCase());
|
|
}
|
|
|
|
export function formatFieldLabel(
|
|
field: MarriageField,
|
|
dictionary?: Record<string, string>,
|
|
): string {
|
|
const englishTitle = titleFromKey(field.key);
|
|
const rawLabel = (field.label || "").trim();
|
|
|
|
// 1. Direct dictionary match for field.label
|
|
if (rawLabel && dictionary?.[rawLabel]) {
|
|
return dictionary[rawLabel];
|
|
}
|
|
|
|
// 2. If rawLabel is in Persian, convert it to its canonical English key via reverse map
|
|
const enKeyFromFa = rawLabel ? persianToEnglishMap[rawLabel] : null;
|
|
if (enKeyFromFa) {
|
|
if (dictionary?.[enKeyFromFa]) {
|
|
return dictionary[enKeyFromFa];
|
|
}
|
|
return enKeyFromFa;
|
|
}
|
|
|
|
// 3. Direct match for English title derived from key
|
|
if (dictionary?.[englishTitle]) {
|
|
return dictionary[englishTitle];
|
|
}
|
|
|
|
// 4. Normalized match for key or label (e.g. ignoring punctuation/spaces like apostrophes and parentheses)
|
|
const normKey = (field.key || "").split(".").pop()?.toLowerCase().replace(/[^a-z0-9]/g, "") || "";
|
|
const matchedCanonicalKey = normalizedEnglishKeys[normKey];
|
|
if (matchedCanonicalKey) {
|
|
if (dictionary?.[matchedCanonicalKey]) {
|
|
return dictionary[matchedCanonicalKey];
|
|
}
|
|
return matchedCanonicalKey;
|
|
}
|
|
|
|
// 5. Fallback
|
|
return rawLabel || englishTitle;
|
|
}
|
|
|
|
export function formatOptionValue(
|
|
value: MarriageFieldValue,
|
|
dictionary?: Record<string, string>,
|
|
): string | null {
|
|
if (Array.isArray(value)) {
|
|
const formattedItems = value
|
|
.map((item) => formatOptionValue(item as MarriageFieldValue, dictionary))
|
|
.filter(Boolean);
|
|
return formattedItems.length ? formattedItems.join(", ") : null;
|
|
}
|
|
|
|
const base = formatFieldValue(value);
|
|
if (!base) return null;
|
|
|
|
const trimmed = base.trim();
|
|
|
|
// 1. Direct dictionary match
|
|
if (dictionary?.[trimmed]) return dictionary[trimmed];
|
|
|
|
// 2. Reverse map if base is in Persian
|
|
const enKeyFromFa = persianToEnglishMap[trimmed];
|
|
if (enKeyFromFa) {
|
|
if (dictionary?.[enKeyFromFa]) {
|
|
return dictionary[enKeyFromFa];
|
|
}
|
|
return enKeyFromFa;
|
|
}
|
|
|
|
// 3. Try replacing underscores with spaces: "single;_never_married" -> "single; never married"
|
|
const withSpaces = trimmed.replace(/_/g, " ").trim();
|
|
if (dictionary?.[withSpaces]) return dictionary[withSpaces];
|
|
|
|
// 4. Try capitalized first letter
|
|
const capitalized = withSpaces.charAt(0).toUpperCase() + withSpaces.slice(1);
|
|
if (dictionary?.[capitalized]) return dictionary[capitalized];
|
|
|
|
// 5. Normalized match for option value
|
|
const normVal = withSpaces.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
const matchedCanonicalKey = normalizedEnglishKeys[normVal];
|
|
if (matchedCanonicalKey) {
|
|
if (dictionary?.[matchedCanonicalKey]) {
|
|
return dictionary[matchedCanonicalKey];
|
|
}
|
|
return matchedCanonicalKey;
|
|
}
|
|
|
|
return withSpaces;
|
|
}
|