1
0
mirror of https://github.com/kamranahmedse/developer-roadmap.git synced 2025-09-25 08:35:42 +02:00

feat: show limit in account sidebar (#8891)

* feat: show limit in account sidebar

* Add sidebar message

---------

Co-authored-by: Kamran Ahmed <kamranahmed.se@gmail.com>
This commit is contained in:
Arik Chakma
2025-07-09 17:43:05 +06:00
committed by GitHub
parent 9a39de1fad
commit ef48708a94
4 changed files with 119 additions and 31 deletions

View File

@@ -7,7 +7,6 @@ import {
Star,
Swords,
X,
Zap,
} from 'lucide-react';
import { useEffect, useState } from 'react';
import { isLoggedIn } from '../../lib/jwt';
@@ -21,6 +20,7 @@ import { getPercentage } from '../../lib/number';
import { AILimitsPopup } from '../GenerateCourse/AILimitsPopup';
import { cn } from '../../lib/classname';
import { UserDropdown } from './UserDropdown';
import { UpgradeSidebarCard } from './UpgradeSidebarCard';
type AITutorSidebarProps = {
isFloating: boolean;
@@ -162,34 +162,9 @@ export function AITutorSidebar(props: AITutorSidebarProps) {
{!isInitialLoad && isLoggedIn() && !isPaidUser && !isLoading && (
<li>
<button
onClick={() => {
setIsUpgradeModalOpen(true);
}}
className="animate-fade-in mx-4 mt-4 rounded-xl bg-amber-100 p-4 text-left transition-colors hover:bg-amber-200/80"
>
<span className="mb-2 flex items-center gap-2">
<Zap className="size-4 text-amber-600" />
<span className="font-medium text-amber-900">Upgrade</span>
</span>
<span className="mt-1 block text-left text-xs leading-4 text-amber-700">
Get access to all features and benefits of the AI Tutor.
</span>
<div className="mt-5">
<div className="relative h-1 w-full rounded-full bg-amber-300/40">
<div
className="absolute inset-0 h-full rounded-full bg-amber-600/80"
style={{
width: `${totalPercentage}%`,
}}
></div>
</div>
<span className="mt-2 block text-xs text-amber-700">
{totalPercentage}% of the daily limit used
</span>
</div>
</button>
<UpgradeSidebarCard
onUpgrade={() => setIsUpgradeModalOpen(true)}
/>
</li>
)}
</ul>

View File

@@ -0,0 +1,77 @@
import { useQuery } from '@tanstack/react-query';
import { Zap } from 'lucide-react';
import { queryClient } from '../../stores/query-client';
import { getAiCourseLimitOptions } from '../../queries/ai-course';
import { getPercentage } from '../../lib/number';
import { cn } from '../../lib/classname';
type UpgradeSidebarCardProps = {
onUpgrade: () => void;
className?: string;
descriptionClassName?: string;
titleClassName?: string;
title?: string;
description?: string;
showLimit?: boolean;
};
export function UpgradeSidebarCard(props: UpgradeSidebarCardProps) {
const {
onUpgrade,
title = 'Upgrade',
description = 'Get access to all features and benefits of the AI Tutor.',
descriptionClassName,
titleClassName,
className,
showLimit = true,
} = props;
const { data: limits, isLoading: isLimitsLoading } = useQuery(
getAiCourseLimitOptions(),
queryClient,
);
const { used, limit } = limits ?? { used: 0, limit: 0 };
const totalPercentage = getPercentage(used, limit);
return (
<button
onClick={onUpgrade}
className={cn(
'animate-fade-in mx-4 mt-4 rounded-xl bg-amber-100 p-4 text-left transition-colors hover:bg-amber-200/80',
className,
)}
>
<span className="mb-2 flex items-center gap-2">
<Zap className="size-4 text-amber-600" />
<span className={cn('font-medium text-amber-900', titleClassName)}>
{title}
</span>
</span>
<span
className={cn(
'mt-1 block text-left text-xs leading-4 text-amber-700',
descriptionClassName,
)}
>
{description}
</span>
{showLimit && (
<div className="mt-5">
<div className="relative h-1 w-full rounded-full bg-amber-300/40">
<div
className="absolute inset-0 h-full rounded-full bg-amber-600/80"
style={{
width: `${totalPercentage}%`,
}}
></div>
</div>
<span className="mt-2 block text-xs text-amber-700">
{totalPercentage}% of the daily limit used
</span>
</div>
)}
</button>
);
}

View File

@@ -0,0 +1,33 @@
import { useIsMounted } from '../../hooks/use-is-mounted';
import { isLoggedIn } from '../../lib/jwt';
import { useIsPaidUser } from '../../queries/billing';
import { UpgradeSidebarCard } from '../AITutor/UpgradeSidebarCard';
import { useState } from 'react';
import { UpgradeAccountModal } from '../Billing/UpgradeAccountModal';
export function AccountSidebarUpgrade() {
const isMounted = useIsMounted();
const { isPaidUser, isLoading: isPaidUserLoading } = useIsPaidUser();
const [isUpgradeModalOpen, setIsUpgradeModalOpen] = useState(false);
if (!isMounted || isPaidUserLoading || !isLoggedIn() || isPaidUser) {
return null;
}
return (
<>
{isUpgradeModalOpen && (
<UpgradeAccountModal onClose={() => setIsUpgradeModalOpen(false)} />
)}
<UpgradeSidebarCard
onUpgrade={() => setIsUpgradeModalOpen(true)}
className="mt-4 -mr-px rounded-r-none ml-0"
descriptionClassName="leading-normal"
title="Upgrade"
description="Unlock premium features including AI tutor and more."
showLimit={false}
/>
</>
);
}

View File

@@ -3,6 +3,7 @@ import AstroIcon from './AstroIcon.astro';
import { TeamDropdown } from './TeamDropdown/TeamDropdown';
import { SidebarFriendsCounter } from './Friends/SidebarFriendsCounter';
import { Map } from 'lucide-react';
import { AccountSidebarUpgrade } from './AccounSidebar/AccountSidebarUpgrade';
export interface Props {
activePageId: string;
@@ -97,7 +98,7 @@ const sidebarLinks = [
</button>
<ul
id='settings-menu-dropdown'
class='absolute left-0 right-0 z-10 mt-1 hidden space-y-1.5 bg-white p-2 shadow-lg'
class='absolute right-0 left-0 z-10 mt-1 hidden space-y-1.5 bg-white p-2 shadow-lg'
>
<li>
<a
@@ -180,7 +181,7 @@ const sidebarLinks = [
{sidebarLink.isNew && !isActive && (
<span class='relative mr-1 flex items-center'>
<span class='relative rounded-full bg-gray-200 p-1 text-xs' />
<span class='absolute bottom-0 left-0 right-0 top-0 animate-ping rounded-full bg-gray-400 p-1 text-xs' />
<span class='absolute top-0 right-0 bottom-0 left-0 animate-ping rounded-full bg-gray-400 p-1 text-xs' />
</span>
)}
@@ -191,6 +192,8 @@ const sidebarLinks = [
</li>
);
})}
<AccountSidebarUpgrade client:load />
</ul>
</nav>
</aside>