Browse Source

feat: implement question navigation system and multi-language support

front-test-2
ghorbani 2 weeks ago
parent
commit
110b192c82
  1. 2
      src/app/questions-list/page.tsx
  2. 100
      src/app/terms/page.test.tsx
  3. 59
      src/app/terms/page.tsx
  4. 24
      src/components/Componentes/question-date.tsx
  5. 14
      src/components/Componentes/question-exit-navigation-button.tsx
  6. 8
      src/components/Componentes/question-section-flow.tsx
  7. 265
      src/components/Componentes/slider-page.test.tsx
  8. 77
      src/components/Componentes/slider-page.tsx
  9. 88
      src/lib/get-submit-path.test.ts
  10. 3
      src/translations/locales/ar.json
  11. 3
      src/translations/locales/az.json
  12. 3
      src/translations/locales/bn.json
  13. 3
      src/translations/locales/da.json
  14. 3
      src/translations/locales/de.json
  15. 4
      src/translations/locales/en.json
  16. 3
      src/translations/locales/es.json
  17. 4
      src/translations/locales/fa.json
  18. 3
      src/translations/locales/fr.json
  19. 3
      src/translations/locales/gu.json
  20. 3
      src/translations/locales/ha.json
  21. 3
      src/translations/locales/he.json
  22. 3
      src/translations/locales/hi.json
  23. 3
      src/translations/locales/id.json
  24. 3
      src/translations/locales/ks.json
  25. 3
      src/translations/locales/pt.json
  26. 3
      src/translations/locales/ru.json
  27. 3
      src/translations/locales/sw.json
  28. 3
      src/translations/locales/tg.json
  29. 3
      src/translations/locales/tr.json
  30. 3
      src/translations/locales/ul.json
  31. 3
      src/translations/locales/ur.json
  32. 3
      src/translations/locales/uz.json
  33. 3
      src/translations/locales/zh.json

2
src/app/questions-list/page.tsx

