import React, { useEffect, useState } from "react"; import { Alert, Box, Button, LinearProgress, Skeleton, Typography } from "@mui/material"; import { PageSection } from "../layout/PageLayout"; import { api } from "../api"; import { useI18n } from "../i18n/I18nProvider"; type Usage = { currentMonth: { calls: number; estimatedTokens: number }; plan: string; monthlyCallLimit: number; monthlyTokenLimit: number; storageUsedBytes: number; storageLimitBytes: number; }; type BillingStatus = { enabled: boolean; canCheckout: boolean; canManage: boolean }; function formatBytes(bytes: number) { if (bytes < 1_000_000) return `${Math.round(bytes / 1_000)} KB`; if (bytes < 1_000_000_000) return `${(bytes / 1_000_000).toFixed(1)} MB`; return `${(bytes / 1_000_000_000).toFixed(1)} GB`; } export default function AiUsageCard() { const { t } = useI18n(); const [usage, setUsage] = useState(null); const [billing, setBilling] = useState(null); const [billingBusy, setBillingBusy] = useState(false); const [billingFailed, setBillingFailed] = useState(false); const [failed, setFailed] = useState(false); useEffect(() => { let active = true; api.get("/ai/usage") .then((response) => { if (active) setUsage(response.data); }) .catch(() => { if (active) setFailed(true); }); api.get("/billing/status") .then((response) => { if (active) setBilling(response.data); }) .catch(() => { /* Billing is optional; usage remains available. */ }); return () => { active = false; }; }, []); const openBilling = async (path: "checkout" | "portal") => { setBillingBusy(true); setBillingFailed(false); try { const response = await api.post<{ url: string }>(`/billing/${path}`); window.location.assign(response.data.url); } catch { setBillingFailed(true); setBillingBusy(false); } }; if (failed) return {t("settingsUsageUnavailable")}; if (!usage) return ; const callsPercent = usage.monthlyCallLimit > 0 ? Math.min(100, usage.currentMonth.calls / usage.monthlyCallLimit * 100) : 0; const tokensPercent = usage.monthlyTokenLimit > 0 ? Math.min(100, usage.currentMonth.estimatedTokens / usage.monthlyTokenLimit * 100) : 0; const storagePercent = Math.min(100, usage.storageUsedBytes / usage.storageLimitBytes * 100); return ( {t("settingsUsagePlan", { plan: usage.plan })}}> {usage.monthlyCallLimit === 0 ? {t("settingsUsageNoAi")} : <> {t("settingsUsageGenerations", { used: usage.currentMonth.calls.toLocaleString(), limit: usage.monthlyCallLimit.toLocaleString() })} {t("settingsUsageTokens", { used: usage.currentMonth.estimatedTokens.toLocaleString(), limit: usage.monthlyTokenLimit.toLocaleString() })} } {t("settingsUsageStorage", { used: formatBytes(usage.storageUsedBytes), limit: formatBytes(usage.storageLimitBytes) })} {t("settingsUsageReset")} {billing?.canCheckout ? : null} {billing?.canManage ? : null} {billingFailed ? {t("settingsBillingUnavailable")} : null} {usage.plan === "free" && billing?.enabled === false ? ( {t("settingsBillingNotConfigured")} ) : null} ); }