From fb3d6d4c8812a0c639ba309d83f5c48584eee55c Mon Sep 17 00:00:00 2001 From: Waleed Date: Sun, 26 Oct 2025 21:36:25 -0700 Subject: [PATCH] feat(files): added usage indicator for file storage to settings (#1736) * feat(files): added usage indicator for file storage to settings * cleanup --- .../app/api/users/me/usage-limits/route.ts | 10 ++- .../components/file-uploads/file-uploads.tsx | 88 +++++++++++++++++-- 2 files changed, 92 insertions(+), 6 deletions(-) diff --git a/apps/sim/app/api/users/me/usage-limits/route.ts b/apps/sim/app/api/users/me/usage-limits/route.ts index df8804fee..2f464863e 100644 --- a/apps/sim/app/api/users/me/usage-limits/route.ts +++ b/apps/sim/app/api/users/me/usage-limits/route.ts @@ -3,6 +3,7 @@ import { checkHybridAuth } from '@/lib/auth/hybrid' import { checkServerSideUsageLimits } from '@/lib/billing' import { getHighestPrioritySubscription } from '@/lib/billing/core/subscription' import { getEffectiveCurrentPeriodCost } from '@/lib/billing/core/usage' +import { getUserStorageLimit, getUserStorageUsage } from '@/lib/billing/storage' import { createLogger } from '@/lib/logs/console/logger' import { createErrorResponse } from '@/app/api/workflows/utils' import { RateLimiter } from '@/services/queue' @@ -37,9 +38,11 @@ export async function GET(request: NextRequest) { ]) // Usage summary (current period cost + limit + plan) - const [usageCheck, effectiveCost] = await Promise.all([ + const [usageCheck, effectiveCost, storageUsage, storageLimit] = await Promise.all([ checkServerSideUsageLimits(authenticatedUserId), getEffectiveCurrentPeriodCost(authenticatedUserId), + getUserStorageUsage(authenticatedUserId), + getUserStorageLimit(authenticatedUserId), ]) const currentPeriodCost = effectiveCost @@ -66,6 +69,11 @@ export async function GET(request: NextRequest) { limit: usageCheck.limit, plan: userSubscription?.plan || 'free', }, + storage: { + usedBytes: storageUsage, + limitBytes: storageLimit, + percentUsed: storageLimit > 0 ? (storageUsage / storageLimit) * 100 : 0, + }, }) } catch (error: any) { logger.error('Error checking usage limits:', error) diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/file-uploads/file-uploads.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/file-uploads/file-uploads.tsx index 06518e0d0..d0dd9e257 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/file-uploads/file-uploads.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/file-uploads/file-uploads.tsx @@ -3,7 +3,7 @@ import { useEffect, useMemo, useRef, useState } from 'react' import { Download, Search, Trash2 } from 'lucide-react' import { useParams } from 'next/navigation' -import { Input } from '@/components/ui' +import { Input, Progress, Skeleton } from '@/components/ui' import { Button } from '@/components/ui/button' import { Table, @@ -16,6 +16,7 @@ import { import { createLogger } from '@/lib/logs/console/logger' import { getFileExtension } from '@/lib/uploads/file-utils' import type { WorkspaceFileRecord } from '@/lib/uploads/workspace-files' +import { cn } from '@/lib/utils' import { getDocumentIcon } from '@/app/workspace/[workspaceId]/knowledge/components' import { useUserPermissions } from '@/hooks/use-user-permissions' import { useWorkspacePermissions } from '@/hooks/use-workspace-permissions' @@ -38,6 +39,17 @@ const SUPPORTED_EXTENSIONS = [ ] as const const ACCEPT_ATTR = '.pdf,.csv,.doc,.docx,.txt,.md,.xlsx,.xls,.html,.htm,.pptx,.ppt' +interface StorageInfo { + usedBytes: number + limitBytes: number + percentUsed: number +} + +interface UsageData { + plan: string + storage: StorageInfo +} + export function FileUploads() { const params = useParams() const workspaceId = params?.workspaceId as string @@ -48,6 +60,9 @@ export function FileUploads() { const [uploadError, setUploadError] = useState(null) const [uploadProgress, setUploadProgress] = useState({ completed: 0, total: 0 }) const fileInputRef = useRef(null) + const [storageInfo, setStorageInfo] = useState(null) + const [planName, setPlanName] = useState('free') + const [storageLoading, setStorageLoading] = useState(true) const { permissions: workspacePermissions, loading: permissionsLoading } = useWorkspacePermissions(workspaceId) @@ -71,8 +86,28 @@ export function FileUploads() { } } + const loadStorageInfo = async () => { + try { + setStorageLoading(true) + const response = await fetch('/api/users/me/usage-limits') + const data = await response.json() + + if (data.success && data.storage) { + setStorageInfo(data.storage) + if (data.usage?.plan) { + setPlanName(data.usage.plan) + } + } + } catch (error) { + logger.error('Error loading storage info:', error) + } finally { + setStorageLoading(false) + } + } + useEffect(() => { void loadFiles() + void loadStorageInfo() }, [workspaceId]) const handleUploadClick = () => { @@ -123,6 +158,7 @@ export function FileUploads() { } await loadFiles() + await loadStorageInfo() if (unsupported.length) { lastError = `Unsupported file type: ${unsupported.join(', ')}` } @@ -171,6 +207,7 @@ export function FileUploads() { if (data.success) { await loadFiles() + await loadStorageInfo() } } catch (error) { logger.error('Error deleting file:', error) @@ -206,6 +243,25 @@ export function FileUploads() { return `${text.slice(0, start)}...${text.slice(-end)}` } + const formatStorageSize = (bytes: number): string => { + if (bytes < 1024) return `${bytes} B` + if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB` + if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(1)} MB` + return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB` + } + + const PLAN_NAMES = { + enterprise: 'Enterprise', + team: 'Team', + pro: 'Pro', + free: 'Free', + } as const + + const displayPlanName = PLAN_NAMES[planName as keyof typeof PLAN_NAMES] || 'Free' + + const GRADIENT_TEXT_STYLES = + 'gradient-text bg-gradient-to-b from-gradient-primary via-gradient-secondary to-gradient-primary' + return (
{/* Header: search left, file count + Upload right */} @@ -220,9 +276,31 @@ export function FileUploads() { />
-
- {files.length} {files.length === 1 ? 'file' : 'files'} -
+ {storageLoading ? ( + + ) : storageInfo ? ( +
+
+ + {displayPlanName} + + + {formatStorageSize(storageInfo.usedBytes)} /{' '} + {formatStorageSize(storageInfo.limitBytes)} + +
+ +
+ ) : null} {userPermissions.canEdit && (
- + Name Size Uploaded