Browse Source

Merge pull request #102 from vernu/dashboard-ui-ux-improvement

dashboard ui/ux improvement
pull/103/head
Israel Abebe 8 months ago
committed by GitHub
parent
commit
dd14c6ba4e
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 41
      api/src/billing/billing.service.ts
  2. 2
      web/app/(app)/dashboard/(components)/api-keys.tsx
  3. 2
      web/app/(app)/dashboard/(components)/device-list.tsx
  4. 110
      web/app/(app)/dashboard/(components)/upgrade-to-pro-alert.tsx

41
api/src/billing/billing.service.ts

@ -53,8 +53,37 @@ export class BillingService {
}) })
.populate('plan') .populate('plan')
// Get user's devices and usage data
const userDevices = await this.deviceModel.find({ user: user._id }, '_id')
const deviceIds = userDevices.map(d => d._id)
const processedSmsToday = await this.smsModel.countDocuments({
device: { $in: deviceIds },
createdAt: { $gte: new Date(new Date().setHours(0, 0, 0, 0)) },
})
const processedSmsLastMonth = await this.smsModel.countDocuments({
device: { $in: deviceIds },
createdAt: {
$gte: new Date(new Date().setMonth(new Date().getMonth() - 1)),
},
})
if (subscription) { if (subscription) {
return subscription
const plan = subscription.plan
return {
...subscription.toObject(),
usage: {
processedSmsToday,
processedSmsLastMonth,
dailyLimit: plan.dailyLimit,
monthlyLimit: plan.monthlyLimit,
dailyRemaining: plan.dailyLimit === -1 ? -1 : plan.dailyLimit - processedSmsToday,
monthlyRemaining: plan.monthlyLimit === -1 ? -1 : plan.monthlyLimit - processedSmsLastMonth,
dailyUsagePercentage: plan.dailyLimit === -1 ? 0 : Math.round((processedSmsToday / plan.dailyLimit) * 100),
monthlyUsagePercentage: plan.monthlyLimit === -1 ? 0 : Math.round((processedSmsLastMonth / plan.monthlyLimit) * 100),
}
}
} }
const plan = await this.planModel.findOne({ name: 'free' }) const plan = await this.planModel.findOne({ name: 'free' })
@ -62,6 +91,16 @@ export class BillingService {
return { return {
plan, plan,
isActive: true, isActive: true,
usage: {
processedSmsToday,
processedSmsLastMonth,
dailyLimit: plan.dailyLimit,
monthlyLimit: plan.monthlyLimit,
dailyRemaining: plan.dailyLimit === -1 ? -1 : plan.dailyLimit - processedSmsToday,
monthlyRemaining: plan.monthlyLimit === -1 ? -1 : plan.monthlyLimit - processedSmsLastMonth,
dailyUsagePercentage: plan.dailyLimit === -1 ? 0 : Math.round((processedSmsToday / plan.dailyLimit) * 100),
monthlyUsagePercentage: plan.monthlyLimit === -1 ? 0 : Math.round((processedSmsLastMonth / plan.monthlyLimit) * 100),
}
} }
} }

2
web/app/(app)/dashboard/(components)/api-keys.tsx

