From 91684c062ec7b212b6db0a19bda918dcd56f17c6 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Thu, 12 Jun 2025 12:37:55 -0700 Subject: [PATCH 1/2] bump better-auth version & fix existing subscription issue Bwhen adding seats --- .../components/team-seats-dialog.tsx | 133 ++++++++++++++++++ .../components/subscription/subscription.tsx | 84 +++-------- .../team-management/team-management.tsx | 121 +++++++++++----- apps/sim/package.json | 4 +- bun.lock | 8 +- 5 files changed, 240 insertions(+), 110 deletions(-) create mode 100644 apps/sim/app/w/components/sidebar/components/settings-modal/components/subscription/components/team-seats-dialog.tsx diff --git a/apps/sim/app/w/components/sidebar/components/settings-modal/components/subscription/components/team-seats-dialog.tsx b/apps/sim/app/w/components/sidebar/components/settings-modal/components/subscription/components/team-seats-dialog.tsx new file mode 100644 index 00000000000..daaf1c26693 --- /dev/null +++ b/apps/sim/app/w/components/sidebar/components/settings-modal/components/subscription/components/team-seats-dialog.tsx @@ -0,0 +1,133 @@ +import { useEffect, useState } from 'react' +import { Button } from '@/components/ui/button' +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from '@/components/ui/dialog' +import { Label } from '@/components/ui/label' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' + +interface TeamSeatsDialogProps { + open: boolean + onOpenChange: (open: boolean) => void + title: string + description: string + currentSeats?: number + initialSeats?: number + isLoading: boolean + onConfirm: (seats: number) => Promise + confirmButtonText: string + showCostBreakdown?: boolean +} + +export function TeamSeatsDialog({ + open, + onOpenChange, + title, + description, + currentSeats, + initialSeats = 1, + isLoading, + onConfirm, + confirmButtonText, + showCostBreakdown = false, +}: TeamSeatsDialogProps) { + const [selectedSeats, setSelectedSeats] = useState(initialSeats) + + useEffect(() => { + if (open) { + setSelectedSeats(initialSeats) + } + }, [open, initialSeats]) + + const costPerSeat = 40 + const totalMonthlyCost = selectedSeats * costPerSeat + const costChange = currentSeats ? (selectedSeats - currentSeats) * costPerSeat : 0 + + const handleConfirm = async () => { + await onConfirm(selectedSeats) + } + + return ( + + + + {title} + {description} + + +
+ + + +

+ Your team will have {selectedSeats} {selectedSeats === 1 ? 'seat' : 'seats'} with a + total of ${totalMonthlyCost} inference credits per month. +

