diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components-new/usage-indicator/usage-indicator.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components-new/usage-indicator/usage-indicator.tsx index c0f824df67..2ed700f9d6 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components-new/usage-indicator/usage-indicator.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components-new/usage-indicator/usage-indicator.tsx @@ -1,22 +1,32 @@ 'use client' -import { useEffect } from 'react' -import { Badge, Progress, Skeleton } from '@/components/ui' +import { useEffect, useMemo } from 'react' +import { Button } from '@/components/emcn' +import { Skeleton } from '@/components/ui' import { createLogger } from '@/lib/logs/console/logger' -import { cn } from '@/lib/utils' +import { MIN_SIDEBAR_WIDTH, useSidebarStore } from '@/stores/sidebar/store' import { useSubscriptionStore } from '@/stores/subscription/store' -// Constants for reusable styles -const GRADIENT_BADGE_STYLES = - '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' -const GRADIENT_TEXT_STYLES = - 'gradient-text bg-gradient-to-b from-gradient-primary via-gradient-secondary to-gradient-primary' -const CONTAINER_STYLES = - 'pointer-events-auto flex-shrink-0 rounded-[10px] border bg-background px-3 py-2.5 shadow-xs cursor-pointer transition-colors hover:bg-muted/50' - const logger = createLogger('UsageIndicator') -// Plan name mapping +/** + * Minimum number of pills to display (at minimum sidebar width) + */ +const MIN_PILL_COUNT = 6 + +/** + * Maximum number of pills to display + */ +const MAX_PILL_COUNT = 8 + +/** + * Width increase (in pixels) required to add one additional pill + */ +const WIDTH_PER_PILL = 50 + +/** + * Plan name mapping + */ const PLAN_NAMES = { enterprise: 'Enterprise', team: 'Team', @@ -30,26 +40,40 @@ interface UsageIndicatorProps { export function UsageIndicator({ onClick }: UsageIndicatorProps) { const { getUsage, getSubscriptionStatus, isLoading } = useSubscriptionStore() + const sidebarWidth = useSidebarStore((state) => state.sidebarWidth) useEffect(() => { useSubscriptionStore.getState().loadData() }, []) + /** + * Calculate pill count based on sidebar width + * Starts at MIN_PILL_COUNT at minimum width, adds 1 pill per WIDTH_PER_PILL increase + */ + const pillCount = useMemo(() => { + const widthDelta = sidebarWidth - MIN_SIDEBAR_WIDTH + const additionalPills = Math.floor(widthDelta / WIDTH_PER_PILL) + const calculatedCount = MIN_PILL_COUNT + additionalPills + return Math.max(MIN_PILL_COUNT, Math.min(MAX_PILL_COUNT, calculatedCount)) + }, [sidebarWidth]) + const usage = getUsage() const subscription = getSubscriptionStatus() if (isLoading) { return ( -
onClick?.()}> -
- {/* Plan and usage info skeleton */} -
- - -
+
+ {/* Top row skeleton */} +
+ + +
- {/* Progress Bar skeleton */} - + {/* Pills skeleton */} +
+ {Array.from({ length: pillCount }).map((_, i) => ( + + ))}
) @@ -67,7 +91,13 @@ export function UsageIndicator({ onClick }: UsageIndicatorProps) { const billingStatus = useSubscriptionStore.getState().getBillingStatus() const isBlocked = billingStatus === 'blocked' - const badgeText = isBlocked ? 'Payment Failed' : planType === 'free' ? 'Upgrade' : undefined + const showUpgradeButton = planType === 'free' || isBlocked + + /** + * Calculate which pills should be filled based on usage percentage + */ + const filledPillsCount = Math.ceil((progressPercentage / 100) * pillCount) + const isAlmostOut = filledPillsCount === pillCount const handleClick = () => { try { @@ -91,32 +121,56 @@ export function UsageIndicator({ onClick }: UsageIndicatorProps) { } return ( -
-
- {/* Plan and usage info */} -
-
- - {PLAN_NAMES[planType]} - - {badgeText ? {badgeText} : null} +
+ {/* Top row */} +
+
+ {PLAN_NAMES[planType]} +
+
+ {isBlocked ? ( + <> + Over + limit + + ) : ( + <> + + ${usage.current.toFixed(2)} + + / + + ${usage.limit} + + + )}
- - {isBlocked ? 'Payment required' : `$${usage.current.toFixed(2)} / $${usage.limit}`} -
+ {showUpgradeButton && ( + + )} +
- {/* Progress Bar */} - + {/* Pills row */} +
+ {Array.from({ length: pillCount }).map((_, i) => { + const isFilled = i < filledPillsCount + return ( +
+ ) + })}
) diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar-new.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar-new.tsx index 6c67312cdc..5bfccc3e39 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar-new.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar-new.tsx @@ -33,7 +33,6 @@ const logger = createLogger('SidebarNew') // Feature flag: Billing usage indicator visibility (matches legacy sidebar behavior) const isBillingEnabled = isTruthy(getEnv('NEXT_PUBLIC_BILLING_ENABLED')) -// const isBillingEnabled = true /** * Sidebar component with resizable width that persists across page refreshes. @@ -610,11 +609,7 @@ export function SidebarNew() {
{/* Usage Indicator */} - {isBillingEnabled && ( -
- -
- )} + {isBillingEnabled && } {/* Footer Navigation */}