@ -169,7 +169,7 @@ export default function ApiKeys() {
)} )}
{apiKeys?.data?.map((apiKey) => ( {apiKeys?.data?.map((apiKey) => (
<Card key={apiKey.id} className='border-0 shadow-none'>
<Card key={apiKey._id} className='border-0 shadow-none'>
<CardContent className='flex items-center p-3'> <CardContent className='flex items-center p-3'>
<Key className='h-6 w-6 mr-3' /> <Key className='h-6 w-6 mr-3' />
<div className='flex-1'> <div className='flex-1'>

2
web/app/(app)/dashboard/(components)/device-list.tsx

@ -77,7 +77,7 @@ export default function DeviceList() {
)} )}
{devices?.data?.map((device) => ( {devices?.data?.map((device) => (
<Card key={device.id} className='border-0 shadow-none'>
<Card key={device._id} className='border-0 shadow-none'>
<CardContent className='flex items-center p-3'> <CardContent className='flex items-center p-3'>
<Smartphone className='h-6 w-6 mr-3' /> <Smartphone className='h-6 w-6 mr-3' />
<div className='flex-1'> <div className='flex-1'>

110
web/app/(app)/dashboard/(components)/upgrade-to-pro-alert.tsx

@ -19,33 +19,58 @@ export default function UpgradeToProAlert() {
.then((res) => res.data), .then((res) => res.data),
}) })
const ctaMessages = useMemo(() => [
"Upgrade to Pro for exclusive features and benefits!",
"Offer: You are eligible for a 30% discount when upgrading to Pro!",
"Unlock premium features with our Pro plan today!",
"Take your experience to the next level with Pro!",
"Pro users get priority support and advanced features!",
"Limited time offer: Upgrade to Pro and save 30%!",
], []);
const monthlyUsagePercentage = currentSubscription?.usage?.monthlyUsagePercentage || 0
const monthlyLimit = currentSubscription?.usage?.monthlyLimit || 0
const processedSmsLastMonth = currentSubscription?.usage?.processedSmsLastMonth || 0
const buttonTexts = useMemo(() => [
"Get Pro Now!",
"Upgrade Today!",
"Go Pro!",
"Unlock Pro!",
"Claim Your Discount!",
"Upgrade & Save!",
], []);
const randomCta = useMemo(() => {
const randomIndex = Math.floor(Math.random() * ctaMessages.length);
return ctaMessages[randomIndex];
}, [ctaMessages]);
const randomButtonText = useMemo(() => {
const randomIndex = Math.floor(Math.random() * buttonTexts.length);
return buttonTexts[randomIndex];
}, [buttonTexts]);
const alertConfig = useMemo(() => {
if (monthlyUsagePercentage >= 100 ) {
return {
bgColor: 'bg-gradient-to-r from-red-600 to-red-800',
message: "⚠️ Monthly limit exceeded! Your requests will be rejected until you upgrade.",
subMessage: `You've used ${processedSmsLastMonth} of ${monthlyLimit} SMS this month.`,
buttonText: "Upgrade Now!",
buttonColor: 'bg-white text-red-600 hover:bg-red-50 hover:text-red-700 border-red-600',
urgency: 'critical'
}
} else if (monthlyUsagePercentage >= 80) {
return {
bgColor: 'bg-gradient-to-r from-orange-500 to-red-500',
message: "⚠️ Approaching limit! Upgrade to Pro to avoid service interruption.",
subMessage: `You've used ${monthlyUsagePercentage}% of your monthly SMS limit (${processedSmsLastMonth}/${monthlyLimit}).`,
buttonText: "Upgrade Before Limit!",
buttonColor: 'bg-white text-orange-600 hover:bg-orange-50 hover:text-orange-700 border-orange-600',
urgency: 'warning'
}
} else {
const ctaMessages = [
"Upgrade to Pro for exclusive features and benefits!",
"Offer: You are eligible for a 30% discount when upgrading to Pro!",
"Unlock premium features with our Pro plan today!",
"Take your experience to the next level with Pro!",
"Pro users get priority support and advanced features!",
"Limited time offer: Upgrade to Pro and save 30%!",
]
const buttonTexts = [
"Get Pro Now!",
"Upgrade Today!",
"Go Pro!",
"Unlock Pro!",
"Claim Your Discount!",
"Upgrade & Save!",
]
const randomIndex = Math.floor(Math.random() * ctaMessages.length)
return {
bgColor: 'bg-gradient-to-r from-purple-500 to-pink-500',
message: ctaMessages[randomIndex],
subMessage: `Use discount code SAVE30P at checkout for a 30% discount!`,
buttonText: buttonTexts[randomIndex],
buttonColor: 'bg-red-500 text-white hover:bg-red-600 border-red-500',
urgency: 'normal'
}
}
}, [monthlyUsagePercentage, monthlyLimit, processedSmsLastMonth])
if (isLoadingSubscription || !currentSubscription || subscriptionError) { if (isLoadingSubscription || !currentSubscription || subscriptionError) {
return null return null
@ -56,32 +81,37 @@ export default function UpgradeToProAlert() {
} }
return ( return (
<Alert className='bg-gradient-to-r from-purple-500 to-pink-500 text-white'>
<Alert className={`${alertConfig.bgColor} text-white`}>
<AlertDescription className='flex flex-col sm:flex-row flex-wrap items-center gap-2 md:gap-4'> <AlertDescription className='flex flex-col sm:flex-row flex-wrap items-center gap-2 md:gap-4'>
<span className='w-full sm:flex-1 text-center sm:text-left text-sm md:text-base font-medium'> <span className='w-full sm:flex-1 text-center sm:text-left text-sm md:text-base font-medium'>
{randomCta}
{alertConfig.message}
</span> </span>
<span className='w-full sm:flex-1 text-center sm:text-left text-xs md:text-sm'> <span className='w-full sm:flex-1 text-center sm:text-left text-xs md:text-sm'>
Use discount code <strong className="text-yellow-200">SAVE30P</strong> at checkout for a 30%
discount!
{alertConfig.urgency === 'normal' ? (
<>Use discount code <strong className="text-yellow-200">SAVE30P</strong> at checkout for a 30% discount!</>
) : (
alertConfig.subMessage
)}
</span> </span>
<div className='w-full sm:w-auto mt-2 sm:mt-0 flex justify-center sm:justify-end flex-wrap gap-1 md:gap-2'> <div className='w-full sm:w-auto mt-2 sm:mt-0 flex justify-center sm:justify-end flex-wrap gap-1 md:gap-2'>
<Button <Button
variant='outline' variant='outline'
size='sm' size='sm'
asChild asChild
className='bg-red-500 text-white hover:bg-red-600 text-xs md:text-sm'
>
<Link href={'/checkout/pro'}>{randomButtonText}</Link>
</Button>
<Button
variant='outline'
size='sm'
asChild
className='bg-orange-500 text-white hover:bg-orange-600 text-xs md:text-sm'
className={`${alertConfig.buttonColor} text-xs md:text-sm`}
> >
<Link href={'/#pricing'}>Learn More</Link>
<Link href={'/checkout/pro'}>{alertConfig.buttonText}</Link>
</Button> </Button>
{alertConfig.urgency === 'normal' && (
<Button
variant='outline'
size='sm'
asChild
className='bg-orange-500 text-white hover:bg-orange-600 text-xs md:text-sm'
>
<Link href={'/#pricing'}>Learn More</Link>
</Button>
)}
</div> </div>
</AlertDescription> </AlertDescription>
</Alert> </Alert>

Loading…
Cancel
Save