|
|
|
@ -39,6 +39,8 @@ import { |
|
|
|
} from "@/hooks/marriage/use-habcoin-payment"; |
|
|
|
import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main"; |
|
|
|
import { getSubmitPath } from "@/lib/get-submit-path"; |
|
|
|
import { copyToClipboard } from "@/lib/webview-actions"; |
|
|
|
import { Ic } from "@/icons"; |
|
|
|
import { localizePath } from "@/translations/config"; |
|
|
|
import { useI18n } from "@/translations/provider"; |
|
|
|
|
|
|
|
@ -96,28 +98,79 @@ function isMarriagePhoneFieldValue( |
|
|
|
} |
|
|
|
|
|
|
|
function getContactInfoPhoneItems( |
|
|
|
contactInfoFields: MarriageField[] | null | undefined, |
|
|
|
contactInfoFields: any[] | null | undefined, |
|
|
|
): ContactInfoPhoneItem[] { |
|
|
|
if (!contactInfoFields) { |
|
|
|
if (!contactInfoFields || !Array.isArray(contactInfoFields)) { |
|
|
|
return []; |
|
|
|
} |
|
|
|
|
|
|
|
// Look for contextual representative info if available
|
|
|
|
const repNameField = contactInfoFields.find( |
|
|
|
(f) => |
|
|
|
typeof f?.key === "string" && |
|
|
|
f.key.toLowerCase().includes("representative_s_full_name") && |
|
|
|
f.value, |
|
|
|
); |
|
|
|
const repRelationField = contactInfoFields.find( |
|
|
|
(f) => |
|
|
|
typeof f?.key === "string" && |
|
|
|
f.key.toLowerCase().includes("relationship_to_representative") && |
|
|
|
f.value, |
|
|
|
); |
|
|
|
|
|
|
|
const repName = repNameField?.value ? String(repNameField.value).trim() : ""; |
|
|
|
const repRelation = repRelationField?.value |
|
|
|
? String(repRelationField.value).trim() |
|
|
|
: ""; |
|
|
|
|
|
|
|
return contactInfoFields |
|
|
|
.map((field) => { |
|
|
|
const phoneNumber = sanitizePhoneNumber(field.value); |
|
|
|
if (!field) return null; |
|
|
|
|
|
|
|
// Extract phone number from field properties
|
|
|
|
let phoneNumber: string | null = null; |
|
|
|
|
|
|
|
if (field.phone_number && typeof field.phone_number === "string") { |
|
|
|
phoneNumber = field.phone_number.trim(); |
|
|
|
} else if (field.raw_number && typeof field.raw_number === "string") { |
|
|
|
const countryCode = field.country_code |
|
|
|
? String(field.country_code).replace(/\D/g, "") |
|
|
|
: "98"; |
|
|
|
phoneNumber = `+${countryCode} ${field.raw_number.trim()}`; |
|
|
|
} else if (field.raw_value && typeof field.raw_value === "object") { |
|
|
|
phoneNumber = sanitizePhoneNumber(field.raw_value); |
|
|
|
} else if (field.value) { |
|
|
|
phoneNumber = sanitizePhoneNumber(field.value); |
|
|
|
} |
|
|
|
|
|
|
|
if (!phoneNumber) { |
|
|
|
// If this field is not a phone field, skip it
|
|
|
|
if ( |
|
|
|
!phoneNumber || |
|
|
|
(field.type && |
|
|
|
field.type !== "phone" && |
|
|
|
!field.phone_number && |
|
|
|
!field.raw_number) |
|
|
|
) { |
|
|
|
return null; |
|
|
|
} |
|
|
|
|
|
|
|
const rawLabel = field.label || field.key; |
|
|
|
const label = rawLabel |
|
|
|
const key = String(field.key || ""); |
|
|
|
const rawLabel = String(field.label || field.key || ""); |
|
|
|
let label = rawLabel |
|
|
|
.replace(/\s+with\s+Country\s+Code/gi, "") |
|
|
|
.replace(/\s+با\s+کد\s+کشور/g, "") |
|
|
|
.trim(); |
|
|
|
|
|
|
|
// Add representative context to label if applicable
|
|
|
|
if (key.includes("representative")) { |
|
|
|
const contextParts = [repRelation, repName].filter(Boolean); |
|
|
|
if (contextParts.length > 0 && !label.includes(contextParts[0])) { |
|
|
|
label = `${label} (${contextParts.join(" - ")})`; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return { |
|
|
|
key: field.key, |
|
|
|
key: key || phoneNumber, |
|
|
|
label, |
|
|
|
phoneNumber, |
|
|
|
}; |
|
|
|
@ -126,54 +179,44 @@ function getContactInfoPhoneItems( |
|
|
|
} |
|
|
|
|
|
|
|
function ContactInfoPhoneCard({ item }: { item: ContactInfoPhoneItem }) { |
|
|
|
const [isCopied, setIsCopied] = useState(false); |
|
|
|
|
|
|
|
const handleCopy = async () => { |
|
|
|
try { |
|
|
|
await copyToClipboard(item.phoneNumber, item.label); |
|
|
|
setIsCopied(true); |
|
|
|
setTimeout(() => { |
|
|
|
setIsCopied(false); |
|
|
|
}, 1500); |
|
|
|
} catch (err) { |
|
|
|
console.error("Error copying contact to clipboard", err); |
|
|
|
} |
|
|
|
}; |
|
|
|
|
|
|
|
return ( |
|
|
|
<div className="flex items-center justify-between rounded-[15px] border border-[#E4E4E4] bg-[#FBFBFB] px-4 py-3.5 shadow-[0_4px_12px_rgba(0,0,0,0.03)] gap-4"> |
|
|
|
<div className="text-left min-w-0"> |
|
|
|
<p className="text-xs font-semibold text-[#8F8F8F] break-words"> |
|
|
|
{item.label} |
|
|
|
</p> |
|
|
|
<p className="text-base font-bold text-[#1F1F1F] dir-ltr"> |
|
|
|
<div className="flex items-center justify-between rounded-[20px] bg-[#FFF5F6] border border-[#FFE4E8] px-5 py-4 shadow-sm transition-all hover:border-[#FFD0D8]"> |
|
|
|
<div className="text-start min-w-0 flex-1 pe-3"> |
|
|
|
<p className="text-[19px] font-black text-[#1F2937] tracking-wider dir-ltr text-start select-all leading-tight"> |
|
|
|
{item.phoneNumber} |
|
|
|
</p> |
|
|
|
<p className="mt-1 text-[13px] font-semibold text-[#F0445B] text-start truncate leading-snug"> |
|
|
|
{item.label} |
|
|
|
</p> |
|
|
|
</div> |
|
|
|
|
|
|
|
<div className="flex items-center gap-2 shrink-0"> |
|
|
|
<a |
|
|
|
href={`tel:${item.phoneNumber}`} |
|
|
|
onClick={(e) => { |
|
|
|
if (typeof window !== "undefined" && "HabibApp" in window) { |
|
|
|
e.preventDefault(); |
|
|
|
try { |
|
|
|
const app = ( |
|
|
|
window as Window & { |
|
|
|
HabibApp?: { postMessage: (msg: string) => void }; |
|
|
|
} |
|
|
|
).HabibApp; |
|
|
|
app?.postMessage( |
|
|
|
JSON.stringify({ |
|
|
|
action: "open_external_url", |
|
|
|
data: { |
|
|
|
url: `tel:${item.phoneNumber}`, |
|
|
|
mode: "externalApplication", |
|
|
|
title: "phone_call", |
|
|
|
}, |
|
|
|
}), |
|
|
|
); |
|
|
|
} catch (err) { |
|
|
|
console.error("Error calling HabibApp bridge", err); |
|
|
|
} |
|
|
|
} |
|
|
|
}} |
|
|
|
className="inline-flex p-3 items-center justify-center rounded-[10px] bg-[#F0445B] text-white shrink-0" |
|
|
|
> |
|
|
|
<Image |
|
|
|
src={"/assets/images/Vecfdastor.svg"} |
|
|
|
width={16} |
|
|
|
height={16} |
|
|
|
alt="call" |
|
|
|
/> |
|
|
|
</a> |
|
|
|
</div> |
|
|
|
<button |
|
|
|
type="button" |
|
|
|
onClick={handleCopy} |
|
|
|
title="Copy" |
|
|
|
aria-label="Copy phone number" |
|
|
|
className="size-[44px] rounded-[14px] bg-[linear-gradient(180deg,#F0445B_0%,#F54B64_100%)] text-white flex items-center justify-center shrink-0 shadow-[0_4px_12px_rgba(240,68,91,0.25)] active:scale-[0.90] transition-all duration-150 cursor-pointer hover:opacity-95" |
|
|
|
> |
|
|
|
{isCopied ? ( |
|
|
|
<Ic name="check" className="size-5 text-white transition-transform scale-110" /> |
|
|
|
) : ( |
|
|
|
<Ic name="copy" className="size-5 text-white" /> |
|
|
|
)} |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
); |
|
|
|
} |
|
|
|
@ -670,7 +713,7 @@ export default function RequestAcceptedClient() { |
|
|
|
)} |
|
|
|
</div> |
|
|
|
) : ( |
|
|
|
<div className="flex mt-8 w-full justify-center gap-3.5 max-w-[360px] mx-auto"> |
|
|
|
<div className="flex mt-8 w-full justify-center gap-3.5 max-w-md mx-auto"> |
|
|
|
{isFemaleProfile ? ( |
|
|
|
<button |
|
|
|
type="button" |
|
|
|
@ -690,7 +733,7 @@ export default function RequestAcceptedClient() { |
|
|
|
) : ( |
|
|
|
<Link |
|
|
|
href={profileHref} |
|
|
|
className="flex-1 max-w-[170px]" |
|
|
|
className="flex-1" |
|
|
|
> |
|
|
|
<div className="w-full min-h-[48px] px-3.5 py-2.5 rounded-[14px] border border-[#E2E8F0] bg-white/95 backdrop-blur-sm text-[#475569] font-bold text-[14px] leading-tight shadow-sm flex items-center justify-center text-center whitespace-nowrap transition-all hover:bg-[#F8FAFC] active:scale-[0.98]"> |
|
|
|
{primaryActionText} |
|
|
|
@ -713,7 +756,7 @@ export default function RequestAcceptedClient() { |
|
|
|
className={ |
|
|
|
isFemaleProfile |
|
|
|
? "flex-1 min-h-[48px] px-3.5 py-2.5 rounded-[14px] bg-gradient-to-r from-[#FF6687] to-[#FF456C] text-white font-bold text-[13.5px] leading-tight flex items-center justify-center text-center whitespace-nowrap shadow-sm transition-all duration-150 cursor-pointer hover:opacity-95 active:scale-[0.96] disabled:opacity-50 disabled:cursor-not-allowed" |
|
|
|
: "flex-1 max-w-[170px] min-h-[48px] appearance-none border-0 bg-transparent p-0 text-center cursor-pointer transition-transform duration-150 active:scale-[0.96] disabled:opacity-50 disabled:cursor-not-allowed" |
|
|
|
: "flex-1 min-h-[48px] appearance-none border-0 bg-transparent p-0 text-center cursor-pointer transition-transform duration-150 active:scale-[0.96] disabled:opacity-50 disabled:cursor-not-allowed" |
|
|
|
} |
|
|
|
> |
|
|
|
{isFemaleProfile ? ( |
|
|
|
@ -743,7 +786,7 @@ export default function RequestAcceptedClient() { |
|
|
|
!isFemaleContactConfirmed && |
|
|
|
!noContactReportedSuccess && |
|
|
|
!(isFemaleProfile && contactStatusMutation.isPending) ? ( |
|
|
|
<div className="border border-[#FDA4AF]/60 bg-[#FFF1F2]/80 backdrop-blur-sm rounded-[16px] mt-6 p-4 max-w-[360px] shadow-sm"> |
|
|
|
<div className="border border-[#FDA4AF]/60 bg-[#FFF1F2]/80 backdrop-blur-sm rounded-[16px] mt-6 p-4 max-w-md mx-auto shadow-sm"> |
|
|
|
<p className="text-[#BE123C] text-[12px] font-medium leading-[1.65] whitespace-pre-line text-justify"> |
|
|
|
{ |
|
|
|
t[ |
|
|
|
@ -773,10 +816,10 @@ export default function RequestAcceptedClient() { |
|
|
|
onGetAdvisor={openAdvisors} |
|
|
|
/> |
|
|
|
|
|
|
|
<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]"> |
|
|
|
<section 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] pt-3" |
|
|
|
className="pointer-events-auto px-[17px] md:px-8 pt-3" |
|
|
|
> |
|
|
|
<div className="flex items-center justify-center w-full gap-1 rounded-[11px] bg-[#DBDBDB] py-3.5"> |
|
|
|
<Image |
|
|
|
|