@ -79,7 +79,9 @@ export default function QuestionsListPage() {
// Add fallback for combined section from the schema adapter which attaches it to questionListItems directly // Add fallback for combined section from the schema adapter which attaches it to questionListItems directly
questionListItems.forEach((item) => { questionListItems.forEach((item) => {
if (!progressBySlug.has(item.slug)) {
progressBySlug.set(item.slug, item.progress); progressBySlug.set(item.slug, item.progress);
}
}); });
return progressBySlug; return progressBySlug;

100
src/app/terms/page.test.tsx

@ -0,0 +1,100 @@
import { render, screen, fireEvent, waitFor, cleanup } from '@testing-library/react';
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
import TermsRoute from './page';
const mockUseMarriageProfileQuery = vi.fn();
const mockReplace = vi.fn();
vi.mock('@/hooks/marriage/use-profile-main', () => ({
useMarriageProfileQuery: () => mockUseMarriageProfileQuery(),
}));
vi.mock('next/navigation', () => ({
useRouter: () => ({
replace: mockReplace,
}),
}));
vi.mock('@/translations/provider', () => ({
useI18n: () => ({
locale: 'en',
dictionary: {
"Failed to load profile. Please try again.": "Failed to load profile. Please try again.",
"Retry": "Retry",
},
}),
}));
vi.mock('@/translations/config', () => ({
localizePath: (path: string) => path,
}));
vi.mock('@/components/Componentes/slider-page', () => ({
default: () => <div data-testid="slider-page">SliderPage</div>,
}));
describe('TermsRoute Guard', () => {
beforeEach(() => {
vi.clearAllMocks();
});
afterEach(() => {
cleanup();
});
it('renders loading state when isLoading is true', () => {
mockUseMarriageProfileQuery.mockReturnValue({ isLoading: true });
render(<TermsRoute />);
expect(screen.queryByTestId('slider-page')).not.toBeInTheDocument();
// The spinner should be visible, maybe just check for a div. We can check slider is absent.
});
it('renders Error/Retry when isError is true', () => {
const mockRefetch = vi.fn();
mockUseMarriageProfileQuery.mockReturnValue({ isError: true, refetch: mockRefetch });
render(<TermsRoute />);
expect(screen.getByText('Failed to load profile. Please try again.')).toBeInTheDocument();
const retryBtn = screen.getByText('Retry');
fireEvent.click(retryBtn);
expect(mockRefetch).toHaveBeenCalled();
expect(screen.queryByTestId('slider-page')).not.toBeInTheDocument();
});
it('pending_onboarding => SliderPage renders', () => {
mockUseMarriageProfileQuery.mockReturnValue({
isLoading: false,
data: { status: 'pending_onboarding' }
});
render(<TermsRoute />);
expect(screen.getByTestId('slider-page')).toBeInTheDocument();
expect(mockReplace).not.toHaveBeenCalled();
});
it('pending_info => redirects to Questions and SliderPage is NOT rendered', async () => {
mockUseMarriageProfileQuery.mockReturnValue({
isLoading: false,
data: { status: 'pending_info' }
});
render(<TermsRoute />);
expect(screen.queryByTestId('slider-page')).not.toBeInTheDocument();
await waitFor(() => {
expect(mockReplace).toHaveBeenCalled(); // Should redirect to /questions-list or /questions-list/personal_info
});
});
it('waiting => redirects to Finding/Waiting Page and SliderPage is NOT rendered', async () => {
mockUseMarriageProfileQuery.mockReturnValue({
isLoading: false,
data: { status: 'waiting' }
});
render(<TermsRoute />);
expect(screen.queryByTestId('slider-page')).not.toBeInTheDocument();
await waitFor(() => {
expect(mockReplace).toHaveBeenCalledWith('/finding-match');
});
});
});

59
src/app/terms/page.tsx

@ -1,5 +1,64 @@
"use client";
import { useEffect } from "react";
import { useRouter } from "next/navigation";
import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main";
import { getSubmitPath } from "@/lib/get-submit-path";
import SliderPage from "@/components/Componentes/slider-page"; import SliderPage from "@/components/Componentes/slider-page";
import Button from "@/components/Componentes/button";
import { useI18n } from "@/translations/provider";
import { localizePath } from "@/translations/config";
export default function TermsRoute() { export default function TermsRoute() {
const { data: profile, isLoading, isError, refetch } = useMarriageProfileQuery();
const router = useRouter();
const { locale, dictionary: t } = useI18n();
useEffect(() => {
if (!isLoading && profile) {
if (profile.status !== "pending_onboarding") {
const target = getSubmitPath(profile);
if (target !== "/terms") {
router.replace(localizePath(target, locale));
}
}
}
}, [profile, isLoading, router, locale]);
if (isLoading) {
return (
<div className="flex h-[100dvh] items-center justify-center bg-[#F5F5F5]">
<div className="size-8 animate-spin rounded-full border-4 border-[#F14B46] border-t-transparent" />
</div>
);
}
if (isError) {
return (
<div className="flex h-[100dvh] flex-col items-center justify-center p-4 bg-[#F5F5F5]">
<p className="mb-4 text-center font-medium text-[#F14B46]">
{/* @ts-expect-error - missing key */}
{t["Failed to load profile. Please try again."] || "Failed to load profile. Please try again."}
</p>
<div className="w-full max-w-[200px]">
<Button onClick={() => refetch()}>
{/* @ts-expect-error - missing key */}
{t["Retry"] || "Retry"}
</Button>
</div>
</div>
);
}
if (profile && profile.status !== "pending_onboarding") {
// If target is /terms but status is not pending_onboarding, we should still not render SliderPage
// to prevent showing Onboarding to users who already finished it but have an unknown status.
return (
<div className="flex h-[100dvh] items-center justify-center bg-[#F5F5F5]">
<div className="size-8 animate-spin rounded-full border-4 border-[#F14B46] border-t-transparent" />
</div>
);
}
return <SliderPage />; return <SliderPage />;
} }

24
src/components/Componentes/question-date.tsx

@ -12,18 +12,18 @@ type QuestionDateProps = {
}; };
const MONTHS = [ const MONTHS = [
{ value: "01", label: "01 - January" },
{ value: "02", label: "02 - February" },
{ value: "03", label: "03 - March" },
{ value: "04", label: "04 - April" },
{ value: "05", label: "05 - May" },
{ value: "06", label: "06 - June" },
{ value: "07", label: "07 - July" },
{ value: "08", label: "08 - August" },
{ value: "09", label: "09 - September" },
{ value: "10", label: "10 - October" },
{ value: "11", label: "11 - November" },
{ value: "12", label: "12 - December" },
{ value: "01", label: "January" },
{ value: "02", label: "February" },
{ value: "03", label: "March" },
{ value: "04", label: "April" },
{ value: "05", label: "May" },
{ value: "06", label: "June" },
{ value: "07", label: "July" },
{ value: "08", label: "August" },
{ value: "09", label: "September" },
{ value: "10", label: "October" },
{ value: "11", label: "November" },
{ value: "12", label: "December" },
]; ];
const DAYS = Array.from({ length: 31 }, (_, i) => { const DAYS = Array.from({ length: 31 }, (_, i) => {

14
src/components/Componentes/question-exit-navigation-button.tsx

@ -25,28 +25,32 @@ export function QuestionExitNavigationButton({
const { locale } = useI18n(); const { locale } = useI18n();
const { flushAnswers } = useQuestionAnswers(); const { flushAnswers } = useQuestionAnswers();
const [isNavigating, setIsNavigating] = useState(false);
return ( return (
<NavigationButton <NavigationButton
{...props} {...props}
onClick={(event) => {
disabled={props.disabled || isNavigating}
onClick={async (event) => {
props.onClick?.(event); props.onClick?.(event);
if (event.defaultPrevented) {
if (event.defaultPrevented || isNavigating) {
return; return;
} }
event.preventDefault(); event.preventDefault();
setIsNavigating(true);
try { try {
markFirstEntryCompleted(); markFirstEntryCompleted();
void flushAnswers({ force: true });
await flushAnswers({ force: true });
} catch { } catch {
// ignore // ignore
}
} finally {
triggerSilentReload(queryClient); triggerSilentReload(queryClient);
const target = localizePath(exitHref || "/questions-list", locale); const target = localizePath(exitHref || "/questions-list", locale);
router.push(target); router.push(target);
}
}} }}
/> />
); );

8
src/components/Componentes/question-section-flow.tsx

@ -54,7 +54,7 @@ function SectionFlowContent({
void flushAnswers({ force: true }); void flushAnswers({ force: true });
}, [flushAnswers]); }, [flushAnswers]);
const handleSubmit = useCallback(() => {
const handleSubmit = useCallback(async () => {
if (isSubmitting) { if (isSubmitting) {
return; return;
} }
@ -62,14 +62,14 @@ function SectionFlowContent({
try { try {
markFirstEntryCompleted(); markFirstEntryCompleted();
void flushAnswers({ force: true });
await flushAnswers({ force: true });
} catch { } catch {
// ignore // ignore
}
} finally {
triggerSilentReload(queryClient); triggerSilentReload(queryClient);
const target = localizePath(exitHref || "/questions-list", locale); const target = localizePath(exitHref || "/questions-list", locale);
router.push(target); router.push(target);
}
}, [exitHref, flushAnswers, locale, router, isSubmitting, queryClient]); }, [exitHref, flushAnswers, locale, router, isSubmitting, queryClient]);
const markOptionalQuestionsPassed = useCallback( const markOptionalQuestionsPassed = useCallback(

265
src/components/Componentes/slider-page.test.tsx

@ -0,0 +1,265 @@
import { render, screen, fireEvent, waitFor, cleanup } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { vi, describe, it, expect, beforeEach, afterEach } from 'vitest';
import SliderPage from './slider-page';
const mockMutateAsync = vi.fn();
const mockGetMarriageProfile = vi.fn();
const mockSetQueryData = vi.fn();
const mockReplace = vi.fn();
const mockRemoveItem = vi.spyOn(Storage.prototype, 'removeItem');
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
vi.mock('@/hooks/marriage/use-profile-basic', () => ({
useUpdateMarriageProfileBasicMutation: () => ({
mutateAsync: mockMutateAsync,
isPending: false,
}),
}));
vi.mock('@/hooks/marriage/use-profile-main', () => ({
getMarriageProfile: (...args: any[]) => mockGetMarriageProfile(...args),
useMarriageProfileQuery: () => ({ data: undefined, refetch: vi.fn() }),
}));
vi.mock('@/hooks/marriage/query-keys', () => ({
marriageQueryKeys: {
profile: () => ['marriage', 'profile'],
},
}));
vi.mock('next/navigation', () => ({
useRouter: () => ({
replace: mockReplace,
back: vi.fn(),
}),
}));
vi.mock('@/translations/provider', () => ({
useI18n: () => ({
locale: 'en',
dictionary: {
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again.",
"Accept & Continue": "Accept & Continue",
},
}),
}));
vi.mock('@/translations/config', () => ({
localizePath: (path: string, locale: string) => path,
}));
describe('SliderPage', () => {
let queryClient: QueryClient;
beforeEach(() => {
vi.resetAllMocks();
queryClient = new QueryClient();
vi.spyOn(queryClient, 'setQueryData');
});
afterEach(() => {
vi.useRealTimers();
cleanup();
});
const navigateToFinalSlide = async () => {
render(
<QueryClientProvider client={queryClient}>
<SliderPage />
</QueryClientProvider>
);
const dots = screen.getAllByRole('button', { name: /go to slide/i });
fireEvent.click(dots[4]); // Go directly to slide 5
return await screen.findByText('Finish');
};
it('a. Success: PATCH ok, GET ok -> replace', async () => {
const finishBtn = await navigateToFinalSlide();
const initialRemoveCount = mockRemoveItem.mock.calls.filter(call => call[0] === 'marriage-slider-answers').length;
let resolveGet: any;
mockMutateAsync.mockResolvedValueOnce({
status: 'pending_info',
gender: 'female',
is_registering_for_self: true
});
mockGetMarriageProfile.mockReturnValueOnce(new Promise(resolve => {
resolveGet = resolve;
}));
fireEvent.click(finishBtn);
// Wait for PATCH to resolve and GET to be called
await waitFor(() => {
expect(mockMutateAsync).toHaveBeenCalled();
expect(mockGetMarriageProfile).toHaveBeenCalled();
});
// Assert navigation and localStorage clear did not happen before GET resolves
expect(mockReplace).not.toHaveBeenCalled();
const currentRemoveCount = mockRemoveItem.mock.calls.filter(call => call[0] === 'marriage-slider-answers').length;
expect(currentRemoveCount).toBe(initialRemoveCount);
// Resolve GET
resolveGet({
status: 'pending_info',
gender: 'female',
is_registering_for_self: true
});
await waitFor(() => {
expect(queryClient.setQueryData).toHaveBeenCalled();
const finalRemoveCount = mockRemoveItem.mock.calls.filter(call => call[0] === 'marriage-slider-answers').length;
expect(finalRemoveCount).toBe(initialRemoveCount + 1);
expect(mockReplace).toHaveBeenCalledWith('/questions-list/personal_info');
});
});
it('b. PATCH failure: no nav, Error UI, Retry works', async () => {
const finishBtn = await navigateToFinalSlide();
const initialRemoveCount = mockRemoveItem.mock.calls.filter(call => call[0] === 'marriage-slider-answers').length;
mockMutateAsync.mockRejectedValueOnce(new Error('Network Error'));
fireEvent.click(finishBtn);
await waitFor(() => {
expect(screen.getByRole('alert')).toHaveTextContent('Failed to update profile basic details. Please try again.');
});
expect(mockReplace).not.toHaveBeenCalled();
const currentRemoveCount = mockRemoveItem.mock.calls.filter(call => call[0] === 'marriage-slider-answers').length;
expect(currentRemoveCount).toBe(initialRemoveCount);
// Retry succeeds
mockMutateAsync.mockResolvedValueOnce({
status: 'pending_info',
gender: 'female',
is_registering_for_self: true
});
mockGetMarriageProfile.mockResolvedValueOnce({
status: 'pending_info',
gender: 'female',
is_registering_for_self: true
});
const retryBtn = screen.getByText('Finish');
fireEvent.click(retryBtn);
await waitFor(() => {
expect(mockReplace).toHaveBeenCalledWith('/questions-list/personal_info');
});
const finalRemoveCount = mockRemoveItem.mock.calls.filter(call => call[0] === 'marriage-slider-answers').length;
expect(finalRemoveCount).toBe(initialRemoveCount + 1);
});
it('c. Slow request: Promise pending -> no redirect', async () => {
const finishBtn = await navigateToFinalSlide();
vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout', 'setInterval', 'clearInterval', 'Date'] });
let resolvePatch: any;
mockMutateAsync.mockReturnValueOnce(new Promise(resolve => {
resolvePatch = resolve;
}));
mockGetMarriageProfile.mockResolvedValueOnce({
status: 'pending_info',
gender: 'female',
is_registering_for_self: true
});
fireEvent.click(finishBtn);
await vi.advanceTimersByTimeAsync(6000);
expect(mockReplace).not.toHaveBeenCalled();
resolvePatch({
status: 'pending_info',
gender: 'female',
is_registering_for_self: true
});
vi.useRealTimers();
await waitFor(() => {
expect(mockReplace).toHaveBeenCalledWith('/questions-list/personal_info');
});
});
it('d. Double-check failure: GET returns pending_onboarding -> Error UI', async () => {
const finishBtn = await navigateToFinalSlide();
mockMutateAsync.mockResolvedValueOnce({
status: 'pending_info',
gender: 'female',
is_registering_for_self: true
});
mockGetMarriageProfile.mockResolvedValueOnce({
status: 'pending_onboarding',
gender: 'female',
is_registering_for_self: true
});
fireEvent.click(finishBtn);
await waitFor(() => {
expect(screen.getByRole('alert')).toHaveTextContent('Failed to update profile basic details. Please try again.');
});
expect(mockReplace).not.toHaveBeenCalled();
});
it('e. Double click: only 1 request', async () => {
const finishBtn = await navigateToFinalSlide();
let resolvePatch: any;
mockMutateAsync.mockReturnValueOnce(new Promise(resolve => {
resolvePatch = resolve;
}));
mockGetMarriageProfile.mockResolvedValueOnce({
status: 'pending_info',
gender: 'female',
is_registering_for_self: true
});
fireEvent.click(finishBtn);
fireEvent.click(finishBtn);
fireEvent.click(finishBtn);
expect(mockMutateAsync).toHaveBeenCalledTimes(1);
resolvePatch({ status: 'pending_info', gender: 'female', is_registering_for_self: true });
await waitFor(() => {
expect(mockReplace).toHaveBeenCalled();
});
});
it('f. Double-check failure: GET returns waiting -> Error UI', async () => {
const finishBtn = await navigateToFinalSlide();
mockMutateAsync.mockResolvedValueOnce({
status: 'pending_info',
gender: 'female',
is_registering_for_self: true
});
mockGetMarriageProfile.mockResolvedValueOnce({
status: 'waiting',
gender: 'female',
is_registering_for_self: true
});
fireEvent.click(finishBtn);
await waitFor(() => {
expect(screen.getByRole('alert')).toHaveTextContent('Failed to update profile basic details. Please try again.');
});
expect(mockReplace).not.toHaveBeenCalled();
});
});

77
src/components/Componentes/slider-page.tsx

@ -3,6 +3,7 @@
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import type { TouchEvent } from "react"; import type { TouchEvent } from "react";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useQueryClient } from "@tanstack/react-query";
import { useUpdateMarriageProfileBasicMutation } from "@/hooks/marriage/use-profile-basic"; import { useUpdateMarriageProfileBasicMutation } from "@/hooks/marriage/use-profile-basic";
import { localizePath } from "@/translations/config"; import { localizePath } from "@/translations/config";
import { useI18n } from "@/translations/provider"; import { useI18n } from "@/translations/provider";
@ -29,6 +30,10 @@ export default function SliderPage() {
const [hasReadRules, setHasReadRules] = useState(false); const [hasReadRules, setHasReadRules] = useState(false);
const [touchStartX, setTouchStartX] = useState<number | null>(null); const [touchStartX, setTouchStartX] = useState<number | null>(null);
const updateProfileBasicMutation = useUpdateMarriageProfileBasicMutation(); const updateProfileBasicMutation = useUpdateMarriageProfileBasicMutation();
const queryClient = useQueryClient();
const [submitError, setSubmitError] = useState<string | null>(null);
const [isSubmitting, setIsSubmitting] = useState(false);
const { dictionary: t } = useI18n();
const hasFinalNotice = true; const hasFinalNotice = true;
const maxSlideIndex = FINAL_SLIDE_COUNT - 1; const maxSlideIndex = FINAL_SLIDE_COUNT - 1;
const displaySlideCount = FINAL_SLIDE_COUNT; const displaySlideCount = FINAL_SLIDE_COUNT;
@ -54,36 +59,51 @@ export default function SliderPage() {
}; };
const completeSlider = async () => { const completeSlider = async () => {
const navigateAway = () => {
if (typeof window !== "undefined") {
window.localStorage.removeItem(SLIDER_ANSWERS_STORAGE_KEY);
// Use hard navigation instead of router.push() to ensure the
// destination page loads with a fresh auth state. SPA (soft)
// navigation sometimes leaves stale query-cache / token state
// that causes the profile query to return 401 and the page to
// stay stuck on the loading spinner.
window.location.href = localizePath(
"/questions-list/personal_info",
locale,
);
return;
}
router.push(localizePath("/questions-list/personal_info", locale));
};
if (isSubmitting) return;
setIsSubmitting(true);
setSubmitError(null);
// Safety timeout — navigate after 5 seconds even if the request hangs
const timeout = setTimeout(navigateAway, 5000);
const genderPayload = selectedGender === "man" ? "male" : "female";
const isRegisteringPayload = selectedRegistration === "self";
try { try {
await updateProfileBasicMutation.mutateAsync({
gender: selectedGender === "man" ? "male" : "female",
is_registering_for_self: selectedRegistration === "self",
const patchResponse = await updateProfileBasicMutation.mutateAsync({
gender: genderPayload,
is_registering_for_self: isRegisteringPayload,
}); });
if (
patchResponse.status === "pending_onboarding" ||
patchResponse.gender !== genderPayload ||
patchResponse.is_registering_for_self !== isRegisteringPayload
) {
throw new Error("Invalid PATCH response");
}
const { getMarriageProfile } = await import("@/hooks/marriage/use-profile-main");
const freshProfile = await getMarriageProfile();
if (
freshProfile.status !== "pending_info" ||
freshProfile.gender !== genderPayload ||
freshProfile.is_registering_for_self !== isRegisteringPayload
) {
throw new Error("Invalid GET double-check response");
}
const { marriageQueryKeys } = await import("@/hooks/marriage/query-keys");
queryClient.setQueryData(marriageQueryKeys.profile(), freshProfile);
if (typeof window !== "undefined") {
window.localStorage.removeItem(SLIDER_ANSWERS_STORAGE_KEY);
}
router.replace(localizePath("/questions-list/personal_info", locale));
} catch (error) { } catch (error) {
console.warn("Failed to update profile basic details:", 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 { } finally {
clearTimeout(timeout);
navigateAway();
setIsSubmitting(false);
} }
}; };
@ -184,11 +204,18 @@ export default function SliderPage() {
disabled={!hasReadRules} disabled={!hasReadRules}
/> />
) : activeSlide === maxSlideIndex ? ( ) : activeSlide === maxSlideIndex ? (
<div className="flex flex-col gap-3">
{submitError && (
<div role="alert" aria-live="assertive" className="text-sm text-red-500 bg-red-50 p-3 rounded-xl border border-red-200">
{submitError}
</div>
)}
<SliderFinalActions <SliderFinalActions
onBack={goToPreviousSlide} onBack={goToPreviousSlide}
isFinishing={updateProfileBasicMutation.isPending}
isFinishing={isSubmitting}
onFinish={completeSlider} onFinish={completeSlider}
/> />
</div>
) : ( ) : (
<SliderStepActions <SliderStepActions
onBack={goToPreviousSlide} onBack={goToPreviousSlide}

88
src/lib/get-submit-path.test.ts

@ -0,0 +1,88 @@
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { getSubmitPath } from './get-submit-path';
import * as firstEntryHelper from './first-entry-helper';
import * as matchStartGrace from './match-start-grace';
import type { MarriageProfileResponse } from '@/hooks/marriage/types';
vi.mock('./first-entry-helper', () => ({
isFirstEntryCompleted: vi.fn(),
}));
vi.mock('./match-start-grace', () => ({
clearLegacyMatchSubmittedFlag: vi.fn(),
isWithinMatchStartGrace: vi.fn(),
}));
describe('getSubmitPath', () => {
beforeEach(() => {
vi.clearAllMocks();
vi.mocked(firstEntryHelper.isFirstEntryCompleted).mockReturnValue(true);
vi.mocked(matchStartGrace.isWithinMatchStartGrace).mockReturnValue(false);
});
const baseProfile: MarriageProfileResponse = {
status: 'pending_onboarding',
id: 1,
gender: 'male',
is_registering_for_self: true,
};
it('pending_onboarding returns /terms', () => {
expect(getSubmitPath({ ...baseProfile, status: 'pending_onboarding' })).toBe('/terms');
});
it('waiting returns /finding-match', () => {
expect(getSubmitPath({ ...baseProfile, status: 'waiting' })).toBe('/finding-match');
});
it('pending_info returns questions path', () => {
expect(getSubmitPath({ ...baseProfile, status: 'pending_info' })).toBe('/questions-list');
vi.mocked(firstEntryHelper.isFirstEntryCompleted).mockReturnValue(false);
expect(getSubmitPath({ ...baseProfile, status: 'pending_info' })).toBe('/questions-list/personal_info');
});
it('matched returns /request-accepted', () => {
expect(getSubmitPath({ ...baseProfile, status: 'matched' })).toBe('/request-accepted');
});
describe('active_case priority', () => {
it('returns /new-match when status="pending_info" but active_case is introduced and action pending', () => {
const profileWithCase = {
...baseProfile,
status: 'pending_info' as const,
active_case: {
status: 'introduced' as const,
my_action: 'pending' as const,
},
};
// Type assertion added because the mock profile object is lacking many properties of the full MarriageProfileResponse,
// but it contains everything needed for the logic being tested.
expect(getSubmitPath(profileWithCase as any)).toBe('/new-match');
});
it('returns /request-accepted when status="pending_info" but active_case is payment_done', () => {
const profileWithCase = {
...baseProfile,
status: 'pending_info' as const,
active_case: {
status: 'payment_done' as const,
my_action: 'pending' as const,
},
};
expect(getSubmitPath(profileWithCase as any)).toBe('/request-accepted');
});
it('returns /request-sent when status="waiting" but active_case is male_accepted and action done', () => {
const profileWithCase = {
...baseProfile,
status: 'waiting' as const,
active_case: {
status: 'male_accepted' as const,
my_action: 'done' as const,
},
};
expect(getSubmitPath(profileWithCase as any)).toBe('/request-sent');
});
});
});

3
src/translations/locales/ar.json

@ -753,5 +753,6 @@
"We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet", "We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet",
"Please select the reason for cancellation:": "Please select the reason for cancellation:", "Please select the reason for cancellation:": "Please select the reason for cancellation:",
"Swipe to continue": "Swipe to continue", "Swipe to continue": "Swipe to continue",
"Swipe to confirm cancellation": "Swipe to confirm cancellation"
"Swipe to confirm cancellation": "Swipe to confirm cancellation",
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again."
} }

3
src/translations/locales/az.json

@ -753,5 +753,6 @@
"We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet", "We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet",
"Please select the reason for cancellation:": "Please select the reason for cancellation:", "Please select the reason for cancellation:": "Please select the reason for cancellation:",
"Swipe to continue": "Swipe to continue", "Swipe to continue": "Swipe to continue",
"Swipe to confirm cancellation": "Swipe to confirm cancellation"
"Swipe to confirm cancellation": "Swipe to confirm cancellation",
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again."
} }

3
src/translations/locales/bn.json

@ -753,5 +753,6 @@
"We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet", "We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet",
"Please select the reason for cancellation:": "Please select the reason for cancellation:", "Please select the reason for cancellation:": "Please select the reason for cancellation:",
"Swipe to continue": "Swipe to continue", "Swipe to continue": "Swipe to continue",
"Swipe to confirm cancellation": "Swipe to confirm cancellation"
"Swipe to confirm cancellation": "Swipe to confirm cancellation",
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again."
} }

3
src/translations/locales/da.json

@ -753,5 +753,6 @@
"We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet", "We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet",
"Please select the reason for cancellation:": "Please select the reason for cancellation:", "Please select the reason for cancellation:": "Please select the reason for cancellation:",
"Swipe to continue": "Swipe to continue", "Swipe to continue": "Swipe to continue",
"Swipe to confirm cancellation": "Swipe to confirm cancellation"
"Swipe to confirm cancellation": "Swipe to confirm cancellation",
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again."
} }

3
src/translations/locales/de.json

@ -753,5 +753,6 @@
"We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet", "We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet",
"Please select the reason for cancellation:": "Please select the reason for cancellation:", "Please select the reason for cancellation:": "Please select the reason for cancellation:",
"Swipe to continue": "Swipe to continue", "Swipe to continue": "Swipe to continue",
"Swipe to confirm cancellation": "Swipe to confirm cancellation"
"Swipe to confirm cancellation": "Swipe to confirm cancellation",
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again."
} }

