diff --git a/backend/utils/billing.py b/backend/utils/billing.py index c4ab9bbf..a839673a 100644 --- a/backend/utils/billing.py +++ b/backend/utils/billing.py @@ -3,7 +3,7 @@ from typing import Dict, Optional, Tuple # Define subscription tiers and their monthly limits (in minutes) SUBSCRIPTION_TIERS = { - 'price_1RGJ9GG6l1KZGqIroxSqgphC': {'name': 'free', 'minutes': 10}, + 'price_1RGJ9GG6l1KZGqIroxSqgphC': {'name': 'free', 'minutes': 0}, 'price_1RGJ9LG6l1KZGqIrd9pwzeNW': {'name': 'base', 'minutes': 300}, # 100 hours = 6000 minutes 'price_1RGJ9JG6l1KZGqIrVUU4ZRv6': {'name': 'extra', 'minutes': 2400} # 100 hours = 6000 minutes } @@ -76,11 +76,14 @@ async def check_billing_status(client, account_id: str) -> Tuple[bool, str, Opti subscription = await get_account_subscription(client, account_id) # If no subscription, they can use free tier - if not subscription: - subscription = { - 'price_id': 'price_1RGJ9GG6l1KZGqIroxSqgphC', # Free tier - 'plan_name': 'Free' - } + # if not subscription: + # subscription = { + # 'price_id': 'price_1RGJ9GG6l1KZGqIroxSqgphC', # Free tier + # 'plan_name': 'Free' + # } + + if not subscription or subscription.get('price_id') is None or subscription.get('price_id') == 'price_1RGJ9GG6l1KZGqIroxSqgphC': + return False, "You are not subscribed to any plan. Please upgrade your plan to continue.", subscription # Get tier info tier_info = SUBSCRIPTION_TIERS.get(subscription['price_id']) diff --git a/frontend/src/app/(dashboard)/layout.tsx b/frontend/src/app/(dashboard)/layout.tsx index 32153347..ee485db4 100644 --- a/frontend/src/app/(dashboard)/layout.tsx +++ b/frontend/src/app/(dashboard)/layout.tsx @@ -7,6 +7,7 @@ import { SidebarProvider, } from "@/components/ui/sidebar" import { MaintenanceAlert } from "@/components/maintenance-alert" +import { useAccounts } from "@/hooks/use-accounts" interface DashboardLayoutProps { children: React.ReactNode @@ -16,6 +17,8 @@ export default function DashboardLayout({ children, }: DashboardLayoutProps) { const [showMaintenanceAlert, setShowMaintenanceAlert] = useState(false) + const { data: accounts } = useAccounts() + const personalAccount = accounts?.find(account => account.personal_account) useEffect(() => { // Show the maintenance alert when component mounts @@ -35,6 +38,7 @@ export default function DashboardLayout({ open={showMaintenanceAlert} onOpenChange={setShowMaintenanceAlert} closeable={true} + accountId={personalAccount?.account_id} /> ) diff --git a/frontend/src/components/maintenance-alert.tsx b/frontend/src/components/maintenance-alert.tsx index cf58fe73..33be48f3 100644 --- a/frontend/src/components/maintenance-alert.tsx +++ b/frontend/src/components/maintenance-alert.tsx @@ -1,94 +1,149 @@ "use client" -import { AlertDialog, AlertDialogAction, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog" -import { Clock, Github, X } from "lucide-react" +import { AlertDialog, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog" +import { AlertCircle, X, Zap, Github } from "lucide-react" import Link from "next/link" import { AnimatePresence, motion } from "motion/react" import { Button } from "@/components/ui/button" +import { Portal } from "@/components/ui/portal" +import { cn } from "@/lib/utils" +import { setupNewSubscription } from "@/lib/actions/billing" +import { SubmitButton } from "@/components/ui/submit-button" +import { siteConfig } from "@/lib/home" interface MaintenanceAlertProps { open: boolean onOpenChange: (open: boolean) => void closeable?: boolean + accountId?: string | null | undefined } -export function MaintenanceAlert({ open, onOpenChange, closeable = true }: MaintenanceAlertProps) { +export function MaintenanceAlert({ open, onOpenChange, closeable = true, accountId }: MaintenanceAlertProps) { + const returnUrl = typeof window !== 'undefined' ? window.location.href : ''; + + if (!open) return null; + + // Filter plans to show only Pro and Enterprise + const premiumPlans = siteConfig.cloudPricingItems.filter(plan => + plan.name === 'Pro' || plan.name === 'Enterprise' + ); + return ( - - - - {/* Background pattern */} -
-
-
- - {closeable && ( - - )} - - - -
-
- + {/* Backdrop */} + onOpenChange(false) : undefined} + aria-hidden="true" + /> + + {/* Modal */} + +
+ {/* Close button */} + {closeable && ( + + )} + + {/* Header */} +
+
+ +
+

+ Free Tier Unavailable At This Time +

+

+ Due to extremely high demand, we cannot offer a free tier at the moment. Upgrade to Pro to continue using our service. +

+
+ + {/* Custom plan comparison wrapper to show Pro, Enterprise and Self-Host side by side */} +
+ {premiumPlans.map((tier) => ( +
+
+

{tier.name}

+

{tier.price}/mo

+

{tier.hours}/month

+
+
+ + + item.name === 'Pro')?.stripePriceId || '' + : siteConfig.cloudPricingItems.find(item => item.name === 'Enterprise')?.stripePriceId || '' + } /> + + Upgrade + +
+
+ ))} + + {/* Self-host Option as the third card */} +
+
+

Self-Host

+

Free

+

Open Source

+
+ + + Self-Host + +
+
-
+ - - - - High Demand Notice - - - - - - Due to exceptionally high demand, our service is currently experiencing slower response times. - We recommend returning tomorrow when our systems will be operating at normal capacity. - Thank you for your understanding. We will notify you via email once the service is fully operational again. - - - - - - - - Explore Self-Hosted Version - - - - - + + )} + + ) } \ No newline at end of file diff --git a/frontend/src/lib/home.tsx b/frontend/src/lib/home.tsx index 6ff51123..f875d3fd 100644 --- a/frontend/src/lib/home.tsx +++ b/frontend/src/lib/home.tsx @@ -83,9 +83,9 @@ export const siteConfig = { buttonText: "Hire Suna", buttonColor: "bg-secondary text-white", isPopular: false, - hours: "10 minutes", + hours: "no free usage at this time", features: [ - "10 minutes usage per month", + "no free usage", // "Community support", // "Single user", // "Standard response time",