-
BINpublic/assets/images/sections/01_personal_identity_details.png
-
BINpublic/assets/images/sections/02_contact_residence_family_communication.png
-
BINpublic/assets/images/sections/03_physical_appearance_health.png
-
BINpublic/assets/images/sections/04_education_career_economic_status.png
-
BINpublic/assets/images/sections/05_family_background.png
-
BINpublic/assets/images/sections/06_marital_status_marriage_history_children.png
-
BINpublic/assets/images/sections/07_beliefs_lifestyle_personal_boundaries.png
-
BINpublic/assets/images/sections/08_future_spouse_criteria_red_lines.png
-
BINpublic/assets/images/sections/09_identity_verification_documents.png
-
BINpublic/assets/images/sections/10_personality_test.png
-
BINpublic/assets/images/sections/11_glasser_5_needs_test.png
-
5src/app/api/proxy/route.ts
-
324src/app/marriage-advisors/page.tsx
-
69src/app/new-match/new-match-client.tsx
-
111src/app/new-match/profile/page.tsx
-
14src/components/Componentes/question-answer-storage.tsx
-
38src/components/Componentes/question-card.tsx
-
18src/components/Componentes/question-phone.test.tsx
-
25src/lib/auth-bridge.ts
-
151src/lib/marriage-field-formatter.ts
-
1src/lib/schema-adapter-overview.test.ts
-
39src/lib/ssr-fetch.ts
-
8src/translations/locales/ar.json
-
8src/translations/locales/az.json
-
8src/translations/locales/bn.json
-
8src/translations/locales/da.json
-
8src/translations/locales/de.json
-
7src/translations/locales/en.json
-
8src/translations/locales/es.json
-
7src/translations/locales/fa.json
-
8src/translations/locales/fr.json
-
8src/translations/locales/gu.json
-
8src/translations/locales/ha.json
-
8src/translations/locales/he.json
-
8src/translations/locales/hi.json
-
8src/translations/locales/id.json
-
8src/translations/locales/ks.json
-
8src/translations/locales/pt.json
-
8src/translations/locales/ru.json
-
8src/translations/locales/sw.json
-
8src/translations/locales/tg.json
-
8src/translations/locales/tr.json
-
8src/translations/locales/ul.json
-
8src/translations/locales/ur.json
-
8src/translations/locales/uz.json
-
8src/translations/locales/zh.json
|
After Width: 256 | Height: 256 | Size: 24 KiB |
|
After Width: 256 | Height: 256 | Size: 27 KiB |
|
After Width: 256 | Height: 256 | Size: 23 KiB |
|
After Width: 256 | Height: 256 | Size: 20 KiB |
|
After Width: 256 | Height: 256 | Size: 23 KiB |
|
After Width: 256 | Height: 256 | Size: 24 KiB |
|
After Width: 256 | Height: 256 | Size: 24 KiB |
|
After Width: 256 | Height: 256 | Size: 20 KiB |
|
After Width: 256 | Height: 256 | Size: 28 KiB |
|
After Width: 256 | Height: 256 | Size: 23 KiB |
|
After Width: 256 | Height: 256 | Size: 15 KiB |
@ -0,0 +1,151 @@ |
|||
import type { |
|||
MarriageField, |
|||
MarriageFieldValue, |
|||
MarriagePhoneFieldValue, |
|||
} 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") { |
|||
return false; |
|||
} |
|||
|
|||
const phoneValue = value as Partial<MarriagePhoneFieldValue>; |
|||
|
|||
return ( |
|||
typeof phoneValue.countryCode === "string" && |
|||
typeof phoneValue.phoneNumber === "string" |
|||
); |
|||
} |
|||
|
|||
export function formatFieldValue(value: MarriageFieldValue): string | null { |
|||
if (value === null || value === "") { |
|||
return null; |
|||
} |
|||
|
|||
if (isMarriagePhoneFieldValue(value)) { |
|||
return `+${value.countryCode}${value.phoneNumber}`; |
|||
} |
|||
|
|||
if (typeof value === "boolean") { |
|||
return value ? "Yes" : "No"; |
|||
} |
|||
|
|||
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; |
|||
} |
|||