diff --git a/web/app/components/billing/progress-bar/index.tsx b/web/app/components/billing/progress-bar/index.tsx
index c41fc53310..a4fdff5cd0 100644
--- a/web/app/components/billing/progress-bar/index.tsx
+++ b/web/app/components/billing/progress-bar/index.tsx
@@ -3,12 +3,23 @@ import { cn } from '@/utils/classnames'
type ProgressBarProps = {
percent: number
color: string
+ indeterminate?: boolean
}
const ProgressBar = ({
percent = 0,
color = '#2970FF',
+ indeterminate = false,
}: ProgressBarProps) => {
+ if (indeterminate) {
+ return (
+
+ )
+ }
+
return (
name: string
tooltip?: string
usage: number
@@ -19,6 +19,11 @@ type Props = {
resetHint?: string
resetInDays?: number
hideIcon?: boolean
+ // Props for the 50MB threshold display logic
+ storageThreshold?: number
+ storageTooltip?: string
+ storageTotalDisplay?: string // e.g., "5GB" or "50MB" for formatted display
+ isSandboxPlan?: boolean
}
const WARNING_THRESHOLD = 80
@@ -35,30 +40,150 @@ const UsageInfo: FC
= ({
resetHint,
resetInDays,
hideIcon = false,
+ storageThreshold = 50,
+ storageTooltip,
+ storageTotalDisplay,
+ isSandboxPlan = false,
}) => {
const { t } = useTranslation()
+ // Special display logic for usage below threshold
+ const isBelowThreshold = usage < storageThreshold
+ // Sandbox at full capacity (usage >= threshold and it's sandbox plan)
+ const isSandboxFull = isSandboxPlan && usage >= storageThreshold
+
const percent = usage / total * 100
- const color = percent >= 100
- ? 'bg-components-progress-error-progress'
- : (percent >= WARNING_THRESHOLD ? 'bg-components-progress-warning-progress' : 'bg-components-progress-bar-progress-solid')
+ const getProgressColor = () => {
+ if (percent >= 100)
+ return 'bg-components-progress-error-progress'
+ if (percent >= WARNING_THRESHOLD)
+ return 'bg-components-progress-warning-progress'
+ return 'bg-components-progress-bar-progress-solid'
+ }
+ const color = getProgressColor()
const isUnlimited = total === NUM_INFINITE
let totalDisplay: string | number = isUnlimited ? t('plansCommon.unlimited', { ns: 'billing' }) : total
if (!isUnlimited && unit && unitPosition === 'inline')
totalDisplay = `${total}${unit}`
const showUnit = !!unit && !isUnlimited && unitPosition === 'suffix'
const resetText = resetHint ?? (typeof resetInDays === 'number' ? t('usagePage.resetsIn', { ns: 'billing', count: resetInDays }) : undefined)
- const rightInfo = resetText
- ? (
+
+ const renderRightInfo = () => {
+ if (resetText) {
+ return (
{resetText}
)
- : (showUnit && (
+ }
+ if (showUnit) {
+ return (
{unit}
- ))
+ )
+ }
+ return null
+ }
+
+ // Render usage display
+ const renderUsageDisplay = () => {
+ // Sandbox user at full capacity
+ if (isSandboxFull) {
+ return (
+
+
+ {storageThreshold}
+
+ /
+
+ {storageThreshold}
+ {' '}
+ {unit}
+
+
+ )
+ }
+ // Usage below threshold - show "< 50 MB" or "< 50 / 5GB"
+ if (isBelowThreshold) {
+ const totalText = storageTotalDisplay || totalDisplay
+ return (
+
+
+ <
+ {' '}
+ {storageThreshold}
+
+ {!isSandboxPlan && (
+ <>
+ /
+ {totalText}
+ >
+ )}
+ {isSandboxPlan && {unit}}
+
+ )
+ }
+ // Pro/Team users with usage >= threshold - show actual usage
+ const totalText = storageTotalDisplay || totalDisplay
+ return (
+
+ {usage}
+ /
+ {totalText}
+
+ )
+ }
+
+ // Render progress bar with optional tooltip wrapper
+ const renderProgressBar = () => {
+ const progressBar = (
+
+ )
+
+ if (storageTooltip) {
+ return (
+
+ {storageTooltip}
+
+ )}
+ asChild={false}
+ >
+
{progressBar}
+
+ )
+ }
+
+ return progressBar
+ }
+
+ // Render usage text with optional tooltip wrapper
+ const renderUsageWithTooltip = () => {
+ const usageDisplay = renderUsageDisplay()
+
+ if (storageTooltip) {
+ return (
+
+ {storageTooltip}
+
+ )}
+ asChild={false}
+ >
+ {usageDisplay}
+
+ )
+ }
+
+ return usageDisplay
+ }
return (
@@ -78,17 +203,10 @@ const UsageInfo: FC
= ({
)}
-
- {usage}
-
/
-
{totalDisplay}
-
- {rightInfo}
+ {renderUsageWithTooltip()}
+ {renderRightInfo()}
-
+ {renderProgressBar()}
)
}
diff --git a/web/app/components/billing/usage-info/vector-space-info.tsx b/web/app/components/billing/usage-info/vector-space-info.tsx
index 11e3a6a1ae..a7f3c61f07 100644
--- a/web/app/components/billing/usage-info/vector-space-info.tsx
+++ b/web/app/components/billing/usage-info/vector-space-info.tsx
@@ -6,21 +6,44 @@ import {
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import { useProviderContext } from '@/context/provider-context'
+import { Plan } from '../type'
import UsageInfo from '../usage-info'
type Props = {
className?: string
}
+// Storage threshold in MB - usage below this shows as "< 50 MB"
+const STORAGE_THRESHOLD_MB = 50
+
const VectorSpaceInfo: FC = ({
className,
}) => {
const { t } = useTranslation()
const { plan } = useProviderContext()
const {
+ type,
usage,
total,
} = plan
+
+ // Determine total based on plan type (in MB)
+ const getTotalInMB = () => {
+ switch (type) {
+ case Plan.sandbox:
+ return STORAGE_THRESHOLD_MB // 50 MB
+ case Plan.professional:
+ return 5 * 1024 // 5 GB = 5120 MB
+ case Plan.team:
+ return 20 * 1024 // 20 GB = 20480 MB
+ default:
+ return total.vectorSpace
+ }
+ }
+
+ const totalInMB = getTotalInMB()
+ const isSandbox = type === Plan.sandbox
+
return (
= ({
name={t('usagePage.vectorSpace', { ns: 'billing' })}
tooltip={t('usagePage.vectorSpaceTooltip', { ns: 'billing' }) as string}
usage={usage.vectorSpace}
- total={total.vectorSpace}
+ total={totalInMB}
unit="MB"
unitPosition="inline"
+ storageThreshold={STORAGE_THRESHOLD_MB}
+ storageTooltip={t('usagePage.storageThresholdTooltip', { ns: 'billing' }) as string}
+ storageTotalDisplay={`${totalInMB}MB`}
+ isSandboxPlan={isSandbox}
/>
)
}
diff --git a/web/eslint-suppressions.json b/web/eslint-suppressions.json
index 86b7095f2b..91a506dd51 100644
--- a/web/eslint-suppressions.json
+++ b/web/eslint-suppressions.json
@@ -1691,11 +1691,6 @@
"count": 3
}
},
- "app/components/billing/usage-info/index.tsx": {
- "ts/no-explicit-any": {
- "count": 1
- }
- },
"app/components/custom/custom-web-app-brand/index.spec.tsx": {
"ts/no-explicit-any": {
"count": 7
diff --git a/web/i18n/en-US/billing.json b/web/i18n/en-US/billing.json
index 3242aa8e78..bfd82e1d67 100644
--- a/web/i18n/en-US/billing.json
+++ b/web/i18n/en-US/billing.json
@@ -172,6 +172,7 @@
"usagePage.documentsUploadQuota": "Documents Upload Quota",
"usagePage.perMonth": "per month",
"usagePage.resetsIn": "Resets in {{count,number}} days",
+ "usagePage.storageThresholdTooltip": "Detailed usage is shown once storage exceeds 50 MB.",
"usagePage.teamMembers": "Team Members",
"usagePage.triggerEvents": "Trigger Events",
"usagePage.vectorSpace": "Knowledge Data Storage",
diff --git a/web/i18n/ja-JP/billing.json b/web/i18n/ja-JP/billing.json
index b23ae6c959..650022f955 100644
--- a/web/i18n/ja-JP/billing.json
+++ b/web/i18n/ja-JP/billing.json
@@ -172,6 +172,7 @@
"usagePage.documentsUploadQuota": "ドキュメント・アップロード・クォータ",
"usagePage.perMonth": "月あたり",
"usagePage.resetsIn": "{{count,number}}日後にリセット",
+ "usagePage.storageThresholdTooltip": "ストレージ使用量が 50 MB を超えると、詳細な使用状況が表示されます。",
"usagePage.teamMembers": "チームメンバー",
"usagePage.triggerEvents": "トリガーイベント数",
"usagePage.vectorSpace": "ナレッジベースのデータストレージ",
diff --git a/web/i18n/zh-Hans/billing.json b/web/i18n/zh-Hans/billing.json
index 9111c1a6d1..997887fe6c 100644
--- a/web/i18n/zh-Hans/billing.json
+++ b/web/i18n/zh-Hans/billing.json
@@ -172,6 +172,7 @@
"usagePage.documentsUploadQuota": "文档上传配额",
"usagePage.perMonth": "每月",
"usagePage.resetsIn": "{{count,number}} 天后重置",
+ "usagePage.storageThresholdTooltip": "存储空间超过 50 MB 后,将显示详细使用情况。",
"usagePage.teamMembers": "团队成员",
"usagePage.triggerEvents": "触发器事件数",
"usagePage.vectorSpace": "知识库数据存储空间",