4
src/translations/locales/en.json

@ -760,7 +760,6 @@
"⚠️ This section is completely confidential and is only used for matching and review by advisors.": "⚠️ This section is completely confidential and is only used for matching and review by advisors.", "⚠️ This section is completely confidential and is only used for matching and review by advisors.": "⚠️ This section is completely confidential and is only used for matching and review by advisors.",
"Please select the reason for cancellation:": "Please select the reason for cancellation:", "Please select the reason for cancellation:": "Please select the reason for cancellation:",
"Marriage advisors": "Marriage advisors", "Marriage advisors": "Marriage advisors",
"Dr. Hasti Masoudi": "Dr. Hasti Masoudi",
"Specialist in clinical psychology": "Specialist in clinical psychology", "Specialist in clinical psychology": "Specialist in clinical psychology",
"Personal Development": "Personal Development", "Personal Development": "Personal Development",
"Family Counselor": "Family Counselor", "Family Counselor": "Family Counselor",
@ -804,5 +803,6 @@
"Improving Mutual Understanding": "Improving Mutual Understanding", "Improving Mutual Understanding": "Improving Mutual Understanding",
"Helps individuals better understand their own needs as well as their partner’s motivations, preferences, and emotional priorities.": "Helps individuals better understand their own needs as well as their partner’s motivations, preferences, and emotional priorities.", "Helps individuals better understand their own needs as well as their partner’s motivations, preferences, and emotional priorities.": "Helps individuals better understand their own needs as well as their partner’s motivations, preferences, and emotional priorities.",
"Supporting More Suitable Match Recommendations": "Supporting More Suitable Match Recommendations", "Supporting More Suitable Match Recommendations": "Supporting More Suitable Match Recommendations",
"Combining needs and personality assessments with interviews and other marriage criteria can help make partner recommendations more personalized and well-matched.": "Combining needs and personality assessments with interviews and other marriage criteria can help make partner recommendations more personalized and well-matched."
"Combining needs and personality assessments with interviews and other marriage criteria can help make partner recommendations more personalized and well-matched.": "Combining needs and personality assessments with interviews and other marriage criteria can help make partner recommendations more personalized and well-matched.",
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again."
} }

