Skip to main content
Glama
ai-credit-usage.tsx8.06 kB
import { useQueryClient } from '@tanstack/react-query'; import { t } from 'i18next'; import { Sparkles, Info, Settings, Plus } from 'lucide-react'; import { useState } from 'react'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader } from '@/components/ui/card'; 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 { flagsHooks } from '@/hooks/flags-hooks'; import { AiCreditsAutoTopUpState, ApEdition, ApFlagId, PlatformBillingInformation, } from '@activepieces/shared'; import { billingMutations } from '../../lib/billing-hooks'; import { AutoTopUpConfigDialog } from './auto-topup-config-dialog'; import { PurchaseAICreditsDialog } from './purchase-ai-credits-dialog'; interface AiCreditUsageProps { platformSubscription: PlatformBillingInformation; } export function AICreditUsage({ platformSubscription }: AiCreditUsageProps) { const queryClient = useQueryClient(); const { plan, usage } = platformSubscription; const [isPurchaseDialogOpen, setIsPurchaseDialogOpen] = useState(false); const [isAutoTopUpDialogOpen, setIsAutoTopUpDialogOpen] = useState(false); const [isAutoTopUpEditing, setIsAutoTopUpEditing] = useState(false); const planIncludedCredits = plan.includedAiCredits; const totalCreditsUsed = usage.totalAiCreditsUsed; const creditsRemaining = usage.aiCreditsRemaining; const autoTopUpState = plan.aiCreditsAutoTopUpState ?? AiCreditsAutoTopUpState.DISABLED; const canBuyCredits = flagsHooks.useFlag<ApEdition>( ApFlagId.CAN_BUY_AI_CREDITS, ); const isAutoTopUpEnabled = autoTopUpState === AiCreditsAutoTopUpState.ENABLED; const { mutate: updateAutoTopUp, isPending: isDisablingAutoTopUp } = billingMutations.useUpdateAutoTopUp(queryClient); const handleToggleAutoTopUp = (checked: boolean) => { if (checked) { setIsAutoTopUpEditing(false); setIsAutoTopUpDialogOpen(true); } else { updateAutoTopUp({ state: AiCreditsAutoTopUpState.DISABLED }); } }; const handleConfigureAutoTopUp = () => { setIsAutoTopUpEditing(true); setIsAutoTopUpDialogOpen(true); }; return ( <Card className="w-full"> <CardHeader className="border-b"> <div className="flex items-center justify-between"> <div className="flex items-center gap-3"> <div className="flex items-center justify-center w-10 h-10 rounded-lg border"> <Sparkles className="w-5 h-5" /> </div> <div> <h3 className="text-lg font-semibold">{t('AI Credits')}</h3> <p className="text-sm text-muted-foreground"> {t('Manage your AI credits wallet and auto-topup')} </p> </div> </div> <div className="flex items-center gap-2"> {canBuyCredits && ( <Button variant="default" className="gap-2" onClick={() => setIsPurchaseDialogOpen(true)} > <Plus className="w-4 h-4" /> {t('Purchase Credits')} </Button> )} </div> </div> </CardHeader> <CardContent className="p-6 space-y-8"> <div className="space-y-6"> <div className="p-4 rounded-lg border bg-primary/5 space-y-2"> <div className="flex items-center gap-2"> <Sparkles className="w-4 h-4 text-primary" /> <span className="text-sm font-medium text-primary"> {t('Wallet Balance')} </span> </div> <div className="flex items-baseline justify-between"> <div className="text-3xl font-bold"> {Math.floor(creditsRemaining).toLocaleString()} <span className="text-sm font-normal text-muted-foreground ml-2"> {t('credits available')} </span> </div> <div className="text-xs text-muted-foreground"> {t('Total lifetime usage')}:{' '} {Math.floor(totalCreditsUsed).toLocaleString()} </div> </div> </div> <div className="space-y-3"> <div className="flex items-center gap-2"> <span className="font-medium"> {t('Monthly Included Credits')} </span> <Tooltip> <TooltipTrigger> <Info className="w-3 h-3 text-muted-foreground" /> </TooltipTrigger> <TooltipContent> {t('Credits included in your plan (resets monthly)')} </TooltipContent> </Tooltip> </div> <div className="flex items-center justify-between text-sm"> <span className="text-muted-foreground"> {usage.totalAiCreditsUsedThisMonth.toLocaleString()} /{' '} {planIncludedCredits.toLocaleString()} {t('used')} </span> <span className="text-xs font-medium text-muted-foreground"> {t('Plan Limit')} </span> </div> <Progress value={Math.min( 100, Math.round( (usage.totalAiCreditsUsedThisMonth / planIncludedCredits) * 100, ), )} className="h-2" /> </div> </div> <Separator /> <div className="space-y-6"> {canBuyCredits && ( <div className="flex items-center justify-between"> <div> <div className="flex items-center gap-2 mb-1"> <h4 className="text-base font-medium">{t('Auto Top-up')}</h4> {isAutoTopUpEnabled && ( <Button variant="ghost" size="icon" className="h-6 w-6" onClick={handleConfigureAutoTopUp} > <Settings className="w-3 h-3" /> </Button> )} </div> <p className="text-sm text-muted-foreground"> {t('Automatically purchase credits when balance is low.')} </p> {isAutoTopUpEnabled && plan.aiCreditsAutoTopUpThreshold && plan.aiCreditsAutoTopUpCreditsToAdd && ( <p className="text-xs text-primary mt-1"> {t( 'Adds {credits} credits when balance drops below {threshold}', { credits: plan.aiCreditsAutoTopUpCreditsToAdd.toLocaleString(), threshold: plan.aiCreditsAutoTopUpThreshold.toLocaleString(), }, )} </p> )} </div> <div className="flex items-center gap-3"> <Switch checked={isAutoTopUpEnabled} onCheckedChange={handleToggleAutoTopUp} disabled={isDisablingAutoTopUp} /> </div> </div> )} </div> <PurchaseAICreditsDialog isOpen={isPurchaseDialogOpen} onOpenChange={setIsPurchaseDialogOpen} /> <AutoTopUpConfigDialog isOpen={isAutoTopUpDialogOpen} onOpenChange={setIsAutoTopUpDialogOpen} isEditing={isAutoTopUpEditing} currentThreshold={plan.aiCreditsAutoTopUpThreshold} currentCreditsToAdd={plan.aiCreditsAutoTopUpCreditsToAdd} /> </CardContent> </Card> ); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/activepieces/activepieces'

If you have feedback or need assistance with the MCP directory API, please join our Discord server