import type { MarriageField, MarriageFieldValue, MarriagePhoneFieldValue, MarriageBirthplaceFieldValue, } from "@/hooks/marriage/types"; import { dictionaries } from "@/translations/dictionaries"; import { resolveCountryName } from "@/data/countries"; const persianToEnglishMap: Record = {}; for (const [enKey, faVal] of Object.entries(dictionaries.fa)) { if (typeof faVal === "string") { persianToEnglishMap[faVal.trim()] = enKey; } } const normalizedEnglishKeys: Record = {}; 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; 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; return ( typeof bpValue.country === "string" && typeof bpValue.city === "string" ); } export function formatFieldValue( value: MarriageFieldValue, dictionary?: Record, ): string | null { if (value === null || value === undefined || value === "") { return null; } if (isMarriagePhoneFieldValue(value)) { return `+${value.countryCode}${value.phoneNumber}`; } if (typeof value === "boolean") { return value ? dictionary?.["Yes"] || "Yes" : dictionary?.["No"] || "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 rawCountry = typeof v.country === "string" ? v.country.trim() : ""; const rawCity = typeof v.city === "string" ? v.city.trim() : ""; const rawState = typeof v.state === "string" ? v.state.trim() : ""; const localizedCountry = rawCountry ? dictionary?.[rawCountry] || resolveCountryName(rawCountry) || rawCountry : ""; const localizedCity = rawCity ? dictionary?.[rawCity] || rawCity : ""; const localizedState = rawState ? dictionary?.[rawState] || rawState : ""; const parts = [localizedCity, localizedState, localizedCountry].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()); } function isEnglishDictionary(dictionary?: Record): boolean { if (!dictionary) return false; return ( dictionary === (dictionaries.en as Record) || (dictionary["Yes"] === "Yes" && dictionary["No"] === "No" && dictionary["Cancel"] === "Cancel") ); } export const FIELD_KEY_TO_CANONICAL_EN: Record = { "personal_identity.date_of_birth": ["Date of Birth"], date_of_birth: ["Date of Birth"], "personal_identity.place_of_birth": [ "Place of Birth (Country and City)", "Place of Birth", "Birthplace", ], place_of_birth: [ "Place of Birth (Country and City)", "Place of Birth", "Birthplace", ], birthplace: ["Birthplace", "Place of Birth"], "personal_identity.place_of_upbringing": [ "Place of Upbringing / Where You Grew Up (Country and City)", "Place of Upbringing", "City of Upbringing / Growing Up", ], place_of_upbringing: [ "Place of Upbringing / Where You Grew Up (Country and City)", "Place of Upbringing", "City of Upbringing / Growing Up", ], "personal_identity.current_nationality_citizenship": [ "Current Nationality / Citizenship", "Current Nationality / Citizenship (or Countries of Citizenship)", "Nationality", "Citizenship", ], current_nationality_citizenship: [ "Current Nationality / Citizenship", "Current Nationality / Citizenship (or Countries of Citizenship)", "Nationality", "Citizenship", ], "personal_identity.ethnicity_family_origin_race": [ "Ethnicity / Family Origin / Race", "Ethnicity", ], ethnicity_family_origin_race: [ "Ethnicity / Family Origin / Race", "Ethnicity", ], "personal_identity.mother_tongue": [ "Mother Tongue", "Native Language", "Language", ], mother_tongue: [ "Mother Tongue", "Native Language", "Language", ], "personal_identity.religion": [ "What is your religion?", "Religion", ], religion: [ "What is your religion?", "Religion", ], "personal_identity.islamic_madhhab": [ "What is your branch / school of thought in Islam?", "Islamic Madhhab", "School of Thought", ], islamic_madhhab: [ "What is your branch / school of thought in Islam?", "Islamic Madhhab", "School of Thought", ], "contact_residence.current_residence_location": [ "Current Place of Residence (Country, City / State)", "Current Place of Residence", "Current Residence", ], current_residence_location: [ "Current Place of Residence (Country, City / State)", "Current Place of Residence", "Current Residence", ], }; export function formatFieldLabel( field: MarriageField, dictionary?: Record, ): string { const englishTitle = titleFromKey(field.key); const rawLabel = (field.label || "").trim(); const isEnglish = isEnglishDictionary(dictionary); // If rawLabel is provided from the backend (the dynamic source of truth) if (rawLabel) { // 1. Direct dictionary match for field.label (e.g. static UI overrides) if (dictionary?.[rawLabel]) { return dictionary[rawLabel]; } // 2. If rawLabel is in Persian reverse map, lookup translation in dictionary const enKeyFromFa = persianToEnglishMap[rawLabel]; if (enKeyFromFa) { if (dictionary?.[enKeyFromFa]) { return dictionary[enKeyFromFa]; } if (isEnglish) { return enKeyFromFa; } } // 3. If active dictionary is English and rawLabel contains non-Latin (Persian/Arabic) characters, fallback to englishTitle const hasPersianOrArabic = /[؀-ۿ]/.test(rawLabel); if (hasPersianOrArabic && isEnglish) { return englishTitle || rawLabel; } // 4. In all non-English locales (Arabic, Persian, etc.), trust the backend's dynamic label directly! return rawLabel; } // If rawLabel is missing, synthesize from canonical keys or key name const canonicalCandidates = FIELD_KEY_TO_CANONICAL_EN[field.key] || FIELD_KEY_TO_CANONICAL_EN[field.key.split(".").pop() || ""] || []; for (const candidate of canonicalCandidates) { if (dictionary?.[candidate]) { return dictionary[candidate]; } } if (dictionary?.[englishTitle]) { return dictionary[englishTitle]; } const normKey = (field.key || "") .split(".") .pop() ?.toLowerCase() .replace(/[^a-z0-9]/g, "") || ""; const matchedCanonicalKey = normalizedEnglishKeys[normKey]; if (matchedCanonicalKey) { if (dictionary?.[matchedCanonicalKey]) { return dictionary[matchedCanonicalKey]; } if (isEnglish) { return matchedCanonicalKey; } } return englishTitle || field.key; } export function formatOptionValue( value: MarriageFieldValue, dictionary?: Record, ): 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, dictionary); 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; } export type SummaryFieldItem = { id: string; key: string; label: string; value: string; iconName: "location" | "ruler" | "graduationCap" | "bookOpen" | "briefcase"; }; export const SUMMARY_FIELD_MATCHERS = { placeOfBirth: [ "place_of_birth", "personal_identity.place_of_birth", "birthplace", "personal_identity.birthplace", "personal_info.birthplace", "birth_place", "country_city_of_birth", "birth_country_city", ], birthCountry: ["country_of_birth", "birth_country"], birthCity: ["city_of_birth", "birth_city"], currentResidence: [ "current_residence_location", "contact_residence.current_residence_location", "current_residence", "contact_residence.current_residence", "contact_residence_family_communication.current_residence", "residence_location", "current_place_of_residence", "residence", ], currentCountry: [ "country_of_current_residence", "current_country", "residence_country", "country", "current_residence_country", ], currentCity: [ "city_state_of_current_residence", "city_of_current_residence", "current_city", "residence_city", "city", "city_state", "state_city", "state", "province", ], height: [ "height_in_centimeters", "appearance_health.height_in_centimeters", "appearance_health_activity.height_in_centimeters", "appearance_health.height", "height", "height_cm", ], highestEducation: [ "highest_completed_education_degree", "education_career.highest_completed_education_degree", "highest_level_of_education", "highest_education_level", "education_career_economic_status.highest_level_of_education", "highest_completed_formal_educational_degree", "education_level", "education", ], fieldOfStudy: [ "field_of_study", "education_career.field_of_study", "education_career_economic_status.field_of_study", "study_field", "study", "major", ], employmentStatus: [ "current_employment_status", "education_career.current_employment_status", "employment_status", "education_career_economic_status.employment_status", ], jobTitle: [ "job_title_and_field_of_activity", "job_title", "education_career.job_title", "education_career_economic_status.job_title", "job_title_and_description", "job_position", "occupation", ], } as const; export function matchesSummaryCandidate( field: MarriageField, candidates: readonly string[], ): boolean { const rawKey = (field.key || "").toLowerCase(); const keyParts = rawKey.split("."); const suffix = keyParts[keyParts.length - 1]; const normalizedKey = rawKey.replace(/[^a-z0-9]/g, ""); const normalizedSuffix = suffix.replace(/[^a-z0-9]/g, ""); for (const c of candidates) { const normC = c.toLowerCase().replace(/[^a-z0-9]/g, ""); if ( normalizedSuffix === normC || normalizedKey.endsWith(normC) || rawKey === c.toLowerCase() || suffix === c.toLowerCase() ) { return true; } } return false; } export function getOrderedSummaryFields( publicInfo: MarriageField[] | null | undefined, dictionary?: Record, ): SummaryFieldItem[] { const fields = publicInfo ?? []; const usedIndexes = new Set(); function pickSummaryField( candidates: readonly string[], fallbackLabelKey?: string, ): { key: string; label: string; value: string } | null { for (const [idx, field] of fields.entries()) { if (usedIndexes.has(idx)) continue; if (matchesSummaryCandidate(field, candidates)) { const val = formatOptionValue(field.value, dictionary); if (val) { usedIndexes.add(idx); const label = formatFieldLabel(field, dictionary) || (fallbackLabelKey && dictionary?.[fallbackLabelKey]) || fallbackLabelKey || field.key; return { key: field.key, label, value: val, }; } } } return null; } // 1. Place of Birth (Country and City) let placeOfBirth = pickSummaryField( SUMMARY_FIELD_MATCHERS.placeOfBirth, "Place of Birth (Country and City)", ); if (!placeOfBirth) { const bCountry = pickSummaryField(SUMMARY_FIELD_MATCHERS.birthCountry); const bCity = pickSummaryField(SUMMARY_FIELD_MATCHERS.birthCity); if (bCountry || bCity) { const combined = [bCity?.value, bCountry?.value].filter(Boolean).join(", "); if (combined) { placeOfBirth = { key: "place_of_birth", label: dictionary?.["Place of Birth (Country and City)"] || dictionary?.["Place of Birth"] || "Place of Birth (Country and City)", value: combined, }; } } } // 2. Current Place of Residence (Country, City / State) let currentResidence = pickSummaryField( SUMMARY_FIELD_MATCHERS.currentResidence, "Current Place of Residence (Country, City / State)", ); if (!currentResidence) { const cCity = pickSummaryField(SUMMARY_FIELD_MATCHERS.currentCity); const cCountry = pickSummaryField(SUMMARY_FIELD_MATCHERS.currentCountry); if (cCountry || cCity) { const combined = [cCity?.value, cCountry?.value].filter(Boolean).join(", "); if (combined) { currentResidence = { key: "current_residence", label: dictionary?.["Current Place of Residence (Country, City / State)"] || dictionary?.["Current Place of Residence"] || dictionary?.["Current Residence"] || "Current Place of Residence (Country, City / State)", value: combined, }; } } } // 3. Height in Centimeters const height = pickSummaryField( SUMMARY_FIELD_MATCHERS.height, "Height in Centimeters", ); // 4. What is your highest completed formal educational degree? const highestEducation = pickSummaryField( SUMMARY_FIELD_MATCHERS.highestEducation, "What is your highest completed formal educational degree?", ); // 5. Field of Study const fieldOfStudy = pickSummaryField( SUMMARY_FIELD_MATCHERS.fieldOfStudy, "Field of Study", ); // 6. What is your current employment status? const employmentStatus = pickSummaryField( SUMMARY_FIELD_MATCHERS.employmentStatus, "What is your current employment status?", ); // 7. Job Title and Field of Activity const jobTitle = pickSummaryField( SUMMARY_FIELD_MATCHERS.jobTitle, "Job Title and Field of Activity", ); const items: SummaryFieldItem[] = []; if (placeOfBirth) { items.push({ id: placeOfBirth.key, key: placeOfBirth.key, label: placeOfBirth.label, value: placeOfBirth.value, iconName: "location", }); } if (currentResidence) { items.push({ id: currentResidence.key, key: currentResidence.key, label: currentResidence.label, value: currentResidence.value, iconName: "location", }); } if (height) { items.push({ id: height.key, key: height.key, label: height.label, value: height.value, iconName: "ruler", }); } if (highestEducation) { items.push({ id: highestEducation.key, key: highestEducation.key, label: highestEducation.label, value: highestEducation.value, iconName: "graduationCap", }); } if (fieldOfStudy) { items.push({ id: fieldOfStudy.key, key: fieldOfStudy.key, label: fieldOfStudy.label, value: fieldOfStudy.value, iconName: "bookOpen", }); } if (employmentStatus) { items.push({ id: employmentStatus.key, key: employmentStatus.key, label: employmentStatus.label, value: employmentStatus.value, iconName: "briefcase", }); } if (jobTitle) { items.push({ id: jobTitle.key, key: jobTitle.key, label: jobTitle.label, value: jobTitle.value, iconName: "briefcase", }); } return items; }