"use client"; import { useEffect } from "react"; import { useQuery } from "convex/react"; import { CreditCard } from "lucide-react"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Progress } from "@/components/ui/progress"; import { api } from "@/convex/_generated/api"; import { formatEurFromCents } from "@/lib/utils"; import { cn } from "@/lib/utils"; import { toast } from "@/lib/toast"; import { msg } from "@/lib/toast-messages"; // --------------------------------------------------------------------------- // Tier-Config — monatliches Credit-Kontingent pro Tier (in Cent) // --------------------------------------------------------------------------- const TIER_MONTHLY_CREDITS: Record = { free: 50, starter: 400, pro: 3300, max: 6700, business: 6700, }; const TIER_BADGE_STYLES: Record = { free: "bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400", starter: "bg-blue-100 text-blue-700 dark:bg-blue-900/40 dark:text-blue-400", pro: "bg-purple-100 text-purple-700 dark:bg-purple-900/40 dark:text-purple-400", max: "bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-400", business: "bg-amber-100 text-amber-700 dark:bg-amber-900/40 dark:text-amber-400", }; // --------------------------------------------------------------------------- // Component // --------------------------------------------------------------------------- const LOW_CREDITS_THRESHOLD = 20; export function CreditOverview() { const router = useRouter(); const balance = useQuery(api.credits.getBalance); const subscription = useQuery(api.credits.getSubscription); const usageStats = useQuery(api.credits.getUsageStats); useEffect(() => { if (balance === undefined) return; const available = balance.available; if (available <= 0 || available >= LOW_CREDITS_THRESHOLD) return; const key = "ls-low-credits-dashboard"; if (typeof window !== "undefined" && sessionStorage.getItem(key)) return; sessionStorage.setItem(key, "1"); const { title, desc } = msg.billing.lowCredits(available); toast.action(title, { description: desc, label: msg.billing.topUp, onClick: () => router.push("/settings/billing"), type: "warning", }); }, [balance, router]); // ── Loading State ────────────────────────────────────────────────────── if ( balance === undefined || subscription === undefined || usageStats === undefined ) { return (
{Array.from({ length: 3 }).map((_, i) => (
))}
); } // ── Computed Values ──────────────────────────────────────────────────── const tier = subscription.tier; const monthlyCredits = TIER_MONTHLY_CREDITS[tier] ?? 0; const usagePercent = monthlyCredits > 0 ? Math.min(100, Math.round((usageStats.monthlyUsage / monthlyCredits) * 100)) : 0; const progressColorClass = usagePercent > 95 ? "[&>[data-slot=progress-indicator]]:bg-destructive" : usagePercent >= 80 ? "[&>[data-slot=progress-indicator]]:bg-amber-500" : ""; return (
{/* ── Block A: Verfügbare Credits ──────────────────────────────── */}

Verfügbare Credits

{formatEurFromCents(balance.available)} {tier.charAt(0).toUpperCase() + tier.slice(1)}
{balance.reserved > 0 && (

({formatEurFromCents(balance.reserved)} reserviert)

)}
{/* ── Block B: Monatlicher Verbrauch ───────────────────────────── */}

Monatlicher Verbrauch

{usagePercent}%
{formatEurFromCents(usageStats.monthlyUsage)} von{" "} {formatEurFromCents(monthlyCredits)} verwendet {usageStats.totalGenerations} Generierungen
{/* ── Block C: Aufladen ───────────────────────────────────────── */}
); }