From 911f34cba4a51021a64494f0212bc9e1064acc2d Mon Sep 17 00:00:00 2001 From: Kamran Ahmed Date: Mon, 14 Jul 2025 13:58:57 +0100 Subject: [PATCH] Refactor and add upgrade button on dashboard --- .../CreateRoadmap/CreateRoadmapButton.tsx | 27 +++- .../CustomRoadmap/RoadmapListPage.tsx | 2 +- .../Dashboard/PersonalDashboard.tsx | 142 ++++++------------ .../HeroSection/FavoriteRoadmaps.tsx | 51 +------ 4 files changed, 73 insertions(+), 149 deletions(-) diff --git a/src/components/CustomRoadmap/CreateRoadmap/CreateRoadmapButton.tsx b/src/components/CustomRoadmap/CreateRoadmap/CreateRoadmapButton.tsx index b02047d9d..f95c4a297 100644 --- a/src/components/CustomRoadmap/CreateRoadmap/CreateRoadmapButton.tsx +++ b/src/components/CustomRoadmap/CreateRoadmap/CreateRoadmapButton.tsx @@ -4,28 +4,53 @@ import { showLoginPopup } from '../../../lib/popup'; import { cn } from '../../../lib/classname'; import { CreateRoadmapModal } from './CreateRoadmapModal'; import { useState } from 'react'; +import { useIsPaidUser } from '../../../queries/billing'; +import { UpgradeAccountModal } from '../../Billing/UpgradeAccountModal'; +import { MAX_ROADMAP_LIMIT } from '../RoadmapListPage'; type CreateRoadmapButtonProps = { className?: string; + existingRoadmapCount?: number; text?: string; teamId?: string; }; export function CreateRoadmapButton(props: CreateRoadmapButtonProps) { - const { teamId, className, text = 'Create your own Roadmap' } = props; + const { + teamId, + className, + text = 'Create your own Roadmap', + existingRoadmapCount = 0, + } = props; const [isCreatingRoadmap, setIsCreatingRoadmap] = useState(false); + const [showUpgradeModal, setShowUpgradeModal] = useState(false); + const { isPaidUser, isLoading: isPaidUserLoading } = useIsPaidUser(); function toggleCreateRoadmapHandler() { if (!isLoggedIn()) { return showLoginPopup(); } + const hasExceededLimit = + !isPaidUser && + existingRoadmapCount > 0 && + existingRoadmapCount >= MAX_ROADMAP_LIMIT; + + if (hasExceededLimit) { + setShowUpgradeModal(true); + return; + } + setIsCreatingRoadmap(true); } return ( <> + {showUpgradeModal && ( + setShowUpgradeModal(false)} /> + )} + {isCreatingRoadmap && ( + {showUpgradeModal && ( + setShowUpgradeModal(false)} /> )} - > - - - {value} {label} - - + + + ); } @@ -124,7 +123,7 @@ function PersonalProfileButton(props: ProfileButtonProps) { @@ -165,22 +164,15 @@ function PersonalProfileButton(props: ProfileButtonProps) { type DashboardStatsProps = { profile: ProfileButtonProps; accountStreak?: StreakResponse; - topicsDoneToday?: number; finishedProjectsCount?: number; isLoading: boolean; }; function DashboardStats(props: DashboardStatsProps) { - const { - accountStreak, - topicsDoneToday = 0, - finishedProjectsCount = 0, - isLoading, - profile, - } = props; + const { isLoading, profile } = props; return ( -
+
- - - +
@@ -232,25 +204,6 @@ export function PersonalDashboard(props: PersonalDashboardProps) { const [personalDashboardDetails, setPersonalDashboardDetails] = useState(); const [projectDetails, setProjectDetails] = useState([]); - const accountStreak = useStore($accountStreak); - - const loadAccountStreak = async () => { - if (accountStreak) { - return; - } - - setIsLoading(true); - const { response, error } = await httpGet( - `${import.meta.env.PUBLIC_API_URL}/v1-streak`, - ); - - if (error || !response) { - toast.error(error?.message || 'Failed to load account streak'); - return; - } - - $accountStreak.set(response); - }; async function loadProgress() { const { response: progressList, error } = @@ -293,11 +246,9 @@ export function PersonalDashboard(props: PersonalDashboardProps) { } useEffect(() => { - Promise.allSettled([ - loadProgress(), - loadAllProjectDetails(), - loadAccountStreak(), - ]).finally(() => setIsLoading(false)); + Promise.allSettled([loadProgress(), loadAllProjectDetails()]).finally(() => + setIsLoading(false), + ); }, []); useEffect(() => { @@ -324,8 +275,6 @@ export function PersonalDashboard(props: PersonalDashboardProps) { return updatedAtB.getTime() - updatedAtA.getTime(); }); - const aiGeneratedRoadmaps = personalDashboardDetails?.aiRoadmaps || []; - const customRoadmaps: UserProgress[] = ( personalDashboardDetails?.progresses || [] ) @@ -376,18 +325,11 @@ export function PersonalDashboard(props: PersonalDashboardProps) { isLoading, }} isLoading={isLoading} - accountStreak={accountStreak} - topicsDoneToday={personalDashboardDetails?.topicDoneToday} - finishedProjectsCount={ - enrichedProjects?.filter((p) => p.submittedAt && p.repositoryUrl) - .length - } /> diff --git a/src/components/HeroSection/FavoriteRoadmaps.tsx b/src/components/HeroSection/FavoriteRoadmaps.tsx index 6f7824689..e4999c223 100644 --- a/src/components/HeroSection/FavoriteRoadmaps.tsx +++ b/src/components/HeroSection/FavoriteRoadmaps.tsx @@ -2,9 +2,9 @@ import { FolderKanban, MapIcon, Plus, - Sparkle, Eye, - EyeOff, SquareCheckBig + EyeOff, + SquareCheckBig, } from 'lucide-react'; import { useState } from 'react'; import type { ProjectStatusDocument } from '../Projects/ListProjectSolutions.tsx'; @@ -28,12 +28,11 @@ type FavoriteRoadmapsProps = { title: string; })[]; customRoadmaps: UserProgress[]; - aiRoadmaps: AIRoadmapType[]; isLoading: boolean; }; export function FavoriteRoadmaps(props: FavoriteRoadmapsProps) { - const { progress, isLoading, customRoadmaps, aiRoadmaps, projects } = props; + const { progress, isLoading, customRoadmaps, projects } = props; const [showCompleted, setShowCompleted] = useState(false); const [isCreatingCustomRoadmap, setIsCreatingCustomRoadmap] = useState(false); @@ -131,49 +130,7 @@ export function FavoriteRoadmaps(props: FavoriteRoadmapsProps) { allowFavorite={false} /> ))} - - - - } - isLoading={isLoading} - title="Your AI roadmaps" - isEmpty={!isLoading && aiRoadmaps.length === 0} - emptyTitle={ - <> - No AI roadmaps found -
- - Generate AI roadmap - - - } - > - {aiRoadmaps.map((aiRoadmap) => ( - - ))} - - - - Generate New - +