diff --git a/src/components/AIRoadmap/AIRoadmap.tsx b/src/components/AIRoadmap/AIRoadmap.tsx index 615287808..5c11b3eba 100644 --- a/src/components/AIRoadmap/AIRoadmap.tsx +++ b/src/components/AIRoadmap/AIRoadmap.tsx @@ -12,6 +12,10 @@ import { GenerateAIRoadmap } from './GenerateAIRoadmap'; import { AIRoadmapContent, type RoadmapNodeDetails } from './AIRoadmapContent'; import { AIRoadmapChat } from './AIRoadmapChat'; import { AlertCircleIcon } from 'lucide-react'; +import { isLoggedIn } from '../../lib/jwt'; +import { showLoginPopup } from '../../lib/popup'; +import { getAiCourseLimitOptions } from '../../queries/ai-course'; +import { billingDetailsOptions } from '../../queries/billing'; export type AIRoadmapChatActions = { handleNodeClick: (node: RoadmapNodeDetails) => void; @@ -42,7 +46,29 @@ export function AIRoadmap(props: AIRoadmapProps) { error: aiRoadmapError, } = useQuery(aiRoadmapOptions(roadmapSlug), queryClient); + const { + data: tokenUsage, + isLoading: isTokenUsageLoading, + refetch: refetchTokenUsage, + } = useQuery(getAiCourseLimitOptions(), queryClient); + + const { data: userBillingDetails, isLoading: isBillingDetailsLoading } = + useQuery(billingDetailsOptions(), queryClient); + + const isLimitExceeded = (tokenUsage?.used || 0) >= (tokenUsage?.limit || 0); + const isPaidUser = userBillingDetails?.status === 'active'; + const handleRegenerate = async (prompt?: string) => { + if (!isLoggedIn()) { + showLoginPopup(); + return; + } + + if (!isPaidUser && isLimitExceeded) { + setShowUpgradeModal(true); + return; + } + flushSync(() => { setIsRegenerating(true); setRegeneratedSvgHtml(null); @@ -76,12 +102,17 @@ export function AIRoadmap(props: AIRoadmapProps) { }, onFinish: () => { setIsRegenerating(false); + refetchTokenUsage(); queryClient.invalidateQueries(aiRoadmapOptions(roadmapSlug)); }, }); }; - const isLoading = isLoadingBySlug || isRegenerating; + const isLoading = + isLoadingBySlug || + isRegenerating || + isTokenUsageLoading || + isBillingDetailsLoading; const handleNodeClick = useCallback( (node: RoadmapNodeDetails) => { diff --git a/src/components/AIRoadmap/AIRoadmapChat.tsx b/src/components/AIRoadmap/AIRoadmapChat.tsx index ca36feb2e..b865471b0 100644 --- a/src/components/AIRoadmap/AIRoadmapChat.tsx +++ b/src/components/AIRoadmap/AIRoadmapChat.tsx @@ -124,6 +124,10 @@ export function AIRoadmapChat(props: AIRoadmapChatProps) { }); sendMessages(newMessages); setInputValue(''); + + setTimeout(() => { + scrollToBottom('smooth'); + }, 0); }, [inputValue, isStreamingMessage, messages, sendMessages, setMessages], ); @@ -174,11 +178,7 @@ export function AIRoadmapChat(props: AIRoadmapChatProps) { useImperativeHandle(aiChatActionsRef, () => ({ handleNodeClick: (node: RoadmapNodeDetails) => { - flushSync(() => { - setInputValue(`Explain what is ${node.nodeTitle} topic in detail.`); - }); - - inputRef.current?.focus(); + handleSubmitInput(`Explain what is "${node.nodeTitle}" topic in detail.`); }, })); @@ -304,6 +304,24 @@ export function AIRoadmapChat(props: AIRoadmapChatProps) { )} + {!isLoggedIn() && ( +
Please login to continue
+ ++ {aiGuideError?.message || 'Something went wrong'} +
+Please login to continue
+ +