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.
 
 
 
 
 

184 lines
7.3 KiB

import { useState } from "react";
import Image from "next/image";
import { useHabcoinInventoryQuery } from "@/hooks/marriage/use-habcoin-inventory";
import { DiscountWidget } from "./discount-widget";
import type { CheckDiscountResult } from "@/hooks/marriage/use-validate-discount";
import {
buyHabibCoinPackages,
isInFlutterWebView,
} from "@/lib/webview-actions";
import { useI18n } from "@/translations/provider";
import InformationSheet from "./information-sheet";
import { LoadingThreeDot } from "./loading-three-dot";
type SubscriptionRequiredSheetProps = {
onClose: () => void;
onPayment: (discountCode?: string) => void;
isPaymentPending?: boolean;
/** Payment error to display above the buttons (e.g. "Not enough coins"). */
errorMessage?: string | null;
/** Show the "Buy Habib Coins" button (Flutter opens its packages page). */
showBuyCoins?: boolean;
price?: number;
planId?: number | string;
};
export function SubscriptionRequiredSheet({
onClose,
onPayment,
isPaymentPending = false,
errorMessage = null,
showBuyCoins = false,
price = 50,
planId,
}: SubscriptionRequiredSheetProps) {
const { dictionary: t } = useI18n();
const [appliedDiscount, setAppliedDiscount] =
useState<CheckDiscountResult | null>(null);
const { data: inventory, isLoading: isInventoryLoading } =
useHabcoinInventoryQuery();
const finalPrice = appliedDiscount?.valid
? appliedDiscount.discountedPrice
: price;
const balance = inventory?.coin_balance ?? 0;
const hasEnoughCoins =
!showBuyCoins && (inventory === undefined ? true : balance >= finalPrice);
if (isInventoryLoading) {
return <InformationSheet isLoading={true} onClose={onClose} />;
}
return (
<InformationSheet
icon="coin"
title={
!hasEnoughCoins ? (
<span className="text-[#BD3F3F]">
{t["You do not have enough Habib Coins"] ||
"You do not have enough Habib Coins"}
</span>
) : (
t["Subscription required"] || "Subscription required"
)
}
description={
!hasEnoughCoins ? (
<p className="text-[14px] text-[#4D4D4D] leading-[1.3] font-normal text-center">
{t[
"Please add more Habib Coins to your balance (or use discount code), so that you can use them to access this content"
] ||
"Please add more Habib Coins to your balance (or use discount code), so that you can use them to access this content"}
</p>
) : (
<div className="space-y-4 text-left">
<p className="text-sm text-center leading-[1.45] font-medium text-[#2B2B2B]">
{t[
"To view profiles, you need a subscription. Each subscription includes up to {count} matches."
]?.replace("{count}", "3") || (
<>
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]">
{t[
"To view profiles, a subscription is required. This helps cover our support and matching services and applies only to male users"
] ||
"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="space-y-3">
{planId ? (
<DiscountWidget
objectId={planId}
onDiscountApplied={(res) => setAppliedDiscount(res)}
onDiscountCleared={() => setAppliedDiscount(null)}
/>
) : null}
{errorMessage && (
<div className="w-full bg-[#FEF2F2] text-[#B91C1C] text-xs p-3 rounded-[11px] text-center font-medium">
{errorMessage}
</div>
)}
<div className="grid w-full grid-cols-[33fr_67fr] gap-3">
<button
type="button"
className="appearance-none border-0 bg-transparent p-0 text-left min-w-0"
onClick={close}
>
<div className="inline-flex w-full items-center justify-center rounded-[11px] border border-[#747474] bg-transparent px-2 h-[52px] text-[16px] font-semibold text-[#747474] transition-opacity active:opacity-90 min-w-0">
<span className="truncate">{t["Back"] || "Back"}</span>
</div>
</button>
{!hasEnoughCoins ? (
<button
type="button"
className="appearance-none border-0 bg-transparent p-0 text-left min-w-0"
onClick={() => buyHabibCoinPackages()}
>
<div className="inline-flex w-full items-center justify-center gap-2 rounded-[11px] bg-[#F0445B] px-4 h-[52px] text-[16px] font-semibold text-white shadow-[0_10px_18px_rgba(240,68,91,0.28)] transition-opacity active:opacity-90 min-w-0">
<span className="truncate">
{t["Buy Habib Coins"] || "Buy Habib Coins"}
</span>
</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 min-w-0"
onClick={() =>
onPayment(
appliedDiscount?.valid ? appliedDiscount.code : undefined,
)
}
>
<div className="inline-flex w-full items-center justify-center gap-2 rounded-[11px] bg-[#F0445B] px-4 h-[52px] text-[16px] font-semibold text-white shadow-[0_10px_18px_rgba(240,68,91,0.28)] transition-opacity active:opacity-90 min-w-0">
{isPaymentPending ? (
<LoadingThreeDot />
) : (
<>
<span className="truncate">
{t["Payment"] || "Payment"}
</span>
<span className="inline-flex items-center gap-1 rounded-[12px] bg-black/15 px-[9px] py-[4px] text-xs font-semibold leading-none text-white shrink-0 min-w-0 whitespace-nowrap">
<Image
src="/assets/images/Inner Plugdsain Iframe.svg"
alt=""
aria-hidden="true"
width={16}
height={16}
className="shrink-0"
/>
<span className="truncate">{finalPrice}</span>
</span>
</>
)}
</div>
</button>
)}
</div>
</div>
)}
/>
);
}
export default SubscriptionRequiredSheet;