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.
411 lines
16 KiB
411 lines
16 KiB
"use client";
|
|
|
|
import Image from "next/image";
|
|
import { useRouter } from "next/navigation";
|
|
import { useEffect, useMemo, useState } from "react";
|
|
import { useHabibWebReady } from "@/hooks/use-habib-web-ready";
|
|
import { LoadingThreeDot } from "@/components/Componentes/loading-three-dot";
|
|
import { PageLoadingSkeleton } from "@/components/Componentes/page-loading-skeleton";
|
|
import CallResultSheet from "@/components/Componentes/call-result-sheet";
|
|
import DismissReasonSheet from "@/components/Componentes/dismiss-reason-sheet";
|
|
import OutcomeSelectionSheet from "@/components/Componentes/outcome-selection-sheet";
|
|
import PageHeader from "@/components/Componentes/page-header";
|
|
import AdvisorActionsCard from "@/components/Componentes/advisor-actions-card";
|
|
import MarriageAdvisorsOverlay, {
|
|
useMarriageAdvisorsOverlay,
|
|
} from "@/components/Componentes/marriage-advisors-overlay";
|
|
import MatchProfileOverlay, {
|
|
useMatchProfileOverlay,
|
|
} from "@/components/Componentes/match-profile-overlay";
|
|
import { PageBackground } from "@/components/Componentes/page-background";
|
|
import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main";
|
|
import {
|
|
useSubmitMarriageContactStatusMutation,
|
|
useSubmitMarriageOutcomeMutation,
|
|
} from "@/hooks/marriage/use-contact-status";
|
|
import { getSubmitPath } from "@/lib/get-submit-path";
|
|
import { localizePath } from "@/translations/config";
|
|
import { useI18n } from "@/translations/provider";
|
|
|
|
const advisorAvatars = [
|
|
{ id: "advisor-primary", src: "/assets/images/Avatar Image.png" },
|
|
{ id: "advisor-secondary", src: "/assets/images/Ellipse 370.png" },
|
|
{ id: "advisor-tertiary", src: "/assets/images/Avatar Image.png" },
|
|
];
|
|
|
|
export default function CandidateContactClient() {
|
|
const { dictionary: t, locale } = useI18n();
|
|
const router = useRouter();
|
|
const [isOutcomeSheetOpen, setIsOutcomeSheetOpen] = useState(false);
|
|
const [isCallResultSheetOpen, setIsCallResultSheetOpen] = useState(false);
|
|
const [isDismissReasonSheetOpen, setIsDismissReasonSheetOpen] =
|
|
useState(false);
|
|
const { isAdvisorOpen, openAdvisors, closeAdvisors } =
|
|
useMarriageAdvisorsOverlay();
|
|
const { isProfileOpen, openProfile, closeProfile } =
|
|
useMatchProfileOverlay();
|
|
|
|
const { data: profile, isLoading: isProfileLoading } =
|
|
useMarriageProfileQuery({
|
|
refetchInterval: 3000,
|
|
});
|
|
|
|
useEffect(() => {
|
|
if (!profile) {
|
|
return;
|
|
}
|
|
const targetPath = getSubmitPath(profile);
|
|
if (targetPath !== "/candidate-contact") {
|
|
router.replace(localizePath(targetPath, locale));
|
|
}
|
|
}, [profile, router, locale]);
|
|
|
|
// Signal Flutter to lift its loading cover once the profile is available.
|
|
useHabibWebReady(!!profile && !isProfileLoading);
|
|
|
|
const isRedirecting = useMemo(() => {
|
|
if (!profile) return false;
|
|
return getSubmitPath(profile) !== "/candidate-contact";
|
|
}, [profile]);
|
|
|
|
const caseId = profile?.active_case?.case_id;
|
|
const caseStatus = profile?.active_case?.status;
|
|
const isFemale = profile?.gender === "female";
|
|
const isFinalized =
|
|
caseStatus === "finalized" || profile?.status === "matched";
|
|
|
|
const contactStatusMutation = useSubmitMarriageContactStatusMutation(
|
|
caseId ?? "",
|
|
);
|
|
|
|
const outcomeMutation = useSubmitMarriageOutcomeMutation(caseId ?? "");
|
|
|
|
const handleNoContactReport = async () => {
|
|
if (!caseId || contactStatusMutation.isPending) return;
|
|
await contactStatusMutation.mutateAsync({
|
|
action: "no_contact",
|
|
custom_note:
|
|
"No contact reported by female candidate after decision window",
|
|
});
|
|
};
|
|
|
|
if (isProfileLoading || isRedirecting) {
|
|
return <PageLoadingSkeleton />;
|
|
}
|
|
|
|
// If female, render the beautiful, customized layout matching the design
|
|
if (isFemale) {
|
|
return (
|
|
<>
|
|
<PageBackground />
|
|
|
|
{isOutcomeSheetOpen ? (
|
|
<OutcomeSelectionSheet
|
|
onClose={() => setIsOutcomeSheetOpen(false)}
|
|
onSubmit={async (status) => {
|
|
if (status === "success") {
|
|
if (caseId) {
|
|
await outcomeMutation.mutateAsync({
|
|
status: "success",
|
|
});
|
|
}
|
|
} else {
|
|
setIsDismissReasonSheetOpen(true);
|
|
}
|
|
}}
|
|
/>
|
|
) : null}
|
|
|
|
{isDismissReasonSheetOpen ? (
|
|
<DismissReasonSheet
|
|
onClose={() => setIsDismissReasonSheetOpen(false)}
|
|
onSubmit={async (value) => {
|
|
if (caseId) {
|
|
await outcomeMutation.mutateAsync({
|
|
status: "failure",
|
|
custom_note: value,
|
|
});
|
|
}
|
|
}}
|
|
/>
|
|
) : null}
|
|
|
|
<main className="-mx-[17px] flex min-h-screen flex-col px-[23px] pt-[max(12px,calc(var(--safe-top)+4px))] pb-[calc(20px+var(--safe-bottom))]">
|
|
<PageHeader
|
|
className="-mx-[6px]"
|
|
profile={profile}
|
|
leftButton={
|
|
isFemale ? { icon: "back", className: "hidden" } : undefined
|
|
}
|
|
/>
|
|
|
|
<div className="flex flex-1 flex-col justify-between gap-6 pt-4">
|
|
<section className="flex flex-col items-center">
|
|
{isFinalized ? (
|
|
<div className="flex flex-col items-center max-w-[320px] text-center my-auto py-12">
|
|
<div className="relative flex items-center justify-center text-[70px] animate-bounce">
|
|
🎉
|
|
</div>
|
|
<h1 className="mt-8 text-center text-[24px] leading-[1.25] font-bold text-[#E03950]">
|
|
{t["Congratulations! 🎉"]}
|
|
</h1>
|
|
<p className="mt-6 text-[#4A4A4A] group-14 font-medium leading-relaxed">
|
|
{
|
|
t[
|
|
"Wishing you a lifetime of love, joy, and happiness. Your profile has been successfully closed."
|
|
]
|
|
}
|
|
</p>
|
|
</div>
|
|
) : (
|
|
<>
|
|
{/* Illustration section */}
|
|
<div className="relative mt-4 flex items-center justify-center">
|
|
<Image
|
|
src="/assets/images/Group 159788fd0467.svg"
|
|
alt={t["Selected candidate contact status"]}
|
|
width={160}
|
|
height={152}
|
|
priority
|
|
/>
|
|
</div>
|
|
|
|
{/* Title / Status message */}
|
|
<h1 className="mt-8 text-center text-[20px] leading-[1.25] font-bold text-[#1A1A1A] max-w-[280px]">
|
|
{
|
|
t[
|
|
"The selected candidate will contact your family shortly."
|
|
]
|
|
}
|
|
</h1>
|
|
|
|
{caseStatus === "contacted" ? (
|
|
<>
|
|
{/* Outcome instructions and button */}
|
|
<div className="w-full border border-[#ECECEC] bg-white rounded-[15px] mt-8 px-4 py-4 text-center shadow-sm">
|
|
<p className="text-[#555555] group-14 font-medium leading-relaxed">
|
|
{
|
|
t[
|
|
"Thank you for giving us feedback, we would be very happy if you also let us know the final result."
|
|
]
|
|
}
|
|
</p>
|
|
</div>
|
|
|
|
<div className="flex mt-6 w-full gap-3">
|
|
<button
|
|
type="button"
|
|
onClick={() => openProfile()}
|
|
className="flex-1 h-[52px] rounded-[15px] border border-[#BFBFBF] bg-white text-[#36363C] font-semibold group-14 flex items-center justify-center transition-transform active:scale-[0.98] cursor-pointer hover:bg-[#F5F5F5]"
|
|
>
|
|
{t["View Profile"]}
|
|
</button>
|
|
|
|
<button
|
|
type="button"
|
|
onClick={() => setIsOutcomeSheetOpen(true)}
|
|
disabled={outcomeMutation.isPending}
|
|
className="flex-1 h-[52px] rounded-[15px] bg-gradient-to-r from-[#FE6F82] to-[#E03950] text-white font-semibold group-14 flex items-center justify-center shadow-lg shadow-[#FE6F82]/30 transition-transform active:scale-[0.98] cursor-pointer hover:opacity-95 disabled:opacity-50"
|
|
>
|
|
{outcomeMutation.isPending ? (
|
|
<LoadingThreeDot className="text-white" />
|
|
) : (
|
|
t["Submit Final Outcome"]
|
|
)}
|
|
</button>
|
|
</div>
|
|
</>
|
|
) : (
|
|
<>
|
|
{/* Action Buttons Row */}
|
|
<div className="flex mt-8 w-full gap-3">
|
|
<button
|
|
type="button"
|
|
onClick={handleNoContactReport}
|
|
disabled={contactStatusMutation.isPending}
|
|
className="flex-1 h-[52px] rounded-[15px] bg-[#F5F5F7] text-[#8E8E93] font-semibold group-14 flex items-center justify-center transition-transform active:scale-[0.98] cursor-pointer hover:bg-[#EAEAEF] disabled:opacity-50"
|
|
>
|
|
{contactStatusMutation.isPending ? (
|
|
<LoadingThreeDot className="text-[#8E8E93]" />
|
|
) : (
|
|
t["Report No Contact"]
|
|
)}
|
|
</button>
|
|
|
|
<button
|
|
type="button"
|
|
onClick={async () => {
|
|
if (caseId) {
|
|
await contactStatusMutation.mutateAsync({
|
|
action: "contacted",
|
|
});
|
|
}
|
|
}}
|
|
disabled={contactStatusMutation.isPending}
|
|
className="flex-1 h-[52px] rounded-[15px] bg-gradient-to-r from-[#FE6F82] to-[#E03950] text-white font-semibold group-14 flex items-center justify-center shadow-lg shadow-[#FE6F82]/30 transition-transform active:scale-[0.98] cursor-pointer hover:opacity-95 disabled:opacity-50"
|
|
>
|
|
{contactStatusMutation.isPending ? (
|
|
<LoadingThreeDot className="text-white" />
|
|
) : (
|
|
t["Confirm Contacted"]
|
|
)}
|
|
</button>
|
|
</div>
|
|
|
|
{/* Red warning box */}
|
|
<div className="w-full border border-[#F0445B] bg-[#FFF5F6] rounded-[15px] mt-4 px-4 py-3 text-center">
|
|
<p className="text-[#F0445B] group-12 font-semibold leading-normal">
|
|
{
|
|
t[
|
|
"To keep the process moving smoothly, the other party has a 48-hour (2-day) window to make initial contact with you or your family. If no contact is established after 2 days, you have the option to decline his request or notify us that he hasn't reached out."
|
|
]
|
|
}
|
|
</p>
|
|
</div>
|
|
</>
|
|
)}
|
|
</>
|
|
)}
|
|
</section>
|
|
|
|
<div className="space-y-4 pb-20">
|
|
{/* Advisor section */}
|
|
<AdvisorActionsCard
|
|
title={t["Get an advisor"]}
|
|
description={
|
|
t[
|
|
"Not sure what to do next? Our psychology section is here to guide you at every step."
|
|
]
|
|
}
|
|
avatars={advisorAvatars}
|
|
extraCount={7}
|
|
getAdvisorLabel={t["Get Advisor"]}
|
|
onGetAdvisor={openAdvisors}
|
|
/>
|
|
|
|
{/* Profile Locked banner */}
|
|
<section className="pointer-events-none fixed bottom-0 left-1/2 z-20 w-full sm:max-w-[375px] -translate-x-1/2 bg-[#F5F5F5]">
|
|
<div
|
|
style={{ paddingBottom: "calc(20px + var(--safe-bottom))" }}
|
|
className="pointer-events-auto px-[17px] pt-3"
|
|
>
|
|
<div className="flex items-center justify-center w-full gap-1 rounded-[11px] bg-[#DBDBDB] py-3.5">
|
|
<Image
|
|
src="/assets/images/material-symbols_lock.svg"
|
|
width={24}
|
|
height={24}
|
|
alt="lock"
|
|
/>
|
|
<h2 className="group-14 leading-none font-semibold text-[#747474]">
|
|
{t["Profile is locked"]}
|
|
</h2>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<MarriageAdvisorsOverlay
|
|
open={isAdvisorOpen}
|
|
onClose={closeAdvisors}
|
|
/>
|
|
|
|
<MatchProfileOverlay
|
|
open={isProfileOpen}
|
|
onClose={closeProfile}
|
|
/>
|
|
</>
|
|
);
|
|
}
|
|
|
|
// Fallback UI for male/other users
|
|
return (
|
|
<>
|
|
<PageBackground />
|
|
|
|
{isCallResultSheetOpen ? (
|
|
<CallResultSheet
|
|
onClose={() => setIsCallResultSheetOpen(false)}
|
|
onOtherReasonsClick={() => setIsDismissReasonSheetOpen(true)}
|
|
onSubmit={async (value) => {
|
|
if (caseId) {
|
|
await contactStatusMutation.mutateAsync({
|
|
action: "contacted",
|
|
custom_note: value,
|
|
});
|
|
}
|
|
}}
|
|
/>
|
|
) : null}
|
|
|
|
{isDismissReasonSheetOpen ? (
|
|
<DismissReasonSheet
|
|
onClose={() => setIsDismissReasonSheetOpen(false)}
|
|
onSubmit={async (value) => {
|
|
if (caseId) {
|
|
await contactStatusMutation.mutateAsync({
|
|
action: "contacted",
|
|
custom_note: value,
|
|
});
|
|
}
|
|
}}
|
|
/>
|
|
) : null}
|
|
|
|
<main className="-mx-[17px] flex min-h-screen flex-col px-[17px] pt-[max(12px,calc(var(--safe-top)+4px))] pb-36">
|
|
<PageHeader profile={profile} />
|
|
|
|
<section className="flex flex-1 items-center justify-center">
|
|
<div className="flex max-w-[300px] flex-col items-center">
|
|
<Image
|
|
src="/assets/images/Group 1597880467.svg"
|
|
alt={t["Selected candidate contact status"]}
|
|
width={131}
|
|
height={125}
|
|
/>
|
|
|
|
<h1 className="mt-10 text-center text-[18px] leading-[1.2] font-bold text-[#1A1A1A]">
|
|
{t["The selected candidate will contact your family shortly."]}
|
|
</h1>
|
|
</div>
|
|
</section>
|
|
|
|
<section
|
|
style={{ paddingBottom: `calc(2rem + var(--safe-bottom))` }}
|
|
className="fixed right-0 bottom-0 left-0 z-20 mx-auto flex w-full sm:max-w-[375px] gap-3 rounded-t-[30px] bg-white px-[17px] pt-6 shadow-[0_-18px_50px_rgba(15,23,42,0.08)]"
|
|
>
|
|
<button
|
|
type="button"
|
|
onClick={() => setIsCallResultSheetOpen(true)}
|
|
className="flex-1 h-[52px] rounded-[11px] bg-gradient-to-tl from-[#FE6F82] to-[#E03950] text-white font-semibold flex items-center justify-center cursor-pointer transition-opacity active:opacity-90"
|
|
>
|
|
{t["Confirm Contacted"]}
|
|
</button>
|
|
<button
|
|
type="button"
|
|
onClick={handleNoContactReport}
|
|
disabled={contactStatusMutation.isPending}
|
|
className="flex-1 h-[52px] rounded-[11px] border border-[#8B8B8B] bg-transparent text-[#8B8B8B] font-semibold flex items-center justify-center cursor-pointer transition-opacity active:opacity-90 disabled:opacity-50"
|
|
>
|
|
{contactStatusMutation.isPending ? (
|
|
<LoadingThreeDot className="text-[#8B8B8B]" />
|
|
) : (
|
|
t["Report No Contact"]
|
|
)}
|
|
</button>
|
|
</section>
|
|
</main>
|
|
|
|
<MarriageAdvisorsOverlay
|
|
open={isAdvisorOpen}
|
|
onClose={closeAdvisors}
|
|
/>
|
|
|
|
<MatchProfileOverlay
|
|
open={isProfileOpen}
|
|
onClose={closeProfile}
|
|
/>
|
|
</>
|
|
);
|
|
}
|