mirror of
https://github.com/kamranahmedse/developer-roadmap.git
synced 2025-08-16 22:23:59 +02:00
Update design for upgrade
This commit is contained in:
@@ -6,5 +6,5 @@ PUBLIC_COURSE_APP_URL=http://localhost:5173
|
|||||||
PUBLIC_STRIPE_INDIVIDUAL_MONTHLY_PRICE_ID=
|
PUBLIC_STRIPE_INDIVIDUAL_MONTHLY_PRICE_ID=
|
||||||
PUBLIC_STRIPE_INDIVIDUAL_YEARLY_PRICE_ID=
|
PUBLIC_STRIPE_INDIVIDUAL_YEARLY_PRICE_ID=
|
||||||
|
|
||||||
PUBLIC_STRIPE_INDIVIDUAL_MONTHLY_PRICE_AMOUNT=20
|
PUBLIC_STRIPE_INDIVIDUAL_MONTHLY_PRICE_AMOUNT=10
|
||||||
PUBLIC_STRIPE_INDIVIDUAL_YEARLY_PRICE_AMOUNT=180
|
PUBLIC_STRIPE_INDIVIDUAL_YEARLY_PRICE_AMOUNT=100
|
@@ -57,36 +57,36 @@ export function UpdatePlanConfirmation(props: UpdatePlanConfirmationProps) {
|
|||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
onClose={isPending ? () => {} : onClose}
|
onClose={isPending ? () => {} : onClose}
|
||||||
bodyClassName="rounded-xl bg-white p-4"
|
bodyClassName="rounded-xl bg-white p-6"
|
||||||
>
|
>
|
||||||
<h3 className="text-xl font-bold">Subscription Update</h3>
|
<h3 className="text-xl font-bold text-black">Subscription Update</h3>
|
||||||
<p className="mt-2 text-balance text-gray-500">
|
<p className="mt-2 text-balance text-gray-600">
|
||||||
Your plan will be updated to the{' '}
|
Your plan will be updated to the{' '}
|
||||||
<b className="text-gray-600">{planDetails.interval}</b> plan, and will
|
<b className="text-black">{planDetails.interval}</b> plan, and will
|
||||||
be charged{' '}
|
be charged{' '}
|
||||||
<b className="text-gray-600">
|
<b className="text-black">
|
||||||
${selectedPrice.amount} {selectedPrice.interval}
|
${selectedPrice.amount}/{selectedPrice.interval}
|
||||||
</b>
|
</b>
|
||||||
.
|
.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="mt-6 grid grid-cols-2 gap-2">
|
<div className="mt-6 grid grid-cols-2 gap-3">
|
||||||
<button
|
<button
|
||||||
className="rounded-md border border-gray-300 py-2 text-sm font-semibold hover:opacity-80 disabled:opacity-50"
|
className="rounded-md border border-gray-200 py-2 text-sm font-semibold hover:bg-gray-50 transition-colors disabled:opacity-50"
|
||||||
onClick={onCancel}
|
onClick={onCancel}
|
||||||
disabled={isPending}
|
disabled={isPending}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
className="flex items-center justify-center rounded-md border border-gray-800 bg-black py-2 text-sm font-semibold text-white hover:opacity-80 disabled:opacity-50"
|
className="flex items-center justify-center rounded-md bg-yellow-400 py-2 text-sm font-semibold text-black hover:bg-yellow-500 transition-colors disabled:opacity-50"
|
||||||
disabled={isPending}
|
disabled={isPending}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
updatePlan({ priceId: selectedPrice.priceId });
|
updatePlan({ priceId: selectedPrice.priceId });
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{isPending && (
|
{isPending && (
|
||||||
<Loader2Icon className="size-4 animate-spin stroke-[2.5]" />
|
<Loader2Icon className="size-4 animate-spin stroke-[2.5] mr-2" />
|
||||||
)}
|
)}
|
||||||
{!isPending && 'Confirm'}
|
{!isPending && 'Confirm'}
|
||||||
</button>
|
</button>
|
||||||
|
@@ -1,4 +1,11 @@
|
|||||||
import { Check, Loader2 } from 'lucide-react';
|
import {
|
||||||
|
Loader2,
|
||||||
|
Zap,
|
||||||
|
Infinity,
|
||||||
|
MessageSquare,
|
||||||
|
Sparkles,
|
||||||
|
Heart,
|
||||||
|
} from 'lucide-react';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { getUser } from '../../lib/jwt';
|
import { getUser } from '../../lib/jwt';
|
||||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||||
@@ -91,15 +98,15 @@ export function UpgradeAccountModal(props: UpgradeAccountModalProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const loader = isLoading ? (
|
const loader = isLoading ? (
|
||||||
<div className="absolute inset-0 flex h-[540px] w-full items-center justify-center">
|
<div className="absolute inset-0 flex h-[540px] w-full items-center justify-center bg-white">
|
||||||
<Loader2 className="h-6 w-6 animate-spin stroke-[3px] text-zinc-500" />
|
<Loader2 className="h-6 w-6 animate-spin stroke-[3px] text-green-600" />
|
||||||
</div>
|
</div>
|
||||||
) : null;
|
) : null;
|
||||||
|
|
||||||
const error = billingError;
|
const error = billingError;
|
||||||
const errorContent = error ? (
|
const errorContent = error ? (
|
||||||
<div className="flex h-full w-full flex-col">
|
<div className="flex h-full w-full flex-col">
|
||||||
<p className="text-center text-red-500">
|
<p className="text-center text-red-400">
|
||||||
{error?.message ||
|
{error?.message ||
|
||||||
'An error occurred while loading the billing details.'}
|
'An error occurred while loading the billing details.'}
|
||||||
</p>
|
</p>
|
||||||
@@ -123,8 +130,9 @@ export function UpgradeAccountModal(props: UpgradeAccountModalProps) {
|
|||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
onClose={onClose}
|
onClose={onClose}
|
||||||
wrapperClassName="rounded-xl max-w-3xl w-full min-h-[540px]"
|
bodyClassName="p-6 bg-white"
|
||||||
bodyClassName="p-6"
|
wrapperClassName="h-auto rounded-xl max-w-3xl w-full min-h-[540px]"
|
||||||
|
overlayClassName="items-start md:items-center"
|
||||||
>
|
>
|
||||||
<div onClick={(e) => e.stopPropagation()}>
|
<div onClick={(e) => e.stopPropagation()}>
|
||||||
{errorContent}
|
{errorContent}
|
||||||
@@ -133,11 +141,15 @@ export function UpgradeAccountModal(props: UpgradeAccountModalProps) {
|
|||||||
{!isLoading && !error && (
|
{!isLoading && !error && (
|
||||||
<div className="flex flex-col">
|
<div className="flex flex-col">
|
||||||
<div className="mb-8 text-left">
|
<div className="mb-8 text-left">
|
||||||
<h2 className="text-xl font-bold">
|
<h2 className="text-2xl font-bold text-black">
|
||||||
Unlock premium features and by-pass the limits.
|
Unlock Premium Features
|
||||||
</h2>
|
</h2>
|
||||||
|
<p className="mt-2 text-gray-600">
|
||||||
|
Supercharge your learning experience with premium benefits
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<div className="grid grid-cols-2 gap-8">
|
|
||||||
|
<div className="mb-8 grid grid-cols-1 gap-6 md:grid-cols-2">
|
||||||
{USER_SUBSCRIPTION_PLAN_PRICES.map((plan) => {
|
{USER_SUBSCRIPTION_PLAN_PRICES.map((plan) => {
|
||||||
const isCurrentPlanSelected =
|
const isCurrentPlanSelected =
|
||||||
currentPlan?.priceId === plan.priceId;
|
currentPlan?.priceId === plan.priceId;
|
||||||
@@ -147,19 +159,19 @@ export function UpgradeAccountModal(props: UpgradeAccountModalProps) {
|
|||||||
<div
|
<div
|
||||||
key={plan.interval}
|
key={plan.interval}
|
||||||
className={cn(
|
className={cn(
|
||||||
'flex flex-col space-y-4 rounded-lg p-6',
|
'flex flex-col space-y-4 rounded-lg bg-white p-6',
|
||||||
isYearly
|
isYearly
|
||||||
? 'border-2 border-yellow-400'
|
? 'border-2 border-yellow-400 shadow-lg shadow-yellow-400/20'
|
||||||
: 'border border-gray-200',
|
: 'border border-gray-200',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<div className="flex items-start justify-between">
|
<div className="flex items-start justify-between">
|
||||||
<div>
|
<div>
|
||||||
<h4 className="font-semibold">
|
<h4 className="font-semibold text-black">
|
||||||
{isYearly ? 'Yearly Payment' : 'Monthly Payment'}
|
{isYearly ? 'Yearly Payment' : 'Monthly Payment'}
|
||||||
</h4>
|
</h4>
|
||||||
{isYearly && (
|
{isYearly && (
|
||||||
<span className="text-sm text-green-500">
|
<span className="text-sm font-medium text-green-600">
|
||||||
(2 months free)
|
(2 months free)
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
@@ -172,21 +184,23 @@ export function UpgradeAccountModal(props: UpgradeAccountModalProps) {
|
|||||||
</div>
|
</div>
|
||||||
<div className="flex items-baseline">
|
<div className="flex items-baseline">
|
||||||
{isYearly && (
|
{isYearly && (
|
||||||
<p className="mr-2 text-sm text-zinc-400 line-through">
|
<p className="mr-2 text-sm text-gray-400 line-through">
|
||||||
$
|
$
|
||||||
{calculateYearlyPrice(
|
{calculateYearlyPrice(
|
||||||
USER_SUBSCRIPTION_PLAN_PRICES[0].amount,
|
USER_SUBSCRIPTION_PLAN_PRICES[0].amount,
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
<p className="text-3xl font-bold text-yellow-400">
|
<p className="text-3xl font-bold text-black">
|
||||||
${plan.amount}{' '}
|
${plan.amount}{' '}
|
||||||
<span className="text-sm font-normal text-zinc-500">
|
<span className="text-sm font-normal text-gray-500">
|
||||||
/ {isYearly ? 'year' : 'month'}
|
/ {isYearly ? 'year' : 'month'}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex-grow"></div>
|
<div className="flex-grow"></div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
className={cn(
|
className={cn(
|
||||||
@@ -227,6 +241,65 @@ export function UpgradeAccountModal(props: UpgradeAccountModalProps) {
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Benefits Section */}
|
||||||
|
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||||
|
<div className="flex items-start space-x-3">
|
||||||
|
<Zap className="mt-0.5 h-5 w-5 text-yellow-400" />
|
||||||
|
<div>
|
||||||
|
<h4 className="font-medium text-black">
|
||||||
|
Unlimited AI Course Generations
|
||||||
|
</h4>
|
||||||
|
<p className="text-sm text-gray-600">
|
||||||
|
Generate as many custom courses as you need
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start space-x-3">
|
||||||
|
<Infinity className="mt-0.5 h-5 w-5 text-yellow-400" />
|
||||||
|
<div>
|
||||||
|
<h4 className="font-medium text-black">
|
||||||
|
No Daily Limits on course features
|
||||||
|
</h4>
|
||||||
|
<p className="text-sm text-gray-600">
|
||||||
|
Use all features without restrictions
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start space-x-3">
|
||||||
|
<MessageSquare className="mt-0.5 h-5 w-5 text-yellow-400" />
|
||||||
|
<div>
|
||||||
|
<h4 className="font-medium text-black">
|
||||||
|
Unlimited Course Follow-ups
|
||||||
|
</h4>
|
||||||
|
<p className="text-sm text-gray-600">
|
||||||
|
Ask as many questions as you need
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start space-x-3">
|
||||||
|
<Sparkles className="mt-0.5 h-5 w-5 text-yellow-400" />
|
||||||
|
<div>
|
||||||
|
<h4 className="font-medium text-black">
|
||||||
|
Early Access to Features
|
||||||
|
</h4>
|
||||||
|
<p className="text-sm text-gray-600">
|
||||||
|
Be the first to try new tools and features
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-start space-x-3">
|
||||||
|
<Heart className="mt-0.5 h-5 w-5 text-yellow-400" />
|
||||||
|
<div>
|
||||||
|
<h4 className="font-medium text-black">
|
||||||
|
Support Development
|
||||||
|
</h4>
|
||||||
|
<p className="text-sm text-gray-600">
|
||||||
|
Help us continue building roadmap.sh
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
import { useEffect } from 'react';
|
import { useEffect } from 'react';
|
||||||
import { Loader2 } from 'lucide-react';
|
import { Loader2, CheckCircle } from 'lucide-react';
|
||||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import { billingDetailsOptions } from '../../queries/billing';
|
import { billingDetailsOptions } from '../../queries/billing';
|
||||||
import { queryClient } from '../../stores/query-client';
|
import { queryClient } from '../../stores/query-client';
|
||||||
import { Modal } from '../Modal';
|
import { Modal } from '../Modal';
|
||||||
@@ -42,31 +42,32 @@ export function VerifyUpgrade(props: VerifyUpgradeProps) {
|
|||||||
onClose={() => {}}
|
onClose={() => {}}
|
||||||
bodyClassName="rounded-xl bg-white p-6"
|
bodyClassName="rounded-xl bg-white p-6"
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between gap-2">
|
<div className="flex flex-col items-center text-center mb-4">
|
||||||
<h3 className="text-xl font-bold">Subscription Activated</h3>
|
<CheckCircle className="h-12 w-12 text-green-600 mb-3" />
|
||||||
|
<h3 className="text-xl font-bold text-black">Subscription Activated</h3>
|
||||||
{isFetching && (
|
|
||||||
<div className="flex animate-pulse items-center gap-2">
|
|
||||||
<Loader2 className="h-4 w-4 animate-spin stroke-[2.5px] text-gray-500" />
|
|
||||||
<span className="text-gray-500">Refreshing</span>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
<p className="mt-2 text-balance text-gray-500">
|
|
||||||
|
<p className="mt-2 text-balance text-gray-600 text-center">
|
||||||
Your subscription has been activated successfully.
|
Your subscription has been activated successfully.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p className="mt-4 text-balance text-gray-500">
|
<p className="mt-4 text-balance text-gray-600 text-center">
|
||||||
It might take a few minutes for the changes to reflect. We will{' '}
|
It might take a few minutes for the changes to reflect. We will{' '}
|
||||||
<b className="text-gray-600">reload</b> the page for you.
|
<b className="text-black">reload</b> the page for you.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p className="mt-4 text-gray-500">
|
{isFetching && (
|
||||||
|
<div className="flex animate-pulse items-center justify-center gap-2 mt-4">
|
||||||
|
<Loader2 className="h-5 w-5 animate-spin stroke-[2.5px] text-green-600" />
|
||||||
|
<span className="text-gray-600">Refreshing</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<p className="mt-6 text-gray-500 text-center text-sm">
|
||||||
If it takes longer than expected, please{' '}
|
If it takes longer than expected, please{' '}
|
||||||
<a className="text-blue-500 underline underline-offset-2 hover:text-blue-300">
|
<a className="text-blue-600 underline underline-offset-2 hover:text-blue-700">
|
||||||
contact us
|
contact us
|
||||||
</a>
|
</a>.
|
||||||
.
|
|
||||||
</p>
|
</p>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
|
@@ -6,9 +6,11 @@ import { UpgradeAccountModal } from '../Billing/UpgradeAccountModal';
|
|||||||
import { billingDetailsOptions } from '../../queries/billing';
|
import { billingDetailsOptions } from '../../queries/billing';
|
||||||
import { getPercentage } from '../../helper/number';
|
import { getPercentage } from '../../helper/number';
|
||||||
import { Gift, Info } from 'lucide-react';
|
import { Gift, Info } from 'lucide-react';
|
||||||
|
import { AILimitsPopup } from './AILimitsPopup';
|
||||||
|
|
||||||
export function AICourseLimit() {
|
export function AICourseLimit() {
|
||||||
const [showUpgradeModal, setShowUpgradeModal] = useState(false);
|
const [showUpgradeModal, setShowUpgradeModal] = useState(false);
|
||||||
|
const [showAILimitsPopup, setShowAILimitsPopup] = useState(false);
|
||||||
|
|
||||||
const { data: limits, isLoading } = useQuery(
|
const { data: limits, isLoading } = useQuery(
|
||||||
getAiCourseLimitOptions(),
|
getAiCourseLimitOptions(),
|
||||||
@@ -30,12 +32,32 @@ export function AICourseLimit() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<button className="mr-1 flex items-center gap-1 text-sm font-medium lg:hidden underline underline-offset-2">
|
<button
|
||||||
|
className="mr-1 flex items-center gap-1 text-sm font-medium underline underline-offset-2 lg:hidden"
|
||||||
|
onClick={() => setShowAILimitsPopup(true)}
|
||||||
|
>
|
||||||
<Info className="size-4" />
|
<Info className="size-4" />
|
||||||
{totalPercentage}% limit used
|
{totalPercentage}% limit used
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button className="relative hidden h-full min-h-[38px] cursor-pointer items-center overflow-hidden rounded-lg border border-gray-300 px-3 py-1.5 text-sm hover:bg-gray-50 lg:flex">
|
{showAILimitsPopup && (
|
||||||
|
<AILimitsPopup
|
||||||
|
used={used}
|
||||||
|
limit={limit}
|
||||||
|
onClose={() => setShowAILimitsPopup(false)}
|
||||||
|
onUpgrade={() => {
|
||||||
|
setShowAILimitsPopup(false);
|
||||||
|
setShowUpgradeModal(true);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
setShowAILimitsPopup(true);
|
||||||
|
}}
|
||||||
|
className="relative hidden h-full min-h-[38px] cursor-pointer items-center overflow-hidden rounded-lg border border-gray-300 px-3 py-1.5 text-sm hover:bg-gray-50 lg:flex"
|
||||||
|
>
|
||||||
<span className="relative z-10">
|
<span className="relative z-10">
|
||||||
{totalPercentage}% of the daily limit used
|
{totalPercentage}% of the daily limit used
|
||||||
</span>
|
</span>
|
||||||
|
86
src/components/GenerateCourse/AILimitsPopup.tsx
Normal file
86
src/components/GenerateCourse/AILimitsPopup.tsx
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import { Gift } from 'lucide-react';
|
||||||
|
import { Modal } from '../Modal';
|
||||||
|
import { formatCommaNumber } from '../../lib/number';
|
||||||
|
|
||||||
|
type AILimitsPopupProps = {
|
||||||
|
used: number;
|
||||||
|
limit: number;
|
||||||
|
onClose: () => void;
|
||||||
|
onUpgrade: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function AILimitsPopup(props: AILimitsPopupProps) {
|
||||||
|
const { used, limit, onClose, onUpgrade } = props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
onClose={onClose}
|
||||||
|
wrapperClassName="rounded-xl max-w-xl w-full h-auto"
|
||||||
|
bodyClassName="p-6"
|
||||||
|
overlayClassName="items-start md:items-center"
|
||||||
|
>
|
||||||
|
<h2 className="mb-8 text-center text-xl font-semibold">
|
||||||
|
Daily AI Limits
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
{/* Usage Progress Bar */}
|
||||||
|
<div className="mb-6">
|
||||||
|
<div className="mb-2 flex justify-between">
|
||||||
|
<span className="text-sm font-medium">
|
||||||
|
Usage: {formatCommaNumber(used)} /
|
||||||
|
{formatCommaNumber(limit)} tokens
|
||||||
|
</span>
|
||||||
|
<span className="text-sm font-medium">
|
||||||
|
{Math.round((used / limit) * 100)}%
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="h-2.5 w-full rounded-full bg-gray-200">
|
||||||
|
<div
|
||||||
|
className="h-2.5 rounded-full bg-yellow-500"
|
||||||
|
style={{ width: `${Math.min(100, (used / limit) * 100)}%` }}
|
||||||
|
></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Usage Stats */}
|
||||||
|
<div className="rounded-lg bg-gray-50 p-4">
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<p className="text-sm text-gray-500">Used Today</p>
|
||||||
|
<p className="text-2xl font-bold">{formatCommaNumber(used)}</p>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<p className="text-sm text-gray-500">Daily Token Limit</p>
|
||||||
|
<p className="text-2xl font-bold">{formatCommaNumber(limit)}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Explanation */}
|
||||||
|
<div className="mt-2">
|
||||||
|
<div className="space-y-3 text-gray-600">
|
||||||
|
<p className="text-sm">
|
||||||
|
Limit resets every 24 hours. Consider upgrading for more tokens.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Action Button */}
|
||||||
|
<div className="mt-auto flex flex-col gap-2 pt-4">
|
||||||
|
<button
|
||||||
|
onClick={onUpgrade}
|
||||||
|
className="flex w-full items-center justify-center gap-2 rounded-lg bg-yellow-400 px-4 py-2.5 font-medium text-black transition-colors hover:bg-yellow-500"
|
||||||
|
>
|
||||||
|
<Gift className="size-4" />
|
||||||
|
Upgrade
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={onClose}
|
||||||
|
className="w-full rounded-lg bg-gray-200 px-4 py-2.5 text-gray-600 transition-colors hover:bg-gray-300"
|
||||||
|
>
|
||||||
|
Dismiss
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
Reference in New Issue
Block a user