Browse Source
feat: update finding match page layout and styles
feat: update finding match page layout and styles
- Adjusted dimensions of the image container in FindingMatchPage. - Modified the submit path logic in Intro component to handle new case statuses. - Changed button to link in NewMatchPage for profile viewing and added a locked profile indicator. - Enhanced NewMatchProfilePage with mutation for responding to marriage cases and improved button states. - Updated QuestionsListPage to change text size for optional info prompt. - Refactored RequestAcceptedPage to include subscription handling and improved layout. - Added SubscriptionRequiredSheet for subscription prompts and payment handling. - Implemented useHabcoinPayment hook for managing Habcoin payments. - Introduced new RequestSentPage for displaying request status. - Added new SVG asset for request sent confirmation.master
15 changed files with 462 additions and 92 deletions
-
21public/assets/images/Group 15978804fdasf68.svg
-
1src/app/[lang]/request-sent/page.tsx
-
2src/app/finding-match/page.tsx
-
6src/app/intro/page.tsx
-
28src/app/new-match/page.tsx
-
56src/app/new-match/profile/page.tsx
-
2src/app/questions-list/page.tsx
-
113src/app/request-accepted/page.tsx
-
76src/app/request-sent/page.tsx
-
2src/components/ui/call-result-sheet.tsx
-
30src/components/ui/dismiss-reason-sheet.tsx
-
81src/components/ui/subscription-required-sheet.tsx
-
5src/hooks/marriage/types.ts
-
85src/hooks/marriage/use-habcoin-payment.ts
-
8src/hooks/marriage/use-profile-main.ts
21
public/assets/images/Group 15978804fdasf68.svg
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -0,0 +1 @@ |
|||
export { default } from "@/app/request-sent/page"; |
|||
@ -0,0 +1,76 @@ |
|||
"use client"; |
|||
|
|||
import Image from "next/image"; |
|||
import { useRouter } from "next/navigation"; |
|||
import Button from "@/components/ui/button"; |
|||
import NavigationButton from "@/components/ui/navigation-button"; |
|||
import { PageBackground } from "@/components/utils/page-background"; |
|||
import { localizePath } from "@/i18n/config"; |
|||
import { useI18n } from "@/i18n/provider"; |
|||
import Link from "next/link"; |
|||
|
|||
export default function RequestSentPage() { |
|||
const { locale } = useI18n(); |
|||
const router = useRouter(); |
|||
|
|||
return ( |
|||
<> |
|||
<PageBackground /> |
|||
|
|||
<main className="-mx-[17px] flex min-h-screen flex-col px-[23px] pt-7 pb-10 text-center"> |
|||
<header className="-mx-[6px] flex items-center justify-between"> |
|||
<NavigationButton icon="back" /> |
|||
<h1 className="font-faminela">Habib Marriage</h1> |
|||
<NavigationButton icon="support" iconLabel="Support" /> |
|||
</header> |
|||
|
|||
<div className="flex flex-1 flex-col justify-between gap-20 pt-[109px]"> |
|||
<section className="flex flex-col items-center"> |
|||
<div className="relative isolate flex items-center justify-center"> |
|||
<Image |
|||
src="/assets/images/Group 15978804fdasf68.svg" |
|||
alt="Request sent" |
|||
width={131} |
|||
height={125} |
|||
priority |
|||
className="relative z-10" |
|||
/> |
|||
</div> |
|||
|
|||
<h1 className="mt-11 text-[22px] leading-none font-black tracking-[0.02em] text-[#171717] uppercase"> |
|||
Request Sent |
|||
</h1> |
|||
|
|||
<p className="mt-4 max-w-[315px] text-[16px] leading-[1.45] font-semibold text-[#777777]"> |
|||
We will propose marriage on your behalf. If they accept, their contact details will be shared with you. |
|||
</p> |
|||
|
|||
<Link href={"/new-match/profile"} className="mt-9 w-full max-w-[212px]"> |
|||
<div className="bg-[#F5F5F5] px-4 py-2 rounded-[15px] shadow text-sm text-center font-semibold text-[#36363C]"> |
|||
Match Profile |
|||
</div> |
|||
</Link > |
|||
</section> |
|||
|
|||
<div className="space-y-8"> |
|||
|
|||
<section className="flex flex-col items-center text-center"> |
|||
<div className="flex items-center justify-center py-3.5 rounded-[11px] gap-1 w-full bg-[#DBDBDB]"> |
|||
<Image |
|||
src={"/assets/images/material-symbols_lock.svg"} |
|||
width={24} |
|||
height={24} |
|||
alt="lock" |
|||
/> |
|||
|
|||
<h2 className="text-[17px] leading-none font-semibold text-[#747474]"> |
|||
Profile is locked |
|||
</h2> |
|||
</div> |
|||
</section> |
|||
</div> |
|||
</div> |
|||
</main> |
|||
</> |
|||
); |
|||
} |
|||
@ -0,0 +1,81 @@ |
|||
"use client"; |
|||
|
|||
import Image from "next/image"; |
|||
import InformationSheet from "@/components/ui/information-sheet"; |
|||
|
|||
type SubscriptionRequiredSheetProps = { |
|||
onClose: () => void; |
|||
onPayment: () => void; |
|||
isPaymentPending?: boolean; |
|||
}; |
|||
|
|||
export function SubscriptionRequiredSheet({ |
|||
onClose, |
|||
onPayment, |
|||
isPaymentPending = false, |
|||
}: SubscriptionRequiredSheetProps) { |
|||
return ( |
|||
<InformationSheet |
|||
icon="coin" |
|||
title="Subscription required" |
|||
description={ |
|||
<div className="space-y-4 text-left"> |
|||
<p className="text-sm text-center leading-[1.45] font-medium text-[#2B2B2B]"> |
|||
To view profiles, you need a subscription. Each subscription |
|||
includes up to{" "} |
|||
<span className="font-bold underline decoration-[1.5px] underline-offset-2"> |
|||
3 |
|||
</span>{" "} |
|||
matches. |
|||
</p> |
|||
|
|||
<div className="rounded-[12px] border border-[#FF4F67] bg-[#FFECEF] px-3.5 py-2.5 text-center"> |
|||
<p className="text-xs leading-[1.45] font-semibold text-[#FF4F67]"> |
|||
To view profiles, a subscription is required. This helps cover our |
|||
support and matching services and applies only to male users |
|||
</p> |
|||
</div> |
|||
</div> |
|||
} |
|||
onClose={onClose} |
|||
buttons={({ close }) => ( |
|||
<div className="grid w-full grid-cols-[1fr_2fr] gap-3"> |
|||
<button |
|||
type="button" |
|||
className="appearance-none border-0 bg-transparent p-0 text-left" |
|||
onClick={close} |
|||
> |
|||
<div className="inline-flex w-full items-center justify-center rounded-[18px] border border-[#9A9A9A] bg-[#F7F7F7] px-4 py-[18px] text-[16px] font-bold text-[#8B8B8B] shadow-[inset_0_1px_0_rgba(255,255,255,0.8)] transition-opacity active:opacity-90"> |
|||
Back |
|||
</div> |
|||
</button> |
|||
|
|||
<button |
|||
type="button" |
|||
disabled={isPaymentPending} |
|||
className="appearance-none border-0 bg-transparent p-0 text-left disabled:cursor-not-allowed disabled:opacity-70" |
|||
onClick={onPayment} |
|||
> |
|||
<div className="inline-flex w-full items-center justify-center gap-3 rounded-[18px] bg-[#F0445B] px-4 py-[16px] text-[16px] font-semibold text-white shadow-[0_10px_18px_rgba(240,68,91,0.28)] transition-opacity active:opacity-90"> |
|||
<span>Payment</span> |
|||
|
|||
<span className="inline-flex items-center gap-1 rounded-full bg-[#E43B51] p-1.5 text-xs font-semibold leading-none text-white shadow-[inset_0_1px_0_rgba(255,255,255,0.12)]"> |
|||
<Image |
|||
src="/assets/images/Inner Plugdsain Iframe.svg" |
|||
alt="" |
|||
aria-hidden="true" |
|||
width={18} |
|||
height={18} |
|||
className="shrink-0" |
|||
/> |
|||
<span>50 Habib Coin</span> |
|||
</span> |
|||
</div> |
|||
</button> |
|||
</div> |
|||
)} |
|||
/> |
|||
); |
|||
} |
|||
|
|||
export default SubscriptionRequiredSheet; |
|||
@ -0,0 +1,85 @@ |
|||
"use client"; |
|||
|
|||
import { useMutation, useQueryClient } from "@tanstack/react-query"; |
|||
import { http } from "@/lib/http"; |
|||
import type { MutationOptions } from "./options"; |
|||
import { marriageQueryKeys } from "./query-keys"; |
|||
|
|||
const HABCOIN_PAY_SERVICE = "marriagesubscriptionplan" as const; |
|||
|
|||
type HabcoinPaymentResponse = unknown; |
|||
|
|||
function getSecurityToken() { |
|||
const token = process.env.NEXT_PUBLIC_SECURITY_KEY; |
|||
|
|||
if (!token) { |
|||
throw new Error("NEXT_PUBLIC_SECURITY_KEY is required"); |
|||
} |
|||
|
|||
return token; |
|||
} |
|||
|
|||
export function extractHabcoinPaymentUrl(response: HabcoinPaymentResponse) { |
|||
if (typeof response === "string") { |
|||
const trimmed = response.trim(); |
|||
|
|||
return /^(https?:\/\/|\/)/i.test(trimmed) ? trimmed : null; |
|||
} |
|||
|
|||
if (!response || typeof response !== "object") { |
|||
return null; |
|||
} |
|||
|
|||
const record = response as Record<string, unknown>; |
|||
const candidates = [ |
|||
record.url, |
|||
record.payment_url, |
|||
record.redirect_url, |
|||
record.checkout_url, |
|||
record.payment_link, |
|||
record.link, |
|||
record.href, |
|||
]; |
|||
|
|||
for (const candidate of candidates) { |
|||
if (typeof candidate === "string" && candidate.trim()) { |
|||
return candidate.trim(); |
|||
} |
|||
} |
|||
|
|||
if (record.data && typeof record.data === "object") { |
|||
return extractHabcoinPaymentUrl(record.data); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
export async function getHabcoinPayment(objectId: number) { |
|||
const { data } = await http.get<HabcoinPaymentResponse>("/habcoin/pay/", { |
|||
params: { |
|||
token: getSecurityToken(), |
|||
service: HABCOIN_PAY_SERVICE, |
|||
object_id: objectId, |
|||
}, |
|||
}); |
|||
|
|||
return data; |
|||
} |
|||
|
|||
export function useHabcoinPaymentMutation( |
|||
options?: MutationOptions<HabcoinPaymentResponse, number>, |
|||
) { |
|||
const queryClient = useQueryClient(); |
|||
|
|||
return useMutation({ |
|||
...options, |
|||
mutationFn: getHabcoinPayment, |
|||
onSuccess: async (data, variables, onMutateResult, context) => { |
|||
await queryClient.invalidateQueries({ |
|||
queryKey: marriageQueryKeys.profile(), |
|||
}); |
|||
|
|||
await options?.onSuccess?.(data, variables, onMutateResult, context); |
|||
}, |
|||
}); |
|||
} |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue