Update UI for billing page

feat/ai-courses
Kamran Ahmed 1 month ago
parent b31a447855
commit 715cb82fc9
  1. 161
      src/components/Billing/BillingPage.tsx
  2. 59
      src/components/Billing/EmptyBillingScreen.tsx

@ -12,6 +12,7 @@ import { UpgradeAccountModal } from './UpgradeAccountModal';
import { getUrlParams } from '../../lib/browser';
import { VerifyUpgrade } from './VerifyUpgrade';
import { EmptyBillingScreen } from './EmptyBillingScreen';
import { Calendar, RefreshCw, Loader2, AlertTriangle } from 'lucide-react';
export type CreateCustomerPortalBody = {};
@ -30,25 +31,28 @@ export function BillingPage() {
queryClient,
);
const { mutate: createCustomerPortal, isPending: isCreatingCustomerPortal } =
useMutation(
{
mutationFn: (body: CreateCustomerPortalBody) => {
return httpPost<CreateCustomerPortalResponse>(
'/v1-create-customer-portal',
body,
);
},
onSuccess: (data) => {
window.location.href = data.url;
},
onError: (error) => {
console.error(error);
toast.error(error?.message || 'Failed to Create Customer Portal');
},
const {
mutate: createCustomerPortal,
isSuccess: isCreatingCustomerPortalSuccess,
isPending: isCreatingCustomerPortal,
} = useMutation(
{
mutationFn: (body: CreateCustomerPortalBody) => {
return httpPost<CreateCustomerPortalResponse>(
'/v1-create-customer-portal',
body,
);
},
queryClient,
);
onSuccess: (data) => {
window.location.href = data.url;
},
onError: (error) => {
console.error(error);
toast.error(error?.message || 'Failed to Create Customer Portal');
},
},
queryClient,
);
useEffect(() => {
if (isLoadingBillingDetails) {
@ -97,75 +101,108 @@ export function BillingPage() {
{showVerifyUpgradeModal && <VerifyUpgrade />}
{billingDetails?.status === 'none' && !isLoadingBillingDetails && (
<EmptyBillingScreen
onUpgrade={() => setShowUpgradeModal(true)}
/>
<EmptyBillingScreen onUpgrade={() => setShowUpgradeModal(true)} />
)}
{billingDetails?.status !== 'none' &&
!isLoadingBillingDetails &&
priceDetails && (
<>
<div className="mt-1 max-w-3xl">
{billingDetails?.status === 'past_due' && (
<div className="mb-4 rounded-md border border-red-300 bg-red-50 p-2 text-sm text-red-500">
We were not able to charge your card. Please update your payment
information.
<div className="mb-6 flex items-center gap-2 rounded-lg border border-red-300 bg-red-50 p-4 text-sm text-red-600">
<AlertTriangle className="h-5 w-5" />
<span>
We were not able to charge your card.{' '}
<button
disabled={
isCreatingCustomerPortal ||
isCreatingCustomerPortalSuccess
}
onClick={() => {
createCustomerPortal({});
}}
className="font-semibold underline underline-offset-4 disabled:cursor-not-allowed disabled:opacity-50"
>
Update payment information.
</button>
</span>
</div>
)}
<div className="flex items-start gap-10">
<div className="flex flex-col">
<span className="text-gray-500">Plan</span>
<span className="mt-1 text-lg font-medium capitalize text-black">
{selectedPlanDetails?.name}
</span>
</div>
<div className="flex grow items-center justify-between gap-2">
<div className="flex flex-col">
<span className="text-gray-500">Payment</span>
<span className="mt-1 text-lg font-medium capitalize text-black">
<h2 className="mb-2 text-xl font-semibold text-black">
Current Subscription
</h2>
<p className="mb-6 text-sm text-gray-500">
Thank you for being a pro member. Your plan details are below.
</p>
<div className="flex flex-col gap-6 sm:flex-row sm:items-center sm:justify-between">
<div className="flex items-center gap-4">
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-gray-100">
<RefreshCw className="size-5 text-gray-600" />
</div>
<div>
<span className="text-xs uppercase tracking-wider text-gray-400">
Payment
</span>
<h3 className="flex items-baseline text-lg font-semibold text-black">
${priceDetails.amount}
<span className="text-sm font-normal text-gray-500">
&nbsp;/ {priceDetails.interval}
<span className="ml-1 text-sm font-normal text-gray-500">
/ {priceDetails.interval}
</span>
</h3>
</div>
</div>
</div>
<div className="mt-6 border-t border-gray-100 pt-6">
<div className="flex items-start gap-4">
<div className="flex h-12 w-12 items-center justify-center rounded-full bg-gray-100">
<Calendar className="size-5 text-gray-600" />
</div>
<div>
<span className="text-xs uppercase tracking-wider text-gray-400">
{billingDetails?.cancelAtPeriodEnd
? 'Expires On'
: 'Renews On'}
</span>
<h3 className="text-lg font-semibold text-black">
{formattedNextBillDate}
</h3>
</div>
</div>
<div className="mt-8 flex flex-wrap gap-3">
{!shouldHideDeleteButton && (
<button
className="inline-flex items-center gap-1 self-end text-xs underline underline-offset-1 hover:text-gray-600"
className="inline-flex items-center justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 shadow-sm transition-colors hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2"
onClick={() => {
setShowUpgradeModal(true);
}}
>
Update Plan
Switch Plan
</button>
)}
</div>
</div>
<div className="mt-4 flex justify-between gap-2">
<div className="flex flex-col">
<span className="text-gray-500">
{billingDetails?.cancelAtPeriodEnd
? 'Expires On'
: 'Renews On'}
</span>
<span className="mt-1 text-lg font-medium capitalize text-black">
{formattedNextBillDate}
</span>
<button
className="inline-flex items-center justify-center rounded-md border border-gray-300 bg-white px-4 py-2 text-sm font-medium text-gray-700 shadow-sm transition-colors hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
onClick={() => {
createCustomerPortal({});
}}
disabled={
isCreatingCustomerPortal || isCreatingCustomerPortalSuccess
}
>
{(isCreatingCustomerPortal ||
isCreatingCustomerPortalSuccess) && (
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
)}
Manage Subscription
</button>
</div>
<button
className="inline-flex self-end text-xs underline underline-offset-1 hover:text-gray-600 disabled:cursor-not-allowed disabled:opacity-50"
onClick={() => {
createCustomerPortal({});
}}
disabled={isCreatingCustomerPortal}
>
Manage my Subscription
</button>
</div>
</>
</div>
)}
</>
);

@ -5,6 +5,7 @@ import {
MessageCircleIcon,
SparklesIcon,
Zap,
CheckCircle,
} from 'lucide-react';
type EmptyBillingScreenProps = {
@ -38,31 +39,45 @@ export function EmptyBillingScreen(props: EmptyBillingScreenProps) {
const { onUpgrade } = props;
return (
<div className="mt-12 flex h-full w-full flex-col items-center">
<CreditCard className="mb-3 h-12 w-12 text-gray-300" />
<h3 className="mb-3 text-xl font-semibold text-black">
No Active Subscription
</h3>
<div className="mt-6 max-w-3xl">
<h2 className="mb-6 text-2xl font-bold text-black">Subscription Details</h2>
<div className="overflow-hidden rounded-lg bg-white shadow-sm">
<div className="p-6">
<div className="flex flex-col items-center text-center">
<div className="flex h-16 w-16 items-center justify-center rounded-full bg-gray-100">
<CreditCard className="h-8 w-8 text-gray-500" />
</div>
<h3 className="mt-4 text-xl font-semibold text-black">
No Active Subscription
</h3>
<p className="text-balance text-gray-700">
Unlock pro benefits by upgrading to a subscription
</p>
<p className="mt-2 max-w-md text-balance text-gray-600">
Unlock premium benefits by upgrading to a subscription
</p>
<div className="my-8 flex flex-col gap-2">
{perks.map((perk) => (
<p className="textsm flex items-center text-gray-600" key={perk.text}>
<perk.icon className="mr-2 h-4 w-4 text-gray-500" />
{perk.text}
</p>
))}
</div>
<div className="mt-6 w-full max-w-md rounded-lg border border-gray-200 bg-gray-50 p-4">
<h4 className="mb-3 font-medium text-gray-800">Premium Benefits</h4>
<div className="flex flex-col gap-3">
{perks.map((perk) => (
<div className="flex items-center gap-2 text-gray-700" key={perk.text}>
<CheckCircle className="h-5 w-5 text-green-500" />
<span>{perk.text}</span>
</div>
))}
</div>
</div>
<button
onClick={onUpgrade}
className="inline-flex items-center justify-center rounded-lg bg-black px-6 py-2.5 text-sm font-medium text-white transition-colors hover:opacity-80 focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2"
>
Upgrade Account
</button>
<button
onClick={onUpgrade}
className="mt-6 inline-flex items-center justify-center rounded-md bg-black px-6 py-2.5 text-sm font-medium text-white shadow-sm transition-colors hover:bg-black/80 focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2"
>
Upgrade Account
</button>
</div>
</div>
</div>
</div>
);
}

Loading…
Cancel
Save