mirror of https://github.com/kortix-ai/suna.git
chore(ux): alert dialog for free tier on agent run
This commit is contained in:
parent
1b3601513c
commit
3cf13c1823
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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';
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue