'use client' import type { MeterTone } from '@langgenius/dify-ui/meter' import type { ComponentType, FC, ReactNode } from 'react' import { cn } from '@langgenius/dify-ui/cn' import { MeterIndicator, MeterRoot, MeterTrack } from '@langgenius/dify-ui/meter' import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import * as React from 'react' import { useTranslation } from 'react-i18next' import { Infotip } from '@/app/components/base/infotip' import { NUM_INFINITE } from '../config' type Props = { className?: string Icon: ComponentType<{ className?: string }> name: string tooltip?: string usage: number total: number unit?: string unitPosition?: 'inline' | 'suffix' resetHint?: string resetInDays?: number hideIcon?: boolean // Props for the 50MB threshold display logic storageMode?: boolean storageThreshold?: number storageTooltip?: string isSandboxPlan?: boolean } const UsageInfo: FC = ({ className, Icon, name, tooltip, usage, total, unit, unitPosition = 'suffix', resetHint, resetInDays, hideIcon = false, storageMode = false, storageThreshold = 50, storageTooltip, isSandboxPlan = false, }) => { const { t } = useTranslation() const isBelowThreshold = storageMode && usage < storageThreshold const isSandboxFull = storageMode && isSandboxPlan && usage >= storageThreshold // Single source of truth: sandbox full is visually clamped to 100%; all other // determinate cases show the real percent capped at 100. Tone derives from // this, so we never need a separate tone override. const rawPercent = total > 0 ? (usage / total) * 100 : 0 const effectivePercent = isSandboxFull ? 100 : Math.min(rawPercent, 100) const tone: MeterTone = effectivePercent >= 100 ? 'error' : effectivePercent >= 80 ? 'warning' : 'neutral' 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: ReactNode = resetText ? (
{resetText}
) : showUnit ? (
{unit}
) : null const usageDisplay: ReactNode = (() => { if (storageMode) { if (isSandboxFull) { return (
{storageThreshold} / {storageThreshold} {' '} {unit}
) } if (isBelowThreshold) { return (
< {' '} {storageThreshold} {!isSandboxPlan && ( <> / {totalDisplay} )} {isSandboxPlan && {unit}}
) } return (
{usage} / {totalDisplay}
) } return (
{usage} / {totalDisplay}
) })() const bar: ReactNode = isBelowThreshold ? ( // Decorative "< N MB" placeholder — not a meter, not a progressbar. } /> {storageTooltip} ) } return children } return (
{!hideIcon && Icon && ( )}
{name}
{tooltip && ( {tooltip} )}
{wrapWithStorageTooltip(usageDisplay)} {rightInfo}
{wrapWithStorageTooltip(bar)}
) } export default React.memo(UsageInfo)