chore(ux): alert dialog for free tier on agent run

This commit is contained in:
Soumyadas15 2025-05-21 01:29:30 +05:30
parent 1b3601513c
commit 3cf13c1823
3 changed files with 108 additions and 13 deletions

View File

@ -9,7 +9,12 @@ import React, {
import { useRouter, useSearchParams } from 'next/navigation';
import {
AlertTriangle,
Brain,
Clock,
Crown,
Lock,
Sparkles,
Zap,
} from 'lucide-react';
import {
BillingError,
@ -32,6 +37,15 @@ import { BillingErrorAlert } from '@/components/billing/usage-limit-alert';
import { isLocalMode } from '@/lib/config';
import { ThreadContent } from '@/components/thread/content/ThreadContent';
import { ThreadSkeleton } from '@/components/thread/content/ThreadSkeleton';
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogDescription,
DialogFooter,
} from '@/components/ui/dialog';
import { Button } from '@/components/ui/button';
import {
UnifiedMessage,
@ -46,6 +60,8 @@ import { useAddUserMessageMutation, useMessagesQuery } from '@/hooks/react-query
import { useProjectQuery } from '@/hooks/react-query/threads/use-project';
import { useAgentRunsQuery, useStartAgentMutation, useStopAgentMutation } from '@/hooks/react-query/threads/use-agent-run';
import { useBillingStatusQuery } from '@/hooks/react-query/threads/use-billing-status';
import { useSubscription, isPlan } from '@/hooks/react-query/subscriptions/use-subscriptions';
import { SubscriptionStatus } from '@/components/thread/chat-input/_use-model-selection';
// Extend the base Message type with the expected database fields
interface ApiMessageType extends BaseApiMessageType {
@ -131,12 +147,17 @@ export default function ThreadPage({
const projectId = threadQuery.data?.project_id || '';
const projectQuery = useProjectQuery(projectId);
const agentRunsQuery = useAgentRunsQuery(threadId);
const billingStatusQuery = useBillingStatusQuery();
const billingStatusQuery = useBillingStatusQuery();
const { data: subscriptionData } = useSubscription();
const addUserMessageMutation = useAddUserMessageMutation();
const startAgentMutation = useStartAgentMutation();
const stopAgentMutation = useStopAgentMutation();
const subscriptionStatus: SubscriptionStatus = subscriptionData?.status === 'active'
? 'active'
: 'no_subscription';
const handleProjectRenamed = useCallback((newName: string) => {
setProjectName(newName);
@ -1041,6 +1062,28 @@ export default function ThreadPage({
setDebugMode(debugParam === 'true');
}, [searchParams]);
useEffect(() => {
if (initialLoadCompleted.current && subscriptionData) {
const hasSeenUpgradeDialog = localStorage.getItem('suna_upgrade_dialog_displayed');
const isFreeTier = subscriptionStatus === 'no_subscription';
if (!hasSeenUpgradeDialog && isFreeTier) {
setShowUpgradeDialog(true);
}
}
}, [subscriptionData, subscriptionStatus, initialLoadCompleted.current]);
const handleDismissUpgradeDialog = () => {
setShowUpgradeDialog(false);
localStorage.setItem('suna_upgrade_dialog_displayed', 'true');
};
const handleUpgradeClick = () => {
router.push('/settings/billing');
setShowUpgradeDialog(false);
localStorage.setItem('suna_upgrade_dialog_displayed', 'true');
};
if (!initialLoadCompleted.current || isLoading) {
return <ThreadSkeleton isSidePanelOpen={isSidePanelOpen} />;
} else if (error) {
@ -1220,6 +1263,67 @@ export default function ThreadPage({
onDismiss={() => setShowBillingAlert(false)}
isOpen={showBillingAlert}
/>
<Dialog open={showUpgradeDialog} onOpenChange={setShowUpgradeDialog}>
<DialogContent onPointerDownOutside={(e) => e.preventDefault()} className="sm:max-w-md">
<DialogHeader>
<DialogTitle className="flex items-center">
<Crown className="h-5 w-5 mr-2 text-primary" />
Unlock the Full Suna Experience
</DialogTitle>
<DialogDescription>
You're currently using Suna's free tier with limited capabilities.
Upgrade now to access our most powerful AI model.
</DialogDescription>
</DialogHeader>
<div className="py-4">
<h3 className="text-sm font-medium text-slate-700 dark:text-slate-300 mb-3">Pro Benefits</h3>
<div className="space-y-3">
<div className="flex items-start">
<div className="rounded-full bg-secondary/10 p-2 flex-shrink-0 mt-0.5">
<Brain className="h-4 w-4 text-secondary" />
</div>
<div className="ml-3">
<h4 className="text-sm font-medium text-slate-900 dark:text-slate-100">Advanced AI Models</h4>
<p className="text-xs text-slate-500 dark:text-slate-400">Get access to advanced models suited for complex tasks</p>
</div>
</div>
<div className="flex items-start">
<div className="rounded-full bg-secondary/10 p-2 flex-shrink-0 mt-0.5">
<Zap className="h-4 w-4 text-secondary" />
</div>
<div className="ml-3">
<h4 className="text-sm font-medium text-slate-900 dark:text-slate-100">Faster Responses</h4>
<p className="text-xs text-slate-500 dark:text-slate-400">Get access to faster models that breeze through your tasks</p>
</div>
</div>
<div className="flex items-start">
<div className="rounded-full bg-secondary/10 p-2 flex-shrink-0 mt-0.5">
<Clock className="h-4 w-4 text-secondary" />
</div>
<div className="ml-3">
<h4 className="text-sm font-medium text-slate-900 dark:text-slate-100">Higher Usage Limits</h4>
<p className="text-xs text-slate-500 dark:text-slate-400">Enjoy more conversations and longer run durations</p>
</div>
</div>
</div>
</div>
<DialogFooter className="flex gap-2">
<Button variant="outline" onClick={handleDismissUpgradeDialog}>
Maybe Later
</Button>
<Button onClick={handleUpgradeClick}>
<Sparkles className="h-4 w-4" />
Upgrade Now
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</div>
);
}

View File

@ -311,11 +311,11 @@ export function FileOperationToolView({
<TabsList className="-mr-2 h-7 bg-zinc-100/70 dark:bg-zinc-800/70 rounded-lg">
<TabsTrigger value="code" className="rounded-md data-[state=active]:bg-white dark:data-[state=active]:bg-zinc-900 data-[state=active]:text-primary">
<Code className="h-4 w-4 mr-1.5" />
<Code className="h-4 w-4" />
Source
</TabsTrigger>
<TabsTrigger value="preview" className="rounded-md data-[state=active]:bg-white dark:data-[state=active]:bg-zinc-900 data-[state=active]:text-primary">
<Eye className="h-4 w-4 mr-1.5" />
<Eye className="h-4 w-4" />
Preview
</TabsTrigger>
</TabsList>

View File

@ -35,12 +35,3 @@ export const isPlan = (
if (!subscriptionData) return planId === 'free';
return subscriptionData.plan_name === planId;
};
export const getPlanName = (
subscriptionData: SubscriptionStatus | null | undefined,
): string => {
if (isPlan(subscriptionData, 'free')) return 'Free';
if (isPlan(subscriptionData, 'base')) return 'Pro';
if (isPlan(subscriptionData, 'extra')) return 'Enterprise';
return 'Unknown';
};