3
src/translations/locales/es.json

@ -753,5 +753,6 @@
"We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet", "We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet",
"Please select the reason for cancellation:": "Please select the reason for cancellation:", "Please select the reason for cancellation:": "Please select the reason for cancellation:",
"Swipe to continue": "Swipe to continue", "Swipe to continue": "Swipe to continue",
"Swipe to confirm cancellation": "Swipe to confirm cancellation"
"Swipe to confirm cancellation": "Swipe to confirm cancellation",
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again."
} }

4
src/translations/locales/fa.json

@ -760,7 +760,6 @@
"⚠️ This section is completely confidential and is only used for matching and review by advisors.": "⚠️ این بخش کاملاً محرمانه است و فقط برای مچینگ و بررسی کارشناسان استفاده میشود.", "⚠️ This section is completely confidential and is only used for matching and review by advisors.": "⚠️ این بخش کاملاً محرمانه است و فقط برای مچینگ و بررسی کارشناسان استفاده میشود.",
"Please select the reason for cancellation:": "Please select the reason for cancellation:", "Please select the reason for cancellation:": "Please select the reason for cancellation:",
"Marriage advisors": "مشاوران ازدواج", "Marriage advisors": "مشاوران ازدواج",
"Dr. Hasti Masoudi": "دکتر هستی مسعودی",
"Specialist in clinical psychology": "متخصص روانشناسی بالینی", "Specialist in clinical psychology": "متخصص روانشناسی بالینی",
"Personal Development": "رشد فردی", "Personal Development": "رشد فردی",
"Family Counselor": "مشاور خانواده", "Family Counselor": "مشاور خانواده",
@ -804,5 +803,6 @@
"Improving Mutual Understanding": "بهبود درک متقابل", "Improving Mutual Understanding": "بهبود درک متقابل",
"Helps individuals better understand their own needs as well as their partner’s motivations, preferences, and emotional priorities.": "به افراد کمک می‌کند تا نیازهای خود و همچنین انگیزه‌ها، ترجیحات و اولویت‌های عاطفی شریک زندگی خود را بهتر درک کنند.", "Helps individuals better understand their own needs as well as their partner’s motivations, preferences, and emotional priorities.": "به افراد کمک می‌کند تا نیازهای خود و همچنین انگیزه‌ها، ترجیحات و اولویت‌های عاطفی شریک زندگی خود را بهتر درک کنند.",
"Supporting More Suitable Match Recommendations": "پشتیبانی از پیشنهادهای همسریابی مناسب‌تر", "Supporting More Suitable Match Recommendations": "پشتیبانی از پیشنهادهای همسریابی مناسب‌تر",
"Combining needs and personality assessments with interviews and other marriage criteria can help make partner recommendations more personalized and well-matched.": "ترکیب ارزیابی نیازها و شخصیت با مصاحبه‌ها و سایر معیارهای ازدواج می‌تواند به شخصی‌سازی و هماهنگی بیشتر پیشنهادهای شریک زندگی کمک کند."
"Combining needs and personality assessments with interviews and other marriage criteria can help make partner recommendations more personalized and well-matched.": "ترکیب ارزیابی نیازها و شخصیت با مصاحبه‌ها و سایر معیارهای ازدواج می‌تواند به شخصی‌سازی و هماهنگی بیشتر پیشنهادهای شریک زندگی کمک کند.",
"Failed to update profile basic details. Please try again.": "به‌روزرسانی اطلاعات اولیه پروفایل ناموفق بود. لطفاً دوباره تلاش کنید."
} }

