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.
 
 
 
 

297 lines
9.6 KiB

"use client";
import Image from "next/image";
import { useMemo } from "react";
import { FaBell } from "react-icons/fa6";
import Button from "@/components/ui/button";
import { PageBackground } from "@/components/utils/page-background";
import type {
MarriageField,
MarriageFieldValue,
MarriageMatchSummary,
} from "@/hooks/marriage/types";
import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main";
import { useI18n } from "@/i18n/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" },
];
const fieldCandidates = {
name: ["name", "full_name", "fullname", "first_name", "display_name"],
occupation: ["occupation", "job", "profession", "career", "work"],
age: ["age"],
city: ["city", "current_city", "residence_city", "location", "residence"],
maritalStatus: ["marital_status", "maritalstatus", "relationship_status"],
cityPreference: [
"city_preference",
"citypreference",
"preferred_city",
"preferred_location",
"future_residence",
],
} as const;
type DisplayField = {
id: string;
label: string;
value: string;
};
function normalizeFieldName(value: string) {
return value
.toLowerCase()
.replace(/^q\d+[_-]?/, "")
.replace(/[^a-z0-9]/g, "");
}
function formatFieldValue(value: MarriageFieldValue) {
if (value === null || value === "") {
return null;
}
if (typeof value === "boolean") {
return value ? "Yes" : "No";
}
return String(value);
}
function titleFromKey(key: string) {
return key
.replace(/^q\d+[_-]?/i, "")
.replace(/[_-]+/g, " ")
.replace(/\s+/g, " ")
.trim()
.replace(/\b\w/g, (letter) => letter.toUpperCase());
}
function toDisplayField(field: MarriageField): DisplayField | null {
const value = formatFieldValue(field.value);
if (!value) {
return null;
}
return {
id: field.key || field.label || value,
label: field.label || titleFromKey(field.key),
value,
};
}
function pickField(
fields: MarriageField[],
candidates: readonly string[],
usedIndexes: Set<number>,
) {
const candidateSet = new Set(candidates.map(normalizeFieldName));
for (const [fieldIndex, field] of fields.entries()) {
if (usedIndexes.has(fieldIndex)) {
continue;
}
const displayField = toDisplayField(field);
if (
displayField &&
[field.key, field.label].some((value) =>
candidateSet.has(normalizeFieldName(value)),
)
) {
usedIndexes.add(fieldIndex);
return displayField;
}
}
return null;
}
function useMatchSummaryDisplay(matchSummary: MarriageMatchSummary | null) {
return useMemo(() => {
const fields = matchSummary?.public_info ?? [];
const usedIndexes = new Set<number>();
const name = pickField(fields, fieldCandidates.name, usedIndexes);
const occupation = pickField(
fields,
fieldCandidates.occupation,
usedIndexes,
);
const age = pickField(fields, fieldCandidates.age, usedIndexes);
const city = pickField(fields, fieldCandidates.city, usedIndexes);
const maritalStatus = pickField(
fields,
fieldCandidates.maritalStatus,
usedIndexes,
);
const cityPreference = pickField(
fields,
fieldCandidates.cityPreference,
usedIndexes,
);
const extraFields = fields
.filter((_, index) => !usedIndexes.has(index))
.map(toDisplayField)
.filter((field): field is DisplayField => Boolean(field))
.slice(0, 4);
return {
age,
city,
cityPreference,
extraFields,
maritalStatus,
name:
name?.value ??
(matchSummary?.id ? `Profile #${matchSummary.id}` : null),
occupation,
};
}, [matchSummary]);
}
function FieldLine({ field }: { field: DisplayField }) {
return (
<p className="break-words text-[10px] leading-[1.85] font-medium text-white">
<span>{field.label}: </span>
<span>{field.value}</span>
</p>
);
}
export default function NewMatchPage() {
const { dictionary: t } = useI18n();
const { data: profile, isError, isLoading } = useMarriageProfileQuery();
const matchSummary = profile?.match_summary ?? null;
const matchDisplay = useMatchSummaryDisplay(matchSummary);
const pairedFields = [matchDisplay.age, matchDisplay.city].filter(
(field): field is DisplayField => Boolean(field),
);
return (
<>
<PageBackground />
<main className="-mx-[17px] flex min-h-screen flex-col px-[5px] pt-5 pb-5 text-center">
<section className="flex flex-col items-center">
<div
aria-hidden="true"
className="relative flex h-[60px] w-[60px] items-center justify-center rounded-full bg-[#FF4E67] shadow-[0_12px_28px_rgba(240,68,91,0.22)]"
>
<span className="absolute top-3 right-3 h-2 w-2 rounded-full border-2 border-[#FFD54B] border-b-0 border-l-0" />
<span className="absolute top-4 right-2 h-3 w-3 rounded-full border-2 border-[#FFD54B] border-b-0 border-l-0" />
<FaBell className="size-8 -rotate-12 text-[#FFD84A] drop-shadow-[0_2px_0_rgba(151,92,0,0.45)]" />
</div>
<h1 className="mt-[15px] text-[17px] leading-none font-black tracking-[0.02em] text-[#111111] uppercase">
You have a new match!
</h1>
<p className="mt-[11px] max-w-[322px] text-[12px] leading-[1.35] font-semibold text-[#7C7C7C]">
A matching profile has been found. Information is provided by the
girl&apos;s family or introducers. If you approve, we&apos;ll share
your profile with her family
</p>
</section>
<section className="mt-[36px] rounded-[11px] bg-[linear-gradient(180deg,#F0445B_0%,#F4556E_100%)] px-[17px] pt-[18px] pb-[17px] text-white shadow-[0_18px_38px_rgba(240,68,91,0.25)]">
{isLoading ? (
<p className="py-8 text-[13px] font-semibold">
Loading match summary...
</p>
) : isError ? (
<p className="py-8 text-[13px] font-semibold">
Unable to load match summary.
</p>
) : matchSummary ? (
<>
<h2 className="break-words text-[13px] leading-[1.4] font-bold">
<span>Name: </span>
<span>{matchDisplay.name}</span>
</h2>
<div className="mt-[3px] min-h-[68px]">
{matchDisplay.occupation ? (
<FieldLine field={matchDisplay.occupation} />
) : null}
{pairedFields.length ? (
<p className="break-words text-[10px] leading-[1.85] font-medium text-white">
{pairedFields.map((field, index) => (
<span key={field.id}>
{index > 0 ? <span> | </span> : null}
<span>
{field.label}: {field.value}
</span>
</span>
))}
</p>
) : null}
{matchDisplay.maritalStatus ? (
<FieldLine field={matchDisplay.maritalStatus} />
) : null}
{matchDisplay.cityPreference ? (
<FieldLine field={matchDisplay.cityPreference} />
) : null}
{matchDisplay.extraFields.map((field) => (
<FieldLine key={field.id} field={field} />
))}
</div>
<Button
className="mt-[15px] rounded-[7px] border-none bg-white bg-none py-[12px] text-[#F0445B]! shadow-none"
href="/new-match/profile"
>
View Profile
</Button>
</>
) : (
<p className="py-8 text-[13px] font-semibold">
No match summary is available yet.
</p>
)}
</section>
<section className="mt-[29px] rounded-[12px] border border-white/80 bg-white/78 px-3 py-3.5 text-left shadow-[0_18px_45px_rgba(15,23,42,0.06)] backdrop-blur-sm">
<h2 className="text-[16px] leading-none font-bold text-[#1C1C1C]">
{t.findingMatch.advisorTitle}
</h2>
<p className="mt-2 max-w-[280px] text-[11px] leading-[1.45] font-semibold text-[#8A8A8A]">
{t.findingMatch.advisorDescription}
</p>
<div className="mt-4 flex items-center justify-between gap-3">
<div className="flex items-center pl-1">
{advisorAvatars.map((avatar) => (
<span
key={avatar.id}
className="-ml-1.5 flex h-[30px] w-[30px] overflow-hidden rounded-full border-2 border-white bg-[#E7E7E7] first:ml-0"
>
<Image
src={avatar.src}
alt=""
width={30}
height={30}
className="h-full w-full object-cover"
/>
</span>
))}
<span className="-ml-1.5 flex h-[30px] w-[30px] items-center justify-center rounded-full border-2 border-white bg-[#EDEEF1] text-[12px] font-semibold text-[#1C1C1C]">
+7
</span>
</div>
<Button
className="w-auto rounded-[9px] border-none bg-[#EBEDF0] bg-none px-5 py-[13px] text-[#111111]! shadow-none"
href="/questions-list"
>
{t.findingMatch.getAdvisor}
</Button>
</div>
</section>
</main>
</>
);
}