diff --git a/src/app/[lang]/page.tsx b/src/app/[lang]/page.tsx index 419e558..6531f24 100644 --- a/src/app/[lang]/page.tsx +++ b/src/app/[lang]/page.tsx @@ -7,11 +7,6 @@ import { isAuthenticatedToken, MARRIAGE_ENTRY_PATH_COOKIE, } from "@/lib/entry-route-cache"; -import { - getSubmitPath, - hasCompletedMarriageProfileBasics, -} from "@/lib/get-submit-path"; -import { fetchProfileSSR } from "@/lib/ssr-fetch"; import { localizePath } from "@/translations/config"; export const dynamic = "force-dynamic"; @@ -27,7 +22,7 @@ export default async function LocaleEntryPage({ cookieStore.get("habib_token")?.value; const hasToken = isAuthenticatedToken(token); - // 1. If not authenticated, render instantly in SSR + // 1. If not authenticated, render instantly if (!hasToken) { return ; } @@ -45,26 +40,6 @@ export default async function LocaleEntryPage({ redirect(localizePath(cachedEntryPath, lang)); } - // 3. Deep SSR Profile Resolution: resolve profile & redirect directly on server - const profile = await fetchProfileSSR(token); - if (profile) { - const isBasicsCompleted = hasCompletedMarriageProfileBasics(profile); - if (!isBasicsCompleted) { - return ; - } - - const targetPath = getSubmitPath(profile); - if (targetPath === "/intro") { - return ; - } - - redirect(localizePath(targetPath, lang)); - } - - // 4. Graceful Fallback if server fetch was unreachable - return ( - <> - - - ); + // 3. Instant non-blocking client-side resolver fallback + return ; } diff --git a/src/app/api/proxy/route.ts b/src/app/api/proxy/route.ts index c007bf1..c33d945 100644 --- a/src/app/api/proxy/route.ts +++ b/src/app/api/proxy/route.ts @@ -150,7 +150,7 @@ function getRequestHeaders(request: NextRequest, targetUrl: URL) { const securityKey = process.env.NEXT_PUBLIC_SECURITY_KEY || process.env.SECURITY_KEY || - "t5yugymks5458fd4ghfg6h6"; + "t5yugymks5458fd4ghfg6h6fg"; if (securityKey && !headers.has("security-key")) { headers.set("security-key", securityKey); } diff --git a/src/app/candidate-contact/candidate-contact-client.tsx b/src/app/candidate-contact/candidate-contact-client.tsx index 6361447..fa0d9e2 100644 --- a/src/app/candidate-contact/candidate-contact-client.tsx +++ b/src/app/candidate-contact/candidate-contact-client.tsx @@ -45,23 +45,26 @@ export default function CandidateContactClient() { const { isProfileOpen, openProfile, closeProfile } = useMatchProfileOverlay(); - const { data: profile, isLoading: isProfileLoading } = - useMarriageProfileQuery({ - refetchInterval: 3000, - }); + const { + data: profile, + isLoading: isProfileLoading, + isFetched, + } = useMarriageProfileQuery({ + refetchInterval: 3000, + }); useEffect(() => { - if (!profile) { + if (!profile || !isFetched) { return; } const targetPath = getSubmitPath(profile); if (targetPath !== "/candidate-contact") { router.replace(localizePath(targetPath, locale)); } - }, [profile, router, locale]); + }, [profile, isFetched, router, locale]); - // Signal Flutter to lift its loading cover once the profile is available. - useHabibWebReady(!!profile && !isProfileLoading); + // Signal Flutter to lift its loading cover immediately on mount + useHabibWebReady(true); const isRedirecting = useMemo(() => { if (!profile) return false; diff --git a/src/app/candidate-contact/page.tsx b/src/app/candidate-contact/page.tsx index 0260429..e64fbd4 100644 --- a/src/app/candidate-contact/page.tsx +++ b/src/app/candidate-contact/page.tsx @@ -1,39 +1,5 @@ -import { cookies } from "next/headers"; -import { - dehydrate, - HydrationBoundary, - QueryClient, -} from "@tanstack/react-query"; -import { isAuthenticatedToken } from "@/lib/entry-route-cache"; -import { fetchProfileSSR } from "@/lib/ssr-fetch"; -import { marriageQueryKeys } from "@/hooks/marriage/query-keys"; import CandidateContactClient from "./candidate-contact-client"; -export const dynamic = "force-dynamic"; - -export default async function CandidateContactPage() { - const cookieStore = await cookies(); - - const token = - cookieStore.get("HABIB_TOKEN")?.value ?? - cookieStore.get("habib_token")?.value; - - const queryClient = new QueryClient({ - defaultOptions: { - queries: { staleTime: 10 * 1000 }, - }, - }); - - if (isAuthenticatedToken(token)) { - await queryClient.prefetchQuery({ - queryKey: marriageQueryKeys.profile(), - queryFn: () => fetchProfileSSR(token!), - }); - } - - return ( - - - - ); +export default function CandidateContactPage() { + return ; } diff --git a/src/app/finding-match/finding-match-client.tsx b/src/app/finding-match/finding-match-client.tsx index 6a619a6..4b44acb 100644 --- a/src/app/finding-match/finding-match-client.tsx +++ b/src/app/finding-match/finding-match-client.tsx @@ -11,7 +11,6 @@ import MarriageAdvisorsOverlay, { useMarriageAdvisorsOverlay, } from "@/components/Componentes/marriage-advisors-overlay"; import Button from "@/components/Componentes/button"; -import { PageLoadingSkeleton } from "@/components/Componentes/page-loading-skeleton"; import { FixToTheEnd } from "@/components/Componentes/fix-to-the-end"; import { PageBackground } from "@/components/Componentes/page-background"; import PageHeader from "@/components/Componentes/page-header"; @@ -32,7 +31,7 @@ export default function FindingMatchClient() { const { dictionary: t, locale } = useI18n(); const { isAdvisorOpen, openAdvisors, closeAdvisors } = useMarriageAdvisorsOverlay(); - const { data: profile, isLoading } = useMarriageProfileQuery({ + const { data: profile, isLoading, isFetched } = useMarriageProfileQuery({ refetchInterval: 3000, }); @@ -48,27 +47,32 @@ export default function FindingMatchClient() { }); useEffect(() => { - if (!profile) { + console.log("🔍 [FindingMatchClient] State Evaluation:", { + hasProfile: Boolean(profile), + isFetched, + status: profile?.status, + active_case: profile?.active_case, + calculatedPath: profile ? getSubmitPath(profile) : null, + }); + if (!profile || !isFetched) { + console.log("⏳ [FindingMatchClient] Skipping redirect because !profile or !isFetched (isFetched=", isFetched, ")"); return; } const targetPath = getSubmitPath(profile); if (targetPath !== "/finding-match") { + console.log("🚀 [FindingMatchClient] Redirecting to:", targetPath); router.replace(localizePath(targetPath, locale)); } - }, [profile, locale, router]); + }, [profile, isFetched, locale, router]); - // Signal Flutter to lift its loading cover once the profile is available. - useHabibWebReady(!!profile && !isLoading); + // Signal Flutter to lift its loading cover immediately so page renders with 0ms latency. + useHabibWebReady(true); const isRedirecting = useMemo(() => { if (!profile) return false; return getSubmitPath(profile) !== "/finding-match"; }, [profile]); - if ((isLoading && !profile) || !profile) { - return ; - } - const copy = { title: t["SEARCH IN PROGRESS"], description: @@ -111,25 +115,27 @@ export default function FindingMatchClient() { />
- -
+
-

+

{copy.title}

-

+

{copy.description}

{unseenRejection && ( @@ -177,38 +183,42 @@ export default function FindingMatchClient() { )}{" "}
- - - - {profile?.can_edit_profile === false ? ( -
-
- ) : ( - - )} -
+
+ +
+ {/* Outside
: page-slide-enter sets will-change:transform which makes + main the containing block for position:fixed and narrows the button. */} + + {profile?.can_edit_profile === false ? ( +
+
+ ) : ( + + )} +
+ fetchProfileSSR(token!), - }); - } - - return ( - - - - ); +export default function FindingMatchPage() { + return ; } diff --git a/src/app/globals.css b/src/app/globals.css index 67bd8fa..937a4c1 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -342,13 +342,11 @@ body.section-overlay-open .app-shell { height: 100%; height: 100dvh; z-index: 50; - overflow-x: hidden; - overflow-y: auto; + overflow: hidden; overscroll-behavior: none; touch-action: pan-y; -webkit-overflow-scrolling: touch; padding-inline: 17px; - padding-bottom: var(--safe-bottom, 0px); box-sizing: border-box; background-color: var(--background); background-image: var(--default-page-background-image); diff --git a/src/app/intro/page.tsx b/src/app/intro/page.tsx index 89223f9..a953d9f 100644 --- a/src/app/intro/page.tsx +++ b/src/app/intro/page.tsx @@ -1,42 +1,5 @@ -import { - dehydrate, - HydrationBoundary, - QueryClient, -} from "@tanstack/react-query"; -import { marriageQueryKeys } from "@/hooks/marriage/query-keys"; -import { fetchConfigSSR } from "@/lib/ssr-fetch"; import IntroClient from "./intro-client"; -export const dynamic = "force-dynamic"; - -/** - * Server Component wrapper for the Intro page. - * - * Follows the same SSR-prefetch pattern as questions-list/page.tsx: - * create a server QueryClient, prefetch critical data, dehydrate, and - * wrap the client component in HydrationBoundary so TanStack Query - * hydrates the cache instantly — no client-side waterfall. - * - * Config is the only data Intro needs. Even if the fetch fails, IntroClient - * has a local fallback image so the UI is never broken. - */ -export default async function IntroPage() { - const queryClient = new QueryClient({ - defaultOptions: { - queries: { staleTime: 30 * 1000 }, - }, - }); - - // Prefetch marriage config — Intro uses it for the video thumbnail. - // Failure is non-blocking because IntroClient has a fallbackSrc. - await queryClient.prefetchQuery({ - queryKey: marriageQueryKeys.config(), - queryFn: () => fetchConfigSSR(), - }); - - return ( - - - - ); +export default function IntroPage() { + return ; } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 7d5f4f4..fbcc616 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -10,6 +10,8 @@ import { isLocale, localeDirections, } from "@/translations/config"; +import { getInitialMarriageProfile } from "@/hooks/marriage/use-profile-main"; +import type { MarriageProfileResponse } from "@/hooks/marriage/types"; const faminela = localFont({ src: "../../public/fonts/Faminela/Faminela.otf", @@ -85,6 +87,27 @@ export default async function RootLayout({ ? cookieLocale : defaultLocale; + const rawMarriageCookie = + cookieStore.get("HABIB_MARRIAGE_DATA")?.value ?? + cookieStore.get("habib_marriage_data")?.value; + + let initialMarriageJson: string | null = null; + let initialProfile: MarriageProfileResponse | undefined; + if (rawMarriageCookie) { + try { + const decoded = decodeURIComponent(rawMarriageCookie); + const parsed = JSON.parse(decoded); + initialMarriageJson = decoded; + initialProfile = getInitialMarriageProfile(parsed); + } catch { + try { + const parsed = JSON.parse(rawMarriageCookie); + initialMarriageJson = rawMarriageCookie; + initialProfile = getInitialMarriageProfile(parsed); + } catch {} + } + } + return ( + {initialMarriageJson && ( +