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:
@@ -7,7 +7,6 @@ import {
|
|||||||
Star,
|
Star,
|
||||||
Swords,
|
Swords,
|
||||||
X,
|
X,
|
||||||
Zap,
|
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { isLoggedIn } from '../../lib/jwt';
|
import { isLoggedIn } from '../../lib/jwt';
|
||||||
@@ -21,6 +20,7 @@ import { getPercentage } from '../../lib/number';
|
|||||||
import { AILimitsPopup } from '../GenerateCourse/AILimitsPopup';
|
import { AILimitsPopup } from '../GenerateCourse/AILimitsPopup';
|
||||||
import { cn } from '../../lib/classname';
|
import { cn } from '../../lib/classname';
|
||||||
import { UserDropdown } from './UserDropdown';
|
import { UserDropdown } from './UserDropdown';
|
||||||
|
import { UpgradeSidebarCard } from './UpgradeSidebarCard';
|
||||||
|
|
||||||
type AITutorSidebarProps = {
|
type AITutorSidebarProps = {
|
||||||
isFloating: boolean;
|
isFloating: boolean;
|
||||||
@@ -162,34 +162,9 @@ export function AITutorSidebar(props: AITutorSidebarProps) {
|
|||||||
|
|
||||||
{!isInitialLoad && isLoggedIn() && !isPaidUser && !isLoading && (
|
{!isInitialLoad && isLoggedIn() && !isPaidUser && !isLoading && (
|
||||||
<li>
|
<li>
|
||||||
<button
|
<UpgradeSidebarCard
|
||||||
onClick={() => {
|
onUpgrade={() => setIsUpgradeModalOpen(true)}
|
||||||
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>
|
|
||||||
</li>
|
</li>
|
||||||
)}
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
|
77
src/components/AITutor/UpgradeSidebarCard.tsx
Normal file
77
src/components/AITutor/UpgradeSidebarCard.tsx
Normal 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>
|
||||||
|
);
|
||||||
|
}
|
33
src/components/AccounSidebar/AccountSidebarUpgrade.tsx
Normal file
33
src/components/AccounSidebar/AccountSidebarUpgrade.tsx
Normal 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}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
@@ -3,6 +3,7 @@ import AstroIcon from './AstroIcon.astro';
|
|||||||
import { TeamDropdown } from './TeamDropdown/TeamDropdown';
|
import { TeamDropdown } from './TeamDropdown/TeamDropdown';
|
||||||
import { SidebarFriendsCounter } from './Friends/SidebarFriendsCounter';
|
import { SidebarFriendsCounter } from './Friends/SidebarFriendsCounter';
|
||||||
import { Map } from 'lucide-react';
|
import { Map } from 'lucide-react';
|
||||||
|
import { AccountSidebarUpgrade } from './AccounSidebar/AccountSidebarUpgrade';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
activePageId: string;
|
activePageId: string;
|
||||||
@@ -97,7 +98,7 @@ const sidebarLinks = [
|
|||||||
</button>
|
</button>
|
||||||
<ul
|
<ul
|
||||||
id='settings-menu-dropdown'
|
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>
|
<li>
|
||||||
<a
|
<a
|
||||||
@@ -180,7 +181,7 @@ const sidebarLinks = [
|
|||||||
{sidebarLink.isNew && !isActive && (
|
{sidebarLink.isNew && !isActive && (
|
||||||
<span class='relative mr-1 flex items-center'>
|
<span class='relative mr-1 flex items-center'>
|
||||||
<span class='relative rounded-full bg-gray-200 p-1 text-xs' />
|
<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>
|
</span>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -191,6 +192,8 @@ const sidebarLinks = [
|
|||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|
||||||
|
<AccountSidebarUpgrade client:load />
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
</aside>
|
</aside>
|
||||||
|
Reference in New Issue
Block a user