3
src/translations/locales/fr.json

@ -753,5 +753,6 @@
"We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet", "We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet",
"Please select the reason for cancellation:": "Please select the reason for cancellation:", "Please select the reason for cancellation:": "Please select the reason for cancellation:",
"Swipe to continue": "Swipe to continue", "Swipe to continue": "Swipe to continue",
"Swipe to confirm cancellation": "Swipe to confirm cancellation"
"Swipe to confirm cancellation": "Swipe to confirm cancellation",
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again."
} }

3
src/translations/locales/gu.json

@ -753,5 +753,6 @@
"We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet", "We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet",
"Please select the reason for cancellation:": "Please select the reason for cancellation:", "Please select the reason for cancellation:": "Please select the reason for cancellation:",
"Swipe to continue": "Swipe to continue", "Swipe to continue": "Swipe to continue",
"Swipe to confirm cancellation": "Swipe to confirm cancellation"
"Swipe to confirm cancellation": "Swipe to confirm cancellation",
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again."
} }

3
src/translations/locales/ha.json

@ -753,5 +753,6 @@
"We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet", "We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet",
"Please select the reason for cancellation:": "Please select the reason for cancellation:", "Please select the reason for cancellation:": "Please select the reason for cancellation:",
"Swipe to continue": "Swipe to continue", "Swipe to continue": "Swipe to continue",
"Swipe to confirm cancellation": "Swipe to confirm cancellation"
"Swipe to confirm cancellation": "Swipe to confirm cancellation",
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again."
} }