+ + {showCostBreakdown && currentSeats !== undefined && ( +
+
+ Current seats: + {currentSeats} +
+
+ New seats: + {selectedSeats} +
+
+ Monthly cost change: + + {costChange > 0 ? '+' : ''}${costChange} + +
+
+ )} +
+ + + + + +
+
+ ) +} diff --git a/apps/sim/app/w/components/sidebar/components/settings-modal/components/subscription/subscription.tsx b/apps/sim/app/w/components/sidebar/components/settings-modal/components/subscription/subscription.tsx index c0a2450d3a9..27bfcfdd713 100644 --- a/apps/sim/app/w/components/sidebar/components/settings-modal/components/subscription/subscription.tsx +++ b/apps/sim/app/w/components/sidebar/components/settings-modal/components/subscription/subscription.tsx @@ -2,26 +2,11 @@ import { useEffect, useState } from 'react' import { AlertCircle } from 'lucide-react' import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert' import { Button } from '@/components/ui/button' -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from '@/components/ui/dialog' -import { Label } from '@/components/ui/label' import { Progress } from '@/components/ui/progress' -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select' import { Skeleton } from '@/components/ui/skeleton' import { useActiveOrganization, useSession, useSubscription } from '@/lib/auth-client' import { createLogger } from '@/lib/logs/console-logger' +import { TeamSeatsDialog } from './components/team-seats-dialog' const logger = createLogger('Subscription') @@ -332,7 +317,7 @@ export function Subscription({ setIsTeamDialogOpen(true) } - const confirmTeamUpgrade = async () => { + const confirmTeamUpgrade = async (selectedSeats?: number) => { if (!session?.user) { setError('You need to be logged in to upgrade your team subscription') return @@ -341,10 +326,12 @@ export function Subscription({ setIsUpgradingTeam(true) setError(null) + const seatsToUse = selectedSeats || seats + try { const result = await subscription.upgrade({ plan: 'team', - seats, + seats: seatsToUse, successUrl: window.location.href, cancelUrl: window.location.href, }) @@ -816,54 +803,19 @@ export function Subscription({ )} - - - - Team Subscription - - Set up a team workspace with collaborative features. Each seat costs $40/month and - gets $40 of inference credits. - - - -
- - - -

- Your team will have {seats} {seats === 1 ? 'seat' : 'seats'} with a total of $ - {seats * 40} inference credits per month. -

-
- - - - - -
-
+ { + setSeats(selectedSeats) + await confirmTeamUpgrade(selectedSeats) + }} + confirmButtonText='Upgrade to Team Plan' + /> )} diff --git a/apps/sim/app/w/components/sidebar/components/settings-modal/components/team-management/team-management.tsx b/apps/sim/app/w/components/sidebar/components/settings-modal/components/team-management/team-management.tsx index e9fe686013a..ec169a17f5f 100644 --- a/apps/sim/app/w/components/sidebar/components/settings-modal/components/team-management/team-management.tsx +++ b/apps/sim/app/w/components/sidebar/components/settings-modal/components/team-management/team-management.tsx @@ -17,6 +17,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { client, useSession } from '@/lib/auth-client' import { createLogger } from '@/lib/logs/console-logger' import { checkEnterprisePlan } from '@/lib/subscription/utils' +import { TeamSeatsDialog } from '../subscription/components/team-seats-dialog' const logger = createLogger('TeamManagement') @@ -115,6 +116,10 @@ export function TeamManagement() { [activeOrganization] ) + const [isAddSeatDialogOpen, setIsAddSeatDialogOpen] = useState(false) + const [newSeatCount, setNewSeatCount] = useState(1) + const [isUpdatingSeats, setIsUpdatingSeats] = useState(false) + const loadData = useCallback(async () => { if (!session?.user) return @@ -281,7 +286,7 @@ export function TeamManagement() { } try { - await updateSeats(currentSeats - 1) + await reduceSeats(currentSeats - 1) await refreshOrganization() } catch (err: any) { setError(err.message || 'Failed to reduce seats') @@ -581,7 +586,7 @@ export function TeamManagement() { if (shouldReduceSeats && subscriptionData) { const currentSeats = subscriptionData.seats || 0 if (currentSeats > 1) { - await updateSeats(currentSeats - 1) + await reduceSeats(currentSeats - 1) } } @@ -637,34 +642,68 @@ export function TeamManagement() { ) } - const updateSeats = useCallback( - async (newSeatCount: number) => { - if (!subscriptionData || !activeOrganization) return + // Handle opening the add seat dialog + const handleAddSeatDialog = () => { + if (subscriptionData) { + setNewSeatCount((subscriptionData.seats || 1) + 1) // Default to current seats + 1 + setIsAddSeatDialogOpen(true) + } + } - // Don't allow enterprise users to modify seats - if (checkEnterprisePlan(subscriptionData)) { - setError('Enterprise plan seats can only be modified by contacting support') - return - } + // Handle reducing seats + const reduceSeats = async (newSeatCount: number) => { + if (!subscriptionData || !activeOrganization) return - try { - setIsLoading(true) - setError(null) + try { + setIsLoading(true) + setError(null) - const { error } = await client.subscription.upgrade({ - plan: 'team', - referenceId: activeOrganization.id, - successUrl: window.location.href, - cancelUrl: window.location.href, - seats: newSeatCount, - }) - if (error) throw new Error(error.message || 'Failed to update seats') - } finally { - setIsLoading(false) + const { error } = await client.subscription.upgrade({ + plan: 'team', + referenceId: activeOrganization.id, + subscriptionId: subscriptionData.id, + seats: newSeatCount, + successUrl: window.location.href, + cancelUrl: window.location.href, + }) + if (error) throw new Error(error.message || 'Failed to reduce seats') + } finally { + setIsLoading(false) + } + } + + // Confirm seat addition + const confirmAddSeats = async (selectedSeats?: number) => { + if (!subscriptionData || !activeOrganization) return + + const seatsToUse = selectedSeats || newSeatCount + + try { + setIsUpdatingSeats(true) + setError(null) + + const { error } = await client.subscription.upgrade({ + plan: 'team', + referenceId: activeOrganization.id, + subscriptionId: subscriptionData.id, + seats: seatsToUse, + successUrl: window.location.href, + cancelUrl: window.location.href, + }) + + if (error) { + setError(error.message || 'Failed to update seats') + } else { + // Close the dialog after successful upgrade + setIsAddSeatDialogOpen(false) + await refreshOrganization() } - }, - [subscriptionData, activeOrganization] - ) + } catch (err: any) { + setError(err.message || 'Failed to update seats') + } finally { + setIsUpdatingSeats(false) + } + } if (isLoading && !activeOrganization && !(hasTeamPlan || hasEnterprisePlan)) { return @@ -935,18 +974,7 @@ export function TeamManagement() {