- {t["A Path to Heavenly Marriage"]} -
-- { - t[ - 'We have come together with the goal of creating a secure and confidential path for "permanent marriage" among Muslims' - ] - } -
-120
-- {t["user profiles"]} -
-14
-- {t["matches"]} -
++ {t["A Path to Heavenly Marriage"]} +
++ { + t[ + 'We have come together with the goal of creating a secure and confidential path for "permanent marriage" among Muslims' + ] + } +
+120
++ {t["user profiles"]} +
+14
++ {t["matches"]} +
+14
++ {t["marriages"]} +
+14
-- {t["marriages"]} -
-- {field.label}: +
+ {field.label}: {field.value}
); @@ -467,69 +522,75 @@ export default function NewMatchClient() {
+
{matchHeadingTitle}
-
+
{matchHeadingDescription}
Unable to load match summary.
) : matchSummary ? ( <> -
+
{matchDisplay.name}
-
- {matchDisplay.occupation ? (
-
+
+ {pairedPersonalFields.length ? (
+
+ {pairedPersonalFields.map((field, index) => (
+
+ {index > 0 ? (
+ |
+ ) : null}
+
+ {field.label}:{" "}
+
+ {field.value}
+
+ ))}
+
) : null}
- {pairedFields.length ? (
-
- {pairedFields.map((field, index) => (
+ {pairedEduFields.length ? (
+
+ {pairedEduFields.map((field, index) => (
- {index > 0 ? | : null}
-
- {field.label}: {field.value}
+ {index > 0 ? (
+ |
+ ) : null}
+
+ {field.label}:{" "}
+ {field.value}
))}
) : null}
+ {matchDisplay.job ? (
+
+ ) : null}
+
+ {matchDisplay.hobbies ? (
+
+ ) : null}
+
{matchDisplay.maritalStatus ? (
) : null}
@@ -648,7 +736,7 @@ export default function NewMatchClient() {
openProfile();
}
}}
- className="mt-[15px] inline-flex w-full items-center justify-center rounded-[10px] border-none bg-white h-[40px] text-[16px] font-semibold text-[#F0445B] no-underline shadow-none hover:bg-white/95 transition-colors cursor-pointer"
+ className="mt-3 inline-flex w-full items-center justify-center rounded-[10px] border-none bg-white h-[40px] text-[16px] font-semibold text-[#F0445B] no-underline shadow-none hover:bg-white/95 transition-colors cursor-pointer"
>
{t["View more details"]}
@@ -660,21 +748,22 @@ export default function NewMatchClient() {
)}
)}
-
-
-
+
+
+ {/* 3. Advisor Section */}
+
+
diff --git a/src/app/questions-list/questions-list-client.tsx b/src/app/questions-list/questions-list-client.tsx
index b838db4..4aa2ba8 100644
--- a/src/app/questions-list/questions-list-client.tsx
+++ b/src/app/questions-list/questions-list-client.tsx
@@ -52,6 +52,7 @@ import { localizePath } from "@/translations/config";
import { useI18n } from "@/translations/provider";
import { prefetchSectionsWithBoundedConcurrency } from "./section-prefetch";
import DevTapInstrumentation from "@/components/Componentes/dev-tap-instrumentation";
+import { fetchGeoCountryCode } from "@/components/Componentes/question-phone";
import SectionsRequest from "./sections-request";
import SectionOverlayHost from "@/components/Componentes/section-overlay-host";
import QuestionDetailClient from "@/app/questions-list/[slug]/question-detail-client";
@@ -101,6 +102,11 @@ export default function QuestionsListClient() {
}
}, [isProfileRedirecting, locale, profileTargetPath, router]);
+ // Background prefetch user's geo country code so phone question is pre-warmed
+ useEffect(() => {
+ void fetchGeoCountryCode();
+ }, []);
+
// Signal Flutter to lift its loading cover once the profile is available
// (either from SSR hydration or client-side fetch).
useHabibWebReady(!!profile && !isProfileLoading);
diff --git a/src/app/test-api/page.tsx b/src/app/test-api/page.tsx
new file mode 100644
index 0000000..05d8de4
--- /dev/null
+++ b/src/app/test-api/page.tsx
@@ -0,0 +1,5 @@
+import TestApiPage from "../[lang]/test-api/page";
+
+export default function RootTestApiPage() {
+ return ;
+}
diff --git a/src/components/Componentes/intro-hero-illustration.tsx b/src/components/Componentes/intro-hero-illustration.tsx
new file mode 100644
index 0000000..2cd0734
--- /dev/null
+++ b/src/components/Componentes/intro-hero-illustration.tsx
@@ -0,0 +1,213 @@
+"use client";
+
+import type { SVGProps } from "react";
+import { useId } from "react";
+
+/**
+ * Inline SVG components for Intro page stats and video controls.
+ * Rendering these icons as inline SVG eliminates external HTTP requests,
+ * lazy-loading delay, and layout shifts — rendering instantly on frame 0.
+ */
+
+export function IntroUsersIcon(props: SVGProps) {
+ const uid = useId().replace(/[^a-zA-Z0-9_-]/g, "");
+ const gradId = `paint0_linear_users_${uid}`;
+
+ return (
+
+ );
+}
+
+export function IntroMatchesIcon(props: SVGProps) {
+ const uid = useId().replace(/[^a-zA-Z0-9_-]/g, "");
+ const maskId = `mask0_matches_${uid}`;
+ const gradId = `paint0_linear_matches_${uid}`;
+
+ return (
+
+ );
+}
+
+export function IntroMarriagesIcon(props: SVGProps) {
+ const uid = useId().replace(/[^a-zA-Z0-9_-]/g, "");
+ const gradId = `paint0_linear_marriages_${uid}`;
+
+ return (
+
+ );
+}
+
+export function IntroPlayButtonIcon(props: SVGProps) {
+ const uid = useId().replace(/[^a-zA-Z0-9_-]/g, "");
+ const filterId = `filter0_d_${uid}`;
+
+ return (
+
+ );
+}
diff --git a/src/components/Componentes/question-answer-storage.tsx b/src/components/Componentes/question-answer-storage.tsx
index d759bf3..cdd55a2 100644
--- a/src/components/Componentes/question-answer-storage.tsx
+++ b/src/components/Componentes/question-answer-storage.tsx
@@ -132,11 +132,27 @@ function createQuestionField(
): MarriageField {
let option_id: string | string[] | undefined;
- if (question.options && Array.isArray(question.options) && question.options.length > 0) {
+ const isChoiceType =
+ question.type === "dropdown" ||
+ question.type === "radio" ||
+ question.type === "checkbox" ||
+ question.type === "scale";
+
+ if (isChoiceType && question.options && Array.isArray(question.options) && question.options.length > 0) {
if (Array.isArray(value)) {
option_id = value;
} else if (typeof value === "string" && value) {
- const selectedOpt = question.options.find((opt) => opt.id === value || opt.value === value);
+ const strVal = value.trim().toLowerCase();
+ const selectedOpt = question.options.find(
+ (opt) =>
+ opt.id === value ||
+ opt.value === value ||
+ opt.label === value ||
+ opt.id.toLowerCase() === strVal ||
+ (typeof opt.value === "string" && opt.value.toLowerCase() === strVal) ||
+ (typeof opt.label === "string" && opt.label.toLowerCase() === strVal) ||
+ opt.id.toLowerCase().endsWith(`.${strVal}`)
+ );
option_id = selectedOpt ? selectedOpt.id : value;
}
}
@@ -149,7 +165,7 @@ function createQuestionField(
type: question.type,
value,
private: question.private,
- option_id: option_id,
+ option_id: isChoiceType ? option_id : undefined,
} as MarriageField;
}
diff --git a/src/components/Componentes/question-answer.test.tsx b/src/components/Componentes/question-answer.test.tsx
index 0f99de7..d5ac6c3 100644
--- a/src/components/Componentes/question-answer.test.tsx
+++ b/src/components/Componentes/question-answer.test.tsx
@@ -90,6 +90,33 @@ function TestComponent({ slug }: { slug: string }) {
Set Checkbox
+
+ setAnswerValue(
+ {
+ id: "q_multi_drop",
+ type: "dropdown",
+ title: "Q Multi Drop",
+ order: 3,
+ required: true,
+ baseRequired: true,
+ isVisible: true,
+ description: "",
+ tooltip: "",
+ extras: { placeHolder: "", options: [], range: [1, 10] },
+ options: [
+ { id: "opt_d1", value: "D1", label: "Option D1", order: 1 },
+ { id: "opt_d2", value: "D2", label: "Option D2", order: 2 },
+ ],
+ },
+ ["opt_d1", "opt_d2"],
+ )
+ }
+ >
+ Set Multi Dropdown
+
+
flushAnswers()}>
Save
@@ -214,6 +241,51 @@ describe("Question Answer & Schema Integration", () => {
});
});
+ it("should send array of option_ids for multi-select dropdown", async () => {
+ const queryClient = new QueryClient({
+ defaultOptions: { queries: { retry: false } },
+ });
+
+ render(
+
+
+
+
+ ,
+ );
+
+ fireEvent.click(screen.getByTestId("set-multi-dropdown"));
+ fireEvent.click(screen.getByTestId("save"));
+
+ await waitFor(() => {
+ expect(capturedPayload).not.toBeNull();
+ const field = capturedPayload.fields.find(
+ (f: any) => f.key === "q_multi_drop",
+ );
+ expect(field.option_id).toEqual(["opt_d1", "opt_d2"]);
+ });
+ });
+
it("batches all dirty answers into one mutation", async () => {
const queryClient = new QueryClient({
defaultOptions: { queries: { retry: false } },
diff --git a/src/components/Componentes/question-birthplace.tsx b/src/components/Componentes/question-birthplace.tsx
index 7265702..25a706e 100644
--- a/src/components/Componentes/question-birthplace.tsx
+++ b/src/components/Componentes/question-birthplace.tsx
@@ -2,7 +2,7 @@
import { useCallback, useEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";
-import { getCountryList, COUNTRIES_EN, COUNTRIES_FA } from "@/data/countries";
+import { getCountryList, resolveCountryName, COUNTRIES_EN, COUNTRIES_FA } from "@/data/countries";
import type { QuestionField } from "@/lib/schema-adapter";
import { useI18n } from "@/translations/provider";
import { useQuestionAnswers } from "./question-answer-storage";
@@ -10,6 +10,7 @@ import QuestionTitle from "./question-title";
import { LoadingThreeDot } from "./loading-three-dot";
import { useSheetScrollLock } from "./use-sheet-scroll-lock";
import { Input } from "@/components/ui/input";
+import { getUserGeoRegion, getStoredUserGeoRegion } from "@/lib/geo-region";
const EXIT_ANIMATION_MS = 220;
@@ -88,9 +89,26 @@ export function QuestionBirthplace({
const { getAnswerValue, setAnswerValue, isLoading } = useQuestionAnswers();
const rawValue = getAnswerValue(question);
+ const isResidence = question.ui_config?.enable_geoip === true;
+ const storedRegion = isResidence ? getStoredUserGeoRegion() : null;
+
const initial = parseValue(rawValue);
- const [selectedCountry, setSelectedCountry] = useState(initial.country);
- const [cityInput, setCityInput] = useState(initial.city);
+ const localizedInitialCountry = resolveCountryName(
+ initial.country || storedRegion?.country,
+ locale,
+ );
+ const initialCity = initial.city || storedRegion?.city || "";
+ const initialLoc =
+ localizedInitialCountry || initialCity
+ ? [initialCity, localizedInitialCountry].filter(Boolean).join(", ")
+ : "";
+
+ const [selectedCountry, setSelectedCountry] = useState(
+ () => localizedInitialCountry || "",
+ );
+ const [cityInput, setCityInput] = useState(
+ () => initialCity,
+ );
const [isOpen, setIsOpen] = useState(false);
const [isClosing, setIsClosing] = useState(false);
@@ -99,11 +117,9 @@ export function QuestionBirthplace({
const listRef = useRef(null);
const isMountedRef = useRef(true);
- const isResidence = question.ui_config?.enable_geoip === true;
-
const [mode, setMode] = useState<"auto" | "manual">("auto");
const [isDetecting, setIsDetecting] = useState(false);
- const [detectedLocation, setDetectedLocation] = useState("");
+ const [detectedLocation, setDetectedLocation] = useState(initialLoc);
useEffect(() => {
isMountedRef.current = true;
@@ -152,89 +168,59 @@ export function QuestionBirthplace({
setAnswerValue(question, formatted);
};
- // GeoIP detection logic
- const detectLocation = (force = false) => {
- const alreadyChecked =
- typeof window !== "undefined"
- ? localStorage.getItem("hasCheckedGeoIPResidence")
- : "true";
- if (alreadyChecked && !force) {
- if (rawValue) {
- const parsed = parseValue(rawValue);
- if (parsed.country && parsed.city) {
- setDetectedLocation(`${parsed.city}, ${parsed.country}`);
- setMode("auto");
- } else {
- setMode("manual");
- }
- } else {
+ // GeoIP detection logic using unified getUserGeoRegion
+ const detectLocation = useCallback(async (force = false) => {
+ // If there is already a saved answer and we are not forcing, display it
+ if (rawValue && !force) {
+ const parsed = parseValue(rawValue);
+ const cName = resolveCountryName(parsed.country, locale);
+ if (cName || parsed.city) {
+ const loc = [parsed.city, cName].filter(Boolean).join(", ");
+ setSelectedCountry(cName);
+ setCityInput(parsed.city);
+ setDetectedLocation(loc);
setMode("auto");
+ return;
}
- return;
}
- // If there is already a saved value and we are not forcing, do not fetch again
- if (rawValue && !force) {
- const parsed = parseValue(rawValue);
- if (parsed.country && parsed.city) {
- setDetectedLocation(`${parsed.city}, ${parsed.country}`);
+ setIsDetecting(true);
+
+ try {
+ const region = await getUserGeoRegion(force);
+ if (!isMountedRef.current) return;
+
+ const city = region.city || "";
+ const rawCountry = region.country || region.countryCode || "";
+ const country = resolveCountryName(rawCountry, locale) || rawCountry;
+
+ if (city || country) {
+ const loc = [city, country].filter(Boolean).join(", ");
+ setSelectedCountry(country);
+ setCityInput(city);
+ setDetectedLocation(loc);
+ updateAnswers(country, city);
setMode("auto");
} else {
setMode("manual");
}
- return;
- }
-
- setIsDetecting(true);
- if (typeof window !== "undefined") {
- localStorage.setItem("hasCheckedGeoIPResidence", "true");
- }
- fetch("https://ipapi.co/json/")
- .then((res) => res.json())
- .then((data) => {
- if (data && data.city && data.country_name) {
- const city = data.city;
- const country = data.country_name;
- setSelectedCountry(country);
- setCityInput(city);
- setDetectedLocation(`${city}, ${country}`);
- updateAnswers(country, city);
- setMode("auto");
- } else {
- throw new Error("Missing data");
- }
- })
- .catch(() => {
- fetch("https://ipwho.is/")
- .then((res) => res.json())
- .then((data) => {
- if (data && data.city && data.country) {
- const city = data.city;
- const country = data.country;
- setSelectedCountry(country);
- setCityInput(city);
- setDetectedLocation(`${city}, ${country}`);
- updateAnswers(country, city);
- setMode("auto");
- } else {
- setMode("manual");
- }
- })
- .catch(() => {
- setMode("manual");
- });
- })
- .finally(() => {
+ } catch {
+ if (isMountedRef.current) {
+ setMode("manual");
+ }
+ } finally {
+ if (isMountedRef.current) {
setIsDetecting(false);
- });
- };
+ }
+ }
+ }, [rawValue, locale]);
useEffect(() => {
if (isLoading) return;
if (isResidence) {
- detectLocation();
+ void detectLocation();
}
- }, [isResidence, isLoading]);
+ }, [isResidence, isLoading, detectLocation]);
const handleAutoClick = () => {
setMode("auto");
@@ -247,7 +233,8 @@ export function QuestionBirthplace({
}
setMode("manual");
const parsed = parseValue(rawValue);
- const country = selectedCountry || parsed.country;
+ const resolvedC = resolveCountryName(selectedCountry || parsed.country, locale);
+ const country = resolvedC || selectedCountry || parsed.country;
const city = cityInput || parsed.city;
setSelectedCountry(country);
setCityInput(city);
@@ -257,16 +244,17 @@ export function QuestionBirthplace({
// Synchronize state if rawValue changes externally
useEffect(() => {
const updated = parseValue(rawValue);
- if (updated.country !== selectedCountry) {
- setSelectedCountry(updated.country);
+ const resolvedC = resolveCountryName(updated.country, locale);
+ if (resolvedC && resolvedC !== selectedCountry) {
+ setSelectedCountry(resolvedC);
}
if (updated.city.trim() !== cityInput.trim()) {
setCityInput(updated.city);
}
- if (updated.country && updated.city) {
- setDetectedLocation(`${updated.city}, ${updated.country}`);
+ if (resolvedC || updated.city) {
+ setDetectedLocation([updated.city, resolvedC].filter(Boolean).join(", "));
}
- }, [rawValue]);
+ }, [rawValue, locale]);
const options = getCountryList(locale);
const filteredOptions = options.filter((option) =>
@@ -576,7 +564,7 @@ export function QuestionBirthplace({
>
e.stopPropagation()}
diff --git a/src/components/Componentes/question-card.tsx b/src/components/Componentes/question-card.tsx
index 3ca0deb..fd78e5d 100644
--- a/src/components/Componentes/question-card.tsx
+++ b/src/components/Componentes/question-card.tsx
@@ -9,6 +9,7 @@ import {
} from "@/lib/schema-adapter";
import { localizePath } from "@/translations/config";
import { useI18n } from "@/translations/provider";
+import { SectionIcon } from "./section-icon";
import { UiIcon, type UiIconName } from "./ui-icon";
type QuestionCardProps = {
@@ -122,22 +123,11 @@ export function QuestionCard({
>
- {iconImageSrc ? (
-
- ) : (
-
- )}
+
diff --git a/src/components/Componentes/question-checkbox.tsx b/src/components/Componentes/question-checkbox.tsx
index 04c8445..9d1e199 100644
--- a/src/components/Componentes/question-checkbox.tsx
+++ b/src/components/Componentes/question-checkbox.tsx
@@ -42,7 +42,7 @@ export function QuestionCheckbox({
const isShortOptions =
options.length <= 4 &&
- options.every((opt) => (opt.label || opt.value || "").length <= 15);
+ options.every((opt) => String(opt.label || opt.value || "").length <= 15);
return (
{
answerMap[q.id] = val;
});
+const httpMocks = vi.hoisted(() => ({
+ get: vi.fn(),
+}));
+
+vi.mock("@/lib/http", () => ({
+ http: { get: httpMocks.get },
+}));
+
vi.mock("@/translations/provider", () => ({
useI18n: () => ({
locale: "en",
@@ -22,6 +30,8 @@ vi.mock("@/translations/provider", () => ({
"Select country": "Select country",
Close: "Close",
Confirm: "Confirm",
+ "Enter a valid phone number with country code.":
+ "Enter a valid phone number with country code.",
},
}),
}));
@@ -62,8 +72,6 @@ const phoneQuestion2: QuestionField = {
options: [],
};
-
-
describe("QuestionPhone IP country detection and shimmer", () => {
beforeEach(() => {
answerMap = {};
@@ -71,13 +79,47 @@ describe("QuestionPhone IP country detection and shimmer", () => {
localStorage.clear();
resetGeoPhoneStateForTesting();
vi.restoreAllMocks();
+ httpMocks.get.mockReset();
});
afterEach(() => {
cleanup();
});
- it("renders shimmer on country button while IP request is pending, then shows resolved country code", async () => {
+ it("renders single unified shimmer on country button while IP request is pending, then shows resolved country code from Habib user region API", async () => {
+ let resolveRegion!: (value: unknown) => void;
+ const regionPromise = new Promise((resolve) => {
+ resolveRegion = resolve;
+ });
+
+ httpMocks.get.mockReturnValue(regionPromise);
+
+ const { container } = render( );
+
+ const shimmerElements = container.querySelectorAll(".shimmer-bg");
+ expect(shimmerElements.length).toBe(1);
+ const input = screen.getByRole("textbox");
+ expect(input.classList.contains("shimmer-bg")).toBe(false);
+
+ await act(async () => {
+ resolveRegion({
+ data: {
+ country: "Iran",
+ country_code: "IR",
+ },
+ });
+ });
+
+ await waitFor(() => {
+ expect(container.querySelectorAll(".shimmer-bg").length).toBe(0);
+ expect(screen.getByText("+98")).toBeDefined();
+ expect(screen.getByText("🇮🇷")).toBeDefined();
+ });
+ });
+
+ it("falls back to secondary fetch when Habib region API fails and shows resolved code", async () => {
+ httpMocks.get.mockRejectedValue(new Error("Network failure"));
+
let resolveIpFetch!: (value: unknown) => void;
const ipPromise = new Promise((resolve) => {
resolveIpFetch = resolve;
@@ -95,11 +137,6 @@ describe("QuestionPhone IP country detection and shimmer", () => {
const { container } = render( );
- const shimmerElements = container.querySelectorAll(".shimmer-bg");
- expect(shimmerElements.length).toBeGreaterThan(0);
- const input = screen.getByRole("textbox");
- expect(input.classList.contains("shimmer-bg")).toBe(false);
-
await act(async () => {
resolveIpFetch({ country_calling_code: "+98" });
});
@@ -111,7 +148,8 @@ describe("QuestionPhone IP country detection and shimmer", () => {
});
});
- it("shows default country code when IP request fails", async () => {
+ it("shows default country code when all IP requests fail", async () => {
+ httpMocks.get.mockRejectedValue(new Error("Network failure"));
vi.spyOn(globalThis, "fetch").mockRejectedValue(
new Error("Network failure"),
);
@@ -128,20 +166,12 @@ describe("QuestionPhone IP country detection and shimmer", () => {
});
it("fetches IP country code only once when multiple fields are rendered and updates both", async () => {
- let resolveIpFetch!: (value: unknown) => void;
- const ipPromise = new Promise((resolve) => {
- resolveIpFetch = resolve;
+ let resolveRegion!: (value: unknown) => void;
+ const regionPromise = new Promise((resolve) => {
+ resolveRegion = resolve;
});
- const fetchSpy = vi.spyOn(globalThis, "fetch").mockImplementation(() =>
- ipPromise.then(
- (data) =>
- ({
- ok: true,
- json: async () => data,
- }) as unknown as Response,
- ),
- );
+ httpMocks.get.mockReturnValue(regionPromise);
render(
<>
@@ -150,10 +180,15 @@ describe("QuestionPhone IP country detection and shimmer", () => {
>,
);
- expect(fetchSpy).toHaveBeenCalledTimes(1);
+ expect(httpMocks.get).toHaveBeenCalledTimes(1);
await act(async () => {
- resolveIpFetch({ country_calling_code: "+98" });
+ resolveRegion({
+ data: {
+ country: "Iran",
+ country_code: "IR",
+ },
+ });
});
await waitFor(() => {
@@ -170,41 +205,127 @@ describe("QuestionPhone IP country detection and shimmer", () => {
phoneNumber: "2025550143",
};
- const fetchSpy = vi.spyOn(globalThis, "fetch");
-
const { container } = render( );
expect(container.querySelectorAll(".shimmer-bg").length).toBe(0);
expect(screen.getByText("+1")).toBeDefined();
- expect(screen.getByDisplayValue("2025550143")).toBeDefined();
- expect(fetchSpy).not.toHaveBeenCalled();
+ expect(screen.getByDisplayValue("202-555-0143")).toBeDefined();
+ expect(httpMocks.get).not.toHaveBeenCalled();
});
it("does not overwrite manual selection when user manually interacts", async () => {
- let resolveIpFetch!: (value: unknown) => void;
- const ipPromise = new Promise((resolve) => {
- resolveIpFetch = resolve;
+ let resolveRegion!: (value: unknown) => void;
+ const regionPromise = new Promise((resolve) => {
+ resolveRegion = resolve;
});
- vi.spyOn(globalThis, "fetch").mockImplementation(() =>
- ipPromise.then(
- (data) =>
- ({
- ok: true,
- json: async () => data,
- }) as unknown as Response,
- ),
- );
+ httpMocks.get.mockReturnValue(regionPromise);
- render( );
+ render( );
const input = screen.getByRole("textbox");
- fireEvent.change(input, { target: { value: "123456" } });
+ fireEvent.change(input, { target: { value: "7400123456" } });
await act(async () => {
- resolveIpFetch({ country_calling_code: "+98" });
+ resolveRegion({
+ data: {
+ country: "Iran",
+ country_code: "IR",
+ },
+ });
+ });
+
+ expect(screen.getByDisplayValue("7400 123456")).toBeDefined();
+ });
+
+ it("formats Iranian phone numbers as 3-3-4 Telegram style as digits are typed", async () => {
+ httpMocks.get.mockResolvedValue({
+ data: { country: "Iran", country_code: "IR" },
});
- expect(screen.getByDisplayValue("123456")).toBeDefined();
+ render( );
+
+ const input = screen.getByRole("textbox") as HTMLInputElement;
+
+ // Type 3 digits
+ fireEvent.change(input, { target: { value: "912" } });
+ expect(input.value).toBe("912");
+
+ // Type 6 digits
+ fireEvent.change(input, { target: { value: "912345" } });
+ expect(input.value).toBe("912 345");
+
+ // Type 10 digits
+ fireEvent.change(input, { target: { value: "9123456789" } });
+ expect(input.value).toBe("912 345 6789");
+ });
+
+ it("shows dynamic placeholder based on resolved country code", async () => {
+ let resolveRegion!: (value: unknown) => void;
+ const regionPromise = new Promise((resolve) => {
+ resolveRegion = resolve;
+ });
+
+ httpMocks.get.mockReturnValue(regionPromise);
+
+ render( );
+
+ const input = screen.getByRole("textbox") as HTMLInputElement;
+ expect(input.placeholder).toBe("7400 123456");
+
+ await act(async () => {
+ resolveRegion({
+ data: {
+ country: "Iran",
+ country_code: "IR",
+ },
+ });
+ });
+
+ await waitFor(() => {
+ expect(input.placeholder).toBe("912 345 6789");
+ });
+ });
+
+ it("does not show validation error on active typing, only shows error on blur if incomplete", async () => {
+ httpMocks.get.mockResolvedValue({
+ data: { country: "Iran", country_code: "IR" },
+ });
+
+ const { container } = render(
+ ,
+ );
+
+ const input = screen.getByRole("textbox") as HTMLInputElement;
+
+ // Focus and start typing incomplete number
+ fireEvent.focus(input);
+ fireEvent.change(input, { target: { value: "912" } });
+
+ // No error while focused and typing
+ expect(
+ screen.queryByText("Enter a valid phone number with country code."),
+ ).toBeNull();
+ expect(container.querySelector(".border-\\[\\#F2465F\\]")).toBeNull();
+
+ // Blur input without completing
+ fireEvent.blur(input);
+
+ // Error appears after blur
+ expect(
+ screen.getByText("Enter a valid phone number with country code."),
+ ).toBeDefined();
+ expect(container.querySelector(".border-\\[\\#F2465F\\]")).not.toBeNull();
+
+ // Focus back and complete valid number
+ fireEvent.focus(input);
+ fireEvent.change(input, { target: { value: "9123456789" } });
+ fireEvent.blur(input);
+
+ // Error is gone
+ expect(
+ screen.queryByText("Enter a valid phone number with country code."),
+ ).toBeNull();
+ expect(container.querySelector(".border-\\[\\#F2465F\\]")).toBeNull();
});
});
diff --git a/src/components/Componentes/question-phone.tsx b/src/components/Componentes/question-phone.tsx
index f490360..0617f7b 100644
--- a/src/components/Componentes/question-phone.tsx
+++ b/src/components/Componentes/question-phone.tsx
@@ -1,9 +1,14 @@
"use client";
-import { PhoneNumberFormat, PhoneNumberUtil } from "google-libphonenumber";
+import {
+ AsYouTypeFormatter,
+ PhoneNumberFormat,
+ PhoneNumberUtil,
+} from "google-libphonenumber";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { createPortal } from "react-dom";
import type { MarriagePhoneFieldValue } from "@/hooks/marriage/types";
+import { http } from "@/lib/http";
import type { QuestionField } from "@/lib/schema-adapter";
import { useI18n } from "@/translations/provider";
import { useQuestionAnswers } from "./question-answer-storage";
@@ -23,134 +28,35 @@ type PhoneValueParts = {
phoneValue: string;
};
-const phoneUtil = PhoneNumberUtil.getInstance();
+import {
+ getUserGeoRegion,
+ getStoredUserGeoRegion,
+ setStoredUserGeoRegion,
+ subscribeToUserGeoRegion,
+ resetUserGeoRegionForTesting,
+} from "@/lib/geo-region";
-// Module-level singleton state for IP phone country resolution
-let cachedGeoCountryCode: string | null = null;
-let geoIpPromise: Promise | null = null;
-const geoListeners = new Set<(code: string) => void>();
+const phoneUtil = PhoneNumberUtil.getInstance();
export function resetGeoPhoneStateForTesting() {
- cachedGeoCountryCode = null;
- geoIpPromise = null;
- geoListeners.clear();
+ resetUserGeoRegionForTesting();
}
function getStoredGeoCode(): string | null {
- if (cachedGeoCountryCode) return cachedGeoCountryCode;
- if (typeof window !== "undefined") {
- try {
- const stored = localStorage.getItem("geoIPPhoneCode");
- if (stored) {
- cachedGeoCountryCode = stored;
- return stored;
- }
- } catch {}
- }
- return null;
+ const region = getStoredUserGeoRegion();
+ return region?.phoneCode || null;
}
export function setManuallySelectedGeoCode(code: string) {
- cachedGeoCountryCode = code;
- if (typeof window !== "undefined") {
- try {
- localStorage.setItem("geoIPPhoneCode", code);
- localStorage.setItem("hasCheckedGeoIPPhone", "true");
- } catch {}
- }
+ const current = getStoredUserGeoRegion() || {};
+ setStoredUserGeoRegion({ ...current, phoneCode: code });
}
-export function fetchGeoCountryCode(defaultCode = "+44"): Promise {
- const existing = getStoredGeoCode();
- if (existing) {
- return Promise.resolve(existing);
- }
-
- if (geoIpPromise) {
- return geoIpPromise.then((res) => res || defaultCode);
- }
-
- geoIpPromise = (async () => {
- try {
- // 1. Primary: ipapi.co with 2s timeout
- const controller = new AbortController();
- const timeoutId = setTimeout(() => controller.abort(), 2000);
- try {
- const res = await fetch("https://ipapi.co/json/", {
- signal: controller.signal,
- });
- clearTimeout(timeoutId);
- if (res?.ok) {
- const data = await res.json();
- if (data?.country_calling_code) {
- const rawCode = String(data.country_calling_code).trim();
- const formatted = rawCode.startsWith("+") ? rawCode : `+${rawCode}`;
- setManuallySelectedGeoCode(formatted);
- geoListeners.forEach((fn) => {
- fn(formatted);
- });
- return formatted;
- }
- }
- } catch {
- clearTimeout(timeoutId);
- }
-
- // 2. Secondary fallback: ipwho.is with 2s timeout
- const secondaryController = new AbortController();
- const secondaryTimeoutId = setTimeout(
- () => secondaryController.abort(),
- 2000,
- );
- try {
- const res = await fetch("https://ipwho.is/", {
- signal: secondaryController.signal,
- });
- clearTimeout(secondaryTimeoutId);
- if (res?.ok) {
- const data = await res.json();
- if (data?.calling_code) {
- const rawCode = String(data.calling_code).trim();
- const formatted = rawCode.startsWith("+") ? rawCode : `+${rawCode}`;
- setManuallySelectedGeoCode(formatted);
- geoListeners.forEach((fn) => {
- fn(formatted);
- });
- return formatted;
- }
- }
- } catch {
- clearTimeout(secondaryTimeoutId);
- }
-
- // 3. Fallback to default
- if (typeof window !== "undefined") {
- try {
- localStorage.setItem("geoIPPhoneCode", defaultCode);
- localStorage.setItem("hasCheckedGeoIPPhone", "true");
- } catch {}
- }
- cachedGeoCountryCode = defaultCode;
- geoListeners.forEach((fn) => {
- fn(defaultCode);
- });
- return defaultCode;
- } catch {
- if (typeof window !== "undefined") {
- try {
- localStorage.setItem("geoIPPhoneCode", defaultCode);
- localStorage.setItem("hasCheckedGeoIPPhone", "true");
- } catch {}
- }
- cachedGeoCountryCode = defaultCode;
- geoListeners.forEach((fn) => {
- fn(defaultCode);
- });
- return defaultCode;
- }
- })();
-
- return geoIpPromise.then((res) => res || defaultCode);
+export async function fetchGeoCountryCode(
+ defaultCode = "+44",
+): Promise {
+ const region = await getUserGeoRegion();
+ return region.phoneCode || defaultCode;
}
function isMarriagePhoneFieldValue(
@@ -235,10 +141,11 @@ function readPhoneValue(value: unknown, fallbackCode: string): PhoneValueParts {
function getMaxLengthForCountry(codeValue: string): number {
try {
const cleanCode = codeValue.replace(/[^\d]/g, "");
- if (!cleanCode) return 15;
+ if (!cleanCode) return 18;
+ if (cleanCode === "98") return 12; // 3 + 1 + 3 + 1 + 4 = 12 chars
const countryCode = Number(cleanCode);
const regionCode = phoneUtil.getRegionCodeForCountryCode(countryCode);
- if (!regionCode || regionCode === "ZZ") return 15;
+ if (!regionCode || regionCode === "ZZ") return 18;
const exampleMobile = phoneUtil.getExampleNumberForType(
regionCode,
@@ -254,9 +161,93 @@ function getMaxLengthForCountry(codeValue: string): number {
: 0;
const baseLen = Math.max(lenMobile, lenGeneral, 8);
- return baseLen + 1;
+ return baseLen + 6;
+ } catch {
+ return 18;
+ }
+}
+
+function getCountryPlaceholder(
+ codeValue: string,
+ defaultPlaceholder?: string,
+): string {
+ try {
+ const cleanCode = codeValue.replace(/[^\d]/g, "");
+ if (!cleanCode) {
+ return defaultPlaceholder?.replace(/^\+\d+\s*/, "") || "912 345 6789";
+ }
+
+ if (cleanCode === "98") {
+ return "912 345 6789";
+ }
+
+ const countryCode = Number(cleanCode);
+ const regionCode = phoneUtil.getRegionCodeForCountryCode(countryCode);
+ if (!regionCode || regionCode === "ZZ") {
+ return defaultPlaceholder?.replace(/^\+\d+\s*/, "") || "912 345 6789";
+ }
+
+ const example =
+ phoneUtil.getExampleNumberForType(
+ regionCode,
+ 1, // MOBILE
+ ) || phoneUtil.getExampleNumber(regionCode);
+
+ if (!example) {
+ return defaultPlaceholder?.replace(/^\+\d+\s*/, "") || "912 345 6789";
+ }
+
+ const intl = phoneUtil.format(example, PhoneNumberFormat.INTERNATIONAL);
+ const prefix = `+${cleanCode} `;
+ if (intl.startsWith(prefix)) {
+ return intl.slice(prefix.length);
+ }
+ return String(example.getNationalNumber());
+ } catch {
+ return defaultPlaceholder?.replace(/^\+\d+\s*/, "") || "912 345 6789";
+ }
+}
+
+function formatPhoneNumberAsYouType(
+ rawInput: string,
+ codeValue: string,
+): string {
+ const cleanCode = codeValue.replace(/[^\d]/g, "");
+ const rawDigits = rawInput.replace(/\D/g, "");
+ if (!rawDigits) return "";
+
+ // Special telegram-style handling for Iran (+98): 3 - 3 - 4 (e.g. 912 345 6789)
+ if (cleanCode === "98") {
+ const digits = rawDigits.startsWith("0") ? rawDigits.slice(1) : rawDigits;
+ const max10 = digits.slice(0, 10);
+ if (max10.length <= 3) return max10;
+ if (max10.length <= 6) return `${max10.slice(0, 3)} ${max10.slice(3)}`;
+ return `${max10.slice(0, 3)} ${max10.slice(3, 6)} ${max10.slice(6)}`;
+ }
+
+ const countryCode = Number(cleanCode);
+ const regionCode = phoneUtil.getRegionCodeForCountryCode(countryCode);
+ if (!regionCode || regionCode === "ZZ") {
+ return rawDigits;
+ }
+
+ try {
+ const formatter = new AsYouTypeFormatter(regionCode);
+ let formatted = "";
+ const fullNumber = `+${cleanCode}${rawDigits}`;
+ for (const char of fullNumber) {
+ formatted = formatter.inputDigit(char);
+ }
+ const codePrefix = `+${cleanCode} `;
+ if (formatted.startsWith(codePrefix)) {
+ return formatted.slice(codePrefix.length);
+ }
+ if (formatted.startsWith(`+${cleanCode}`)) {
+ return formatted.slice(`+${cleanCode}`.length).trim();
+ }
+ return rawDigits;
} catch {
- return 15;
+ return rawDigits;
}
}
@@ -404,13 +395,23 @@ export function QuestionPhone({
}, [value, defaultCodeValue]);
const initialPhone = useMemo(() => {
- return readPhoneValue(value, defaultCodeValue).phoneValue;
- }, [value, defaultCodeValue]);
+ const rawPhone = readPhoneValue(value, defaultCodeValue).phoneValue;
+ return formatPhoneNumberAsYouType(rawPhone, initialCode);
+ }, [value, defaultCodeValue, initialCode]);
const [codeValue, setCodeValue] = useState(initialCode);
const [phoneValue, setPhoneValue] = useState(initialPhone);
+ const [isFocused, setIsFocused] = useState(false);
+ const [isTouched, setIsTouched] = useState(false);
const lastCommittedValueRef = useRef(value);
+ const dynamicPlaceholder = useMemo(() => {
+ return getCountryPlaceholder(
+ codeValue || defaultCodeValue,
+ question.extras?.placeHolder,
+ );
+ }, [codeValue, defaultCodeValue, question.extras?.placeHolder]);
+
const [isOpen, setIsOpen] = useState(false);
const [isClosing, setIsClosing] = useState(false);
const [searchQuery, setSearchQuery] = useState("");
@@ -439,19 +440,30 @@ export function QuestionPhone({
let isMounted = true;
- const onGeoCodeResolved = (resolvedCode: string) => {
+ const unsubscribe = subscribeToUserGeoRegion((region) => {
if (!isMounted || userInteractedRef.current) return;
- setCodeValue(resolvedCode);
- setIsResolvingCountry(false);
- };
-
- geoListeners.add(onGeoCodeResolved);
+ if (region.phoneCode) {
+ setCodeValue(region.phoneCode);
+ if (phoneValue) {
+ setPhoneValue((prev) =>
+ formatPhoneNumberAsYouType(prev, region.phoneCode!),
+ );
+ }
+ setIsResolvingCountry(false);
+ }
+ });
fetchGeoCountryCode(defaultCodeValue)
.then((resolvedCode) => {
if (!isMounted) return;
if (!userInteractedRef.current) {
- setCodeValue(resolvedCode || defaultCodeValue);
+ const finalCode = resolvedCode || defaultCodeValue;
+ setCodeValue(finalCode);
+ if (phoneValue) {
+ setPhoneValue((prev) =>
+ formatPhoneNumberAsYouType(prev, finalCode),
+ );
+ }
}
setIsResolvingCountry(false);
})
@@ -465,12 +477,14 @@ export function QuestionPhone({
return () => {
isMounted = false;
- geoListeners.delete(onGeoCodeResolved);
+ unsubscribe();
};
- }, [hasExplicitValue, isResolvingCountry, defaultCodeValue]);
+ }, [hasExplicitValue, isResolvingCountry, defaultCodeValue, phoneValue]);
const normalizedPhoneState = getNormalizedPhoneValue(codeValue, phoneValue);
const showInvalidState =
+ isTouched &&
+ !isFocused &&
codeValue.trim().length > 0 &&
phoneValue.trim().length > 0 &&
!normalizedPhoneState.isValid;
@@ -602,13 +616,19 @@ export function QuestionPhone({
if (explicit) {
setIsResolvingCountry(false);
setCodeValue(nextValue.codeValue);
- const maxLen = getMaxLengthForCountry(nextValue.codeValue);
- setPhoneValue(nextValue.phoneValue.slice(0, maxLen));
+ const formatted = formatPhoneNumberAsYouType(
+ nextValue.phoneValue,
+ nextValue.codeValue,
+ );
+ setPhoneValue(formatted);
} else if (value !== null && value !== undefined) {
const resolvedCode = cachedCode || defaultCodeValue;
setCodeValue(resolvedCode);
- const maxLen = getMaxLengthForCountry(resolvedCode);
- setPhoneValue(nextValue.phoneValue.slice(0, maxLen));
+ const formatted = formatPhoneNumberAsYouType(
+ nextValue.phoneValue,
+ resolvedCode,
+ );
+ setPhoneValue(formatted);
}
lastCommittedValueRef.current = value;
@@ -635,12 +655,11 @@ export function QuestionPhone({
userInteractedRef.current = true;
setIsResolvingCountry(false);
setManuallySelectedGeoCode(selectedCode);
- const maxLen = getMaxLengthForCountry(selectedCode);
- const truncatedPhone = phoneValue.slice(0, maxLen);
+ const reformatted = formatPhoneNumberAsYouType(phoneValue, selectedCode);
setCodeValue(selectedCode);
- setPhoneValue(truncatedPhone);
- updateStoredValue(selectedCode, truncatedPhone);
+ setPhoneValue(reformatted);
+ updateStoredValue(selectedCode, reformatted);
closeSheet();
};
@@ -691,12 +710,10 @@ export function QuestionPhone({
>
{isResolvingCountry ? (
) : (
<>
@@ -741,19 +758,27 @@ export function QuestionPhone({
data-bwignore="true"
data-form-type="other"
disabled={disabled}
- placeholder={question.extras.placeHolder?.replace(/^\+\d+\s*/, "")}
+ placeholder={dynamicPlaceholder}
value={phoneValue}
maxLength={getMaxLengthForCountry(codeValue)}
+ onFocus={() => {
+ setIsFocused(true);
+ }}
+ onBlur={() => {
+ setIsFocused(false);
+ setIsTouched(true);
+ }}
onChange={(event) => {
userInteractedRef.current = true;
setIsResolvingCountry(false);
setManuallySelectedGeoCode(codeValue);
- const nextPhoneValue = sanitizePhoneNumber(event.target.value);
- const maxLen = getMaxLengthForCountry(codeValue);
- const truncatedPhone = nextPhoneValue.slice(0, maxLen);
+ const formatted = formatPhoneNumberAsYouType(
+ event.target.value,
+ codeValue,
+ );
- setPhoneValue(truncatedPhone);
- updateStoredValue(codeValue, truncatedPhone);
+ setPhoneValue(formatted);
+ updateStoredValue(codeValue, formatted);
}}
dir="ltr"
className="h-full w-full border-0 bg-transparent p-0 text-left text-[15px] font-medium leading-none text-[#181818] tabular-nums outline-none placeholder:text-[#98A2B3]"
@@ -762,7 +787,8 @@ export function QuestionPhone({
{showInvalidState ? (
- Enter a valid phone number with country code.
+ {t["Enter a valid phone number with country code."] ||
+ "Enter a valid phone number with country code."}
) : null}
@@ -782,7 +808,7 @@ export function QuestionPhone({
>
diff --git a/src/components/Componentes/question-radio.tsx b/src/components/Componentes/question-radio.tsx
index 34ad9b0..826e785 100644
--- a/src/components/Componentes/question-radio.tsx
+++ b/src/components/Componentes/question-radio.tsx
@@ -25,7 +25,7 @@ export function QuestionRadio({
// Render horizontally if all options are short (e.g. Single, Divorced, Widowed)
const isShortOptions =
options.length <= 4 &&
- options.every((opt) => (opt.label || opt.value || "").length <= 15);
+ options.every((opt) => String(opt.label || opt.value || "").length <= 15);
return (
({
useI18n: vi.fn(() => ({ locale: "fa", dictionary: { Confirm: "تایید" } })),
}));
+vi.mock("@/hooks/marriage/use-profile-main", () => ({
+ useMarriageProfileQuery: vi.fn(() => ({
+ data: { id: 1, can_edit_profile: true },
+ isLoading: false,
+ })),
+}));
+
vi.mock("@/hooks/marriage/use-section-data", () => ({
applyProfilePatchResultToCache: vi.fn(),
useMarriageSectionDataQuery: vi.fn(() => ({
@@ -91,16 +98,19 @@ describe("QuestionSheet component", () => {
fireEvent.click(iranBtn);
// Trigger should now show Iran
- await waitFor(() => {
- expect(screen.getByText("ایران")).toBeDefined();
- expect(document.body.classList.contains("dropdown-open")).toBe(false);
- expect(document.body.classList.contains("question-sheet-open")).toBe(
- false,
- );
- expect(
- (document.querySelector(".app-shell") as HTMLElement).style.overflowY,
- ).toBe("");
- });
+ await waitFor(
+ () => {
+ expect(screen.getByText("ایران")).toBeDefined();
+ expect(document.body.classList.contains("dropdown-open")).toBe(false);
+ expect(document.body.classList.contains("question-sheet-open")).toBe(
+ false,
+ );
+ expect(
+ (document.querySelector(".app-shell") as HTMLElement).style.overflowY,
+ ).toBe("");
+ },
+ { timeout: 1000 },
+ );
});
it("supports multi-selection with confirm button", async () => {
diff --git a/src/components/Componentes/question-sheet.tsx b/src/components/Componentes/question-sheet.tsx
index 89926b8..9e159f4 100644
--- a/src/components/Componentes/question-sheet.tsx
+++ b/src/components/Componentes/question-sheet.tsx
@@ -2,7 +2,8 @@
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { createPortal } from "react-dom";
-import { getLanguageList, LANGUAGES_EN, LANGUAGES_FA } from "@/data/languages";
+import { COUNTRIES_EN, COUNTRIES_FA } from "@/data/countries";
+import { LANGUAGE_EN_TO_FA, LANGUAGES_EN, LANGUAGES_FA } from "@/data/languages";
import type { QuestionField } from "@/lib/schema-adapter";
import { useI18n } from "@/translations/provider";
import { Button } from "./button";
@@ -81,63 +82,143 @@ export function QuestionSheet({ question, disabled }: QuestionSheetProps) {
question.title?.includes("زبان") ||
question.ui_config?.dataset === "languages";
+ const isCountryQuestion =
+ question.id?.toLowerCase().includes("nationality") ||
+ question.id?.toLowerCase().includes("citizenship") ||
+ question.id?.toLowerCase().includes("country") ||
+ question.id?.toLowerCase().includes("birthplace") ||
+ question.id?.toLowerCase().includes("residence") ||
+ question.title?.toLowerCase().includes("nationality") ||
+ question.title?.toLowerCase().includes("citizenship") ||
+ question.title?.toLowerCase().includes("country") ||
+ question.title?.includes("ملیت") ||
+ question.title?.includes("تابعیت") ||
+ question.title?.includes("کشور") ||
+ question.title?.includes("سکونت") ||
+ question.ui_config?.dataset === "countries" ||
+ question.ui_config?.dataset === "nationalities";
+
const options = useMemo(() => {
const rawOptions = question.options || [];
- if (!isLanguageQuestion) {
- return rawOptions;
+
+ if (isLanguageQuestion) {
+ const existingByLabel = new Map(
+ rawOptions.map((opt) => [opt.label.toLowerCase().trim(), opt]),
+ );
+ const existingByVal = new Map(
+ rawOptions.map((opt) => [String(opt.value).toLowerCase().trim(), opt]),
+ );
+ const existingById = new Map(
+ rawOptions.map((opt) => [opt.id.toLowerCase().trim(), opt]),
+ );
+
+ const mergedOptions: typeof rawOptions = [];
+ const seenIds = new Set();
+
+ LANGUAGES_EN.forEach((enLang, idx) => {
+ const localizedLabel =
+ (locale === "fa" || locale === "fa-ir"
+ ? (LANGUAGE_EN_TO_FA[enLang] || LANGUAGES_FA[idx])
+ : (t as any)[enLang]) || enLang;
+ const cleanSlug = enLang
+ .toLowerCase()
+ .replace(/[^a-z0-9]+/g, "_")
+ .replace(/^_+|_+$/g, "");
+ const generatedId = `${question.id}.${cleanSlug}`;
+
+ const existing =
+ existingByLabel.get(enLang.toLowerCase()) ||
+ existingByLabel.get(localizedLabel.toLowerCase()) ||
+ existingByVal.get(cleanSlug) ||
+ existingById.get(generatedId.toLowerCase());
+
+ const finalId = existing?.id || generatedId;
+ if (seenIds.has(finalId)) return;
+ seenIds.add(finalId);
+
+ const isPersian = locale === "fa" || locale === "fa-ir";
+ const displayLabel = isPersian
+ ? (LANGUAGE_EN_TO_FA[enLang] || localizedLabel)
+ : enLang;
+
+ mergedOptions.push({
+ id: finalId,
+ value: existing?.value ?? cleanSlug,
+ label: displayLabel,
+ order: existing?.order ?? idx,
+ });
+ });
+
+ rawOptions.forEach((opt) => {
+ if (!seenIds.has(opt.id)) {
+ seenIds.add(opt.id);
+ mergedOptions.push(opt);
+ }
+ });
+
+ return mergedOptions;
}
- const existingByLabel = new Map(
- rawOptions.map((opt) => [opt.label.toLowerCase().trim(), opt]),
- );
- const existingByVal = new Map(
- rawOptions.map((opt) => [String(opt.value).toLowerCase().trim(), opt]),
- );
- const existingById = new Map(
- rawOptions.map((opt) => [opt.id.toLowerCase().trim(), opt]),
- );
+ if (isCountryQuestion) {
+ const existingByLabel = new Map(
+ rawOptions.map((opt) => [opt.label.toLowerCase().trim(), opt]),
+ );
+ const existingByVal = new Map(
+ rawOptions.map((opt) => [String(opt.value).toLowerCase().trim(), opt]),
+ );
+ const existingById = new Map(
+ rawOptions.map((opt) => [opt.id.toLowerCase().trim(), opt]),
+ );
+
+ const mergedOptions: typeof rawOptions = [];
+ const seenIds = new Set();
+
+ COUNTRIES_EN.forEach((enCountry, idx) => {
+ const localizedLabel =
+ (locale === "fa" || locale === "fa-ir"
+ ? COUNTRIES_FA[idx]
+ : (t as any)[enCountry]) || enCountry;
+ const cleanSlug = enCountry
+ .toLowerCase()
+ .replace(/[^a-z0-9]+/g, "_")
+ .replace(/^_+|_+$/g, "");
+ const generatedId = `${question.id}.${cleanSlug}`;
+
+ const existing =
+ existingByLabel.get(enCountry.toLowerCase()) ||
+ existingByLabel.get(localizedLabel.toLowerCase()) ||
+ existingByVal.get(cleanSlug) ||
+ existingById.get(generatedId.toLowerCase());
+
+ const finalId = existing?.id || generatedId;
+ if (seenIds.has(finalId)) return;
+ seenIds.add(finalId);
+
+ const isPersian = locale === "fa" || locale === "fa-ir";
+ const displayLabel = isPersian
+ ? (COUNTRIES_FA[idx] || localizedLabel)
+ : enCountry;
+
+ mergedOptions.push({
+ id: finalId,
+ value: existing?.value ?? cleanSlug,
+ label: displayLabel,
+ order: existing?.order ?? idx,
+ });
+ });
- const mergedOptions: typeof rawOptions = [];
- const seenIds = new Set();
-
- LANGUAGES_EN.forEach((enLang, idx) => {
- const localizedLabel =
- (locale === "fa" || locale === "fa-ir"
- ? LANGUAGES_FA[idx]
- : (t as any)[enLang]) || enLang;
- const cleanSlug = enLang
- .toLowerCase()
- .replace(/[^a-z0-9]+/g, "_")
- .replace(/^_+|_+$/g, "");
- const generatedId = `${question.id}.${cleanSlug}`;
-
- const existing =
- existingByLabel.get(enLang.toLowerCase()) ||
- existingByLabel.get(localizedLabel.toLowerCase()) ||
- existingByVal.get(cleanSlug) ||
- existingById.get(generatedId.toLowerCase());
-
- const finalId = existing?.id || generatedId;
- if (seenIds.has(finalId)) return;
- seenIds.add(finalId);
-
- mergedOptions.push({
- id: finalId,
- value: existing?.value ?? cleanSlug,
- label: existing?.label ?? localizedLabel,
- order: existing?.order ?? idx,
+ rawOptions.forEach((opt) => {
+ if (!seenIds.has(opt.id)) {
+ seenIds.add(opt.id);
+ mergedOptions.push(opt);
+ }
});
- });
- rawOptions.forEach((opt) => {
- if (!seenIds.has(opt.id)) {
- seenIds.add(opt.id);
- mergedOptions.push(opt);
- }
- });
+ return mergedOptions;
+ }
- return mergedOptions;
- }, [question, isLanguageQuestion, locale, t]);
+ return rawOptions;
+ }, [question, isLanguageQuestion, isCountryQuestion, locale, t]);
const COMPACT_OPTIONS_MAX = 6;
@@ -166,7 +247,14 @@ export function QuestionSheet({ question, disabled }: QuestionSheetProps) {
);
const getCleanLabel = (optId: string) => {
- const opt = options.find((o) => o.id === optId);
+ const normalized = String(optId || "").toLowerCase().trim();
+ const opt = options.find(
+ (o) =>
+ o.id === optId ||
+ o.id.toLowerCase() === normalized ||
+ String(o.value).toLowerCase() === normalized ||
+ o.id.endsWith(`.${normalized}`),
+ );
if (!opt) return optId;
return opt.label.split(" - ")[0];
};
@@ -287,7 +375,9 @@ export function QuestionSheet({ question, disabled }: QuestionSheetProps) {
ref={sheetRef}
className={[
"flex w-full flex-col overflow-hidden rounded-t-[22px] bg-white pb-[env(safe-area-inset-bottom)] shadow-[0_-10px_32px_rgba(0,0,0,0.12)] transition-transform duration-[300ms] ease-out sm:max-w-[375px] animate-in slide-in-from-bottom",
- isCompact ? "h-auto max-h-[82svh]" : "h-[82svh]",
+ isCompact && !showSearch
+ ? "h-auto max-h-[82svh]"
+ : "h-[82svh] min-h-[82svh] max-h-[82svh]",
isClosing ? "translate-y-full" : "translate-y-0",
].join(" ")}
>
diff --git a/src/components/Componentes/question-snap-list.tsx b/src/components/Componentes/question-snap-list.tsx
index 53d5a75..b73a6e5 100644
--- a/src/components/Componentes/question-snap-list.tsx
+++ b/src/components/Componentes/question-snap-list.tsx
@@ -14,20 +14,20 @@ import {
useQuestionViewportCoordinator,
} from "./question-viewport-coordinator";
-const WHEEL_GESTURE_IDLE_MS = 320;
-const BACKGROUND_DRAG_SLOP = 10;
-const OPTION_CARD_DRAG_SLOP = 16;
-const FAST_FLICK_MIN_DISTANCE = 36;
-const DRAG_COMMIT_RATIO = 0.3;
-const DRAG_FLICK_VELOCITY = 0.4;
-const SNAP_ANIMATION_MS = 340;
-const SNAP_EASE = "cubic-bezier(0.22, 1, 0.36, 1)";
-const RUBBER_BAND_RESISTANCE = 0.4;
+const WHEEL_GESTURE_IDLE_MS = 280;
+const BACKGROUND_DRAG_SLOP = 8;
+const OPTION_CARD_DRAG_SLOP = 14;
+const TEXT_INPUT_DRAG_SLOP = 16;
+const FAST_FLICK_MIN_DISTANCE = 24;
+const DRAG_COMMIT_RATIO = 0.15;
+const DRAG_FLICK_VELOCITY = 0.22;
+const SNAP_ANIMATION_MS = 240;
+const SNAP_EASE = "cubic-bezier(0.16, 1, 0.3, 1)";
+const RUBBER_BAND_RESISTANCE = 0.35;
/**
- * Text inputs and direct embedded controls that MUST be completely isolated
- * from the snap/drag gesture recognizer so first-tap focus, virtual keyboard,
- * and caret manipulation work 100% reliably.
+ * Text inputs and direct embedded controls that MUST allow clean 1st-tap focus
+ * while still permitting intentional vertical swipe when dragged beyond slop.
*/
const TEXT_INPUT_SELECTOR = [
'input:not([type="radio"]):not([type="checkbox"])',
@@ -58,6 +58,7 @@ const OPTION_CARD_SELECTOR = [
type SnapDragState = {
pointerDown: boolean;
hardIgnored: boolean;
+ isTextInput: boolean;
isOptionCard: boolean;
engaged: boolean;
didDrag: boolean;
@@ -113,6 +114,7 @@ export function QuestionSnapList({
const dragRef = useRef({
pointerDown: false,
hardIgnored: false,
+ isTextInput: false,
isOptionCard: false,
engaged: false,
didDrag: false,
@@ -397,14 +399,19 @@ export function QuestionSnapList({
const movedPastFlick = Math.abs(drag.offset) >= FAST_FLICK_MIN_DISTANCE;
let direction: 0 | 1 | -1 = 0;
+ let reason = "none";
if (!cancelled && (draggedFar || flicked || movedPastFlick)) {
if ((drag.offset > 0 || drag.velocity > 0) && canNext) {
direction = 1;
+ reason = flicked ? "flick_next" : (draggedFar ? "drag_far_next" : "moved_past_flick_next");
} else if ((drag.offset < 0 || drag.velocity < 0) && canPrev) {
direction = -1;
+ reason = flicked ? "flick_prev" : (draggedFar ? "drag_far_prev" : "moved_past_flick_prev");
}
}
+ console.log(`[Snap] FinishDrag -> dir: ${direction} (${reason}) | offset: ${drag.offset.toFixed(0)}px | vel: ${drag.velocity.toFixed(2)} | threshold: ${(drag.height * DRAG_COMMIT_RATIO).toFixed(0)}px`);
+
if (direction === 0) {
snapPanelsTo(0);
return;
@@ -491,6 +498,8 @@ export function QuestionSnapList({
drag.startTime = performance.now();
drag.lastMoveTime = drag.startTime;
touchStartYRef.current = drag.startY;
+
+ console.log(`[Snap] TouchStart at (${drag.startX.toFixed(0)}, ${drag.startY.toFixed(0)}) on <${target?.tagName?.toLowerCase()}> (isOption: ${isOptionCard})`);
};
const onTouchMove = (event: TouchEvent) => {
@@ -526,7 +535,7 @@ export function QuestionSnapList({
}
// Must be primarily vertical gesture (avoid hijacking horizontal swipe / scroll)
- if (absY < absX * 1.1) {
+ if (absY < absX * 0.9) {
return;
}
@@ -535,8 +544,13 @@ export function QuestionSnapList({
drag.didDrag = true;
suppressNextClickRef.current = true;
+ if (document.activeElement instanceof HTMLElement) {
+ document.activeElement.blur();
+ }
+
const elapsed = Math.max(1, now - drag.startTime);
drag.velocity = (drag.startY - currentY) / elapsed;
+ console.log(`[Snap] Drag Engaged: deltaY=${deltaY.toFixed(0)}px, slop=${slop}px`);
}
// Non-passive preventDefault stops native pan and guarantees gesture ownership
diff --git a/src/components/Componentes/question-viewport-coordinator.ts b/src/components/Componentes/question-viewport-coordinator.ts
index 111d21b..e63b281 100644
--- a/src/components/Componentes/question-viewport-coordinator.ts
+++ b/src/components/Componentes/question-viewport-coordinator.ts
@@ -3,7 +3,7 @@
import { useEffect } from "react";
import { viewPaddingsBridge } from "@/lib/view-paddings";
-const KEYBOARD_THRESHOLD = 80;
+const KEYBOARD_THRESHOLD = 20;
const QUESTION_GAP = 16;
const LIFT_VARIABLE = "--question-lift-y";
@@ -82,19 +82,31 @@ export function isActiveQuestionKeyboardInput(
);
}
+function getDefaultKeyboardHeight(): number {
+ if (typeof window !== "undefined" && window.innerHeight > 0) {
+ // 37.5% of screen height matches standard mobile soft keyboards (e.g. 287px on 765px screen, 316px on 844px screen)
+ return Math.round(window.innerHeight * 0.375);
+ }
+ return 285;
+}
+
function setLift(nextLift: number) {
- currentLift = Math.max(0, nextLift);
+ const prevLift = currentLift;
+ currentLift = Math.max(0, Math.round(nextLift));
document.documentElement.style.setProperty(LIFT_VARIABLE, `${currentLift}px`);
- document.body.classList.toggle(
- "question-keyboard-open",
- currentLift > 0 && keyboardVisible && activeQuestionInput !== null,
- );
+ const isOpen = currentLift > 0 && keyboardVisible && activeQuestionInput !== null;
+ document.body.classList.toggle("question-keyboard-open", isOpen);
+ console.log(`[Coord] setLift: ${prevLift}px -> ${currentLift}px | visible: ${keyboardVisible} | class: ${isOpen}`);
}
function getKeyboardTop() {
if (!keyboardVisible || !activeQuestionInput) return null;
- const flutterTop = window.innerHeight - keyboardHeight;
+ const fullKeyboardHeight =
+ lastKeyboardHeight > 200 ? lastKeyboardHeight : getDefaultKeyboardHeight();
+ const targetHeight = Math.max(keyboardHeight, fullKeyboardHeight);
+
+ const flutterTop = window.innerHeight - targetHeight;
const viewport = window.visualViewport;
const viewportBottom = viewport
? viewport.offsetTop + viewport.height
@@ -103,11 +115,29 @@ function getKeyboardTop() {
return Math.min(flutterTop, viewportBottom);
}
+function getBaseGeometry(content: HTMLElement) {
+ const item = content.closest(".question-snap-item");
+ if (item && typeof item.getBoundingClientRect === "function" && content.offsetHeight > 0) {
+ const itemRect = item.getBoundingClientRect();
+ const top = itemRect.top + content.offsetTop;
+ return {
+ baseTop: top,
+ baseBottom: top + content.offsetHeight,
+ };
+ }
+ const rect = content.getBoundingClientRect();
+ return {
+ baseTop: rect.top + currentLift,
+ baseBottom: rect.bottom + currentLift,
+ };
+}
+
function updateLift() {
const content = document.querySelector(
'.question-snap-item[aria-current="step"] .question-snap-content',
);
if (!content) {
+ console.log('[Coord] updateLift: no content element found -> lift 0');
setLift(0);
return;
}
@@ -117,29 +147,28 @@ function updateLift() {
(value): value is number => value !== null,
);
if (visibleBottomCandidates.length === 0) {
+ console.log('[Coord] updateLift: no visibleBottom candidates -> lift 0');
setLift(0);
return;
}
- const rect = content.getBoundingClientRect();
const snapList = content.closest(".question-snap-list");
- const baseTop = rect.top + currentLift;
- const baseBottom = rect.bottom + currentLift;
+ const { baseTop, baseBottom } = getBaseGeometry(content);
const safeTop = Number.parseFloat(
getComputedStyle(document.documentElement).getPropertyValue("--safe-top"),
);
- setLift(
- computeQuestionLift({
- baseTop,
- baseBottom,
- visibleTop: Math.max(
- Number.isFinite(safeTop) ? safeTop : 0,
- snapList?.getBoundingClientRect().top ?? 0,
- ),
- visibleBottom: Math.min(...visibleBottomCandidates),
- }),
- );
+ const computed = computeQuestionLift({
+ baseTop,
+ baseBottom,
+ visibleTop: Math.max(
+ Number.isFinite(safeTop) ? safeTop : 0,
+ snapList?.getBoundingClientRect().top ?? 0,
+ ),
+ visibleBottom: Math.min(...visibleBottomCandidates),
+ });
+ console.log(`[Coord] computed lift: ${computed}px (baseTop: ${baseTop.toFixed(0)}, baseBottom: ${baseBottom.toFixed(0)}, kTop: ${keyboardTop?.toFixed(0)})`);
+ setLift(computed);
}
function scheduleLiftUpdate() {
@@ -151,13 +180,34 @@ function scheduleLiftUpdate() {
}
export function updateQuestionKeyboardHeight(height: number) {
- keyboardHeight = Math.max(0, height);
- keyboardVisible = keyboardHeight > KEYBOARD_THRESHOLD;
- if (keyboardVisible) {
- lastKeyboardHeight = keyboardHeight;
+ const nextHeight = Math.max(0, height);
+ if (Math.abs(nextHeight - keyboardHeight) < 1) return;
+
+ console.log(`[Coord] updateHeight: ${keyboardHeight} -> ${nextHeight}`);
+
+ if (nextHeight > 0) {
+ keyboardVisible = true;
+ const defaultKHeight = getDefaultKeyboardHeight();
+ const expectedHeight =
+ lastKeyboardHeight > 200 ? lastKeyboardHeight : defaultKHeight;
+
+ if (activeQuestionInput && nextHeight < expectedHeight - 15) {
+ // Keyboard is in the middle of opening animation frames (e.g. Samsung 8.7px, 182px, 205px, 246px, 262px, 273px)
+ // Keep keyboardHeight at expectedHeight so lift doesn't stutter/jitter during intermediate animation frames.
+ keyboardHeight = expectedHeight;
+ } else {
+ // Settled at or above target height
+ lastKeyboardHeight = Math.max(lastKeyboardHeight, nextHeight);
+ keyboardHeight = nextHeight;
+ }
+
if (isActiveQuestionKeyboardInput(document.activeElement)) {
activeQuestionInput = document.activeElement;
}
+ } else {
+ // nextHeight === 0: keyboard was closed
+ keyboardHeight = 0;
+ keyboardVisible = false;
}
scheduleLiftUpdate();
}
@@ -176,13 +226,16 @@ export function resetQuestionKeyboardState() {
window.cancelAnimationFrame(geometryFrame);
geometryFrame = null;
}
- if (isActiveQuestionKeyboardInput(document.activeElement)) {
+ if (
+ typeof document !== "undefined" &&
+ document.activeElement instanceof HTMLElement &&
+ isActiveQuestionKeyboardInput(document.activeElement)
+ ) {
document.activeElement.blur();
}
- activeQuestionInput = null;
keyboardVisible = false;
keyboardHeight = 0;
- compactSheetTop = null;
+ activeQuestionInput = null;
setLift(0);
}
@@ -193,25 +246,29 @@ export function useQuestionViewportCoordinator() {
const handleFocusIn = (event: FocusEvent) => {
if (!isActiveQuestionKeyboardInput(event.target)) return;
activeQuestionInput = event.target;
-
- // Start with the last known keyboard geometry so lifting begins in the
- // same frame as focus; Flutter then replaces it with the exact height.
- if (!keyboardVisible && lastKeyboardHeight > KEYBOARD_THRESHOLD) {
- keyboardHeight = lastKeyboardHeight;
- keyboardVisible = true;
- }
+ const expectedHeight =
+ lastKeyboardHeight > 200 ? lastKeyboardHeight : getDefaultKeyboardHeight();
+ keyboardHeight = expectedHeight;
+ keyboardVisible = true;
+ console.log('[Coord] focusin on input:', (event.target as HTMLElement).tagName, 'targetKHeight:', expectedHeight);
scheduleLiftUpdate();
};
const handleFocusOut = () => {
+ console.log('[Coord] focusout event');
window.setTimeout(() => {
activeQuestionInput = isActiveQuestionKeyboardInput(
document.activeElement,
)
? document.activeElement
: null;
- if (!activeQuestionInput) scheduleLiftUpdate();
- }, 0);
+ console.log('[Coord] focusout settled, active:', activeQuestionInput?.tagName ?? 'none');
+ if (!activeQuestionInput) {
+ keyboardVisible = false;
+ keyboardHeight = 0;
+ scheduleLiftUpdate();
+ }
+ }, 50);
};
const unsubscribeConfig = viewPaddingsBridge.subscribeConfig((config) => {
@@ -222,21 +279,21 @@ export function useQuestionViewportCoordinator() {
const viewportHeight =
window.visualViewport?.height ?? window.innerHeight;
const reduction = closedViewportHeight - viewportHeight;
+ console.log(`[Coord] visualViewport resize: vpHeight=${viewportHeight}, reduction=${reduction}`);
if (reduction > KEYBOARD_THRESHOLD) {
if (isActiveQuestionKeyboardInput(document.activeElement)) {
activeQuestionInput = document.activeElement;
keyboardVisible = true;
- keyboardHeight = Math.max(keyboardHeight, reduction);
- lastKeyboardHeight = keyboardHeight;
- scheduleLiftUpdate();
+ if (reduction > 250) {
+ lastKeyboardHeight = Math.max(lastKeyboardHeight, reduction);
+ keyboardHeight = reduction;
+ }
}
- } else if (keyboardHeight <= KEYBOARD_THRESHOLD) {
- keyboardVisible = false;
- keyboardHeight = 0;
+ } else if (reduction <= KEYBOARD_THRESHOLD && !keyboardVisible && !activeQuestionInput) {
closedViewportHeight = viewportHeight;
- scheduleLiftUpdate();
}
+ scheduleLiftUpdate();
};
document.addEventListener("focusin", handleFocusIn);
diff --git a/src/components/Componentes/section-icon.tsx b/src/components/Componentes/section-icon.tsx
new file mode 100644
index 0000000..0058642
--- /dev/null
+++ b/src/components/Componentes/section-icon.tsx
@@ -0,0 +1,63 @@
+"use client";
+
+import type { SVGProps } from "react";
+
+export type SectionIconName =
+ | "profile"
+ | "contact"
+ | "health"
+ | "education"
+ | "family"
+ | "marriage"
+ | "family_marital"
+ | "lifestyle"
+ | "criteria"
+ | "verification"
+ | "personality"
+ | "glasser"
+ | "details"
+ | "checklist"
+;
+
+const SECTION_ICON_DATA_URIS: Record = {
+ "profile": "data:image/webp;base64,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",
+ "contact": "data:image/webp;base64,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",
+ "health": "data:image/webp;base64,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",
+ "education": "data:image/webp;base64,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",
+ "family": "data:image/webp;base64,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",
+ "marriage": "data:image/webp;base64,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",
+ "family_marital": "data:image/webp;base64,UklGRvwOAABXRUJQVlA4WAoAAAAQAAAAXwAAXwAAQUxQSC4FAAAB8HZrkyFt27ZPaI2IOmzbtm3btm3bPk7btm3b9nnYNrfOyFg/VHV1dVVGxARguFpVioKrAIBog4gUR4E1jjxyLcAANQBiUhTFbj+fIUc/3xVmgC29KAApiOJskimRvBBY9W3/fu6Jn58MSDEMOzJSkOHBM499gY3fnEekECKz/JHOxgiSKUf4iF9VLYRhB2aOjxxsHPEwWBkqXB+pRcsU34OWwfAxdhJ8dAFIERRfo3fzzBK9ElWrq8hEhg8ydfP0Yr0RM0Vbq1RaVbi8m8y/zYJ+mgHAXMtuvv+RRx6y68ZLGgCYtFBsw+gi8d2wHogBmGenm35w18tsfv5vnz57LQAmTVrZLH9m7iBz2z4YgK3f8wDrkT3nCJIc/ebsJQETiCmA2d7JNJnze1BMWxWzHv0rkpE8B5sjeyL5zJtWBCoAK53+qftmOHnkFzbUqRlw0F/J8ODE4U4+e828mP+E77/Mbp03ASbTqbDil0nP7Dic/Nsb7yfpnrtgPPaWZQCdgigOf5KeOcVwku7B7p+9ajZYZyK4iUycck6ZU4xE/mFzqHQjYh+nBwceztfPh0gXovo5puDwM/leg3RgeDdHLGIkfmlWkYkMZzOx4+gbOeJXKpEJDFsmj66GmPgJmLQSnfs/zBxgkNEJR7wOVSvDbUwsaaTYA9pCZc1XPYrCzAcXUm2BT9NZ2MT3w8YY1p6JKA3dN4eNeycT+x3RVbTiD6FNWOhJRs+Yu2qfuR206Rg6+x59cH5p3CcjlYjx2mrQ2jz3Mfeun86rUNW2SMFC/U5EAJzFXKbgzMpQAO+jl4nOo1AB+BlzoRLfCgPwH0ahnN+DAniqJzGAzP/NDgGe60mfY0zw6cXK1HZmJSjwTMG4Vu1/xfsVc7Fizdrn6EOJmFLw5WVrlw9n6pmPLAgBdo4olPPvlQBY5DFGqb4OA4Cvh5cp8camk1mozD2blnyRUYhoF3x+UQgAw2eZCjGhxzehaNgmcimiHU9EVYPiu/ReRA9aB19YSrXBsFX0ItjvxPcATTB8jN6Dvgfv+/DG0AaRRR6JXJr662fCajDsyxTFiRTcD1ZDhZs5Kg7p8f+5RGpi+BxTeZi5K6wG0dm+xlF5UpyFqgGCWb/MFKXJPGQcVOxdpBcg2gRfWxE6BiI4/WWmXBKP70LRUgzr/5xMuaPIPQm2DOeOsDaAQU+7j8weE0VOZI5etHZ+AYoJFVjwsntIevIcYz05ydG/SO9X5l3z2ESAAXMf+rmnOKn/9db1ZrnMmaJPEY/uC5sMYgAW3+8N37v7hVx7/fHfffyM9QQAdvgH6TGFmIRBngqbDBBTAJh9ifU322LTTVaeH/VKxTDXtc+RKXfWYc6xO7QDAGKVoqWZom7Ayu9+mcyeox/MfHIJ6aYuoo0iGC8VsNJNd5GkJ/ecp8bEd6LnasCc+77nP8GeRry4Us8ANQCzr3P0rZ/6wV//+uLUmHl57wAxQ/NcDzJPLX40AACiVplh3WBMK/jYMBoVK742PTIGJGJ/y3l6HBAM53OmKXJXwaeGJDrHL5jc3RM7z/zdkCBY5FtsfiB15HzToCDATrd89jNvP2vzxR5i7iRGmw0LIhj7HqYunN/D4K1ezSIbRI4OErcZ3ljFBziaLPG90FKILnAX0yQj/n4ulVJAsc5jHEWbnPiHRSEop2Ldf5MpRy3cyS8tBEVJDQu/c4ZkTokk7zoJUJRVgbXv+MdrJPn0d0+YByIorRhQrbr/ccdutQgAQ4nV0KwmKLRoVVUmaAlWUDggqAkAAPAoAJ0BKmAAYAA+TSCMRCKiIReJ5vAoBMS2AGaCAEAz6DzMbA/fvIr1DRTe53+N61f8n6l/MF52fmc/Zb1av+R6tv7L6hv9a6jP0LfLh9jX+x/8/9zvau6gDhO/6b9Ffkr/tPCvzMcXuJB9N4DzBrLPez6216NxoNAT+Xf2b/sfcB8nOfp6b9gn+bf1X/q+uB1GX7COeGJjVDUg3rWVKaavmwdPxkwWjTomOr5Uy56QW150AJoYrpYxPcWZFIT8DHv/616ufYOoeJ8YbA4SeHryuhOtmfJrVZA6rnCkphvLoGMQ4Qm08g1cWi52MYC+Vk31NQTE5UKlydOHc4kGu9m7Q7UhyAJyCjl87o4khQP/nfAgWwPx8sQr+OA16kJ319FC9GHovhCrKraZRQd6php+F0Nt2egkwVlPgCDbX61HvoztD/kyoEx+Kpbzj+WEAAD+/Qbsv/A+vESv/L/LCxGREMmQsxvpHJ3+iyjTcXn3kgH0FO4ePJr8s9JLGxso46oL3JdOBS6gI+ET7k6NHULK9FUMyohUMeXyED2gUp9FvWceQ8BVUXaJQic38YrMH7m4PH0ZCuaqAPIsUvlrve4wW/Qyr/oePzq86YEONihU+UFzTaUGujUpir4YHKNvTspkIEV8zTVnx87qfT5SDRtkVl7Uoao0Hqnv7VS/ePk+ysDKOXLfoD7J6NeTDuFodqJEkZWgPPB/7CGhhUwn15laRPwGBGTAzd2ymdGRdoyjxRj/hU+kURnIyZ+Wi1PesD2LLDj0ZbpnBuTA5+yKttaFv1vMBD5f4YvXYf/p2zwr5jXaqpc3svgjlpUpW/PTmWXl4W/g6Z6HxwK08ioQB7C0BNM3KUB+gf83YJa0/8hs+VCETlBbGPfCFFy8JtkrcANHlH9hOYqUrROEslMPPQqNoP+GL0JEr3CczsOZ7siw3R2ZiZWGnoEmJ67uyITNUe/gGSHZSOcF5FfSsd7fAmRfp8A3vSp4mbwi6WG+F1auY8IXUGkYzSKpVu/PxofEo8CHdwkb3ZxyOYIfFjHgOIFY/0hieJnX2/e1m3BGaImXmtF5RIB0Fw/9Vt8kDwwETujV58dTMz1LsJ8k+ZZSAayA8wxGw2yu9Q4ksY8XmtCIVE+AatklFHzmA+YghoDtvmjyDL3ov5ya/1QEgHJgKdzCi+HGqPLqZN6jbcCpXDK56C+sR+vIONXgbyhPWf+ozEP2MhQfQMChuHtQaiiFbnyc0FeK/eHVC1fpHR96oFpYdPEjoWwaSi5gvYujjpD0NfYWANLcSHWsl3lWuqjovwysrkJXakLzYsu4uu5AsSUt/m28BJ99pxhESbkWIealbgv3b4+7/ytVNE24N/vnwna7gSOxceZG01Sqvsap4u0CWda/gjbq3AbuGPSFbApauQ2QmIOAfc2Hxt+5rpQqDb54/wYzcztWW8AyV3GSsBecwQdxUpgM1PX26yToSGS4wz3WQrr5+w9cTrqO+5y3tEfYcmRd1RNcIO9nrte8jJijYlLF2ij58MHvuSl0NwBeN8jE0DaYqsrL2oW3zjj2tWbRPEi+IPGGnoLwwqKZ6/L3tU2+YL7b2C1NoFHg2X2TSLLRLK6e07QxoPjhYzwT336eF285XpKU6xwUaMSHlt7cbvQTC/fhk/GHuC5IX7cG/LMeh4OJZIAC2r2sLTFGSMzvY+7jgsfm//bWm+74kFFj7TD3i8OSGn5aleFeHIZ22DKsxmfkVtKxKZi3dR9+MxxkFxAkQJfO0sV3EPa3gih0xgO0l1z67G2OUiFLKklr8v70XpSBidEKn0O70SsSPp4O28689BUJVzxNg0+i6ifxc0I3OgPc5e//SURvbd0ZRge+MUPODCQHNy0jdnE5WGzv6Qzv8SesBRBBlPXtXTApEWWit1jgvOpi9ujhxseBf4zFazNr23g+q6wMVx/qlzDpzI1FBFjY3JL6ysZTy39oiAPtFJ6grlo4ial9jfkLvY2d8UtDnkb3cYCfkXZJMePeRRMxkr5epyl1mC8BVj5epL2J/+7qnFuDE+rh23xmaJYTP50GHfzOpFhC04yHtjdVOs8c7c4Am6bRt0KpYHu9uuuPy8Va43EaNLBEtkpwjDiVjRpeqTJIH+aRtmGG7VrNG1zWvro/zg7x2T5bGVRBbdw1KCr9jR1D75Qmjf8cKvNYqIDw9LJv9tjAAvDB0TCizoaJfrOV+rxPX3TakySRlAfSctc/hq1uCNJgCO4+Ocy518cNxSkJdwHLSu+yl5YXDR5Nql+lrsycgABKlQSLaoIaQH0pcRsv25QM+iFezVV3JHX46F9XWdQ1rMnCc4EIrCmW8Hu7XHaDIpDncoecF3+uX6fa2w81sabp8M6KPhYG+t19uJg+Ne0KECgR68NJqXHr/LgySwQnKmNeBNw0nF215l+3/+RbJSNkkvE2s1uybEC9v0qoTCHnKGpa1nemhC3xu3sVyNcM9ZE+dYQX+f1yxeW03X5thWxqKrgnDgwFA3F/h57NcLisZQOyVH5jVmwcWmsA4+f5nFv+mbwtDQiGZqlC5wj+wvJ3Ys2e+vSC6RYOqcFd5DVPp4+dk8QhrQyBV3laf/t9ZjDmuXfiKCVeXc1qsH7op3oMldied4VYBdojnO3dzq/oygM6caXEOUjB/uI6DHzPUn8svA746Ld827RLzrLyrsQTZ5PxDzssroSJn7bcDxRCDkt4FpBWOKjWyPRqFhQggc1Aaff0MM4GzMtLrBX2HFIh20a3Fex2+Upw9+IHK/V7TJ38ZPf/OrWOqvlnv3981ayJBzCHrN6UgR8/Xp8bbdF2Mvf8X+ksn9FOT2njzZfldZyShIxCQcoogdB8NLxaC+eIySZKRlmv8N7jNj+RcWlWBtdQjUAebCBghD3NcSQ5v/2U8d9Miqfmi6Tx4X76/tIL+Rze44WHEkZ3fIOrUgWHuvFFgUPpFJICZRO1ARBARqcHzkOObhLWrj0zuCZmrnYU4gAAAGluGb4601ED/d++RM5xpq/4gxTsTuxdHmqbuLToWzUbz9+jX0uY6O6Mn0/RZYTfOEdUF4f5rZ6hdYZQrD6yUBUcL7faRyE5fUO1SeGfqW29s1sqha5xb528ujwsJzMbBFa1l4mcFMEFMuOl+vanAdhRLeiIbOFcNA3jZC93nQ3PBUWNCg3/rHAgPH7nvAj19lSiw1NB45tMKaYpOblkOnbEeVh7/aHrrMRVAVKBSBRgLFjwMfZCFiWeYujzNYpeR852vpQeItSsTw9Lh520A7OOhhICwfs1FWFJYUYmO7ULytvGrAAAAA==",
+ "lifestyle": "data:image/webp;base64,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",
+ "criteria": "data:image/webp;base64,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",
+ "verification": "data:image/webp;base64,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",
+ "personality": "data:image/webp;base64,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",
+ "glasser": "data:image/webp;base64,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",
+ "details": "data:image/webp;base64,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",
+ "checklist": "data:image/webp;base64,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"
+};
+
+export function SectionIcon({
+ name,
+ className = "size-[30px]",
+ ...props
+}: {
+ name: SectionIconName;
+ className?: string;
+} & SVGProps) {
+ const dataUri = SECTION_ICON_DATA_URIS[name];
+ if (!dataUri) return null;
+
+ return (
+
+ );
+}
+
+export default SectionIcon;
diff --git a/src/components/Componentes/slider-page.test.tsx b/src/components/Componentes/slider-page.test.tsx
index b0f678c..3077a8c 100644
--- a/src/components/Componentes/slider-page.test.tsx
+++ b/src/components/Componentes/slider-page.test.tsx
@@ -32,6 +32,7 @@ vi.mock('@/hooks/marriage/query-keys', () => ({
vi.mock('next/navigation', () => ({
useRouter: () => ({
replace: mockReplace,
+ prefetch: vi.fn(),
back: vi.fn(),
}),
}));
diff --git a/src/components/Componentes/slider-page.tsx b/src/components/Componentes/slider-page.tsx
index 7cc1677..60b30c0 100644
--- a/src/components/Componentes/slider-page.tsx
+++ b/src/components/Componentes/slider-page.tsx
@@ -1,7 +1,6 @@
"use client";
import { useRouter } from "next/navigation";
-import type { TouchEvent } from "react";
import { useEffect, useState } from "react";
import { useQueryClient } from "@tanstack/react-query";
import { useUpdateMarriageProfileBasicMutation } from "@/hooks/marriage/use-profile-basic";
@@ -21,10 +20,13 @@ import { SliderSlideThree } from "./slider-slide-three";
import { SliderSlideTwo } from "./slider-slide-two";
const FINAL_SLIDE_COUNT = 5;
-const SWIPE_THRESHOLD = 40;
const SLIDER_ANSWERS_STORAGE_KEY = "marriage-slider-answers";
-export default function SliderPage() {
+export type SliderPageProps = {
+ onClose?: () => void;
+};
+
+export default function SliderPage({ onClose }: SliderPageProps = {}) {
const router = useRouter();
const { locale } = useI18n();
const [activeSlide, setActiveSlide] = useState(0);
@@ -32,7 +34,6 @@ export default function SliderPage() {
const [selectedRegistration, setSelectedRegistration] =
useState("self");
const [hasReadRules, setHasReadRules] = useState(false);
- const [touchStartX, setTouchStartX] = useState(null);
const updateProfileBasicMutation = useUpdateMarriageProfileBasicMutation();
const queryClient = useQueryClient();
const [submitError, setSubmitError] = useState(null);
@@ -109,42 +110,17 @@ export default function SliderPage() {
window.localStorage.removeItem(SLIDER_ANSWERS_STORAGE_KEY);
}
- router.replace(localizePath(getSubmitPath(freshProfile), locale));
+ const targetPath = getSubmitPath(freshProfile);
+ const localizedTarget = localizePath(targetPath, locale);
+ router.prefetch?.(localizedTarget);
+ router.replace(localizedTarget);
} catch (error) {
console.error("Failed to complete onboarding:", error);
setSubmitError(t["Failed to update profile basic details. Please try again."] || "Failed to update profile basic details. Please try again.");
- } finally {
setIsSubmitting(false);
}
};
- const handleTouchStart = (event: TouchEvent) => {
- setTouchStartX(event.touches[0]?.clientX ?? null);
- };
-
- const handleTouchEnd = (event: TouchEvent) => {
- if (touchStartX === null) {
- return;
- }
-
- const touchEndX = event.changedTouches[0]?.clientX ?? touchStartX;
- const deltaX = touchStartX - touchEndX;
-
- if (Math.abs(deltaX) >= SWIPE_THRESHOLD) {
- if (deltaX > 0) {
- if (activeSlide === 0 && !hasReadRules) {
- setTouchStartX(null);
- return;
- }
- goToNextSlide();
- } else {
- goToPreviousSlide();
- }
- }
-
- setTouchStartX(null);
- };
-
return (
@@ -153,6 +129,10 @@ export default function SliderPage() {
icon="back"
iconLabel="Close slider"
onClick={() => {
+ if (activeSlide > 0) {
+ goToPreviousSlide();
+ return;
+ }
if (typeof window !== "undefined") {
try {
window.sessionStorage.setItem("skip_intro_redirect", "true");
@@ -160,7 +140,11 @@ export default function SliderPage() {
console.warn("sessionStorage is not accessible:", e);
}
}
- router.back();
+ if (onClose) {
+ onClose();
+ } else {
+ router.back();
+ }
}}
/>
-
+
- Back
+ {t["Back"] || "Back"}
- Next
+ {t["Next"] || "Next"}
);
@@ -285,6 +266,7 @@ function SliderFinalActions({
onBack,
onFinish,
}: SliderFinalActionsProps) {
+ const { dictionary: t } = useI18n();
return (
- Back
+ {t["Back"] || "Back"}
- Finish
+ {t["Finish"] || "Finish"}
);
diff --git a/src/components/Componentes/slider-slide-two.tsx b/src/components/Componentes/slider-slide-two.tsx
index 5b117f5..9d08344 100644
--- a/src/components/Componentes/slider-slide-two.tsx
+++ b/src/components/Componentes/slider-slide-two.tsx
@@ -3,6 +3,7 @@
import Image from "next/image";
import { useState } from "react";
import { useMarriageConfigQuery } from "@/hooks/marriage/use-marriage-config";
+import { useI18n } from "@/translations/provider";
import { SliderHeader } from "./slider-header";
import type { SliderSlideProps } from "./slider-slide";
import VideoPlayer from "./video-player";
@@ -11,6 +12,7 @@ import NetworkImage from "./network-image";
export function SliderSlideTwo({ index }: SliderSlideProps) {
const [isPlayerOpen, setIsPlayerOpen] = useState(false);
const { data: config } = useMarriageConfigQuery();
+ const { dictionary: t } = useI18n();
const thumbnailSrc =
config?.video_step_2_thumbnail_url ||
@@ -24,13 +26,16 @@ export function SliderSlideTwo({ index }: SliderSlideProps) {
aria-label={`Slide ${index + 1}`}
className="flex h-full min-h-0 w-full shrink-0 flex-col"
>
-
-
+
+
+
+
+
setIsPlayerOpen(true)}
>
-
+
-
+
+
- Dr. Hasti Masoudi
+ {t["Dr. Hasti Masoudi"] || "Dr. Hasti Masoudi"}
-
- We have come together with the goal of creating a secure and
- confidential path for "permanent marriage" among Muslims
+
+ {t[
+ 'We have come together with the goal of creating a secure and confidential path for "permanent marriage" among Muslims'
+ ] ||
+ 'We have come together with the goal of creating a secure and confidential path for "permanent marriage" among Muslims'}
diff --git a/src/components/Componentes/ui-config.test.tsx b/src/components/Componentes/ui-config.test.tsx
index 0ba597f..ff72739 100644
--- a/src/components/Componentes/ui-config.test.tsx
+++ b/src/components/Componentes/ui-config.test.tsx
@@ -78,6 +78,49 @@ describe("UI Config based behavior", () => {
expect(screen.queryByText("خودکار")).toBeNull();
});
+ it("should auto-detect and display city and country when ui_config.enable_geoip is true", async () => {
+ const qWithGeo = {
+ id: "q_residence",
+ title: "Current Residence",
+ type: "birthplace",
+ order: 1,
+ required: true,
+ isVisible: true,
+ description: "",
+ tooltip: "",
+ extras: {},
+ options: [],
+ ui_config: { enable_geoip: true },
+ } as any;
+
+ const { setStoredUserGeoRegion } = await import("@/lib/geo-region");
+ const { marriageQueryKeys } = await import("@/hooks/marriage/query-keys");
+ setStoredUserGeoRegion({
+ city: "Tehran",
+ country: "Iran",
+ countryCode: "IR",
+ phoneCode: "+98",
+ });
+
+ queryClient.setQueryData(marriageQueryKeys.sectionData("test-geoip"), {
+ status: "success",
+ data: [],
+ version: 1,
+ });
+
+ render(
+
+
+
+
+ ,
+ );
+
+ await waitFor(() => {
+ expect(screen.getByText(/Tehran.*(Iran|ایران)/i)).toBeDefined();
+ });
+ });
+
it("should trigger currency behavior only when ui_config.currency_enabled is true", () => {
// Title is random, but currency_enabled is true
const qWithCurrency = {
diff --git a/src/data/countries.ts b/src/data/countries.ts
index c429779..fa74570 100644
--- a/src/data/countries.ts
+++ b/src/data/countries.ts
@@ -399,3 +399,52 @@ export function getCountryList(locale: string): string[] {
}
return COUNTRIES_EN;
}
+
+export function resolveCountryName(
+ countryOrCode: string | undefined | null,
+ locale: string = "fa",
+): string {
+ if (!countryOrCode) return "";
+ const trimmed = countryOrCode.trim();
+ if (!trimmed) return "";
+
+ const isFa = String(locale || "en").toLowerCase().startsWith("fa");
+
+ // 1. If 2-letter ISO code (e.g. "DE", "IR", "US", "TJ")
+ if (trimmed.length === 2 && /^[a-zA-Z]{2}$/.test(trimmed)) {
+ try {
+ const displayNames = new Intl.DisplayNames([isFa ? "fa" : "en"], {
+ type: "region",
+ });
+ const name = displayNames.of(trimmed.toUpperCase());
+ if (name) return name;
+ } catch {}
+ }
+
+ // 2. If it matches an English country name in COUNTRIES_EN
+ const enIndex = COUNTRIES_EN.findIndex(
+ (c) => c.toLowerCase() === trimmed.toLowerCase(),
+ );
+ if (enIndex !== -1) {
+ return isFa ? COUNTRIES_FA[enIndex] || COUNTRIES_EN[enIndex] : COUNTRIES_EN[enIndex];
+ }
+
+ // 3. If it matches a Persian country name in COUNTRIES_FA
+ const faIndex = COUNTRIES_FA.findIndex((c) => c === trimmed);
+ if (faIndex !== -1) {
+ return isFa ? COUNTRIES_FA[faIndex] : COUNTRIES_EN[faIndex] || COUNTRIES_FA[faIndex];
+ }
+
+ // 4. Try Intl.DisplayNames fallback if 2-3 chars
+ if (trimmed.length <= 3) {
+ try {
+ const displayNames = new Intl.DisplayNames([isFa ? "fa" : "en"], {
+ type: "region",
+ });
+ const name = displayNames.of(trimmed.toUpperCase());
+ if (name) return name;
+ } catch {}
+ }
+
+ return trimmed;
+}
diff --git a/src/data/languages.ts b/src/data/languages.ts
index 3610934..6765184 100644
--- a/src/data/languages.ts
+++ b/src/data/languages.ts
@@ -153,6 +153,85 @@ export const LANGUAGES_FA = [
"سایر",
];
+export const LANGUAGE_EN_TO_FA: Record = {
+ Afrikaans: "آفریکانس",
+ Albanian: "آلبانیایی",
+ Amharic: "امهری",
+ Arabic: "عربی",
+ Armenian: "ارمنی",
+ Azerbaijani: "آذربایجانی",
+ Balochi: "بلوچی",
+ Bengali: "بنگالی",
+ Bosnian: "بوسنیایی",
+ Bulgarian: "بلغاری",
+ Burmese: "میانماری (برمهای)",
+ Catalan: "کاتالان",
+ Chinese: "چینی",
+ Croatian: "کرواتی",
+ Czech: "چکی",
+ Danish: "دانمارکی",
+ Dutch: "هلندی",
+ English: "انگلیسی",
+ Estonian: "استونیایی",
+ Finnish: "فنلاندی",
+ French: "فرانسوی",
+ Georgian: "گرجی",
+ German: "آلمانی",
+ Greek: "یونانی",
+ Gujarati: "گجراتی",
+ Hausa: "هوسا",
+ Hebrew: "عبری",
+ Hindi: "هندی",
+ Hungarian: "مجاری",
+ Icelandic: "ایسلندی",
+ Indonesian: "اندونزیایی",
+ Irish: "ایرلندی",
+ Italian: "ایتالیایی",
+ Japanese: "ژاپنی",
+ Kashmiri: "کشمیری",
+ Kazakh: "قزاقی",
+ Khmer: "خمری",
+ Korean: "کرهای",
+ Kurdish: "کردی",
+ Kyrgyz: "قرقیزی",
+ Latvian: "لتونیایی",
+ Lithuanian: "لیتوانیایی",
+ Macedonian: "مقدونیهای",
+ Malay: "مالایی",
+ Maltese: "مالتی",
+ Mongolian: "مغولی",
+ Nepali: "نپالی",
+ Norwegian: "نروژی",
+ Pashto: "پشتو",
+ Persian: "فارسی",
+ "Persian (Farsi)": "فارسی",
+ Polish: "لهستانی",
+ Portuguese: "پرتغالی",
+ Punjabi: "پنجابی",
+ Romanian: "رومانیایی",
+ Russian: "روسی",
+ Serbian: "صربی",
+ Sinhala: "سینهالی",
+ Slovak: "اسلواکی",
+ Slovenian: "اسلوونیایی",
+ Somali: "سومالیایی",
+ Spanish: "اسپانیایی",
+ Swahili: "سواحیلی",
+ Swedish: "سوئدی",
+ Tajik: "تاجیکی",
+ Tamil: "تامیلی",
+ Telugu: "تلوگو",
+ Thai: "تایلندی",
+ Turkish: "ترکی",
+ Turkmen: "ترکمنی",
+ Ukrainian: "اوکراینی",
+ Urdu: "اردو",
+ "Urdu Roman (Latin)": "اردو (لاتین)",
+ Uzbek: "ازبکی",
+ Vietnamese: "ویتنامی",
+ Other: "سایر",
+};
+
export function getLanguageList(locale: string): string[] {
const normalized = String(locale || "en").toLowerCase();
if (normalized === "fa" || normalized === "fa-ir") {
diff --git a/src/hooks/marriage/use-profile-main.test.ts b/src/hooks/marriage/use-profile-main.test.ts
index 0652a93..cd09895 100644
--- a/src/hooks/marriage/use-profile-main.test.ts
+++ b/src/hooks/marriage/use-profile-main.test.ts
@@ -32,7 +32,7 @@ describe("getMarriageProfile", () => {
expect(mocks.setCachedEntryPath).toHaveBeenCalledWith("/questions-list");
});
- it("clears the route for an incomplete onboarding profile", async () => {
+ it("caches the intro route for an incomplete onboarding profile", async () => {
mocks.get.mockResolvedValue({
data: {
status: "pending_onboarding",
@@ -43,7 +43,7 @@ describe("getMarriageProfile", () => {
await getMarriageProfile();
- expect(mocks.setCachedEntryPath).toHaveBeenCalledWith(null);
+ expect(mocks.setCachedEntryPath).toHaveBeenCalledWith("/intro");
});
it("overwrites the old questions route when the backend advances state", async () => {
diff --git a/src/hooks/marriage/use-profile-main.ts b/src/hooks/marriage/use-profile-main.ts
index de7e027..3151832 100644
--- a/src/hooks/marriage/use-profile-main.ts
+++ b/src/hooks/marriage/use-profile-main.ts
@@ -31,7 +31,7 @@ export async function getMarriageProfile() {
});
setCachedMarriageEntryPath(
- hasCompletedMarriageProfileBasics(data) ? getSubmitPath(data) : null,
+ hasCompletedMarriageProfileBasics(data) ? getSubmitPath(data) : "/intro",
);
return data;
diff --git a/src/lib/entry-route-cache.test.ts b/src/lib/entry-route-cache.test.ts
index 707a4e6..143e75a 100644
--- a/src/lib/entry-route-cache.test.ts
+++ b/src/lib/entry-route-cache.test.ts
@@ -21,7 +21,8 @@ describe("entry route cache", () => {
);
expect(normalizeCachedMarriageEntryPath("https://example.com")).toBeNull();
expect(normalizeCachedMarriageEntryPath("//example.com")).toBeNull();
- expect(normalizeCachedMarriageEntryPath("/intro")).toBeNull();
+ expect(normalizeCachedMarriageEntryPath("/intro")).toBe("/intro");
+ expect(normalizeCachedMarriageEntryPath("/invalid-path")).toBeNull();
});
it("requires a real token before trusting the cached route", () => {
diff --git a/src/lib/entry-route-cache.ts b/src/lib/entry-route-cache.ts
index 3e2ceca..9a688ef 100644
--- a/src/lib/entry-route-cache.ts
+++ b/src/lib/entry-route-cache.ts
@@ -7,6 +7,7 @@ import {
export const MARRIAGE_ENTRY_PATH_COOKIE = "HABIB_MARRIAGE_ENTRY_PATH";
const AUTHENTICATED_ENTRY_PATHS = new Set([
+ "/intro",
"/terms",
"/questions-list",
"/finding-match",
diff --git a/src/lib/geo-region.ts b/src/lib/geo-region.ts
new file mode 100644
index 0000000..7948f3f
--- /dev/null
+++ b/src/lib/geo-region.ts
@@ -0,0 +1,236 @@
+"use client";
+
+import { PhoneNumberUtil } from "google-libphonenumber";
+import { http } from "./http";
+import { resolveCountryName } from "@/data/countries";
+
+export type UserGeoRegion = {
+ ip?: string;
+ city?: string;
+ country?: string;
+ countryCode?: string; // e.g. "IR", "US", "GB"
+ phoneCode?: string; // e.g. "+98", "+1", "+44"
+};
+
+const phoneUtil = PhoneNumberUtil.getInstance();
+
+const STORAGE_KEY = "user_geo_region";
+const PHONE_STORAGE_KEY = "geoIPPhoneCode";
+
+let cachedRegion: UserGeoRegion | null = null;
+let geoRegionPromise: Promise | null = null;
+const listeners = new Set<(region: UserGeoRegion) => void>();
+
+export function resetUserGeoRegionForTesting() {
+ cachedRegion = null;
+ geoRegionPromise = null;
+ listeners.clear();
+}
+
+export function subscribeToUserGeoRegion(
+ fn: (region: UserGeoRegion) => void,
+): () => void {
+ listeners.add(fn);
+ return () => {
+ listeners.delete(fn);
+ };
+}
+
+export function getStoredUserGeoRegion(): UserGeoRegion | null {
+ if (cachedRegion) return cachedRegion;
+ if (typeof window !== "undefined") {
+ try {
+ const stored = localStorage.getItem(STORAGE_KEY);
+ if (stored) {
+ const parsed = JSON.parse(stored) as UserGeoRegion;
+ if (parsed && (parsed.country || parsed.phoneCode)) {
+ cachedRegion = parsed;
+ return parsed;
+ }
+ }
+ } catch {}
+ }
+ return null;
+}
+
+export function setStoredUserGeoRegion(region: UserGeoRegion) {
+ cachedRegion = region;
+ if (typeof window !== "undefined") {
+ try {
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(region));
+ if (region.phoneCode) {
+ localStorage.setItem(PHONE_STORAGE_KEY, region.phoneCode);
+ }
+ } catch {}
+ }
+ listeners.forEach((fn) => fn(region));
+}
+
+function resolvePhoneCodeFromCountryCode(isoCode?: string): string | undefined {
+ if (!isoCode) return undefined;
+ try {
+ const callingCode = phoneUtil.getCountryCodeForRegion(
+ isoCode.toUpperCase(),
+ );
+ if (callingCode) {
+ return `+${callingCode}`;
+ }
+ } catch {}
+ return undefined;
+}
+
+export function getUserGeoRegion(force = false): Promise {
+ if (!force) {
+ const existing = getStoredUserGeoRegion();
+ if (existing && (existing.city || existing.country)) {
+ return Promise.resolve(existing);
+ }
+ } else {
+ cachedRegion = null;
+ geoRegionPromise = null;
+ }
+
+ if (geoRegionPromise) {
+ return geoRegionPromise;
+ }
+
+ geoRegionPromise = (async () => {
+ try {
+ // 1. Primary: Habib Backend User Region API (/account/auth/user/region/)
+ try {
+ console.log("[GEO_AUTO_LOG] 🚀 Requesting /account/auth/user/region/ from backend...");
+ const response = await http.get<{
+ ip?: string;
+ country?: string;
+ country_code?: string;
+ city?: string;
+ }>("/account/auth/user/region/", {
+ timeout: 4000,
+ });
+
+ const data = response.data;
+ console.log("[GEO_AUTO_LOG] 📥 Backend Raw Response:", JSON.stringify(data));
+ if (data && (data.country || data.country_code || data.city)) {
+ const isoCode =
+ data.country_code ||
+ (data.country && data.country.trim().length === 2
+ ? data.country.trim().toUpperCase()
+ : undefined);
+ const phoneCode = resolvePhoneCodeFromCountryCode(isoCode);
+ const countryName =
+ resolveCountryName(data.country || isoCode, "en") || data.country;
+ const countryFa =
+ resolveCountryName(data.country || isoCode, "fa") || countryName;
+
+ console.log(
+ `[GEO_AUTO_LOG] 🌍 Resolved Country: FA=${countryFa} | EN=${countryName} | Code=${isoCode} | City=${data.city || "None"} | IP=${data.ip || "None"}`,
+ );
+
+ const region: UserGeoRegion = {
+ ip: data.ip,
+ city: data.city,
+ country: countryName,
+ countryCode: isoCode,
+ phoneCode: phoneCode || "+44",
+ };
+ setStoredUserGeoRegion(region);
+ return region;
+ }
+ } catch (err: any) {
+ console.error(
+ "[GEO_AUTO_LOG] ❌ Backend Region Error:",
+ err?.response?.data || err?.message || err,
+ );
+ }
+
+ // 2. Secondary fallback: ipapi.co with 2s timeout
+ const controller = new AbortController();
+ const timeoutId = setTimeout(() => controller.abort(), 2000);
+ try {
+ const res = await fetch("https://ipapi.co/json/", {
+ signal: controller.signal,
+ });
+ clearTimeout(timeoutId);
+ if (res?.ok) {
+ const data = await res.json();
+ if (
+ data &&
+ (data.country_name || data.city || data.country_calling_code)
+ ) {
+ const rawPhone = data.country_calling_code
+ ? String(data.country_calling_code).trim()
+ : "";
+ const phoneCode = rawPhone.startsWith("+")
+ ? rawPhone
+ : rawPhone
+ ? `+${rawPhone}`
+ : "+44";
+ const region: UserGeoRegion = {
+ ip: data.ip,
+ city: data.city,
+ country: data.country_name,
+ countryCode: data.country_code,
+ phoneCode,
+ };
+ setStoredUserGeoRegion(region);
+ return region;
+ }
+ }
+ } catch {
+ clearTimeout(timeoutId);
+ }
+
+ // 3. Tertiary fallback: ipwho.is with 2s timeout
+ const secondaryController = new AbortController();
+ const secondaryTimeoutId = setTimeout(
+ () => secondaryController.abort(),
+ 2000,
+ );
+ try {
+ const res = await fetch("https://ipwho.is/", {
+ signal: secondaryController.signal,
+ });
+ clearTimeout(secondaryTimeoutId);
+ if (res?.ok) {
+ const data = await res.json();
+ if (data && (data.country || data.city || data.calling_code)) {
+ const rawPhone = data.calling_code
+ ? String(data.calling_code).trim()
+ : "";
+ const phoneCode = rawPhone.startsWith("+")
+ ? rawPhone
+ : rawPhone
+ ? `+${rawPhone}`
+ : "+44";
+ const region: UserGeoRegion = {
+ ip: data.ip,
+ city: data.city,
+ country: data.country,
+ countryCode: data.country_code,
+ phoneCode,
+ };
+ setStoredUserGeoRegion(region);
+ return region;
+ }
+ }
+ } catch {
+ clearTimeout(secondaryTimeoutId);
+ }
+
+ // 4. Default fallback
+ const defaultRegion: UserGeoRegion = {
+ phoneCode: "+44",
+ };
+ setStoredUserGeoRegion(defaultRegion);
+ return defaultRegion;
+ } catch {
+ const defaultRegion: UserGeoRegion = {
+ phoneCode: "+44",
+ };
+ setStoredUserGeoRegion(defaultRegion);
+ return defaultRegion;
+ }
+ })();
+
+ return geoRegionPromise;
+}
diff --git a/src/lib/marriage-field-formatter.ts b/src/lib/marriage-field-formatter.ts
index 25d0c74..9a4ad25 100644
--- a/src/lib/marriage-field-formatter.ts
+++ b/src/lib/marriage-field-formatter.ts
@@ -36,7 +36,7 @@ export function isMarriagePhoneFieldValue(
}
export function formatFieldValue(value: MarriageFieldValue): string | null {
- if (value === null || value === "") {
+ if (value === null || value === undefined || value === "") {
return null;
}
@@ -48,6 +48,16 @@ export function formatFieldValue(value: MarriageFieldValue): string | null {
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.country, v.state, v.city]
+ .map((p) => (typeof p === "string" ? p.trim() : ""))
+ .filter(Boolean);
+ return parts.join(", ");
+ }
+ }
+
return String(value);
}
diff --git a/src/translations/locales/ar.json b/src/translations/locales/ar.json
index 0b92805..a4d1241 100644
--- a/src/translations/locales/ar.json
+++ b/src/translations/locales/ar.json
@@ -141,7 +141,7 @@
"Current Housing Status": "حالة السكن الحالية",
"Current Marital Status": "الحالة الاجتماعية الحالية",
"Current Nationality / Citizenship": "الجنسية الحالية / المواطنة",
- "Current Residence": "الإقامة الحالية",
+ "Current Residence": "مكان الإقامة الحالي",
"Current user": "المستخدم الحالي",
"Currently building suitable financial conditions": "حاليا بناء الظروف المالية المناسبة",
"Curvy/Full": "متعرج / كامل",
@@ -260,7 +260,7 @@
"Height in Centimeters": "Height in Centimeters",
"Help": "يساعد",
"High School Diploma": "دبلوم المدرسة الثانوية",
- "Highest Level of Education": "أعلى مستوى من التعليم",
+ "Highest Level of Education": "أعلى مستوى تعليمي",
"Homemaker": "ربة منزل",
"Homeowner": "صاحب المنزل",
"Hookah": "الشيشة",
@@ -308,7 +308,7 @@
"Iraq": "Iraq",
"Items Per Page": "العناصر لكل صفحة",
"Job Seeking / Unemployed": "Job Seeking / Unemployed",
- "Job Title": "مسمى وظيفي",
+ "Job Title": "المسمى الوظيفي",
"Joint custody or periodic visitation/relocation between parents.": "الحضانة المشتركة أو الزيارة الدورية/الانتقال بين الوالدين.",
"Kurdish": "كردي",
"Language Learning": "تعلم اللغة",
@@ -725,7 +725,7 @@
"You have active access to view candidates.": "لديك حق الوصول النشط لعرض المرشحين.",
"You will be contacted by your consultant.": "سيتم التواصل معك من قبل المستشار الخاص بك.",
"You've completed all required fields. However, filling in all sections will help us find better matches for you": "لقد أكملت كافة الحقول المطلوبة. ومع ذلك، فإن ملء جميع الأقسام سيساعدنا في العثور على تطابقات أفضل لك",
- "Your Hobbies and Main Interests": "هواياتك واهتماماتك الرئيسية",
+ "Your Hobbies and Main Interests": "الهوايات والاهتمامات الرئيسية",
"Your Personality Traits": "سمات شخصيتك",
"Your details are only used for the matching process.": "يتم استخدام التفاصيل الخاصة بك فقط لعملية المطابقة.",
"Your information is kept strictly confidential.": "يتم الاحتفاظ بمعلوماتك بسرية تامة.",
@@ -807,5 +807,6 @@
"Enter medication name and reason for use...": "أدخل اسم الدواء وسبب الاستخدام...",
"نام دارو و دلیل مصرف را وارد نمایید...": "أدخل اسم الدواء وسبب الاستخدام...",
"وضعیت سلامت جسمانی": "حالة الصحة الجسدية",
- "توضیحات وضعیت جسمانی": "وصف الصحة الجسدية"
+ "توضیحات وضعیت جسمانی": "وصف الصحة الجسدية",
+ "Name": "الاسم"
}
\ No newline at end of file
diff --git a/src/translations/locales/az.json b/src/translations/locales/az.json
index c1101a5..be8ef8f 100644
--- a/src/translations/locales/az.json
+++ b/src/translations/locales/az.json
@@ -141,7 +141,7 @@
"Current Housing Status": "Cari Mənzil Vəziyyəti",
"Current Marital Status": "Cari Ailə Vəziyyəti",
"Current Nationality / Citizenship": "Hazırkı Vətəndaşlıq / Vətəndaşlıq",
- "Current Residence": "Hazırkı Yaşayış yeri",
+ "Current Residence": "Hazırkı yaşayış yeri",
"Current user": "Cari istifadəçi",
"Currently building suitable financial conditions": "Hal-hazırda uyğun maliyyə şəraiti yaratmaq",
"Curvy/Full": "Qıvrımlı/Tam",
@@ -199,7 +199,7 @@
"Education, Career, and Economic Status": "Təhsil, Karyera və İqtisadi Vəziyyət",
"Emotional": "Emosional",
"Employed": "İşlədi",
- "Employment Status": "Məşğulluq Vəziyyəti",
+ "Employment Status": "Məşğulluq vəziyyəti",
"English": "İngilis dili",
"English, French, etc.": "İngilis, Fransız və s.",
"Enter details here...": "Təfərrüatları bura daxil edin...",
@@ -219,7 +219,7 @@
"Father alive": "Ata sağdır",
"Father has passed away": "Ata rəhmətə gedib",
"Feel free to briefly explain your decision...": "Qərarınızı qısaca izah etməkdən çekinmeyin...",
- "Field of Study": "Tədqiqat sahəsi",
+ "Field of Study": "Təhsil sahəsi",
"Final Consent": "Yekun razılıq",
"Final Match Introduction": "Final matçının təqdimatı",
"Final Notice": "Yekun bildiriş",
@@ -235,7 +235,7 @@
"Full Hijab with modest clothing - Modest styling with hair completely covered.": "Təvazökar geyimli tam hicab - Tamamilə örtülmüş saçlarla təvazökar üslub.",
"Full Islamic covering (Maximum Hijab) - Abaya, Jilbab, Chador, or Niqab with full observance.": "Tam İslam örtüyü (Maksimum Hicab) - Abaya, Cilbab, Çador və ya Niqab tam riayət etməklə.",
"Full Islamic covering mandatory": "Tam İslami örtük məcburidir",
- "Full Name": "Tam adı",
+ "Full Name": "Tam ad",
"Full makeup": "Tam makiyaj",
"Full-time Employed": "Tam iş günü",
"Fully able to support expenses": "Xərcləri tam dəstəkləyə bilir",
@@ -260,7 +260,7 @@
"Height in Centimeters": "Hündürlüyü Santimetrlə",
"Help": "Kömək edin",
"High School Diploma": "Orta məktəb diplomu",
- "Highest Level of Education": "Ən Yüksək Təhsil Səviyyəsi",
+ "Highest Level of Education": "Ən yüksək təhsil səviyyəsi",
"Homemaker": "Evdar",
"Homeowner": "Ev sahibi",
"Hookah": "Qəlyan",
@@ -308,7 +308,7 @@
"Iraq": "İraq",
"Items Per Page": "Səhifə Başına Elementlər",
"Job Seeking / Unemployed": "İş Axtarıram/İşsiz",
- "Job Title": "Vəzifə adı",
+ "Job Title": "Vəzifə / Peşə adı",
"Joint custody or periodic visitation/relocation between parents.": "Valideynlər arasında birgə qəyyumluq və ya dövri ziyarət/köçürmə.",
"Kurdish": "kürd",
"Language Learning": "Dil Öyrənilməsi",
@@ -684,7 +684,7 @@
"View More Details": "Ətraflı Məlumata Baxın",
"View Profile": "Profilə baxın",
"View contact number": "Əlaqə nömrəsinə baxın",
- "View more details": "Ətraflı təfərrüata baxın",
+ "View more details": "Daha çox təfərrüata baxın",
"View profile": "Profilə baxın",
"Watch Video": "Videoya baxın",
"We did not reach an agreement": "Razılığa gəlmədik",
@@ -725,7 +725,7 @@
"You have active access to view candidates.": "Namizədlərə baxmaq üçün aktiv girişiniz var.",
"You will be contacted by your consultant.": "Məsləhətçiniz sizinlə əlaqə saxlayacaq.",
"You've completed all required fields. However, filling in all sections will help us find better matches for you": "Bütün tələb olunan sahələri doldurmusunuz. Bununla belə, bütün bölmələri doldurmaq bizə sizin üçün daha yaxşı uyğunluqlar tapmağa kömək edəcək",
- "Your Hobbies and Main Interests": "Hobbiləriniz və Əsas Maraqlarınız",
+ "Your Hobbies and Main Interests": "Hobbiləriniz və əsas maraqlarınız",
"Your Personality Traits": "Şəxsiyyət xüsusiyyətləriniz",
"Your details are only used for the matching process.": "Detallarınız yalnız uyğunlaşma prosesi üçün istifadə olunur.",
"Your information is kept strictly confidential.": "Məlumatlarınız ciddi şəkildə məxfi saxlanılır.",
@@ -807,5 +807,6 @@
"Enter medication name and reason for use...": "Dərmanın adını və istifadə səbəbini daxil edin...",
"نام دارو و دلیل مصرف را وارد نمایید...": "Dərmanın adını və istifadə səbəbini daxil edin...",
"وضعیت سلامت جسمانی": "Fiziki Sağlamlıq Vəziyyəti",
- "توضیحات وضعیت جسمانی": "Fiziki Sağlamlıq Təsviri"
+ "توضیحات وضعیت جسمانی": "Fiziki Sağlamlıq Təsviri",
+ "Name": "Ad"
}
\ No newline at end of file
diff --git a/src/translations/locales/bn.json b/src/translations/locales/bn.json
index 2c933da..cd3a011 100644
--- a/src/translations/locales/bn.json
+++ b/src/translations/locales/bn.json
@@ -199,7 +199,7 @@
"Education, Career, and Economic Status": "শিক্ষা, কর্মজীবন, এবং অর্থনৈতিক অবস্থা",
"Emotional": "আবেগপ্রবণ",
"Employed": "নিযুক্ত",
- "Employment Status": "কর্মসংস্থানের অবস্থা",
+ "Employment Status": "কর্মসংস্থান অবস্থা",
"English": "ইংরেজি",
"English, French, etc.": "ইংরেজি, ফরাসি, ইত্যাদি",
"Enter details here...": "এখানে বিস্তারিত লিখুন...",
@@ -260,7 +260,7 @@
"Height in Centimeters": "সেন্টিমিটারে উচ্চতা",
"Help": "সাহায্য",
"High School Diploma": "High School Diploma",
- "Highest Level of Education": "শিক্ষার সর্বোচ্চ স্তর",
+ "Highest Level of Education": "সর্বোচ্চ শিক্ষাগত যোগ্যতা",
"Homemaker": "গৃহিণী",
"Homeowner": "বাড়ির মালিক",
"Hookah": "হুক্কা",
@@ -308,7 +308,7 @@
"Iraq": "ইরাক",
"Items Per Page": "পৃষ্ঠা প্রতি আইটেম",
"Job Seeking / Unemployed": "চাকরির খোঁজ / বেকার",
- "Job Title": "চাকরির শিরোনাম",
+ "Job Title": "কাজের পদবি",
"Joint custody or periodic visitation/relocation between parents.": "যৌথ হেফাজত বা পিতামাতার মধ্যে পর্যায়ক্রমিক পরিদর্শন/স্থানান্তর।",
"Kurdish": "কুর্দি",
"Language Learning": "ভাষা শিক্ষা",
@@ -684,7 +684,7 @@
"View More Details": "আরো বিস্তারিত দেখুন",
"View Profile": "প্রোফাইল দেখুন",
"View contact number": "যোগাযোগের নম্বর দেখুন",
- "View more details": "আরো বিস্তারিত দেখুন",
+ "View more details": "আরও বিস্তারিত দেখুন",
"View profile": "প্রোফাইল দেখুন",
"Watch Video": "ভিডিও দেখুন",
"We did not reach an agreement": "আমরা সমঝোতায় পৌঁছাইনি",
@@ -725,7 +725,7 @@
"You have active access to view candidates.": "প্রার্থীদের দেখতে আপনার সক্রিয় অ্যাক্সেস আছে।",
"You will be contacted by your consultant.": "আপনার পরামর্শদাতা দ্বারা আপনার সাথে যোগাযোগ করা হবে।",
"You've completed all required fields. However, filling in all sections will help us find better matches for you": "আপনি সমস্ত প্রয়োজনীয় ক্ষেত্র সম্পূর্ণ করেছেন। যাইহোক, সমস্ত বিভাগ পূরণ করা আমাদের আপনার জন্য আরও ভাল মিল খুঁজে পেতে সাহায্য করবে",
- "Your Hobbies and Main Interests": "আপনার শখ এবং প্রধান আগ্রহ",
+ "Your Hobbies and Main Interests": "আপনার শখ ও প্রধান আগ্রহসমূহ",
"Your Personality Traits": "আপনার ব্যক্তিত্ব বৈশিষ্ট্য",
"Your details are only used for the matching process.": "আপনার বিবরণ শুধুমাত্র মিল প্রক্রিয়ার জন্য ব্যবহার করা হয়.",
"Your information is kept strictly confidential.": "আপনার তথ্য কঠোরভাবে গোপন রাখা হয়.",
@@ -807,5 +807,6 @@
"Enter medication name and reason for use...": "ওষুধের নাম এবং কারণ লিখুন...",
"نام دارو و دلیل مصرف را وارد نمایید...": "ওষুধের নাম এবং কারণ লিখুন...",
"وضعیت سلامت جسمانی": "শারীরিক স্বাস্থ্যের অবস্থা",
- "توضیحات وضعیت جسمانی": "শারীরিক স্বাস্থ্যের বিবরণ"
+ "توضیحات وضعیت جسمانی": "শারীরিক স্বাস্থ্যের বিবরণ",
+ "Name": "নাম"
}
\ No newline at end of file
diff --git a/src/translations/locales/da.json b/src/translations/locales/da.json
index 2882d48..6ca63b0 100644
--- a/src/translations/locales/da.json
+++ b/src/translations/locales/da.json
@@ -141,7 +141,7 @@
"Current Housing Status": "Nuværende boligstatus",
"Current Marital Status": "Nuværende ægteskabelig status",
"Current Nationality / Citizenship": "Nuværende nationalitet / statsborgerskab",
- "Current Residence": "Nuværende bolig",
+ "Current Residence": "Nuværende bopæl",
"Current user": "Nuværende bruger",
"Currently building suitable financial conditions": "Opbygger i øjeblikket passende økonomiske forhold",
"Curvy/Full": "Kurvet/fuld",
@@ -199,7 +199,7 @@
"Education, Career, and Economic Status": "Uddannelse, karriere og økonomisk status",
"Emotional": "Følelsesmæssigt",
"Employed": "Ansat",
- "Employment Status": "Ansættelsesstatus",
+ "Employment Status": "Beskæftigelsesstatus",
"English": "engelsk",
"English, French, etc.": "engelsk, fransk osv.",
"Enter details here...": "Indtast detaljer her...",
@@ -235,7 +235,7 @@
"Full Hijab with modest clothing - Modest styling with hair completely covered.": "Fuld Hijab med beskedent tøj - Beskeden styling med hår helt dækket.",
"Full Islamic covering (Maximum Hijab) - Abaya, Jilbab, Chador, or Niqab with full observance.": "Fuld islamisk dækning (Maksimal Hijab) - Abaya, Jilbab, Chador eller Niqab med fuld overholdelse.",
"Full Islamic covering mandatory": "Fuld islamisk dækning obligatorisk",
- "Full Name": "Fuldt navn",
+ "Full Name": "Fulde navn",
"Full makeup": "Fuld makeup",
"Full-time Employed": "Fuldtidsansat",
"Fully able to support expenses": "Fuldt ud i stand til at dække udgifter",
@@ -308,7 +308,7 @@
"Iraq": "Irak",
"Items Per Page": "Items Per Page",
"Job Seeking / Unemployed": "Jobsøgende / ledig",
- "Job Title": "Jobtitel",
+ "Job Title": "Stilling",
"Joint custody or periodic visitation/relocation between parents.": "Fælles forældremyndighed eller periodisk samvær/flytning mellem forældre.",
"Kurdish": "kurdisk",
"Language Learning": "Sprogindlæring",
@@ -725,7 +725,7 @@
"You have active access to view candidates.": "Du har aktiv adgang til at se kandidater.",
"You will be contacted by your consultant.": "Du vil blive kontaktet af din konsulent.",
"You've completed all required fields. However, filling in all sections will help us find better matches for you": "You've completed all required fields. However, filling in all sections will help us find better matches for you",
- "Your Hobbies and Main Interests": "Dine hobbyer og hovedinteresser",
+ "Your Hobbies and Main Interests": "Dine hobbyer og vigtigste interesser",
"Your Personality Traits": "Dine personlighedstræk",
"Your details are only used for the matching process.": "Your details are only used for the matching process.",
"Your information is kept strictly confidential.": "Your information is kept strictly confidential.",
@@ -807,5 +807,6 @@
"Enter medication name and reason for use...": "Indtast medicinnavn og årsag...",
"نام دارو و دلیل مصرف را وارد نمایید...": "Indtast medicinnavn og årsag...",
"وضعیت سلامت جسمانی": "Fysisk helbredstilstand",
- "توضیحات وضعیت جسمانی": "Beskrivelse af fysisk helbred"
+ "توضیحات وضعیت جسمانی": "Beskrivelse af fysisk helbred",
+ "Name": "Navn"
}
\ No newline at end of file
diff --git a/src/translations/locales/de.json b/src/translations/locales/de.json
index 33ff350..4f19576 100644
--- a/src/translations/locales/de.json
+++ b/src/translations/locales/de.json
@@ -141,7 +141,7 @@
"Current Housing Status": "Aktueller Wohnstatus",
"Current Marital Status": "Aktueller Familienstand",
"Current Nationality / Citizenship": "Aktuelle Nationalität/Staatsbürgerschaft",
- "Current Residence": "Aktueller Wohnsitz",
+ "Current Residence": "Aktueller Wohnort",
"Current user": "Aktueller Benutzer",
"Currently building suitable financial conditions": "Derzeit werden geeignete finanzielle Rahmenbedingungen aufgebaut",
"Curvy/Full": "Kurvig/voll",
@@ -260,7 +260,7 @@
"Height in Centimeters": "Höhe in Zentimetern",
"Help": "Help",
"High School Diploma": "High-School-Diplom",
- "Highest Level of Education": "Höchstes Bildungsniveau",
+ "Highest Level of Education": "Höchster Bildungsabschluss",
"Homemaker": "Hausfrau",
"Homeowner": "Hausbesitzer",
"Hookah": "Shisha",
@@ -807,5 +807,6 @@
"Enter medication name and reason for use...": "Geben Sie den Medikamentennamen und den Grund ein...",
"نام دارو و دلیل مصرف را وارد نمایید...": "Geben Sie den Medikamentennamen und den Grund ein...",
"وضعیت سلامت جسمانی": "Körperlicher Gesundheitszustand",
- "توضیحات وضعیت جسمانی": "Beschreibung des körperlichen Zustands"
+ "توضیحات وضعیت جسمانی": "Beschreibung des körperlichen Zustands",
+ "Name": "Name"
}
\ No newline at end of file
diff --git a/src/translations/locales/en.json b/src/translations/locales/en.json
index ba5a3dc..a34416a 100644
--- a/src/translations/locales/en.json
+++ b/src/translations/locales/en.json
@@ -843,5 +843,7 @@
"Do you have a history of treatment or counseling related to mental health in the past?": "Do you have a history of treatment or counseling related to mental health in the past?",
"Do you currently take any medication on an ongoing basis?": "Do you currently take any medication on an ongoing basis?",
"Medication Name and Reason for Use": "Medication Name and Reason for Use",
- "Enter medication name and reason for use...": "Enter medication name and reason for use..."
+ "Enter medication name and reason for use...": "Enter medication name and reason for use...",
+ "Enter a valid phone number with country code.": "Enter a valid phone number with country code.",
+ "Name": "Name"
}
\ No newline at end of file
diff --git a/src/translations/locales/es.json b/src/translations/locales/es.json
index 14548b4..cdbd522 100644
--- a/src/translations/locales/es.json
+++ b/src/translations/locales/es.json
@@ -46,7 +46,7 @@
"Additional Comments on Economic and Housing Status": "Comentarios adicionales sobre la situación económica y de vivienda",
"Additional details about family responsibility": "Detalles adicionales sobre la responsabilidad familiar",
"Afghanistan": "Afganistán",
- "Age": "edad",
+ "Age": "Edad",
"Alcohol is a serious red line": "El alcohol es una línea roja grave",
"Alcohol is a serious red line for me": "El alcohol es una línea roja grave para mí.",
"Alcoholic Beverages": "Bebidas Alcohólicas",
@@ -199,7 +199,7 @@
"Education, Career, and Economic Status": "Educación, carrera y situación económica",
"Emotional": "Emocional",
"Employed": "Empleado",
- "Employment Status": "Estado de empleo",
+ "Employment Status": "Estado laboral",
"English": "ingles",
"English, French, etc.": "inglés, francés, etc.",
"Enter details here...": "Ingrese los detalles aquí...",
@@ -260,7 +260,7 @@
"Height in Centimeters": "Altura en centímetros",
"Help": "Help",
"High School Diploma": "Diploma de escuela secundaria",
- "Highest Level of Education": "Nivel más alto de educación",
+ "Highest Level of Education": "Nivel educativo más alto",
"Homemaker": "ama de casa",
"Homeowner": "Propietario de casa",
"Hookah": "cachimba",
@@ -308,7 +308,7 @@
"Iraq": "Irak",
"Items Per Page": "Items Per Page",
"Job Seeking / Unemployed": "Buscando empleo / Desempleado",
- "Job Title": "Título del trabajo",
+ "Job Title": "Título del puesto",
"Joint custody or periodic visitation/relocation between parents.": "Custodia compartida o visitas periódicas/reubicación entre padres.",
"Kurdish": "kurdo",
"Language Learning": "Aprendizaje de idiomas",
@@ -807,5 +807,6 @@
"Enter medication name and reason for use...": "Ingrese el nombre del medicamento y el motivo...",
"نام دارو و دلیل مصرف را وارد نمایید...": "Ingrese el nombre del medicamento y el motivo...",
"وضعیت سلامت جسمانی": "Estado de salud física",
- "توضیحات وضعیت جسمانی": "Descripción de la salud física"
+ "توضیحات وضعیت جسمانی": "Descripción de la salud física",
+ "Name": "Nombre"
}
\ No newline at end of file
diff --git a/src/translations/locales/fa.json b/src/translations/locales/fa.json
index 3bbd532..bb11a05 100644
--- a/src/translations/locales/fa.json
+++ b/src/translations/locales/fa.json
@@ -855,5 +855,6 @@
"Enter medication name and reason for use...": "نام دارو و دلیل مصرف را وارد نمایید...",
"نام دارو و دلیل مصرف را وارد نمایید...": "نام دارو و دلیل مصرف را وارد نمایید...",
"وضعیت سلامت جسمانی": "وضعیت سلامت جسمانی",
- "توضیحات وضعیت جسمانی": "توضیحات وضعیت جسمانی"
+ "توضیحات وضعیت جسمانی": "توضیحات وضعیت جسمانی",
+ "Name": "نام"
}
\ No newline at end of file
diff --git a/src/translations/locales/fr.json b/src/translations/locales/fr.json
index be5d825..282e887 100644
--- a/src/translations/locales/fr.json
+++ b/src/translations/locales/fr.json
@@ -199,7 +199,7 @@
"Education, Career, and Economic Status": "Éducation, carrière et statut économique",
"Emotional": "Émotionnel",
"Employed": "Employé",
- "Employment Status": "Statut d'emploi",
+ "Employment Status": "Statut professionnel",
"English": "Anglais",
"English, French, etc.": "Anglais, français, etc.",
"Enter details here...": "Entrez les détails ici...",
@@ -219,7 +219,7 @@
"Father alive": "Père vivant",
"Father has passed away": "Le père est décédé",
"Feel free to briefly explain your decision...": "Your explanatory text ...",
- "Field of Study": "Domaine d'études",
+ "Field of Study": "Domaine d’études",
"Final Consent": "Final Consent",
"Final Match Introduction": "Final Match Introduction",
"Final Notice": "Final Notice",
@@ -260,7 +260,7 @@
"Height in Centimeters": "Hauteur en centimètres",
"Help": "Help",
"High School Diploma": "Diplôme d'études secondaires",
- "Highest Level of Education": "Niveau d'éducation le plus élevé",
+ "Highest Level of Education": "Plus haut niveau d’études",
"Homemaker": "Femme au foyer",
"Homeowner": "Propriétaire",
"Hookah": "Narguilé",
@@ -725,7 +725,7 @@
"You have active access to view candidates.": "Vous disposez d'un accès actif pour visualiser les candidats.",
"You will be contacted by your consultant.": "Vous serez contacté par votre consultant.",
"You've completed all required fields. However, filling in all sections will help us find better matches for you": "You've completed all required fields. However, filling in all sections will help us find better matches for you",
- "Your Hobbies and Main Interests": "Vos passe-temps et principaux intérêts",
+ "Your Hobbies and Main Interests": "Vos loisirs et principaux centres d’intérêt",
"Your Personality Traits": "Vos traits de personnalité",
"Your details are only used for the matching process.": "Your details are only used for the matching process.",
"Your information is kept strictly confidential.": "Your information is kept strictly confidential.",
@@ -807,5 +807,6 @@
"Enter medication name and reason for use...": "Entrez le nom du médicament et le motif...",
"نام دارو و دلیل مصرف را وارد نمایید...": "Entrez le nom du médicament et le motif...",
"وضعیت سلامت جسمانی": "État de santé physique",
- "توضیحات وضعیت جسمانی": "Description de la santé physique"
+ "توضیحات وضعیت جسمانی": "Description de la santé physique",
+ "Name": "Nom"
}
\ No newline at end of file
diff --git a/src/translations/locales/gu.json b/src/translations/locales/gu.json
index f559cb0..fe99916 100644
--- a/src/translations/locales/gu.json
+++ b/src/translations/locales/gu.json
@@ -141,7 +141,7 @@
"Current Housing Status": "વર્તમાન હાઉસિંગ સ્થિતિ",
"Current Marital Status": "વર્તમાન વૈવાહિક સ્થિતિ",
"Current Nationality / Citizenship": "વર્તમાન રાષ્ટ્રીયતા / નાગરિકતા",
- "Current Residence": "વર્તમાન રહેઠાણ",
+ "Current Residence": "હાલનું રહેઠાણ",
"Current user": "વર્તમાન વપરાશકર્તા",
"Currently building suitable financial conditions": "હાલમાં યોગ્ય નાણાકીય પરિસ્થિતિઓ બનાવી રહી છે",
"Curvy/Full": "કર્વી/પૂર્ણ",
@@ -219,7 +219,7 @@
"Father alive": "પિતા જીવંત",
"Father has passed away": "પિતા ગુજરી ગયા છે",
"Feel free to briefly explain your decision...": "Your explanatory text ...",
- "Field of Study": "અભ્યાસ ક્ષેત્ર",
+ "Field of Study": "અભ્યાસનું ક્ષેત્ર",
"Final Consent": "Final Consent",
"Final Match Introduction": "Final Match Introduction",
"Final Notice": "Final Notice",
@@ -260,7 +260,7 @@
"Height in Centimeters": "સેન્ટીમીટરમાં ઊંચાઈ",
"Help": "Help",
"High School Diploma": "હાઇ સ્કૂલ ડિપ્લોમા",
- "Highest Level of Education": "શિક્ષણનું ઉચ્ચતમ સ્તર",
+ "Highest Level of Education": "ઉચ્ચતમ શિક્ષણ સ્તર",
"Homemaker": "ગૃહિણી",
"Homeowner": "ઘરમાલિક",
"Hookah": "હુક્કો",
@@ -308,7 +308,7 @@
"Iraq": "ઈરાક",
"Items Per Page": "Items Per Page",
"Job Seeking / Unemployed": "નોકરીની શોધ / બેરોજગાર",
- "Job Title": "જોબ શીર્ષક",
+ "Job Title": "નોકરીનું પદ",
"Joint custody or periodic visitation/relocation between parents.": "સંયુક્ત કસ્ટડી અથવા માતા-પિતા વચ્ચે સમયાંતરે મુલાકાત/સ્થાપન.",
"Kurdish": "કુર્દિશ",
"Language Learning": "ભાષા શીખવી",
@@ -807,5 +807,6 @@
"Enter medication name and reason for use...": "દવાનું નામ અને કારણ દાખલ કરો...",
"نام دارو و دلیل مصرف را وارد نمایید...": "દવાનું નામ અને કારણ દાખલ કરો...",
"وضعیت سلامت جسمانی": "શારીરિક સ્વાસ્થ્ય સ્થિતિ",
- "توضیحات وضعیت جسمانی": "શારીરિક સ્વાસ્થ્ય વર્ણન"
+ "توضیحات وضعیت جسمانی": "શારીરિક સ્વાસ્થ્ય વર્ણન",
+ "Name": "નામ"
}
\ No newline at end of file
diff --git a/src/translations/locales/ha.json b/src/translations/locales/ha.json
index 2b08607..986c768 100644
--- a/src/translations/locales/ha.json
+++ b/src/translations/locales/ha.json
@@ -141,7 +141,7 @@
"Current Housing Status": "Matsayin Gidajen Yanzu",
"Current Marital Status": "Matsayin Aure na Yanzu",
"Current Nationality / Citizenship": "Dan Kasa / Dan Kasa na Yanzu",
- "Current Residence": "Mazauni na Yanzu",
+ "Current Residence": "Wurin zama na yanzu",
"Current user": "Mai amfani na yanzu",
"Currently building suitable financial conditions": "A halin yanzu gina yanayin kuɗi masu dacewa",
"Curvy/Full": "Curvy/cikakke",
@@ -219,7 +219,7 @@
"Father alive": "Uba a raye",
"Father has passed away": "Uban ya rasu",
"Feel free to briefly explain your decision...": "Your explanatory text ...",
- "Field of Study": "Filin Karatu",
+ "Field of Study": "Fannin Karatu",
"Final Consent": "Final Consent",
"Final Match Introduction": "Final Match Introduction",
"Final Notice": "Final Notice",
@@ -260,7 +260,7 @@
"Height in Centimeters": "Tsawon santimita",
"Help": "Help",
"High School Diploma": "High School Diploma",
- "Highest Level of Education": "Highest Level of Education",
+ "Highest Level of Education": "Mafi girman matakin ilimi",
"Homemaker": "Homemaker",
"Homeowner": "Mai gida",
"Hookah": "Hookah",
@@ -725,7 +725,7 @@
"You have active access to view candidates.": "Kuna da damar shiga don duba 'yan takara.",
"You will be contacted by your consultant.": "Mai ba da shawara zai tuntube ku.",
"You've completed all required fields. However, filling in all sections will help us find better matches for you": "You've completed all required fields. However, filling in all sections will help us find better matches for you",
- "Your Hobbies and Main Interests": "Abubuwan sha'awarku da Babban Sha'awar ku",
+ "Your Hobbies and Main Interests": "Abubuwan sha’awa da manyan bukatu",
"Your Personality Traits": "Halayen Halinku",
"Your details are only used for the matching process.": "Your details are only used for the matching process.",
"Your information is kept strictly confidential.": "Your information is kept strictly confidential.",
@@ -807,5 +807,6 @@
"Enter medication name and reason for use...": "Shigar da sunan magani da dalilin sha...",
"نام دارو و دلیل مصرف را وارد نمایید...": "Shigar da sunan magani da dalilin sha...",
"وضعیت سلامت جسمانی": "Yanayin Lafiyar Jiki",
- "توضیحات وضعیت جسمانی": "Bayanin Lafiyar Jiki"
+ "توضیحات وضعیت جسمانی": "Bayanin Lafiyar Jiki",
+ "Name": "Suna"
}
\ No newline at end of file
diff --git a/src/translations/locales/he.json b/src/translations/locales/he.json
index cbe0f30..0fbd3ef 100644
--- a/src/translations/locales/he.json
+++ b/src/translations/locales/he.json
@@ -346,5 +346,14 @@
"Physical Health Status": "מצב בריאות פיזית",
"وضعیت سلامت جسمانی": "מצב בריאות פיזית",
"Physical Health Description": "תיאור בריאות פיזית",
- "توضیحات وضعیت جسمانی": "תיאור בריאות פיזית"
+ "توضیحات وضعیت جسمانی": "תיאור בריאות פיזית",
+ "Full Name": "שם מלא",
+ "Name": "שם",
+ "Current Residence": "מקום מגורים נוכחי",
+ "Highest Level of Education": "רמת ההשכלה הגבוהה ביותר",
+ "Field of Study": "תחום לימודים",
+ "Job Title": "תואר התפקיד",
+ "Employment Status": "מצב תעסוקתי",
+ "Your Hobbies and Main Interests": "התחביבים ותחומי העניין העיקריים שלך",
+ "View more details": "הצג פרטים נוספים"
}
\ No newline at end of file
diff --git a/src/translations/locales/hi.json b/src/translations/locales/hi.json
index 9b44413..edfc79b 100644
--- a/src/translations/locales/hi.json
+++ b/src/translations/locales/hi.json
@@ -199,7 +199,7 @@
"Education, Career, and Economic Status": "शिक्षा, करियर और आर्थिक स्थिति",
"Emotional": "भावुक",
"Employed": "कार्यरत",
- "Employment Status": "रोजगार की स्थिति",
+ "Employment Status": "रोज़गार की स्थिति",
"English": "अंग्रेजी",
"English, French, etc.": "अंग्रेजी, फ्रेंच, आदि",
"Enter details here...": "यहां विवरण दर्ज करें...",
@@ -260,7 +260,7 @@
"Height in Centimeters": "ऊंचाई सेंटीमीटर में",
"Help": "Help",
"High School Diploma": "हाई स्कूल डिप्लोमा",
- "Highest Level of Education": "शिक्षा का उच्चतम स्तर",
+ "Highest Level of Education": "उच्चतम शिक्षा स्तर",
"Homemaker": "गृहिणी",
"Homeowner": "गृहस्वामी",
"Hookah": "हुक्का",
@@ -308,7 +308,7 @@
"Iraq": "इराक",
"Items Per Page": "Items Per Page",
"Job Seeking / Unemployed": "नौकरी तलाशने वाले/बेरोजगार",
- "Job Title": "नौकरी का शीर्षक",
+ "Job Title": "पद / व्यवसाय",
"Joint custody or periodic visitation/relocation between parents.": "माता-पिता के बीच संयुक्त अभिरक्षा या आवधिक मुलाक़ात/स्थानांतरण।",
"Kurdish": "कुर्दिश",
"Language Learning": "भाषा सीखना",
@@ -725,7 +725,7 @@
"You have active access to view candidates.": "उम्मीदवारों को देखने के लिए आपके पास सक्रिय पहुंच है।",
"You will be contacted by your consultant.": "आपका सलाहकार आपसे संपर्क करेगा.",
"You've completed all required fields. However, filling in all sections will help us find better matches for you": "You've completed all required fields. However, filling in all sections will help us find better matches for you",
- "Your Hobbies and Main Interests": "आपके शौक और मुख्य रुचियाँ",
+ "Your Hobbies and Main Interests": "आपके शौक और मुख्य रुचियां",
"Your Personality Traits": "आपके व्यक्तित्व के लक्षण",
"Your details are only used for the matching process.": "Your details are only used for the matching process.",
"Your information is kept strictly confidential.": "Your information is kept strictly confidential.",
@@ -807,5 +807,6 @@
"Enter medication name and reason for use...": "दवा का नाम और उपयोग का कारण दर्ज करें...",
"نام دارو و دلیل مصرف را وارد نمایید...": "दवा का नाम और उपयोग का कारण दर्ज करें...",
"وضعیت سلامت جسمانی": "शारीरिक स्वास्थ्य की स्थिति",
- "توضیحات وضعیت جسمانی": "शारीरिक स्वास्थ्य का विवरण"
+ "توضیحات وضعیت جسمانی": "शारीरिक स्वास्थ्य का विवरण",
+ "Name": "नाम"
}
\ No newline at end of file
diff --git a/src/translations/locales/id.json b/src/translations/locales/id.json
index bd256cd..15bb748 100644
--- a/src/translations/locales/id.json
+++ b/src/translations/locales/id.json
@@ -346,5 +346,14 @@
"Physical Health Status": "Status Kesehatan Fisik",
"وضعیت سلامت جسمانی": "Status Kesehatan Fisik",
"Physical Health Description": "Deskripsi Kesehatan Fisik",
- "توضیحات وضعیت جسمانی": "Deskripsi Kesehatan Fisik"
+ "توضیحات وضعیت جسمانی": "Deskripsi Kesehatan Fisik",
+ "Full Name": "Nama Lengkap",
+ "Name": "Nama",
+ "Current Residence": "Tempat Tinggal Saat Ini",
+ "Highest Level of Education": "Tingkat Pendidikan Tertinggi",
+ "Field of Study": "Bidang Studi",
+ "Job Title": "Jabatan / Pekerjaan",
+ "Employment Status": "Status Pekerjaan",
+ "Your Hobbies and Main Interests": "Hobi dan Minat Utama Anda",
+ "View more details": "Lihat detail selengkapnya"
}
\ No newline at end of file
diff --git a/src/translations/locales/ks.json b/src/translations/locales/ks.json
index 1202a0b..ff18a96 100644
--- a/src/translations/locales/ks.json
+++ b/src/translations/locales/ks.json
@@ -285,7 +285,7 @@
"Day": "دوہ",
"Month": "ماہ",
"Year": "ؤری",
- "Age": "عمر",
+ "Age": "وٲنٛس",
"Confirm": "تصدیق کریں",
"Failed to load advisors.": "مشیرن لوڈ کرنأک ناکام۔",
"Try again": "دوبآ کوشش کریو",
@@ -346,5 +346,14 @@
"Physical Health Status": "جسمانی صحتٕچ حالت",
"وضعیت سلامت جسمانی": "جسمانی صحتٕچ حالت",
"Physical Health Description": "جسمانی صحتٕچ وضاحت",
- "توضیحات وضعیت جسمانی": "جسمانی صحتٕچ وضاحت"
+ "توضیحات وضعیت جسمانی": "جسمانی صحتٕچ وضاحت",
+ "Full Name": "پوٗرٕ ناڤ",
+ "Name": "ناو",
+ "Current Residence": "مَوجوٗدٕ رِہایش",
+ "Highest Level of Education": "ساروِی کھۄتہٕ زیٛادٕ تٲلیٖم",
+ "Field of Study": "تٲلیٖمی شۄعبہٕ",
+ "Job Title": "کٲم ہُنٛد ناو",
+ "Employment Status": "مُلازمتٕچ حالت",
+ "Your Hobbies and Main Interests": "تُہنٛدؠ شۄق تہٕ اَہَم دِلچسپی",
+ "View more details": "مزید تفصیل وُچھِو"
}
\ No newline at end of file
diff --git a/src/translations/locales/pt.json b/src/translations/locales/pt.json
index 4929988..5ed018f 100644
--- a/src/translations/locales/pt.json
+++ b/src/translations/locales/pt.json
@@ -346,5 +346,14 @@
"Physical Health Status": "Estado de Saúde Física",
"وضعیت سلامت جسمانی": "Estado de Saúde Física",
"Physical Health Description": "Descrição da Saúde Física",
- "توضیحات وضعیت جسمانی": "Descrição da Saúde Física"
+ "توضیحات وضعیت جسمانی": "Descrição da Saúde Física",
+ "Full Name": "Nome completo",
+ "Name": "Nome",
+ "Current Residence": "Residência atual",
+ "Highest Level of Education": "Nível de escolaridade mais alto",
+ "Field of Study": "Área de estudo",
+ "Job Title": "Cargo / Título profissional",
+ "Employment Status": "Situação profissional",
+ "Your Hobbies and Main Interests": "Seus hobbies e principais interesses",
+ "View more details": "Ver mais detalhes"
}
\ No newline at end of file
diff --git a/src/translations/locales/ru.json b/src/translations/locales/ru.json
index 6b50212..268671b 100644
--- a/src/translations/locales/ru.json
+++ b/src/translations/locales/ru.json
@@ -221,7 +221,7 @@
"Father alive": "Отец жив",
"Father has passed away": "Отец скончался",
"Feel free to briefly explain your decision...": "Не стесняйтесь кратко объяснить свое решение...",
- "Field of Study": "Область исследования",
+ "Field of Study": "Специальность / область обучения",
"Final Consent": "Окончательное согласие",
"Final Match Introduction": "Введение в финальный матч",
"Final Notice": "Последнее уведомление",
@@ -262,7 +262,7 @@
"Height in Centimeters": "Рост в сантиметрах",
"Help": "Помощь",
"High School Diploma": "Диплом средней школы",
- "Highest Level of Education": "Высший уровень образования",
+ "Highest Level of Education": "Наивысший уровень образования",
"Homemaker": "Домохозяйка",
"Homeowner": "домовладелец",
"Hookah": "Кальян",
@@ -310,7 +310,7 @@
"Iraq": "Ирак",
"Items Per Page": "Элементов на странице",
"Job Seeking / Unemployed": "Ищу работу / Безработный",
- "Job Title": "Должность",
+ "Job Title": "Должность / Профессия",
"Joint custody or periodic visitation/relocation between parents.": "Совместная опека или периодическое посещение/переезд родителей.",
"Kurdish": "курдский",
"Language Learning": "Изучение языка",
@@ -693,7 +693,7 @@
"View More Details": "Посмотреть более подробную информацию",
"View Profile": "Посмотреть профиль",
"View contact number": "Посмотреть контактный номер",
- "View more details": "Посмотреть более подробную информацию",
+ "View more details": "Посмотреть подробнее",
"View profile": "Посмотреть профиль",
"Watch Video": "Посмотреть видео",
"We are in the acquaintance/proposal process and nothing is finalized yet": "Мы находимся в процессе знакомства/предложения, и еще ничего не решено",
@@ -811,5 +811,6 @@
"Enter medication name and reason for use...": "Введите название препарата и причину приема...",
"نام دارو و دلیل مصرف را وارد نمایید...": "Введите название препарата и причину приема...",
"وضعیت سلامت جسمانی": "Физическое состояние здоровья",
- "توضیحات وضعیت جسمانی": "Описание физического состояния"
+ "توضیحات وضعیت جسمانی": "Описание физического состояния",
+ "Name": "Имя"
}
\ No newline at end of file
diff --git a/src/translations/locales/sw.json b/src/translations/locales/sw.json
index 48ad34d..224c42e 100644
--- a/src/translations/locales/sw.json
+++ b/src/translations/locales/sw.json
@@ -346,5 +346,14 @@
"Physical Health Status": "Hali ya Afya ya Kimwili",
"وضعیت سلامت جسمانی": "Hali ya Afya ya Kimwili",
"Physical Health Description": "Maelezo ya Afya ya Kimwili",
- "توضیحات وضعیت جسمانی": "Maelezo ya Afya ya Kimwili"
+ "توضیحات وضعیت جسمانی": "Maelezo ya Afya ya Kimwili",
+ "Full Name": "Jina Kamili",
+ "Name": "Jina",
+ "Current Residence": "Makazi ya Sasa",
+ "Highest Level of Education": "Kiwango cha Juu cha Elimu",
+ "Field of Study": "Uwanja wa Masomo",
+ "Job Title": "Wadhifa wa Kazi",
+ "Employment Status": "Hali ya Ajira",
+ "Your Hobbies and Main Interests": "Mambo unayopenda na Maslahi Kuu",
+ "View more details": "Angalia maelezo zaidi"
}
\ No newline at end of file
diff --git a/src/translations/locales/tg.json b/src/translations/locales/tg.json
index 68d67ba..b0dbf5e 100644
--- a/src/translations/locales/tg.json
+++ b/src/translations/locales/tg.json
@@ -346,5 +346,14 @@
"Physical Health Status": "Вазъи саломатии ҷисмонӣ",
"وضعیت سلامت جسمانی": "Вазъи саломатии ҷисмонӣ",
"Physical Health Description": "Тавсифи саломатии ҷисмонӣ",
- "توضیحات وضعیت جسمانی": "Тавсифи саломатии ҷисмонӣ"
+ "توضیحات وضعیت جسمانی": "Тавсифи саломатии ҷисмонӣ",
+ "Full Name": "Ному насаб",
+ "Name": "Ном",
+ "Current Residence": "Ҷои истиқомати ҳозира",
+ "Highest Level of Education": "Дараҷаи олии таҳсилот",
+ "Field of Study": "Соҳаи таҳсил",
+ "Job Title": "Унвони вазифа",
+ "Employment Status": "Вазъи шуғл",
+ "Your Hobbies and Main Interests": "Машғулиятҳо ва манфиатҳои асосии шумо",
+ "View more details": "Дидани тафсилоти бештар"
}
\ No newline at end of file
diff --git a/src/translations/locales/tr.json b/src/translations/locales/tr.json
index 9b909ff..0f5be69 100644
--- a/src/translations/locales/tr.json
+++ b/src/translations/locales/tr.json
@@ -346,5 +346,14 @@
"Physical Health Status": "Fiziksel Sağlık Durumu",
"وضعیت سلامت جسمانی": "Fiziksel Sağlık Durumu",
"Physical Health Description": "Fiziksel Sağlık Açıklaması",
- "توضیحات وضعیت جسمانی": "Fiziksel Sağlık Açıklaması"
+ "توضیحات وضعیت جسمانی": "Fiziksel Sağlık Açıklaması",
+ "Full Name": "Tam Adı",
+ "Name": "İsim",
+ "Current Residence": "Mevcut İkametgah",
+ "Highest Level of Education": "En Yüksek Eğitim Düzeyi",
+ "Field of Study": "Öğrenim Alanı / Bölüm",
+ "Job Title": "Meslek / Unvan",
+ "Employment Status": "Çalışma Durumu",
+ "Your Hobbies and Main Interests": "Hobileriniz ve Temel İlgi Alanlarınız",
+ "View more details": "Daha fazla ayrıntı gör"
}
\ No newline at end of file
diff --git a/src/translations/locales/ul.json b/src/translations/locales/ul.json
index 96bc765..3ba710b 100644
--- a/src/translations/locales/ul.json
+++ b/src/translations/locales/ul.json
@@ -285,7 +285,7 @@
"Day": "كۈن",
"Month": "ئاي",
"Year": "يىل",
- "Age": "يېشى",
+ "Age": "Age",
"Confirm": "جەزملەش",
"Failed to load advisors.": "مشاورین لوڈ نہیں ہو سکے۔",
"Try again": "دوبارہ کوشش کریں",
@@ -346,5 +346,14 @@
"Physical Health Status": "Jismoniy salomatlik",
"وضعیت سلامت جسمانی": "Jismoniy salomatlik",
"Physical Health Description": "Jismoniy salomatlik tavsifi",
- "توضیحات وضعیت جسمانی": "Jismoniy salomatlik tavsifi"
+ "توضیحات وضعیت جسمانی": "Jismoniy salomatlik tavsifi",
+ "Full Name": "Full Name",
+ "Name": "Name",
+ "Current Residence": "Current Residence",
+ "Highest Level of Education": "Highest Level of Education",
+ "Field of Study": "Field of Study",
+ "Job Title": "Job Title",
+ "Employment Status": "Employment Status",
+ "Your Hobbies and Main Interests": "Your Hobbies and Main Interests",
+ "View more details": "View more details"
}
\ No newline at end of file
diff --git a/src/translations/locales/ur.json b/src/translations/locales/ur.json
index 23a0461..7677d0e 100644
--- a/src/translations/locales/ur.json
+++ b/src/translations/locales/ur.json
@@ -346,5 +346,14 @@
"Physical Health Status": "جسمانی صحت کی حالت",
"وضعیت سلامت جسمانی": "جسمانی صحت کی حالت",
"Physical Health Description": "جسمانی صحت کی تفصیل",
- "توضیحات وضعیت جسمانی": "جسمانی صحت کی تفصیل"
+ "توضیحات وضعیت جسمانی": "جسمانی صحت کی تفصیل",
+ "Full Name": "مکمل نام",
+ "Name": "نام",
+ "Current Residence": "موجودہ رہائش",
+ "Highest Level of Education": "اعلیٰ ترین تعلیمی قابلیت",
+ "Field of Study": "شعبہ تعلیم",
+ "Job Title": "عہدہ / ملازمت کا عنوان",
+ "Employment Status": "ملازمت کی صورتحال",
+ "Your Hobbies and Main Interests": "آپ کے مشاغل اور اہم دلچسپیاں",
+ "View more details": "مزید تفصیلات دیکھیں"
}
\ No newline at end of file
diff --git a/src/translations/locales/uz.json b/src/translations/locales/uz.json
index a62fba7..9c69367 100644
--- a/src/translations/locales/uz.json
+++ b/src/translations/locales/uz.json
@@ -346,5 +346,14 @@
"Physical Health Status": "Jismoniy salomatlik holati",
"وضعیت سلامت جسمانی": "Jismoniy salomatlik holati",
"Physical Health Description": "Jismoniy salomatlik tavsifi",
- "توضیحات وضعیت جسمانی": "Jismoniy salomatlik tavsifi"
+ "توضیحات وضعیت جسمانی": "Jismoniy salomatlik tavsifi",
+ "Full Name": "Toʻliq ism",
+ "Name": "Ism",
+ "Current Residence": "Hozirgi yashash joyi",
+ "Highest Level of Education": "Eng yuqori taʼlim darajasi",
+ "Field of Study": "Oʻqish sohasi / Yoʻnalishi",
+ "Job Title": "Kasb / Lavozim",
+ "Employment Status": "Bandlik holati",
+ "Your Hobbies and Main Interests": "Qiziqishlaringiz va asosiy mashgʻulotlaringiz",
+ "View more details": "Batafsil maʼlumotni koʻrish"
}
\ No newline at end of file
diff --git a/src/translations/locales/zh.json b/src/translations/locales/zh.json
index ba0e472..9cd097a 100644
--- a/src/translations/locales/zh.json
+++ b/src/translations/locales/zh.json
@@ -46,7 +46,7 @@
"Additional Comments on Economic and Housing Status": "关于经济和住房状况的补充意见",
"Additional details about family responsibility": "有关家庭责任的其他详细信息",
"Afghanistan": "Afghanistan",
- "Age": "Age",
+ "Age": "年龄",
"Alcohol is a serious red line": "酒精是一条严重的红线",
"Alcohol is a serious red line for me": "Alcohol is a serious red line for me",
"Alcoholic Beverages": "Alcoholic Beverages",
@@ -141,7 +141,7 @@
"Current Housing Status": "目前的住房状况",
"Current Marital Status": "Current Marital Status",
"Current Nationality / Citizenship": "Current Nationality / Citizenship",
- "Current Residence": "Current Residence",
+ "Current Residence": "现居住地",
"Current user": "当前用户",
"Currently building suitable financial conditions": "目前正在建立适当的财务条件",
"Curvy/Full": "曲线/丰满",
@@ -219,7 +219,7 @@
"Father alive": "父亲还活着",
"Father has passed away": "父亲去世了",
"Feel free to briefly explain your decision...": "Your explanatory text ...",
- "Field of Study": "研究领域",
+ "Field of Study": "专业 / 学习领域",
"Final Consent": "Final Consent",
"Final Match Introduction": "Final Match Introduction",
"Final Notice": "Final Notice",
@@ -260,7 +260,7 @@
"Height in Centimeters": "高度(厘米)",
"Help": "Help",
"High School Diploma": "高中文凭",
- "Highest Level of Education": "最高教育水平",
+ "Highest Level of Education": "最高学历",
"Homemaker": "家庭主妇",
"Homeowner": "房主",
"Hookah": "水烟",
@@ -725,7 +725,7 @@
"You have active access to view candidates.": "您可以主动访问查看候选人。",
"You will be contacted by your consultant.": "您的顾问将与您联系。",
"You've completed all required fields. However, filling in all sections will help us find better matches for you": "You've completed all required fields. However, filling in all sections will help us find better matches for you",
- "Your Hobbies and Main Interests": "你的爱好和主要兴趣",
+ "Your Hobbies and Main Interests": "您的爱好和主要兴趣",
"Your Personality Traits": "你的性格特征",
"Your details are only used for the matching process.": "Your details are only used for the matching process.",
"Your information is kept strictly confidential.": "Your information is kept strictly confidential.",
@@ -807,5 +807,6 @@
"Enter medication name and reason for use...": "输入药物名称和使用原因...",
"نام دارو و دلیل مصرف را وارد نمایید...": "输入药物名称和使用原因...",
"وضعیت سلامت جسمانی": "身体健康状况",
- "توضیحات وضعیت جسمانی": "身体健康说明"
+ "توضیحات وضعیت جسمانی": "身体健康说明",
+ "Name": "姓名"
}
\ No newline at end of file
diff --git a/src/translations/provider.tsx b/src/translations/provider.tsx
index f1dad0b..aa6bd1b 100644
--- a/src/translations/provider.tsx
+++ b/src/translations/provider.tsx
@@ -1,8 +1,9 @@
"use client";
-import { createContext, type ReactNode, useContext, useMemo } from "react";
+import { createContext, type ReactNode, useContext, useEffect, useMemo } from "react";
import { defaultLocale, type Locale } from "@/translations/config";
import { type Dictionary, getDictionary } from "@/translations/dictionaries";
+import { getUserGeoRegion } from "@/lib/geo-region";
type I18nContextValue = {
locale: Locale;
@@ -20,6 +21,10 @@ type I18nProviderProps = {
};
export function I18nProvider({ children, locale }: I18nProviderProps) {
+ useEffect(() => {
+ void getUserGeoRegion(true);
+ }, []);
+
const value = useMemo(
() => ({
locale,
+ {pairedPersonalFields.map((field, index) => ( + + {index > 0 ? ( + | + ) : null} + + {field.label}:{" "} + + {field.value} + + ))} +
) : null} - {pairedFields.length ? ( -- {pairedFields.map((field, index) => ( + {pairedEduFields.length ? ( +
+ {pairedEduFields.map((field, index) => ( - {index > 0 ? | : null} - - {field.label}: {field.value} + {index > 0 ? ( + | + ) : null} + + {field.label}:{" "} + {field.value} ))}
) : null} + {matchDisplay.job ? ( +- Dr. Hasti Masoudi + {t["Dr. Hasti Masoudi"] || "Dr. Hasti Masoudi"}
- We have come together with the goal of creating a secure and - confidential path for "permanent marriage" among Muslims +
+ {t[ + 'We have come together with the goal of creating a secure and confidential path for "permanent marriage" among Muslims' + ] || + 'We have come together with the goal of creating a secure and confidential path for "permanent marriage" among Muslims'}