3
src/translations/locales/he.json

@ -279,5 +279,6 @@
"We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet", "We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet",
"Please select the reason for cancellation:": "Please select the reason for cancellation:", "Please select the reason for cancellation:": "Please select the reason for cancellation:",
"Swipe to continue": "Swipe to continue", "Swipe to continue": "Swipe to continue",
"Swipe to confirm cancellation": "Swipe to confirm cancellation"
"Swipe to confirm cancellation": "Swipe to confirm cancellation",
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again."
} }

3
src/translations/locales/hi.json

@ -753,5 +753,6 @@
"We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet", "We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet",
"Please select the reason for cancellation:": "Please select the reason for cancellation:", "Please select the reason for cancellation:": "Please select the reason for cancellation:",
"Swipe to continue": "Swipe to continue", "Swipe to continue": "Swipe to continue",
"Swipe to confirm cancellation": "Swipe to confirm cancellation"
"Swipe to confirm cancellation": "Swipe to confirm cancellation",
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again."
} }

3
src/translations/locales/id.json

@ -279,5 +279,6 @@
"We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet", "We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet",
"Please select the reason for cancellation:": "Please select the reason for cancellation:", "Please select the reason for cancellation:": "Please select the reason for cancellation:",
"Swipe to continue": "Swipe to continue", "Swipe to continue": "Swipe to continue",
"Swipe to confirm cancellation": "Swipe to confirm cancellation"
"Swipe to confirm cancellation": "Swipe to confirm cancellation",
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again."
} }

