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,
|
||||
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>
|
||||
|
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 { 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>
|
||||
|
Reference in New Issue
Block a user