You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

261 lines
9.3 KiB

"use client";
import Image from "next/image";
import { useRouter } from "next/navigation";
import { useCallback, useEffect, useState } from "react";
import Button from "@/components/Componentes/button";
import NetworkImage from "@/components/Componentes/network-image";
import PageHeader from "@/components/Componentes/page-header";
import SectionOverlayHost from "@/components/Componentes/section-overlay-host";
import SliderPage from "@/components/Componentes/slider-page";
import VideoPlayer from "@/components/Componentes/video-player";
import {
IntroMatchesIcon,
IntroMarriagesIcon,
IntroPlayButtonIcon,
IntroUsersIcon,
} from "@/components/Componentes/intro-hero-illustration";
import { useMarriageConfigQuery } from "@/hooks/marriage/use-marriage-config";
import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main";
import { useHabibWebReady } from "@/hooks/use-habib-web-ready";
import { useHardwareBackHandler } from "@/hooks/use-hardware-back-handler";
import { authBridge } from "@/lib/auth-bridge";
import { SHOW_INTRO_VIDEOS } from "@/config/features";
import { getSubmitPath } from "@/lib/get-submit-path";
import { localizePath } from "@/translations/config";
import { useI18n } from "@/translations/provider";
import { PageBackground } from "@/components/Componentes/page-background";
export default function IntroClient() {
const router = useRouter();
const { dictionary: t, locale } = useI18n();
const { data: profile, refetch } = useMarriageProfileQuery({
enabled: false,
retry: false,
});
const [isSubmitting, setIsSubmitting] = useState(false);
const [isPlayerOpen, setIsPlayerOpen] = useState(false);
const [isStepsOpen, setIsStepsOpen] = useState(false);
const { data: config } = useMarriageConfigQuery();
// Signal Flutter that the Intro UI is ready. Config has a local fallback
// image so we don't need to wait for it — announce immediately on mount.
useHabibWebReady(true);
useEffect(() => {
const readStepsFromUrl = () => {
if (typeof window === "undefined") return;
const params = new URLSearchParams(window.location.search);
setIsStepsOpen(params.get("steps") === "open");
};
readStepsFromUrl();
window.addEventListener("popstate", readStepsFromUrl);
return () => window.removeEventListener("popstate", readStepsFromUrl);
}, []);
const handleOpenSteps = useCallback(() => {
setIsStepsOpen(true);
if (typeof window !== "undefined") {
const url = new URL(window.location.href);
url.searchParams.set("steps", "open");
window.history.replaceState({ steps: "open" }, "", url.toString());
}
}, []);
const handleCloseSteps = useCallback(() => {
setIsStepsOpen(false);
if (typeof window !== "undefined") {
const params = new URLSearchParams(window.location.search);
if (params.get("steps") === "open") {
const url = new URL(window.location.href);
url.searchParams.delete("steps");
window.history.replaceState({}, "", url.toString());
}
}
}, []);
useHardwareBackHandler(() => {
if (isStepsOpen) {
handleCloseSteps();
return true; // handled: keep WebView open, just close the overlay
}
return false; // Root: tell Flutter to close WebView screen
}, isStepsOpen);
const handleSubmit = async () => {
if (isSubmitting) {
return;
}
setIsSubmitting(true);
try {
if (!authBridge.isAuthenticated()) {
const token = await authBridge.ensureToken();
if (!token) {
console.warn("No token from bridge – login was not completed");
return;
}
}
let profileResponse = profile;
try {
const { data: freshProfile } = await refetch();
profileResponse = freshProfile ?? profile;
} catch (refetchError) {
console.warn(
"Could not refetch profile data – using fallback",
refetchError,
);
}
const submitPath = getSubmitPath(profileResponse);
if (submitPath === "/intro" || submitPath === "/terms") {
handleOpenSteps();
} else {
router.push(localizePath(submitPath, locale));
}
} catch (error) {
console.error("Submission/redirect failed", error);
handleOpenSteps();
} finally {
setIsSubmitting(false);
}
};
return (
<>
<PageBackground />
<PageHeader profile={profile} sticky />
<main
style={{
minHeight: SHOW_INTRO_VIDEOS
? undefined
: "calc(100dvh - 56px - max(12px, calc(var(--safe-top) + 6px)))",
}}
className={[
"flex min-h-0 flex-1 flex-col text-center pb-[calc(90px+var(--safe-bottom))]",
SHOW_INTRO_VIDEOS ? "pt-4" : "justify-center",
].join(" ")}
>
<div className="flex flex-col items-center">
<Image
src={"/assets/images/Group 1597880466.svg"}
alt={t["heavenly marriage"]}
width={168}
height={155}
priority
loading="eager"
/>
<h2 className="group-16 text-[#475569] font-bold mt-5 text-center">
{t["A Path to Heavenly Marriage"]}
</h2>
<p className="text-center mt-2 group-12 text-[#4D4D4D]">
{
t[
'We have come together with the goal of creating a secure and confidential path for "permanent marriage" among Muslims'
]
}
</p>
</div>
<div className="flex items-center justify-between gap-2 mt-8 w-full">
<div className="flex min-w-0 flex-1 items-center gap-1 p-2 border border-[#FD6ABB]/10 rounded-2xl">
<IntroUsersIcon className="shrink-0" />
<div className="flex min-w-0 flex-1 flex-col items-start justify-center">
<p className="w-full truncate text-start text-[#36363C] group-16 font-bold leading-tight" title="120">
120
</p>
<p
className="w-full truncate text-start text-[#36363C] group-10 font-semibold capitalize"
title={t["user profiles"]}
>
{t["user profiles"]}
</p>
</div>
</div>
<div className="flex min-w-0 flex-1 items-center gap-1 p-2 border border-[#FD6ABB]/10 rounded-2xl">
<IntroMatchesIcon className="shrink-0" />
<div className="flex min-w-0 flex-1 flex-col items-start justify-center">
<p className="w-full truncate text-start text-[#36363C] group-16 font-bold leading-tight" title="14">
14
</p>
<p
className="w-full truncate text-start text-[#36363C] group-10 font-semibold capitalize"
title={t["matches"]}
>
{t["matches"]}
</p>
</div>
</div>
<div className="flex min-w-0 flex-1 items-center gap-1 p-2 border border-[#FD6ABB]/10 rounded-2xl">
<IntroMarriagesIcon className="shrink-0" />
<div className="flex min-w-0 flex-1 flex-col items-start justify-center">
<p className="w-full truncate text-start text-[#36363C] group-16 font-bold leading-tight" title="14">
14
</p>
<p
className="w-full truncate text-start text-[#36363C] group-10 font-semibold capitalize"
title={t["marriages"]}
>
{t["marriages"]}
</p>
</div>
</div>
</div>
{SHOW_INTRO_VIDEOS && (
<>
<div
className="mt-14 relative cursor-pointer group rounded-2xl overflow-hidden aspect-[344/221] max-w-[344px] w-full mx-auto"
onClick={() => setIsPlayerOpen(true)}
>
<NetworkImage
src={config?.intro_video_thumbnail_url}
fallbackSrc="/assets/images/Frame 2095586523.png"
alt={t["video"]}
fill
className="object-cover transition-transform duration-300 group-hover:scale-105"
priority
/>
<div className="absolute inset-0 bg-black/10 transition-colors duration-300 group-hover:bg-black/20" />
<IntroPlayButtonIcon className="absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 transition-transform duration-300 group-hover:scale-110 active:scale-95" />
</div>
<VideoPlayer
isOpen={isPlayerOpen}
onClose={() => setIsPlayerOpen(false)}
videoUrl={config?.intro_video_url}
/>
</>
)}
<div className="pointer-events-none fixed bottom-0 left-1/2 z-20 w-full max-w-[834px] -translate-x-1/2 bg-[#F5F5F5]">
<div
style={{ paddingBottom: "calc(20px + var(--safe-bottom))" }}
className="pointer-events-auto px-[17px] md:px-8 pt-3"
>
<Button
onClick={handleSubmit}
disabled={isSubmitting}
isLoading={isSubmitting}
>
{t["Submit"]}
</Button>
</div>
</div>
</main>
<SectionOverlayHost
open={isStepsOpen}
onClose={handleCloseSteps}
>
{isStepsOpen && (
<SliderPage onClose={handleCloseSteps} />
)}
</SectionOverlayHost>
</>
);
}