3
src/translations/locales/ks.json

@ -279,5 +279,6 @@
"We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet", "We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet",
"Please select the reason for cancellation:": "Please select the reason for cancellation:", "Please select the reason for cancellation:": "Please select the reason for cancellation:",
"Swipe to continue": "Swipe to continue", "Swipe to continue": "Swipe to continue",
"Swipe to confirm cancellation": "Swipe to confirm cancellation"
"Swipe to confirm cancellation": "Swipe to confirm cancellation",
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again."
} }

3
src/translations/locales/pt.json

@ -279,5 +279,6 @@
"We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet", "We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet",
"Please select the reason for cancellation:": "Please select the reason for cancellation:", "Please select the reason for cancellation:": "Please select the reason for cancellation:",
"Swipe to continue": "Swipe to continue", "Swipe to continue": "Swipe to continue",
"Swipe to confirm cancellation": "Swipe to confirm cancellation"
"Swipe to confirm cancellation": "Swipe to confirm cancellation",
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again."
} }

3
src/translations/locales/ru.json

@ -758,5 +758,6 @@
"{completed} of {total} required steps completed": "Выполнено {completed} из {total} требуемых шагов", "{completed} of {total} required steps completed": "Выполнено {completed} из {total} требуемых шагов",
"{days} days remaining of your subscription.": "{days} дней до конца вашей подписки.", "{days} days remaining of your subscription.": "{days} дней до конца вашей подписки.",
"⚠️ This section is completely confidential and is only used for matching and review by advisors.": "⚠️ Этот раздел полностью конфиденциальен и используется только консультантами для сопоставления и проверки.", "⚠️ This section is completely confidential and is only used for matching and review by advisors.": "⚠️ Этот раздел полностью конфиденциальен и используется только консультантами для сопоставления и проверки.",
"Please select the reason for cancellation:": "Пожалуйста, выберите причину отмены:"
"Please select the reason for cancellation:": "Пожалуйста, выберите причину отмены:",
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again."
} }

