From 8f051d4fab411309123996b954992413f22e30df Mon Sep 17 00:00:00 2001 From: sina_sajjadi Date: Sun, 3 May 2026 13:27:11 +0330 Subject: [PATCH] fix: update icon source and add new SVG assets; enhance localization and UI components - Changed the icon source for the check icon in information-sheet.tsx. - Added new localization strings for decline and request proceed in dictionaries.ts. - Introduced new image assets: Group 1597880481.png and Icfdason.svg. - Created a new page for the new match profile with improved UI and functionality. - Implemented request and reject profile functionality with corresponding UI sheets. --- next.config.ts | 9 +- public/assets/images/Group 1597880481.png | Bin 0 -> 9557 bytes public/assets/images/Icfdason.svg | 3 + public/assets/images/Vectofdasr.svg | 3 + src/app/[lang]/new-match/profile/page.tsx | 1 + src/app/intro/page.tsx | 4 +- src/app/new-match/page.tsx | 424 +++++++++++++-------- src/app/new-match/profile/page.tsx | 205 ++++++++++ src/components/ui/dismiss-reason-sheet.tsx | 83 +++- src/components/ui/information-sheet.tsx | 2 +- src/i18n/dictionaries.ts | 7 +- 11 files changed, 567 insertions(+), 174 deletions(-) create mode 100644 public/assets/images/Group 1597880481.png create mode 100644 public/assets/images/Icfdason.svg create mode 100644 public/assets/images/Vectofdasr.svg create mode 100644 src/app/[lang]/new-match/profile/page.tsx create mode 100644 src/app/new-match/profile/page.tsx diff --git a/next.config.ts b/next.config.ts index e9ffa30..64aa975 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,14 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - /* config options here */ + images: { + remotePatterns: [ + { + protocol: "https", + hostname: "habibapp.com", + }, + ], + }, }; export default nextConfig; diff --git a/public/assets/images/Group 1597880481.png b/public/assets/images/Group 1597880481.png new file mode 100644 index 0000000000000000000000000000000000000000..0c3f4b6df669435f4eaeec2f87f36decef245c3c GIT binary patch literal 9557 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 literal 0 HcmV?d00001 diff --git a/public/assets/images/Icfdason.svg b/public/assets/images/Icfdason.svg new file mode 100644 index 0000000..e8a187c --- /dev/null +++ b/public/assets/images/Icfdason.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/assets/images/Vectofdasr.svg b/public/assets/images/Vectofdasr.svg new file mode 100644 index 0000000..42ece4b --- /dev/null +++ b/public/assets/images/Vectofdasr.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/app/[lang]/new-match/profile/page.tsx b/src/app/[lang]/new-match/profile/page.tsx new file mode 100644 index 0000000..5b7a853 --- /dev/null +++ b/src/app/[lang]/new-match/profile/page.tsx @@ -0,0 +1 @@ +export { default } from "@/app/new-match/profile/page"; diff --git a/src/app/intro/page.tsx b/src/app/intro/page.tsx index 495c275..40eb395 100644 --- a/src/app/intro/page.tsx +++ b/src/app/intro/page.tsx @@ -17,7 +17,9 @@ export default function Intro() { ? "/questions-list" : profile?.status === "waiting" ? "/finding-match" - : "/slider"; + : profile?.status === "in_case" || profile?.status === "matched" + ? "/new-match" + : "/slider"; const submitHref = localizePath(submitPath, locale); return ( diff --git a/src/app/new-match/page.tsx b/src/app/new-match/page.tsx index 9de6abf..86ff49e 100644 --- a/src/app/new-match/page.tsx +++ b/src/app/new-match/page.tsx @@ -1,191 +1,295 @@ "use client"; import Image from "next/image"; -import { useRouter } from "next/navigation"; -import { useState } from "react"; -import { IoClose } from "react-icons/io5"; +import { useMemo } from "react"; +import { FaBell } from "react-icons/fa6"; import Button from "@/components/ui/button"; -import InformationSheet from "@/components/ui/information-sheet"; -import NavigationButton from "@/components/ui/navigation-button"; -import StickyHeader from "@/components/ui/sticky-header"; -import { localizePath } from "@/i18n/config"; +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"; -type MatchFieldProps = { +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; - hint?: string; }; -type MatchSplitFieldProps = { - label: string; - hint?: string; - values: [string, string]; -}; +function normalizeFieldName(value: string) { + return value + .toLowerCase() + .replace(/^q\d+[_-]?/, "") + .replace(/[^a-z0-9]/g, ""); +} -function MatchField({ label, value, hint }: MatchFieldProps) { - return ( -
-

- {label} - {hint ? ( - - {hint} - - ) : null} -

-
- {value} -
-
- ); +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 MatchSplitField({ label, hint, values }: MatchSplitFieldProps) { +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, +) { + 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(); + 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 ( -
-

- {label} - {hint ? ( - - {hint} - - ) : null} -

-
- {values.map((value) => ( -
- {value} -
- ))} -
-
+

+ {field.label}: + {field.value} +

); } export default function NewMatchPage() { - const { dictionary: t, locale } = useI18n(); - const [isAcceptSheetOpen, setIsAcceptSheetOpen] = useState(false); - const [shouldNavigateToRequestAccepted, setShouldNavigateToRequestAccepted] = - useState(false); - const router = useRouter(); + 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 ( <> - {isAcceptSheetOpen ? ( - { - setIsAcceptSheetOpen(false); - - if (shouldNavigateToRequestAccepted) { - setShouldNavigateToRequestAccepted(false); - router.push(localizePath("/request-accepted", locale)); - } - }} - buttons={({ close }) => ( -
- + + +
+
+ + +

+ You have a new match! +

+ +

+ A matching profile has been found. Information is provided by the + girl's family or introducers. If you approve, we'll share + your profile with her family +

+
+ +
+ {isLoading ? ( +

+ Loading match summary... +

+ ) : isError ? ( +

+ Unable to load match summary. +

+ ) : matchSummary ? ( + <> +

+ Name: + {matchDisplay.name} +

+ +
+ {matchDisplay.occupation ? ( + + ) : null} + + {pairedFields.length ? ( +

+ {pairedFields.map((field, index) => ( + + {index > 0 ? | : null} + + {field.label}: {field.value} + + + ))} +

+ ) : null} + + {matchDisplay.maritalStatus ? ( + + ) : null} + {matchDisplay.cityPreference ? ( + + ) : null} + {matchDisplay.extraFields.map((field) => ( + + ))} +
+ -
+ + ) : ( +

+ No match summary is available yet. +

)} - /> - ) : null} - -
- -
- -

{t.match.title}

- -
-
- -
-
- - - - - - - - -
-
-
- +
+

+ {t.findingMatch.advisorTitle} +

+

+ {t.findingMatch.advisorDescription} +

+ +
+
+ {advisorAvatars.map((avatar) => ( + + + + ))} + + +7 + +
+ +
-
diff --git a/src/app/new-match/profile/page.tsx b/src/app/new-match/profile/page.tsx new file mode 100644 index 0000000..c41cad5 --- /dev/null +++ b/src/app/new-match/profile/page.tsx @@ -0,0 +1,205 @@ +"use client"; + +import Image from "next/image"; +import { useRouter } from "next/navigation"; +import { useState } from "react"; +import Button from "@/components/ui/button"; +import DismissReasonSheet from "@/components/ui/dismiss-reason-sheet"; +import InformationSheet from "@/components/ui/information-sheet"; +import NavigationButton from "@/components/ui/navigation-button"; +import StickyHeader from "@/components/ui/sticky-header"; +import { PageBackground } from "@/components/utils/page-background"; +import type { MarriageField, MarriageFieldValue } from "@/hooks/marriage/types"; +import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main"; +import { localizePath } from "@/i18n/config"; +import { useI18n } from "@/i18n/provider"; + +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 isImageField(field: MarriageField) { + return /(avatar|image|photo|picture|portrait|upload)/i.test( + `${field.key} ${field.label}`, + ); +} + +function MatchField({ field }: { field: MarriageField }) { + const value = formatFieldValue(field.value); + + if (!value || isImageField(field)) { + return null; + } + + const label = field.label || titleFromKey(field.key); + + return ( +
+

{label}

+

{value}

+
+ ); +} + +export default function NewMatchProfilePage() { + const { dictionary: t, locale } = useI18n(); + const router = useRouter(); + const [isRequestSheetOpen, setIsRequestSheetOpen] = useState(false); + const [isRejectSheetOpen, setIsRejectSheetOpen] = useState(false); + const [isDismissReasonSheetOpen, setIsDismissReasonSheetOpen] = + useState(false); + const { data: profile } = useMarriageProfileQuery(); + + return ( + <> + + {isRequestSheetOpen ? ( + ( +
+ + +
+ )} + onClose={() => setIsRequestSheetOpen(false)} + /> + ) : null} + {isRejectSheetOpen ? ( + ( +
+ + +
+ )} + onClose={() => setIsRejectSheetOpen(false)} + /> + ) : null} + {isDismissReasonSheetOpen ? ( + setIsDismissReasonSheetOpen(false)} + onSubmit={() => { + router.push(localizePath("/candidate-contact", locale)); + }} + /> + ) : null} + +
+ +
+ +

{t.match.title}

+
+
+ + +
+
+ +
+

+ {profile?.match_summary?.public_info.find( + (field) => field.key === "q1_full_name", + )?.value || "Name not available"} +

+

+ {profile?.match_summary?.public_info.find( + (field) => field.key === "q1_full_name", + )?.value || "Name not available"} +

+
+
+ +
+ {profile?.match_summary?.public_info.map((field) => ( + + ))} +
+
+ +
+
+
+ + +
+
+
+
+ + ); +} diff --git a/src/components/ui/dismiss-reason-sheet.tsx b/src/components/ui/dismiss-reason-sheet.tsx index 9e0a22c..90bf4d7 100644 --- a/src/components/ui/dismiss-reason-sheet.tsx +++ b/src/components/ui/dismiss-reason-sheet.tsx @@ -1,7 +1,7 @@ "use client"; import type { HTMLAttributes } from "react"; -import { useEffect, useState } from "react"; +import { useEffect, useId, useState } from "react"; import Button from "@/components/ui/button"; import { useI18n } from "@/i18n/provider"; @@ -24,9 +24,12 @@ export function DismissReasonSheet({ ...props }: DismissReasonSheetProps) { const { dictionary: t } = useI18n(); + const options = t.sheets.callOptions; + const groupId = useId(); const [isVisible, setIsVisible] = useState(true); const [isEntering, setIsEntering] = useState(true); const [isClosing, setIsClosing] = useState(false); + const [selectedReason, setSelectedReason] = useState(options[0]); const [reasonText, setReasonText] = useState(""); const closeSheet = () => { @@ -128,26 +131,86 @@ export function DismissReasonSheet({ {t.sheets.dismissReasons} +
+ + {t.sheets.dismissReasons} + + +
+ {options.map((option) => { + const checked = selectedReason === option; + const showTextArea = option === options[options.length - 1]; + + return ( +