+const SettingRowSkeleton = ({
+ hasInfoButton = false,
+ isSwitch = false,
+}: {
+ hasInfoButton?: boolean
+ isSwitch?: boolean
+}) => (
+
-
+
+ {hasInfoButton && }
-
+ {isSwitch ? (
+
+ ) : (
+
+ )}
)
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/settings-navigation/settings-navigation.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/settings-navigation/settings-navigation.tsx
index 851a1d7d70..1100f90610 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/settings-navigation/settings-navigation.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/settings-navigation/settings-navigation.tsx
@@ -1,13 +1,13 @@
import {
Bot,
CreditCard,
- KeyRound,
- KeySquare,
- Lock,
+ FileCode,
+ Key,
Settings,
Shield,
- UserCircle,
+ User,
Users,
+ Waypoints,
} from 'lucide-react'
import { getEnv, isTruthy } from '@/lib/env'
import { isHosted } from '@/lib/environment'
@@ -56,29 +56,29 @@ const allNavigationItems: NavigationItem[] = [
label: 'General',
icon: Settings,
},
+ {
+ id: 'credentials',
+ label: 'Integrations',
+ icon: Waypoints,
+ },
{
id: 'environment',
label: 'Environment',
- icon: KeyRound,
+ icon: FileCode,
},
{
id: 'account',
label: 'Account',
- icon: UserCircle,
- },
- {
- id: 'credentials',
- label: 'Credentials',
- icon: Lock,
+ icon: User,
},
{
id: 'apikeys',
label: 'API Keys',
- icon: KeySquare,
+ icon: Key,
},
{
id: 'copilot',
- label: 'Copilot',
+ label: 'Copilot Keys',
icon: Bot,
},
{
@@ -126,22 +126,36 @@ export function SettingsNavigation({
})
return (
-
+
{navigationItems.map((item) => (
-
onSectionChange(item.id)}
- className={cn(
- 'flex w-full items-center gap-3 px-4 py-2.5 text-sm transition-colors',
- 'hover:bg-muted/50',
- activeSection === item.id
- ? 'bg-muted/50 font-medium text-foreground'
- : 'text-muted-foreground hover:text-foreground'
- )}
- >
-
- {item.label}
-
+
+ onSectionChange(item.id)}
+ className={cn(
+ 'group flex h-9 w-full cursor-pointer items-center rounded-[8px] px-2 py-2 font-medium font-sans text-sm transition-colors',
+ activeSection === item.id ? 'bg-muted' : 'hover:bg-muted'
+ )}
+ >
+
+
+ {item.label}
+
+
+
))}
)
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/billing-summary.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/billing-summary.tsx
deleted file mode 100644
index 3d223ddafd..0000000000
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/billing-summary.tsx
+++ /dev/null
@@ -1,144 +0,0 @@
-import { useEffect, useState } from 'react'
-import { AlertCircle } from 'lucide-react'
-import { Badge } from '@/components/ui/badge'
-import { useActiveOrganization, useSession } from '@/lib/auth-client'
-import { createLogger } from '@/lib/logs/console/logger'
-
-const logger = createLogger('BillingSummary')
-
-interface BillingSummaryData {
- type: 'individual' | 'organization'
- plan: string
- currentUsage: number
- planMinimum: number
- projectedCharge: number
- usageLimit: number
- percentUsed: number
- isWarning: boolean
- isExceeded: boolean
- daysRemaining: number
- organizationData?: {
- seatCount: number
- averageUsagePerSeat: number
- totalMinimum: number
- }
-}
-
-interface BillingSummaryProps {
- showDetails?: boolean
- className?: string
- onDataLoaded?: (data: BillingSummaryData) => void
- onError?: (error: string) => void
-}
-
-export function BillingSummary({
- showDetails = true,
- className = '',
- onDataLoaded,
- onError,
-}: BillingSummaryProps) {
- const { data: session } = useSession()
- const { data: activeOrg } = useActiveOrganization()
-
- const [billingSummary, setBillingSummary] = useState
(null)
- const [isLoading, setIsLoading] = useState(true)
- const [error, setError] = useState(null)
-
- useEffect(() => {
- async function loadBillingSummary() {
- if (!session?.user?.id) return
-
- try {
- setIsLoading(true)
-
- const url = new URL('/api/billing', window.location.origin)
- if (activeOrg?.id) {
- url.searchParams.set('context', 'organization')
- url.searchParams.set('id', activeOrg.id)
- } else {
- url.searchParams.set('context', 'user')
- }
-
- const response = await fetch(url.toString())
- if (!response.ok) {
- throw new Error(`Failed to fetch billing summary: ${response.statusText}`)
- }
-
- const result = await response.json()
- if (!result.success) {
- throw new Error(result.error || 'Failed to load billing data')
- }
-
- setBillingSummary(result.data)
- setError(null)
- onDataLoaded?.(result.data)
- } catch (err) {
- const errorMessage = err instanceof Error ? err.message : 'Failed to load billing data'
- setError(errorMessage)
- onError?.(errorMessage)
- logger.error('Failed to load billing summary', { error: err })
- } finally {
- setIsLoading(false)
- }
- }
-
- loadBillingSummary()
- }, [session?.user?.id, activeOrg?.id, onDataLoaded, onError])
-
- const getStatusBadge = () => {
- if (!billingSummary) return null
-
- if (billingSummary.isExceeded) {
- return (
-
-
- Limit Exceeded
-
- )
- }
- if (billingSummary.isWarning) {
- return (
-
-
- Approaching Limit
-
- )
- }
- return null
- }
-
- const formatCurrency = (amount: number) => `$${amount.toFixed(2)}`
-
- if (isLoading || error || !billingSummary) {
- return null
- }
-
- return (
-
- {/* Status Badge */}
- {getStatusBadge()}
-
- {/* Billing Details */}
- {showDetails && (
-
-
- Plan minimum:
- {formatCurrency(billingSummary.planMinimum)}
-
-
- Projected charge:
- {formatCurrency(billingSummary.projectedCharge)}
-
- {billingSummary.organizationData && (
-
- Team seats:
- {billingSummary.organizationData.seatCount}
-
- )}
-
- )}
-
- )
-}
-
-export type { BillingSummaryData }
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/cancel-subscription.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/cancel-subscription/cancel-subscription.tsx
similarity index 60%
rename from apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/cancel-subscription.tsx
rename to apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/cancel-subscription/cancel-subscription.tsx
index e31061f5e5..ee24a02644 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/cancel-subscription.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/cancel-subscription/cancel-subscription.tsx
@@ -1,16 +1,20 @@
-import { useState } from 'react'
-import { Alert, AlertDescription } from '@/components/ui/alert'
-import { Button } from '@/components/ui/button'
+'use client'
+
+import { useEffect, useState } from 'react'
import {
- Dialog,
- DialogContent,
- DialogDescription,
- DialogFooter,
- DialogHeader,
- DialogTitle,
-} from '@/components/ui/dialog'
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+} from '@/components/ui/alert-dialog'
+import { Button } from '@/components/ui/button'
import { useSession, useSubscription } from '@/lib/auth-client'
import { createLogger } from '@/lib/logs/console/logger'
+import { cn } from '@/lib/utils'
import { useOrganizationStore } from '@/stores/organization'
import { useSubscriptionStore } from '@/stores/subscription/store'
@@ -37,6 +41,16 @@ export function CancelSubscription({ subscription, subscriptionData }: CancelSub
const { activeOrganization } = useOrganizationStore()
const { getSubscriptionStatus } = useSubscriptionStore()
+ // Clear error after 3 seconds
+ useEffect(() => {
+ if (error) {
+ const timer = setTimeout(() => {
+ setError(null)
+ }, 3000)
+ return () => clearTimeout(timer)
+ }
+ }, [error])
+
// Don't show for free plans
if (!subscription.isPaid) {
return null
@@ -115,44 +129,41 @@ export function CancelSubscription({ subscription, subscriptionData }: CancelSub
return (
<>
-
-
-
-
Cancel Subscription
-
- You'll keep access until {formatDate(periodEndDate)}
-
-
-
setIsDialogOpen(true)}
- disabled={isLoading}
- >
- Cancel
-
+
+
+
Manage Subscription
+
+ You'll keep access until {formatDate(periodEndDate)}
+
-
- {error && (
-
- {error}
-
- )}
+
setIsDialogOpen(true)}
+ disabled={isLoading}
+ className={cn(
+ 'h-8 rounded-[8px] font-medium text-xs transition-all duration-200',
+ error
+ ? 'border-red-500 text-red-500 dark:border-red-500 dark:text-red-500'
+ : 'text-muted-foreground hover:border-red-500 hover:bg-red-500 hover:text-white dark:hover:border-red-500 dark:hover:bg-red-500'
+ )}
+ >
+ {error ? 'Error' : 'Manage'}
+
-
-
-
- Cancel {subscription.plan} subscription?
-
+
+
+
+ Cancel {subscription.plan} subscription?
+
You'll be redirected to Stripe to manage your subscription. You'll keep access until{' '}
{formatDate(periodEndDate)}, then downgrade to free plan.
-
-
+
+
-
-
-
+
+
+
• Keep all features until {formatDate(periodEndDate)}
• No more charges
• Data preserved
@@ -161,16 +172,24 @@ export function CancelSubscription({ subscription, subscriptionData }: CancelSub
-
- setIsDialogOpen(false)} disabled={isLoading}>
+
+ setIsDialogOpen(false)}
+ disabled={isLoading}
+ >
Keep Subscription
-
-
+
+
{isLoading ? 'Redirecting...' : 'Continue'}
-
-
-
-
+
+
+
+
>
)
}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/cancel-subscription/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/cancel-subscription/index.ts
new file mode 100644
index 0000000000..a7d0def45c
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/cancel-subscription/index.ts
@@ -0,0 +1 @@
+export { CancelSubscription } from './cancel-subscription'
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/index.ts
index 17c5db87cf..73b2e8bc7d 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/index.ts
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/index.ts
@@ -1,6 +1,4 @@
-export { BillingSummary } from './billing-summary'
export { CancelSubscription } from './cancel-subscription'
-export { EditMemberLimitDialog } from './edit-member-limit-dialog'
-export { TeamSeatsDialog } from './team-seats-dialog'
-export { TeamUsageOverview } from './team-usage-overview'
-export { UsageLimitEditor } from './usage-limit-editor'
+export { PlanCard, type PlanCardProps, type PlanFeature } from './plan-card'
+export type { UsageLimitRef } from './usage-limit'
+export { UsageLimit } from './usage-limit'
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/plan-card/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/plan-card/index.ts
new file mode 100644
index 0000000000..5685e9ac3b
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/plan-card/index.ts
@@ -0,0 +1 @@
+export { PlanCard, type PlanCardProps, type PlanFeature } from './plan-card'
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/plan-card/plan-card.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/plan-card/plan-card.tsx
new file mode 100644
index 0000000000..a8599da299
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/plan-card/plan-card.tsx
@@ -0,0 +1,123 @@
+'use client'
+
+import type { ReactNode } from 'react'
+import type { LucideIcon } from 'lucide-react'
+import { Button } from '@/components/ui'
+import { cn } from '@/lib/utils'
+
+export interface PlanFeature {
+ icon: LucideIcon
+ text: string
+}
+
+export interface PlanCardProps {
+ name: string
+ price: string | ReactNode
+ priceSubtext?: string
+ features: PlanFeature[]
+ buttonText: string
+ onButtonClick: () => void
+ isError?: boolean
+ variant?: 'default' | 'compact'
+ layout?: 'vertical' | 'horizontal'
+ className?: string
+}
+
+/**
+ * PlanCard component for displaying subscription plan information
+ * Supports both vertical and horizontal layouts with flexible pricing display
+ */
+export function PlanCard({
+ name,
+ price,
+ priceSubtext,
+ features,
+ buttonText,
+ onButtonClick,
+ isError = false,
+ variant = 'default',
+ layout = 'vertical',
+ className,
+}: PlanCardProps) {
+ const isHorizontal = layout === 'horizontal'
+
+ const renderPrice = () => {
+ if (typeof price === 'string') {
+ return (
+ <>
+ {price}
+ {priceSubtext && (
+ {priceSubtext}
+ )}
+ >
+ )
+ }
+ return price
+ }
+
+ const renderFeatures = () => {
+ if (isHorizontal) {
+ return (
+
+ {features.map((feature, index) => (
+
+
+
{feature.text}
+ {index < features.length - 1 && (
+
+ )}
+
+ ))}
+
+ )
+ }
+
+ return (
+
+ {features.map((feature, index) => (
+
+
+ {feature.text}
+
+ ))}
+
+ )
+ }
+
+ return (
+
+
+ {name}
+ {renderPrice()}
+ {isHorizontal && renderFeatures()}
+
+
+ {!isHorizontal && renderFeatures()}
+
+
+
+ {isError ? 'Error' : buttonText}
+
+
+
+ )
+}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/usage-limit-editor.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/usage-limit-editor.tsx
deleted file mode 100644
index c356c02c95..0000000000
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/usage-limit-editor.tsx
+++ /dev/null
@@ -1,91 +0,0 @@
-import { useEffect, useState } from 'react'
-import { Input } from '@/components/ui/input'
-import { createLogger } from '@/lib/logs/console/logger'
-import { useSubscriptionStore } from '@/stores/subscription/store'
-
-const logger = createLogger('UsageLimitEditor')
-
-interface UsageLimitEditorProps {
- currentLimit: number
- canEdit: boolean
- minimumLimit: number
- onLimitUpdated?: (newLimit: number) => void
-}
-
-export function UsageLimitEditor({
- currentLimit,
- canEdit,
- minimumLimit,
- onLimitUpdated,
-}: UsageLimitEditorProps) {
- const [inputValue, setInputValue] = useState(currentLimit.toString())
- const [isSaving, setIsSaving] = useState(false)
-
- const { updateUsageLimit } = useSubscriptionStore()
-
- useEffect(() => {
- setInputValue(currentLimit.toString())
- }, [currentLimit])
-
- const handleSubmit = async () => {
- const newLimit = Number.parseInt(inputValue, 10)
-
- if (Number.isNaN(newLimit) || newLimit < minimumLimit) {
- setInputValue(currentLimit.toString())
- return
- }
-
- if (newLimit === currentLimit) {
- return
- }
-
- setIsSaving(true)
-
- try {
- const result = await updateUsageLimit(newLimit)
-
- if (!result.success) {
- throw new Error(result.error || 'Failed to update limit')
- }
-
- setInputValue(newLimit.toString())
- onLimitUpdated?.(newLimit)
- } catch (error) {
- logger.error('Failed to update usage limit', { error })
- setInputValue(currentLimit.toString())
- } finally {
- setIsSaving(false)
- }
- }
-
- const handleKeyDown = (e: React.KeyboardEvent) => {
- if (e.key === 'Enter') {
- e.preventDefault()
- handleSubmit()
- }
- }
-
- return (
-
- $
- {canEdit ? (
- setInputValue(e.target.value)}
- onKeyDown={handleKeyDown}
- onBlur={handleSubmit}
- className='h-8 w-20 font-medium text-sm'
- min={minimumLimit}
- step='1'
- disabled={isSaving}
- autoComplete='off'
- data-form-type='other'
- name='usage-limit'
- />
- ) : (
- {currentLimit}
- )}
-
- )
-}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/usage-limit/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/usage-limit/index.ts
new file mode 100644
index 0000000000..d09782c8e6
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/usage-limit/index.ts
@@ -0,0 +1,2 @@
+export type { UsageLimitRef } from './usage-limit'
+export { UsageLimit } from './usage-limit'
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/usage-limit/usage-limit.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/usage-limit/usage-limit.tsx
new file mode 100644
index 0000000000..a3b8fe1be7
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/usage-limit/usage-limit.tsx
@@ -0,0 +1,209 @@
+'use client'
+
+import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from 'react'
+import { Check, Pencil, X } from 'lucide-react'
+import { Button } from '@/components/ui/button'
+import { createLogger } from '@/lib/logs/console/logger'
+import { cn } from '@/lib/utils'
+import { useSubscriptionStore } from '@/stores/subscription/store'
+
+const logger = createLogger('UsageLimit')
+
+interface UsageLimitProps {
+ currentLimit: number
+ currentUsage: number
+ canEdit: boolean
+ minimumLimit: number
+ onLimitUpdated?: (newLimit: number) => void
+}
+
+export interface UsageLimitRef {
+ startEdit: () => void
+}
+
+export const UsageLimit = forwardRef(
+ ({ currentLimit, currentUsage, canEdit, minimumLimit, onLimitUpdated }, ref) => {
+ const [inputValue, setInputValue] = useState(currentLimit.toString())
+ const [isSaving, setIsSaving] = useState(false)
+ const [hasError, setHasError] = useState(false)
+ const [errorType, setErrorType] = useState<'general' | 'belowUsage' | null>(null)
+ const [isEditing, setIsEditing] = useState(false)
+ const inputRef = useRef(null)
+
+ const { updateUsageLimit } = useSubscriptionStore()
+
+ const handleStartEdit = () => {
+ if (!canEdit) return
+ setIsEditing(true)
+ setInputValue(currentLimit.toString())
+ }
+
+ // Expose startEdit method through ref
+ useImperativeHandle(
+ ref,
+ () => ({
+ startEdit: handleStartEdit,
+ }),
+ [canEdit, currentLimit]
+ )
+
+ useEffect(() => {
+ setInputValue(currentLimit.toString())
+ }, [currentLimit])
+
+ // Focus input when entering edit mode
+ useEffect(() => {
+ if (isEditing && inputRef.current) {
+ inputRef.current.focus()
+ inputRef.current.select()
+ }
+ }, [isEditing])
+
+ // Clear error after 2 seconds
+ useEffect(() => {
+ if (hasError) {
+ const timer = setTimeout(() => {
+ setHasError(false)
+ setErrorType(null)
+ }, 2000)
+ return () => clearTimeout(timer)
+ }
+ }, [hasError])
+
+ const handleSubmit = async () => {
+ const newLimit = Number.parseInt(inputValue, 10)
+
+ if (Number.isNaN(newLimit) || newLimit < minimumLimit) {
+ setInputValue(currentLimit.toString())
+ setIsEditing(false)
+ return
+ }
+
+ // Check if new limit is below current usage
+ if (newLimit < currentUsage) {
+ setHasError(true)
+ setErrorType('belowUsage')
+ // Don't reset input value - let user see what they typed
+ return
+ }
+
+ if (newLimit === currentLimit) {
+ setIsEditing(false)
+ return
+ }
+
+ setIsSaving(true)
+
+ try {
+ const result = await updateUsageLimit(newLimit)
+
+ if (!result.success) {
+ throw new Error(result.error || 'Failed to update limit')
+ }
+
+ setInputValue(newLimit.toString())
+ onLimitUpdated?.(newLimit)
+ setIsEditing(false)
+ setErrorType(null)
+ } catch (error) {
+ logger.error('Failed to update usage limit', { error })
+
+ // Check if the error is about being below current usage
+ if (error instanceof Error && error.message.includes('below current usage')) {
+ setErrorType('belowUsage')
+ } else {
+ setErrorType('general')
+ }
+
+ setHasError(true)
+ } finally {
+ setIsSaving(false)
+ }
+ }
+
+ const handleCancelEdit = () => {
+ setIsEditing(false)
+ setInputValue(currentLimit.toString())
+ setHasError(false)
+ setErrorType(null)
+ }
+
+ const handleKeyDown = (e: React.KeyboardEvent) => {
+ if (e.key === 'Enter') {
+ e.preventDefault()
+ handleSubmit()
+ } else if (e.key === 'Escape') {
+ e.preventDefault()
+ handleCancelEdit()
+ }
+ }
+
+ return (
+
+ {isEditing ? (
+ <>
+
$
+
setInputValue(e.target.value)}
+ onKeyDown={handleKeyDown}
+ onBlur={(e) => {
+ // Don't submit if clicking on the button (it will handle submission)
+ const relatedTarget = e.relatedTarget as HTMLElement
+ if (relatedTarget?.closest('button')) {
+ return
+ }
+ handleSubmit()
+ }}
+ className={cn(
+ 'w-[3ch] border-0 bg-transparent p-0 text-xs tabular-nums',
+ 'outline-none focus:outline-none focus:ring-0 focus-visible:outline-none focus-visible:ring-0 focus-visible:ring-offset-0',
+ '[appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none',
+ hasError && 'text-red-500'
+ )}
+ min={minimumLimit}
+ max='999'
+ step='1'
+ disabled={isSaving}
+ autoComplete='off'
+ autoCorrect='off'
+ autoCapitalize='off'
+ spellCheck='false'
+ />
+ >
+ ) : (
+
${currentLimit}
+ )}
+ {canEdit && (
+
+ {isEditing ? (
+ hasError ? (
+
+ ) : (
+
+ )
+ ) : (
+
+ )}
+ {isEditing ? 'Save limit' : 'Edit limit'}
+
+ )}
+
+ )
+ }
+)
+
+UsageLimit.displayName = 'UsageLimit'
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/plan-configs.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/plan-configs.ts
new file mode 100644
index 0000000000..c4bb43bd2b
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/plan-configs.ts
@@ -0,0 +1,35 @@
+import {
+ Building2,
+ Clock,
+ Database,
+ HeadphonesIcon,
+ Infinity as InfinityIcon,
+ MessageSquare,
+ Server,
+ Users,
+ Workflow,
+ Zap,
+} from 'lucide-react'
+import type { PlanFeature } from './components/plan-card'
+
+export const PRO_PLAN_FEATURES: PlanFeature[] = [
+ { icon: Zap, text: '25 runs per minute (sync)' },
+ { icon: Clock, text: '200 runs per minute (async)' },
+ { icon: Building2, text: 'Unlimited workspaces' },
+ { icon: Workflow, text: 'Unlimited workflows' },
+ { icon: Users, text: 'Unlimited invites' },
+ { icon: Database, text: 'Unlimited log retention' },
+]
+
+export const TEAM_PLAN_FEATURES: PlanFeature[] = [
+ { icon: Zap, text: '75 runs per minute (sync)' },
+ { icon: Clock, text: '500 runs per minute (async)' },
+ { icon: InfinityIcon, text: 'Everything in Pro' },
+ { icon: MessageSquare, text: 'Dedicated Slack channel' },
+]
+
+export const ENTERPRISE_PLAN_FEATURES: PlanFeature[] = [
+ { icon: Zap, text: 'Custom rate limits' },
+ { icon: Server, text: 'Enterprise hosting' },
+ { icon: HeadphonesIcon, text: 'Dedicated support' },
+]
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/subscription-permissions.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/subscription-permissions.ts
new file mode 100644
index 0000000000..cf945ca47c
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/subscription-permissions.ts
@@ -0,0 +1,69 @@
+export interface SubscriptionPermissions {
+ canUpgradeToPro: boolean
+ canUpgradeToTeam: boolean
+ canViewEnterprise: boolean
+ canManageTeam: boolean
+ canEditUsageLimit: boolean
+ canCancelSubscription: boolean
+ showTeamMemberView: boolean
+ showUpgradePlans: boolean
+}
+
+export interface SubscriptionState {
+ isFree: boolean
+ isPro: boolean
+ isTeam: boolean
+ isEnterprise: boolean
+ isPaid: boolean
+ plan: string
+ status: string
+}
+
+export interface UserRole {
+ isTeamAdmin: boolean
+ userRole: string
+}
+
+export function getSubscriptionPermissions(
+ subscription: SubscriptionState,
+ userRole: UserRole
+): SubscriptionPermissions {
+ const { isFree, isPro, isTeam, isEnterprise, isPaid } = subscription
+ const { isTeamAdmin } = userRole
+
+ return {
+ canUpgradeToPro: isFree,
+ canUpgradeToTeam: isFree || (isPro && !isTeam),
+ canViewEnterprise: !isEnterprise && !(isTeam && !isTeamAdmin), // Don't show to enterprise users or team members
+ canManageTeam: isTeam && isTeamAdmin,
+ canEditUsageLimit: (isFree || (isPro && !isTeam)) && !isEnterprise, // Free users see upgrade badge, Pro (non-team) users see pencil
+ canCancelSubscription: isPaid && !isEnterprise && !(isTeam && !isTeamAdmin), // Team members can't cancel
+ showTeamMemberView: isTeam && !isTeamAdmin,
+ showUpgradePlans: isFree || (isPro && !isTeam) || (isTeam && isTeamAdmin), // Free users, Pro users, Team owners see plans
+ }
+}
+
+export function getVisiblePlans(
+ subscription: SubscriptionState,
+ userRole: UserRole
+): ('pro' | 'team' | 'enterprise')[] {
+ const plans: ('pro' | 'team' | 'enterprise')[] = []
+ const { isFree, isPro, isTeam } = subscription
+ const { isTeamAdmin } = userRole
+
+ // Free users see all plans
+ if (isFree) {
+ plans.push('pro', 'team', 'enterprise')
+ }
+ // Pro users see team and enterprise
+ else if (isPro && !isTeam) {
+ plans.push('team', 'enterprise')
+ }
+ // Team owners see only enterprise (no team plan since they already have it)
+ else if (isTeam && isTeamAdmin) {
+ plans.push('enterprise')
+ }
+ // Team members, Enterprise users see no plans
+
+ return plans
+}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/subscription.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/subscription.tsx
index 3c47e7f55c..51a34cc9bb 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/subscription.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/subscription.tsx
@@ -1,41 +1,188 @@
-import { useCallback, useEffect, useState } from 'react'
-import { AlertCircle, Users } from 'lucide-react'
-import {
- Alert,
- AlertDescription,
- AlertTitle,
- Button,
- Card,
- CardContent,
- CardHeader,
- CardTitle,
- Skeleton,
-} from '@/components/ui'
+'use client'
+
+import { useCallback, useEffect, useRef, useState } from 'react'
+import { Badge, Progress, Skeleton } from '@/components/ui'
import { useSession, useSubscription } from '@/lib/auth-client'
-import { DEFAULT_FREE_CREDITS } from '@/lib/billing/constants'
import { createLogger } from '@/lib/logs/console/logger'
+import { cn } from '@/lib/utils'
import {
- BillingSummary,
CancelSubscription,
- TeamSeatsDialog,
- UsageLimitEditor,
+ PlanCard,
+ UsageLimit,
+ type UsageLimitRef,
} from '@/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components'
+import {
+ ENTERPRISE_PLAN_FEATURES,
+ PRO_PLAN_FEATURES,
+ TEAM_PLAN_FEATURES,
+} from '@/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/plan-configs'
+import {
+ getSubscriptionPermissions,
+ getVisiblePlans,
+} from '@/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/subscription-permissions'
import { useOrganizationStore } from '@/stores/organization'
import { useSubscriptionStore } from '@/stores/subscription/store'
+// Logger
const logger = createLogger('Subscription')
+// Constants
+const CONSTANTS = {
+ UPGRADE_ERROR_TIMEOUT: 3000, // 3 seconds
+ TYPEFORM_ENTERPRISE_URL: 'https://form.typeform.com/to/jqCO12pF',
+ PRO_PRICE: '$20',
+ TEAM_PRICE: '$40',
+ INITIAL_TEAM_SEATS: 1,
+} as const
+
+// Styles
+const STYLES = {
+ GRADIENT_BADGE:
+ 'gradient-text h-[1.125rem] rounded-[6px] border-gradient-primary/20 bg-gradient-to-b from-gradient-primary via-gradient-secondary to-gradient-primary px-2 py-0 font-medium text-xs cursor-pointer',
+} as const
+
+// Types
+type TargetPlan = 'pro' | 'team'
+
interface SubscriptionProps {
onOpenChange: (open: boolean) => void
}
+/**
+ * Skeleton component for subscription loading state
+ */
+function SubscriptionSkeleton() {
+ return (
+
+
+ {/* Current Plan skeleton - matches usage indicator style */}
+
+
+ {/* Plan cards skeleton */}
+
+ {/* Pro and Team skeleton grid */}
+
+ {/* Pro Plan Card Skeleton */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Team Plan Card Skeleton */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Enterprise skeleton - horizontal layout */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+
+// Utility functions
+const formatPlanName = (plan: string): string => plan.charAt(0).toUpperCase() + plan.slice(1)
+
+/**
+ * Subscription management component
+ * Handles plan display, upgrades, and billing management
+ */
export function Subscription({ onOpenChange }: SubscriptionProps) {
const { data: session } = useSession()
const betterAuthSubscription = useSubscription()
const {
isLoading,
- error,
getSubscriptionStatus,
getUsage,
getBillingStatus,
@@ -43,18 +190,13 @@ export function Subscription({ onOpenChange }: SubscriptionProps) {
subscriptionData,
} = useSubscriptionStore()
- const {
- activeOrganization,
- organizationBillingData,
- isLoadingOrgBilling,
- loadOrganizationBillingData,
- getUserRole,
- addSeats,
- } = useOrganizationStore()
+ const { activeOrganization, organizationBillingData, loadOrganizationBillingData, getUserRole } =
+ useOrganizationStore()
- const [isSeatsDialogOpen, setIsSeatsDialogOpen] = useState(false)
- const [isUpdatingSeats, setIsUpdatingSeats] = useState(false)
+ const [upgradeError, setUpgradeError] = useState<'pro' | 'team' | null>(null)
+ const usageLimitRef = useRef(null)
+ // Get real subscription data from store
const subscription = getSubscriptionStatus()
const usage = getUsage()
const billingStatus = getBillingStatus()
@@ -64,19 +206,73 @@ export function Subscription({ onOpenChange }: SubscriptionProps) {
if (subscription.isTeam && activeOrgId) {
loadOrganizationBillingData(activeOrgId)
}
- }, [activeOrgId, subscription.isTeam])
+ }, [activeOrgId, subscription.isTeam, loadOrganizationBillingData])
- // Determine if user is team admin/owner
+ // Auto-clear upgrade error
+ useEffect(() => {
+ if (upgradeError) {
+ const timer = setTimeout(() => {
+ setUpgradeError(null)
+ }, CONSTANTS.UPGRADE_ERROR_TIMEOUT)
+ return () => clearTimeout(timer)
+ }
+ }, [upgradeError])
+
+ // User role and permissions
const userRole = getUserRole(session?.user?.email)
const isTeamAdmin = ['owner', 'admin'].includes(userRole)
- const shouldShowOrgBilling = subscription.isTeam && isTeamAdmin && organizationBillingData
+
+ // Get permissions based on subscription state and user role
+ const permissions = getSubscriptionPermissions(
+ {
+ isFree: subscription.isFree,
+ isPro: subscription.isPro,
+ isTeam: subscription.isTeam,
+ isEnterprise: subscription.isEnterprise,
+ isPaid: subscription.isPaid,
+ plan: subscription.plan || 'free',
+ status: subscription.status || 'inactive',
+ },
+ {
+ isTeamAdmin,
+ userRole: userRole || 'member',
+ }
+ )
+
+ // Get visible plans based on current subscription
+ const visiblePlans = getVisiblePlans(
+ {
+ isFree: subscription.isFree,
+ isPro: subscription.isPro,
+ isTeam: subscription.isTeam,
+ isEnterprise: subscription.isEnterprise,
+ isPaid: subscription.isPaid,
+ plan: subscription.plan || 'free',
+ status: subscription.status || 'inactive',
+ },
+ {
+ isTeamAdmin,
+ userRole: userRole || 'member',
+ }
+ )
+
+ // UI state computed values
+ const showBadge = permissions.canEditUsageLimit && !permissions.showTeamMemberView
+ const badgeText = subscription.isFree ? 'Upgrade' : 'Add'
+
+ const handleBadgeClick = () => {
+ if (subscription.isFree) {
+ handleUpgrade('pro')
+ } else if (permissions.canEditUsageLimit && usageLimitRef.current) {
+ usageLimitRef.current.startEdit()
+ }
+ }
const handleUpgrade = useCallback(
- async (targetPlan: 'pro' | 'team') => {
+ async (targetPlan: TargetPlan) => {
if (!session?.user?.id) return
- // Get current subscription data including stripeSubscriptionId
- const subscriptionData = useSubscriptionStore.getState().subscriptionData
+ const { subscriptionData } = useSubscriptionStore.getState()
const currentSubscriptionId = subscriptionData?.stripeSubscriptionId
let referenceId = session.user.id
@@ -84,33 +280,32 @@ export function Subscription({ onOpenChange }: SubscriptionProps) {
referenceId = activeOrgId
}
- const currentUrl = window.location.origin + window.location.pathname
+ const currentUrl = `${window.location.origin}${window.location.pathname}`
try {
- const upgradeParams: any = {
+ const upgradeParams = {
plan: targetPlan,
referenceId,
successUrl: currentUrl,
cancelUrl: currentUrl,
- seats: targetPlan === 'team' ? 1 : undefined,
- }
+ ...(targetPlan === 'team' && { seats: CONSTANTS.INITIAL_TEAM_SEATS }),
+ } as const
- // Add subscriptionId if we have an existing subscription to ensure proper plan switching
- if (currentSubscriptionId) {
- upgradeParams.subscriptionId = currentSubscriptionId
- logger.info('Upgrading existing subscription', {
+ // Add subscriptionId for existing subscriptions to ensure proper plan switching
+ const finalParams = currentSubscriptionId
+ ? { ...upgradeParams, subscriptionId: currentSubscriptionId }
+ : upgradeParams
+
+ logger.info(
+ currentSubscriptionId ? 'Upgrading existing subscription' : 'Creating new subscription',
+ {
targetPlan,
currentSubscriptionId,
referenceId,
- })
- } else {
- logger.info('Creating new subscription (no existing subscription found)', {
- targetPlan,
- referenceId,
- })
- }
+ }
+ )
- await betterAuthSubscription.upgrade(upgradeParams)
+ await betterAuthSubscription.upgrade(finalParams)
} catch (error) {
logger.error('Failed to initiate subscription upgrade:', error)
alert('Failed to initiate upgrade. Please try again or contact support.')
@@ -119,310 +314,213 @@ export function Subscription({ onOpenChange }: SubscriptionProps) {
[session?.user?.id, subscription.isTeam, activeOrgId, betterAuthSubscription]
)
- const handleSeatsUpdate = useCallback(
- async (seats: number) => {
- if (!activeOrgId) {
- logger.error('No active organization found for seat update')
- return
- }
+ const renderPlanCard = useCallback(
+ (planType: 'pro' | 'team' | 'enterprise', layout: 'vertical' | 'horizontal' = 'vertical') => {
+ const handleContactEnterprise = () => window.open(CONSTANTS.TYPEFORM_ENTERPRISE_URL, '_blank')
- try {
- setIsUpdatingSeats(true)
- await addSeats(seats)
- setIsSeatsDialogOpen(false)
- } catch (error) {
- logger.error('Failed to update seats:', error)
- } finally {
- setIsUpdatingSeats(false)
+ switch (planType) {
+ case 'pro':
+ return (
+ handleUpgrade('pro')}
+ isError={upgradeError === 'pro'}
+ layout={layout}
+ />
+ )
+
+ case 'team':
+ return (
+ handleUpgrade('team')}
+ isError={upgradeError === 'team'}
+ layout={layout}
+ />
+ )
+
+ case 'enterprise':
+ return (
+ Custom}
+ priceSubtext={
+ layout === 'horizontal'
+ ? 'Custom solutions tailored to your enterprise needs'
+ : undefined
+ }
+ features={ENTERPRISE_PLAN_FEATURES}
+ buttonText='Contact'
+ onButtonClick={handleContactEnterprise}
+ layout={layout}
+ />
+ )
+
+ default:
+ return null
}
},
- [activeOrgId]
+ [subscription.isFree, upgradeError, handleUpgrade]
)
if (isLoading) {
- return (
-
-
-
-
-
- )
- }
-
- if (error) {
- return (
-
-
-
- Error
- {error}
-
-
- )
+ return
}
return (
-
-
- {/* Current Plan & Usage Overview */}
-
-
-
Current Plan
-
-
- {subscription.plan} Plan
-
- {!subscription.isFree && }
-
-
+
+
+ {/* Current Plan & Usage Overview - Styled like usage-indicator */}
+
+
+
+ {/* Plan and usage info */}
+
+
+
+ {formatPlanName(subscription.plan)}
+
+ {showBadge && (
+ {
+ e.stopPropagation()
+ handleBadgeClick()
+ }}
+ >
+ {badgeText}
+
+ )}
+ {/* Team seats info for admins */}
+ {permissions.canManageTeam && (
+
+ ({organizationBillingData?.totalSeats || subscription.seats || 1} seats)
+
+ )}
+
+
+ ${usage.current.toFixed(2)}
+ /
+ {!subscription.isFree &&
+ (permissions.canEditUsageLimit ||
+ permissions.showTeamMemberView ||
+ subscription.isEnterprise) ? (
+
+ ) : (
+ ${usage.limit}
+ )}
+
+
-
-
- ${usage.current.toFixed(2)} / ${usage.limit}
-
-
-
- {usage.percentUsed}% used this period
-
+ {/* Progress Bar */}
+
- {/* Usage Alerts */}
- {billingStatus === 'exceeded' && (
-
-
- Usage Limit Exceeded
-
- You've exceeded your usage limit of ${usage.limit}. Please upgrade your plan or
- increase your limit.
-
-
- )}
-
- {billingStatus === 'warning' && (
-
-
- Approaching Usage Limit
-
- You've used {usage.percentUsed}% of your ${usage.limit} limit. Consider upgrading or
- increasing your limit.
-
-
- )}
-
- {/* Usage Limit Editor */}
-
-
-
- {subscription.isTeam ? 'Individual Limit' : 'Monthly Limit'}
-
- {isLoadingOrgBilling ? (
-
- ) : (
-
- )}
-
- {subscription.isFree && (
-
- Upgrade to Pro ($20 minimum) or Team ($40 minimum) to customize your usage limit.
+ {/* Team Member Notice */}
+ {permissions.showTeamMemberView && (
+
+
+ Contact your team admin to increase limits
- )}
- {subscription.isPro && (
-
- Pro plan minimum: $20. You can set your individual limit higher.
-
- )}
- {subscription.isTeam && !isTeamAdmin && (
-
- Contact your team owner to adjust your limit. Team plan minimum: $40.
-
- )}
- {subscription.isTeam && isTeamAdmin && (
-
- Team plan minimum: $40 per member. Manage team member limits in the Team tab.
-
- )}
-
-
- {/* Team Management */}
- {subscription.isTeam && (
-
- {isLoadingOrgBilling ? (
-
-
-
-
-
-
-
-
-
- ) : shouldShowOrgBilling ? (
-
-
-
-
-
- Team Plan
-
-
-
-
- {/* Team Summary */}
-
-
- Licensed Seats
-
- {organizationBillingData.totalSeats} seats
-
-
-
- Monthly Bill
-
- ${organizationBillingData.totalSeats * 40}
-
-
-
- Current Usage
-
- ${organizationBillingData.totalCurrentUsage?.toFixed(2) || 0}
-
-
-
-
- {/* Simple Explanation */}
-
-
- You pay ${organizationBillingData.totalSeats * 40}/month for{' '}
- {organizationBillingData.totalSeats} licensed seats, regardless of usage. If
- your team uses more than ${organizationBillingData.totalSeats * 40}, you'll be
- charged for the overage.
-
-
-
-
- ) : (
-
-
-
-
- Team Plan
-
-
-
-
-
- Your monthly allowance
- ${usage.limit}
-
-
- Contact your team owner to adjust your limit
-
-
-
-
- )}
)}
- {/* Upgrade Actions */}
- {subscription.isFree && (
-
-
handleUpgrade('pro')} className='w-full'>
- Upgrade to Pro - $20/month
-
-
handleUpgrade('team')} variant='outline' className='w-full'>
- Upgrade to Team - $40/seat/month
-
-
-
- Need a custom plan?{' '}
-
- Contact us
- {' '}
- for Enterprise pricing
-
-
-
- )}
+ {/* Upgrade Plans */}
+ {permissions.showUpgradePlans && (
+
+ {/* Render plans based on what should be visible */}
+ {(() => {
+ const totalPlans = visiblePlans.length
+ const hasEnterprise = visiblePlans.includes('enterprise')
- {subscription.isPro && !subscription.isTeam && (
-
handleUpgrade('team')} className='w-full'>
- Upgrade to Team - $40/seat/month
-
+ // Special handling for Pro users - show team and enterprise side by side
+ if (subscription.isPro && totalPlans === 2) {
+ return (
+
+ {visiblePlans.map((plan) => renderPlanCard(plan, 'vertical'))}
+
+ )
+ }
+
+ // Default behavior for other users
+ const otherPlans = visiblePlans.filter((p) => p !== 'enterprise')
+
+ // Layout logic:
+ // Free users (3 plans): Pro and Team vertical in grid, Enterprise horizontal below
+ // Team admins (1 plan): Enterprise horizontal
+ const enterpriseLayout =
+ totalPlans === 1 || totalPlans === 3 ? 'horizontal' : 'vertical'
+
+ return (
+ <>
+ {otherPlans.length > 0 && (
+
+ {otherPlans.map((plan) => renderPlanCard(plan, 'vertical'))}
+
+ )}
+
+ {/* Enterprise plan */}
+ {hasEnterprise && renderPlanCard('enterprise', enterpriseLayout)}
+ >
+ )
+ })()}
+
)}
{subscription.isEnterprise && (
-
-
- Enterprise plan - Contact support for changes
+
+
+ Contact enterprise for support usage limit changes
)}
{/* Cancel Subscription */}
-
-
- {/* Team Seats Dialog */}
-
+ {permissions.canCancelSubscription && (
+
+
+
+ )}
)
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/index.ts
index 3a6d760567..91e4106026 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/index.ts
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/index.ts
@@ -1,8 +1,11 @@
export { MemberInvitationCard } from './member-invitation-card'
+export { MemberLimit } from './member-limit'
export { NoOrganizationView } from './no-organization-view'
export { OrganizationCreationDialog } from './organization-creation-dialog'
export { OrganizationSettingsTab } from './organization-settings-tab'
export { PendingInvitationsList } from './pending-invitations-list'
export { RemoveMemberDialog } from './remove-member-dialog'
export { TeamMembersList } from './team-members-list'
+export { TeamSeats } from './team-seats'
export { TeamSeatsOverview } from './team-seats-overview'
+export { TeamUsage } from './team-usage'
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/member-invitation-card/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/member-invitation-card/index.ts
new file mode 100644
index 0000000000..2a0bb699f6
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/member-invitation-card/index.ts
@@ -0,0 +1 @@
+export { MemberInvitationCard } from './member-invitation-card'
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/member-invitation-card.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/member-invitation-card/member-invitation-card.tsx
similarity index 88%
rename from apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/member-invitation-card.tsx
rename to apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/member-invitation-card/member-invitation-card.tsx
index 3ecf49d7ca..a30e59dddd 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/member-invitation-card.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/member-invitation-card/member-invitation-card.tsx
@@ -98,14 +98,14 @@ export function MemberInvitationCard({
const selectedCount = selectedWorkspaces.length
return (
-
-
- Invite Team Members
+
+
+ Invite Team Members
Add new members to your team and optionally give them access to specific workspaces
-
+
{showWorkspaceInvite ? 'Hide' : 'Add'} Workspaces
{selectedCount > 0 && (
-
+
{selectedCount}
)}
@@ -142,7 +145,7 @@ export function MemberInvitationCard({
size='sm'
onClick={onInviteMember}
disabled={!inviteEmail || isInviting}
- className='shrink-0 gap-2'
+ className='h-9 shrink-0 gap-2 rounded-[8px]'
>
{isInviting ? : }
Invite
@@ -153,8 +156,8 @@ export function MemberInvitationCard({
-
Workspace Access
-
+ Workspace Access
+
Optional
@@ -174,7 +177,7 @@ export function MemberInvitationCard({
) : (
-
+
{userWorkspaces.map((workspace) => {
const isSelected = selectedWorkspaces.some((w) => w.workspaceId === workspace.id)
const selectedWorkspace = selectedWorkspaces.find(
@@ -185,13 +188,13 @@ export function MemberInvitationCard({
-
+
{workspace.name}
{workspace.isOwner && (
-
+
Owner
)}
@@ -242,7 +248,7 @@ export function MemberInvitationCard({
)}
{inviteSuccess && (
-
+
Invitation sent successfully
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/member-limit/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/member-limit/index.ts
new file mode 100644
index 0000000000..f09d182a32
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/member-limit/index.ts
@@ -0,0 +1 @@
+export { MemberLimit } from './member-limit'
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/edit-member-limit-dialog.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/member-limit/member-limit.tsx
similarity index 98%
rename from apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/edit-member-limit-dialog.tsx
rename to apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/member-limit/member-limit.tsx
index 19470105e5..d78038fc67 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/edit-member-limit-dialog.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/member-limit/member-limit.tsx
@@ -14,7 +14,7 @@ import {
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
-interface EditMemberLimitDialogProps {
+interface MemberLimitProps {
open: boolean
onOpenChange: (open: boolean) => void
member: {
@@ -30,14 +30,14 @@ interface EditMemberLimitDialogProps {
planType?: string
}
-export function EditMemberLimitDialog({
+export function MemberLimit({
open,
onOpenChange,
member,
onSave,
isLoading,
planType = 'team',
-}: EditMemberLimitDialogProps) {
+}: MemberLimitProps) {
const [limitValue, setLimitValue] = useState('')
const [error, setError] = useState(null)
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/no-organization-view/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/no-organization-view/index.ts
new file mode 100644
index 0000000000..2d540c4f7e
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/no-organization-view/index.ts
@@ -0,0 +1 @@
+export { NoOrganizationView } from './no-organization-view'
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/no-organization-view.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/no-organization-view/no-organization-view.tsx
similarity index 85%
rename from apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/no-organization-view.tsx
rename to apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/no-organization-view/no-organization-view.tsx
index 6ec678b5ad..614b25b63a 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/no-organization-view.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/no-organization-view/no-organization-view.tsx
@@ -2,7 +2,7 @@ import { RefreshCw } from 'lucide-react'
import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
-import { OrganizationCreationDialog } from './'
+import { OrganizationCreationDialog } from '../organization-creation-dialog'
interface NoOrganizationViewProps {
hasTeamPlan: boolean
@@ -35,11 +35,11 @@ export function NoOrganizationView({
}: NoOrganizationViewProps) {
if (hasTeamPlan || hasEnterprisePlan) {
return (
-
+
-
Create Your Team Workspace
+
Create Your Team Workspace
-
+
You're subscribed to a {hasEnterprisePlan ? 'enterprise' : 'team'} plan. Create your
workspace to start collaborating with your team.
@@ -47,7 +47,7 @@ export function NoOrganizationView({
-
+
Team Name
-
+
Team URL
-
+
sim.ai/team/
{error && (
-
+
Error
{error}
@@ -87,6 +87,7 @@ export function NoOrganizationView({
{isCreatingOrg && }
Create Team Workspace
@@ -111,9 +112,9 @@ export function NoOrganizationView({
}
return (
-
+
-
No Team Workspace
+
No Team Workspace
You don't have a team workspace yet. To collaborate with others, first upgrade to a team
or enterprise plan.
@@ -127,6 +128,7 @@ export function NoOrganizationView({
})
window.dispatchEvent(event)
}}
+ className='h-9 rounded-[8px]'
>
Upgrade to Team Plan
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/organization-creation-dialog/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/organization-creation-dialog/index.ts
new file mode 100644
index 0000000000..6377972fa4
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/organization-creation-dialog/index.ts
@@ -0,0 +1 @@
+export { OrganizationCreationDialog } from './organization-creation-dialog'
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/organization-creation-dialog.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/organization-creation-dialog/organization-creation-dialog.tsx
similarity index 82%
rename from apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/organization-creation-dialog.tsx
rename to apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/organization-creation-dialog/organization-creation-dialog.tsx
index c444976c5e..976f252dcf 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/organization-creation-dialog.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/organization-creation-dialog/organization-creation-dialog.tsx
@@ -46,18 +46,18 @@ export function OrganizationCreationDialog({
-
+
Team Name
-
+
Team URL
-
+
sim.ai/team/
{error && (
-
+
Error
{error}
)}
- onOpenChange(false)} disabled={isCreating}>
+ onOpenChange(false)}
+ disabled={isCreating}
+ className='h-9 rounded-[8px]'
+ >
Cancel
-
+
{isCreating && }
Create Team Workspace
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/organization-settings-tab/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/organization-settings-tab/index.ts
new file mode 100644
index 0000000000..5c4f4fb5e5
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/organization-settings-tab/index.ts
@@ -0,0 +1 @@
+export { OrganizationSettingsTab } from './organization-settings-tab'
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/organization-settings-tab.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/organization-settings-tab/organization-settings-tab.tsx
similarity index 87%
rename from apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/organization-settings-tab.tsx
rename to apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/organization-settings-tab/organization-settings-tab.tsx
index d1f519d411..46d9507753 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/organization-settings-tab.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/organization-settings-tab/organization-settings-tab.tsx
@@ -28,23 +28,23 @@ export function OrganizationSettingsTab({
orgSettingsSuccess,
}: OrganizationSettingsTabProps) {
return (
-
+
{orgSettingsError && (
-
+
Error
{orgSettingsError}
)}
{orgSettingsSuccess && (
-
+
Success
{orgSettingsSuccess}
)}
{!isAdminOrOwner && (
-
+
Read Only
You need owner or admin permissions to modify team settings.
@@ -52,12 +52,12 @@ export function OrganizationSettingsTab({
)}
-
-
- Basic Information
+
+
+ Basic Information
Update your team's basic information and branding
-
+
Team Name
-
-
- Team Information
+
+
+ Team Information
-
+
Team ID:
{organization.id}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/pending-invitations-list/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/pending-invitations-list/index.ts
new file mode 100644
index 0000000000..213a663f83
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/pending-invitations-list/index.ts
@@ -0,0 +1 @@
+export { PendingInvitationsList } from './pending-invitations-list'
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/pending-invitations-list.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/pending-invitations-list/pending-invitations-list.tsx
similarity index 73%
rename from apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/pending-invitations-list.tsx
rename to apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/pending-invitations-list/pending-invitations-list.tsx
index 2ce65353ea..f183cd22dd 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/pending-invitations-list.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/pending-invitations-list/pending-invitations-list.tsx
@@ -20,8 +20,8 @@ export function PendingInvitationsList({
}
return (
-
-
Pending Invitations
+
+
Pending Invitations
{pendingInvitations.map((invitation: Invitation) => (
@@ -31,13 +31,18 @@ export function PendingInvitationsList({
{invitation.email.charAt(0).toUpperCase()}
-
{invitation.email}
-
Invitation pending
+
{invitation.email}
+
Invitation pending
-
onCancelInvitation(invitation.id)}>
+ onCancelInvitation(invitation.id)}
+ className='h-8 w-8 rounded-[8px] p-0'
+ >
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/remove-member-dialog/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/remove-member-dialog/index.ts
new file mode 100644
index 0000000000..2eed30381d
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/remove-member-dialog/index.ts
@@ -0,0 +1 @@
+export { RemoveMemberDialog } from './remove-member-dialog'
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/remove-member-dialog.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/remove-member-dialog/remove-member-dialog.tsx
similarity index 82%
rename from apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/remove-member-dialog.tsx
rename to apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/remove-member-dialog/remove-member-dialog.tsx
index b139ef7101..7abddd8fcb 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/remove-member-dialog.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/remove-member-dialog/remove-member-dialog.tsx
@@ -42,11 +42,11 @@ export function RemoveMemberDialog({
onShouldReduceSeatsChange(e.target.checked)}
/>
-
+
Also reduce seat count in my subscription
@@ -56,10 +56,14 @@ export function RemoveMemberDialog({
-
+
Cancel
- onConfirmRemove(shouldReduceSeats)}>
+ onConfirmRemove(shouldReduceSeats)}
+ className='h-9 rounded-[8px]'
+ >
Remove
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-members-list/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-members-list/index.ts
new file mode 100644
index 0000000000..8b0fb52066
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-members-list/index.ts
@@ -0,0 +1 @@
+export { TeamMembersList } from './team-members-list'
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-members-list.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-members-list/team-members-list.tsx
similarity index 70%
rename from apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-members-list.tsx
rename to apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-members-list/team-members-list.tsx
index 5082b570c2..7c79e6eb8b 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-members-list.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-members-list/team-members-list.tsx
@@ -17,8 +17,8 @@ export function TeamMembersList({
}: TeamMembersListProps) {
if (!organization.members || organization.members.length === 0) {
return (
-
-
Team Members
+
+
Team Members
No members in this organization yet.
@@ -27,8 +27,8 @@ export function TeamMembersList({
}
return (
-
-
Team Members
+
+
Team Members
{organization.members.map((member: Member) => (
@@ -38,10 +38,10 @@ export function TeamMembersList({
{(member.user?.name || member.user?.email || 'U').charAt(0).toUpperCase()}
-
{member.user?.name || 'Unknown'}
-
{member.user?.email}
+
{member.user?.name || 'Unknown'}
+
{member.user?.email}
-
+
{member.role.charAt(0).toUpperCase() + member.role.slice(1)}
@@ -51,7 +51,12 @@ export function TeamMembersList({
{isAdminOrOwner &&
member.role !== 'owner' &&
member.user?.email !== currentUserEmail && (
-
onRemoveMember(member)}>
+ onRemoveMember(member)}
+ className='h-8 w-8 rounded-[8px] p-0'
+ >
)}
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-seats-overview/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-seats-overview/index.ts
new file mode 100644
index 0000000000..c8f65cc4dc
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-seats-overview/index.ts
@@ -0,0 +1 @@
+export { TeamSeatsOverview } from './team-seats-overview'
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-seats-overview.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-seats-overview/team-seats-overview.tsx
similarity index 78%
rename from apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-seats-overview.tsx
rename to apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-seats-overview/team-seats-overview.tsx
index 13713dd209..bea33520e9 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-seats-overview.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-seats-overview/team-seats-overview.tsx
@@ -47,12 +47,12 @@ export function TeamSeatsOverview({
}: TeamSeatsOverviewProps) {
if (isLoadingSubscription) {
return (
-
-
- Team Seats Overview
+
+
+ Team Seats Overview
Manage your team's seat allocation and billing
-
+
@@ -61,18 +61,18 @@ export function TeamSeatsOverview({
if (!subscriptionData) {
return (
-
-
- Team Seats Overview
+
+
+ Team Seats Overview
Manage your team's seat allocation and billing
-
+
-
No Team Subscription Found
+
No Team Subscription Found
Your subscription may need to be transferred to this organization.
@@ -82,6 +82,7 @@ export function TeamSeatsOverview({
onConfirmTeamUpgrade(2) // Start with 2 seats as default
}}
disabled={isLoading}
+ className='h-9 rounded-[8px]'
>
Set Up Team Subscription
@@ -92,24 +93,24 @@ export function TeamSeatsOverview({
}
return (
-
+
Team Seats Overview
Manage your team's seat allocation and billing
-
+
-
{subscriptionData.seats || 0}
+
{subscriptionData.seats || 0}
Licensed Seats
-
{usedSeats}
+
{usedSeats}
Used Seats
-
{(subscriptionData.seats || 0) - usedSeats}
+
{(subscriptionData.seats || 0) - usedSeats}
Available
@@ -121,7 +122,7 @@ export function TeamSeatsOverview({
{usedSeats} of {subscriptionData.seats || 0} seats
-
+
@@ -135,7 +136,7 @@ export function TeamSeatsOverview({
{checkEnterprisePlan(subscriptionData) ? (
-
+
Enterprise Plan
Contact support to modify seats
@@ -146,11 +147,16 @@ export function TeamSeatsOverview({
size='sm'
onClick={onReduceSeats}
disabled={(subscriptionData.seats || 0) <= 1 || isLoading}
- className='flex-1'
+ className='h-9 flex-1 rounded-[8px]'
>
Remove Seat
-
+
Add Seat
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-seats/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-seats/index.ts
new file mode 100644
index 0000000000..5a45773dc2
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-seats/index.ts
@@ -0,0 +1 @@
+export { TeamSeats } from './team-seats'
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/team-seats-dialog.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-seats/team-seats.tsx
similarity index 96%
rename from apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/team-seats-dialog.tsx
rename to apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-seats/team-seats.tsx
index 4cd3ab874e..231944ce63 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/team-seats-dialog.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-seats/team-seats.tsx
@@ -18,7 +18,7 @@ import {
} from '@/components/ui/select'
import { env } from '@/lib/env'
-interface TeamSeatsDialogProps {
+interface TeamSeatsProps {
open: boolean
onOpenChange: (open: boolean) => void
title: string
@@ -31,7 +31,7 @@ interface TeamSeatsDialogProps {
showCostBreakdown?: boolean
}
-export function TeamSeatsDialog({
+export function TeamSeats({
open,
onOpenChange,
title,
@@ -42,7 +42,7 @@ export function TeamSeatsDialog({
onConfirm,
confirmButtonText,
showCostBreakdown = false,
-}: TeamSeatsDialogProps) {
+}: TeamSeatsProps) {
const [selectedSeats, setSelectedSeats] = useState(initialSeats)
useEffect(() => {
@@ -73,7 +73,7 @@ export function TeamSeatsDialog({
value={selectedSeats.toString()}
onValueChange={(value) => setSelectedSeats(Number.parseInt(value))}
>
-
+
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-usage/index.ts b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-usage/index.ts
new file mode 100644
index 0000000000..635cd8a1c4
--- /dev/null
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-usage/index.ts
@@ -0,0 +1 @@
+export { TeamUsage } from './team-usage'
diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/team-usage-overview.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-usage/team-usage.tsx
similarity index 98%
rename from apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/team-usage-overview.tsx
rename to apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-usage/team-usage.tsx
index 7aeabf74f3..79514cec8d 100644
--- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/team-usage-overview.tsx
+++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/team-management/components/team-usage/team-usage.tsx
@@ -9,15 +9,15 @@ import { useActiveOrganization } from '@/lib/auth-client'
import { createLogger } from '@/lib/logs/console/logger'
import { useOrganizationStore } from '@/stores/organization'
import type { MemberUsageData } from '@/stores/organization/types'
-import { EditMemberLimitDialog } from './'
+import { MemberLimit } from '../member-limit'
-const logger = createLogger('TeamUsageOverview')
+const logger = createLogger('TeamUsage')
-interface TeamUsageOverviewProps {
+interface TeamUsageProps {
hasAdminAccess: boolean
}
-export function TeamUsageOverview({ hasAdminAccess }: TeamUsageOverviewProps) {
+export function TeamUsage({ hasAdminAccess }: TeamUsageProps) {
const { data: activeOrg } = useActiveOrganization()
const [editDialogOpen, setEditDialogOpen] = useState(false)
const [selectedMember, setSelectedMember] = useState(null)
@@ -335,7 +335,7 @@ export function TeamUsageOverview({ hasAdminAccess }: TeamUsageOverviewProps) {
{/* Edit Member Limit Dialog */}
-
+
@@ -271,14 +269,14 @@ export function TeamManagement() {
}
return (
-
+
-
Team Management
+
Team Management
{organizations.length > 1 && (
setActiveOrganization(e.target.value)}
>
@@ -293,7 +291,7 @@ export function TeamManagement() {
{error && (
-
+
Error
{error}
@@ -351,7 +349,7 @@ export function TeamManagement() {
-
+
@@ -373,10 +371,10 @@ export function TeamManagement() {
open={removeMemberDialog.open}
memberName={removeMemberDialog.memberName}
shouldReduceSeats={removeMemberDialog.shouldReduceSeats}
- onOpenChange={(open) => {
+ onOpenChange={(open: boolean) => {
if (!open) setRemoveMemberDialog({ ...removeMemberDialog, open: false })
}}
- onShouldReduceSeatsChange={(shouldReduce) =>
+ onShouldReduceSeatsChange={(shouldReduce: boolean) =>
setRemoveMemberDialog({
...removeMemberDialog,
shouldReduceSeats: shouldReduce,
@@ -393,11 +391,11 @@ export function TeamManagement() {
}
/>
- state.loadSettings)
const { activeOrganization } = useOrganizationStore()
const hasLoadedInitialData = useRef(false)
+ const environmentCloseHandler = useRef<((open: boolean) => void) | null>(null)
useEffect(() => {
async function loadAllSettings() {
@@ -96,27 +96,25 @@ export function SettingsModal({ open, onOpenChange }: SettingsModalProps) {
const isSubscriptionEnabled = isBillingEnabled
+ // Handle dialog close - delegate to environment component if it's active
+ const handleDialogOpenChange = (newOpen: boolean) => {
+ if (!newOpen && activeSection === 'environment' && environmentCloseHandler.current) {
+ environmentCloseHandler.current(newOpen)
+ } else {
+ onOpenChange(newOpen)
+ }
+ }
+
return (
-