"use client"; import { useSession } from "next-auth/react"; import Link from "next/link"; import { useRouter, usePathname } from "next/navigation"; import { useState, useMemo } from "react"; import { useForm } from "react-hook-form"; import dayjs from "@calcom/dayjs"; import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider"; import ServerTrans from "@calcom/lib/components/ServerTrans"; import { IS_SMS_CREDITS_ENABLED } from "@calcom/lib/constants"; import { downloadAsCsv } from "@calcom/lib/csvUtils"; import { useLocale } from "@calcom/lib/hooks/useLocale"; import { useParamsWithFallback } from "@calcom/lib/hooks/useParamsWithFallback"; import { trpc } from "@calcom/trpc/react"; import { Button } from "@calcom/ui/components/button"; import { Select } from "@calcom/ui/components/form"; import { TextField, Label, InputError } from "@calcom/ui/components/form"; import { ProgressBar } from "@calcom/ui/components/progress-bar"; import { showToast } from "@calcom/ui/components/toast"; import { BillingCreditsSkeleton } from "./BillingCreditsSkeleton"; type MonthOption = { value: string; label: string; startDate: string; endDate: string; }; const getMonthOptions = (): MonthOption[] => { const options: MonthOption[] = []; const minDate = dayjs.utc("2025-05-01"); let date = dayjs.utc(); let count = 0; while ((date.isAfter(minDate) || date.isSame(minDate, "month")) && count < 12) { const startDate = date.startOf("month"); const endDate = date.endOf("month"); options.push({ value: date.format("MMMM YYYY"), label: date.format("MMMM YYYY"), startDate: startDate.toISOString(), endDate: endDate.toISOString(), }); date = date.subtract(1, "month"); count++; } return options; }; export default function BillingCredits() { const { t } = useLocale(); const router = useRouter(); const pathname = usePathname(); const session = useSession(); const orgBranding = useOrgBranding(); const monthOptions = useMemo(() => getMonthOptions(), []); const [selectedMonth, setSelectedMonth] = useState(monthOptions[0]); const [isDownloading, setIsDownloading] = useState(false); const utils = trpc.useUtils(); const { register, handleSubmit, setValue, formState: { errors }, } = useForm<{ quantity: number }>({ defaultValues: { quantity: 50 } }); const params = useParamsWithFallback(); const orgId = session.data?.user?.org?.id; const parsedTeamId = Number(params.id); const teamId: number | undefined = Number.isFinite(parsedTeamId) ? parsedTeamId : typeof orgId === "number" ? orgId : undefined; const tokens = (pathname ?? "").split("/").filter(Boolean); const settingsIndex = tokens.indexOf("settings"); const isOrgScopedPath = settingsIndex >= 0 && ["organizations", "teams"].includes(tokens[settingsIndex + 1]); const shouldRender = IS_SMS_CREDITS_ENABLED && !(orgId && !isOrgScopedPath && !orgBranding?.slug); const { data: creditsData, isLoading } = trpc.viewer.credits.getAllCredits.useQuery( { teamId }, { enabled: shouldRender } ); if (!shouldRender) return null; const buyCreditsMutation = trpc.viewer.credits.buyCredits.useMutation({ onSuccess: (data) => { if (data.sessionUrl) { router.push(data.sessionUrl); } }, onError: () => { showToast(t("credit_purchase_failed"), "error"); }, }); const handleDownload = async () => { setIsDownloading(true); try { const result = await utils.viewer.credits.downloadExpenseLog.fetch({ teamId, startDate: selectedMonth.startDate, endDate: selectedMonth.endDate, }); if (result?.csvData) { const filename = `credit-expense-log-${selectedMonth.value.toLowerCase().replace(" ", "-")}.csv`; downloadAsCsv(result.csvData, filename); } else { showToast(t("error_downloading_expense_log"), "error"); } } catch (error) { showToast(t("error_downloading_expense_log"), "error"); } finally { setIsDownloading(false); } }; if (isLoading && teamId) return ; if (!creditsData) return null; const onSubmit = (data: { quantity: number }) => { buyCreditsMutation.mutate({ quantity: data.quantity, teamId }); }; const teamCreditsPercentageUsed = creditsData.credits.totalMonthlyCredits > 0 ? (creditsData.credits.totalRemainingMonthlyCredits / creditsData.credits.totalMonthlyCredits) * 100 : 0; return (

{t("credits")}

Learn more , ]} />

{creditsData.credits.totalMonthlyCredits > 0 ? (
{t("total_credits", { totalCredits: creditsData.credits.totalMonthlyCredits, })}
{t("remaining_credits", { remainingCredits: creditsData.credits.totalRemainingMonthlyCredits, })}
) : ( <> )}
{creditsData.credits.additionalCredits}

setValue("quantity", Number(e.target.value))} min={50} addOnSuffix={<>{t("credits")}} /> {errors.quantity && }