import { useQueryClient } from '@tanstack/react-query'; import { t } from 'i18next'; import { Sparkles, Info, Loader2 } from 'lucide-react'; import { useEffect, useState, useMemo, useCallback } from 'react'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Progress } from '@/components/ui/progress'; import { Separator } from '@/components/ui/separator'; import { Switch } from '@/components/ui/switch'; import { Tooltip, TooltipTrigger, TooltipContent, } from '@/components/ui/tooltip'; import { ApSubscriptionStatus } from '@activepieces/ee-shared'; import { AiOverageState, PlatformBillingInformation, } from '@activepieces/shared'; import { billingMutations } from '../lib/billing-hooks'; import { EnableAIOverageDialog } from './enable-ai-credits-overage'; interface AiCreditUsageProps { platformSubscription: PlatformBillingInformation; } export function AICreditUsage({ platformSubscription }: AiCreditUsageProps) { const queryClient = useQueryClient(); const { plan, usage } = platformSubscription; const [isOpen, setIsOpen] = useState(false); const planIncludedCredits = plan.includedAiCredits; const overageLimit = plan.aiCreditsOverageLimit; const totalCreditsUsed = usage.aiCredits; const hasActiveSubscription = plan.stripeSubscriptionStatus === ApSubscriptionStatus.ACTIVE; const aiOverrageState = plan.aiCreditsOverageState ?? AiOverageState.NOT_ALLOWED; const overageConfig = useMemo(() => { const isAllowed = aiOverrageState !== AiOverageState.NOT_ALLOWED; const isEnabled = aiOverrageState === AiOverageState.ALLOWED_AND_ON; return { allowed: isAllowed, enabled: isEnabled, canToggle: isAllowed, }; }, [aiOverrageState]); const [usageBasedEnabled, setUsageBasedEnabled] = useState( overageConfig.enabled, ); const [usageLimit, setUsageLimit] = useState(overageLimit ?? 500); const { mutate: setAiCreditOverageLimit, isPending: settingAiCreditsOverageLimit, } = billingMutations.useSetAiCreditOverageLimit(queryClient); const { mutate: toggleAiCreditsOverageEnabled, isPending: togglingAiCreditsOverageEnabled, } = billingMutations.useToggleAiCreditOverageEnabled(queryClient); const creditMetrics = useMemo(() => { const creditsUsedFromPlan = Math.min(totalCreditsUsed, planIncludedCredits); const overageCreditsUsed = Math.max( 0, totalCreditsUsed - planIncludedCredits, ); const planUsagePercentage = Math.min( 100, Math.round((creditsUsedFromPlan / planIncludedCredits) * 100), ); const overageUsagePercentage = usageBasedEnabled && overageLimit ? Math.min(100, Math.round((overageCreditsUsed / overageLimit) * 100)) : 0; return { creditsUsedFromPlan, overageCreditsUsed, planUsagePercentage, overageUsagePercentage, isPlanLimitApproaching: planUsagePercentage > 80, isPlanLimitExceeded: totalCreditsUsed > planIncludedCredits, isOverageLimitApproaching: overageUsagePercentage > 80, }; }, [totalCreditsUsed, planIncludedCredits, usageBasedEnabled, overageLimit]); const handleSaveAiCreditUsageLimit = useCallback(() => { setAiCreditOverageLimit({ limit: usageLimit }); }, [setAiCreditOverageLimit, usageLimit]); const handleToggleAiCreditUsage = useCallback(() => { const newState = usageBasedEnabled ? AiOverageState.ALLOWED_BUT_OFF : AiOverageState.ALLOWED_AND_ON; if (!hasActiveSubscription) { setIsOpen(true); } else { toggleAiCreditsOverageEnabled( { state: newState }, { onSuccess: () => { setUsageBasedEnabled(!usageBasedEnabled); }, }, ); } }, [usageBasedEnabled, toggleAiCreditsOverageEnabled]); useEffect(() => { setUsageBasedEnabled(overageConfig.enabled); }, [overageConfig.enabled]); useEffect(() => { setUsageLimit(overageLimit ?? 500); }, [overageLimit]); return (

{t('AI Credits')}

Manage your AI usage and limits

{overageConfig.canToggle && (
{t('Usage Based Billing')}
)}

{t('Plan Credits Usage')}

Credits reset monthly with your billing cycle
{Math.round(creditMetrics.creditsUsedFromPlan)} /{' '} {planIncludedCredits} {t('Plan Included')}
{creditMetrics.planUsagePercentage}% of plan credits used {creditMetrics.isPlanLimitApproaching && !creditMetrics.isPlanLimitExceeded && ( Approaching limit )} {creditMetrics.isPlanLimitExceeded && ( Plan limit exceeded )}
{usageBasedEnabled && overageConfig.canToggle && ( <>

{t('Additional Credits Usage')}

Credits used beyond your plan limit ($0.01 each)
{creditMetrics.overageCreditsUsed} /{' '} {overageLimit ?? 'unknown'} {t('Usage Limit')}
{creditMetrics.overageUsagePercentage}% of usage limit used {creditMetrics.isOverageLimitApproaching && ( Approaching usage limit )}
{t('Set Usage Limit')}

Set a maximum number of additional AI credits to prevent unexpected charges

setUsageLimit(Number(e.target.value))} className="w-full" min="0" />

Recommended: Set 20-50% above your expected monthly overage usage

{t('$1 per 1000 additional credits beyond plan limit')}
)}
); }