3
src/translations/locales/sw.json

@ -279,5 +279,6 @@
"We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet", "We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet",
"Please select the reason for cancellation:": "Please select the reason for cancellation:", "Please select the reason for cancellation:": "Please select the reason for cancellation:",
"Swipe to continue": "Swipe to continue", "Swipe to continue": "Swipe to continue",
"Swipe to confirm cancellation": "Swipe to confirm cancellation"
"Swipe to confirm cancellation": "Swipe to confirm cancellation",
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again."
} }

3
src/translations/locales/tg.json

@ -279,5 +279,6 @@
"We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet", "We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet",
"Please select the reason for cancellation:": "Please select the reason for cancellation:", "Please select the reason for cancellation:": "Please select the reason for cancellation:",
"Swipe to continue": "Swipe to continue", "Swipe to continue": "Swipe to continue",
"Swipe to confirm cancellation": "Swipe to confirm cancellation"
"Swipe to confirm cancellation": "Swipe to confirm cancellation",
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again."
} }

3
src/translations/locales/tr.json

@ -279,5 +279,6 @@
"We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet", "We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet",
"Please select the reason for cancellation:": "Please select the reason for cancellation:", "Please select the reason for cancellation:": "Please select the reason for cancellation:",
"Swipe to continue": "Swipe to continue", "Swipe to continue": "Swipe to continue",
"Swipe to confirm cancellation": "Swipe to confirm cancellation"
"Swipe to confirm cancellation": "Swipe to confirm cancellation",
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again."
} }

3
src/translations/locales/ul.json

@ -279,5 +279,6 @@
"We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet", "We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet",
"Please select the reason for cancellation:": "Please select the reason for cancellation:", "Please select the reason for cancellation:": "Please select the reason for cancellation:",
"Swipe to continue": "Swipe to continue", "Swipe to continue": "Swipe to continue",
"Swipe to confirm cancellation": "Swipe to confirm cancellation"
"Swipe to confirm cancellation": "Swipe to confirm cancellation",
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again."
} }

3
src/translations/locales/ur.json

@ -279,5 +279,6 @@
"We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet", "We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet",
"Please select the reason for cancellation:": "Please select the reason for cancellation:", "Please select the reason for cancellation:": "Please select the reason for cancellation:",
"Swipe to continue": "Swipe to continue", "Swipe to continue": "Swipe to continue",
"Swipe to confirm cancellation": "Swipe to confirm cancellation"
"Swipe to confirm cancellation": "Swipe to confirm cancellation",
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again."
} }

3
src/translations/locales/uz.json

@ -279,5 +279,6 @@
"We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet", "We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet",
"Please select the reason for cancellation:": "Please select the reason for cancellation:", "Please select the reason for cancellation:": "Please select the reason for cancellation:",
"Swipe to continue": "Swipe to continue", "Swipe to continue": "Swipe to continue",
"Swipe to confirm cancellation": "Swipe to confirm cancellation"
"Swipe to confirm cancellation": "Swipe to confirm cancellation",
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again."
} }

3
src/translations/locales/zh.json

@ -753,5 +753,6 @@
"We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet", "We are in the acquaintance/proposal process and nothing is finalized yet": "We are in the acquaintance/proposal process and nothing is finalized yet",
"Please select the reason for cancellation:": "Please select the reason for cancellation:", "Please select the reason for cancellation:": "Please select the reason for cancellation:",
"Swipe to continue": "Swipe to continue", "Swipe to continue": "Swipe to continue",
"Swipe to confirm cancellation": "Swipe to confirm cancellation"
"Swipe to confirm cancellation": "Swipe to confirm cancellation",
"Failed to update profile basic details. Please try again.": "Failed to update profile basic details. Please try again."
} }
Loading…
Cancel
Save