'use client'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle, } from '@/components/ui/card'; import { Skeleton } from '@/components/ui/skeleton'; import { websiteConfig } from '@/config/website'; import { useCreditBalance, useCreditStats } from '@/hooks/use-credits-query'; import { useMounted } from '@/hooks/use-mounted'; import { usePayment } from '@/hooks/use-payment'; import { useLocaleRouter } from '@/i18n/navigation'; import { formatDate } from '@/lib/formatter'; import { cn } from '@/lib/utils'; import { Routes } from '@/routes'; import { RefreshCwIcon } from 'lucide-react'; import { useTranslations } from 'next-intl'; import { useSearchParams } from 'next/navigation'; import { useCallback, useEffect, useRef } from 'react'; import { toast } from 'sonner'; /** * Credits balance card, show credit balance */ export default function CreditsBalanceCard() { // Don't render if credits are disabled - move this check before any hooks if (!websiteConfig.credits.enableCredits) { return null; } const t = useTranslations('Dashboard.settings.credits.balance'); const searchParams = useSearchParams(); const localeRouter = useLocaleRouter(); const hasHandledSession = useRef(false); const mounted = useMounted(); // Use TanStack Query hooks for credits const { data: balance = 0, isLoading: isLoadingBalance, error, refetch: refetchCredits, } = useCreditBalance(); // Get payment info to check plan type const { currentPlan } = usePayment(); // TanStack Query hook for credit statistics const { data: creditStats, isLoading: isLoadingStats, error: statsError, refetch: refetchCreditStats, } = useCreditStats(); // Check for payment success and show success message useEffect(() => { const sessionId = searchParams.get('credits_session_id'); if (sessionId && !hasHandledSession.current) { hasHandledSession.current = true; setTimeout(() => { // Show success toast and refresh data after payment toast.success(t('creditsAdded')); // Force refresh credits data to show updated balance refetchCredits(); // Refresh credit stats refetchCreditStats(); }, 0); // Clean up URL parameters const url = new URL(window.location.href); url.searchParams.delete('credits_session_id'); localeRouter.replace(Routes.SettingsCredits + url.search); } }, [searchParams, localeRouter, refetchCredits, refetchCreditStats, t]); // Retry all data fetching const handleRetry = useCallback(() => { // console.log('handleRetry, refetch credits data'); // Force refresh credits balance (ignore cache) refetchCredits(); // Refresh credit stats refetchCreditStats(); }, [refetchCredits, refetchCreditStats]); // Render loading skeleton const isPageLoading = isLoadingBalance || isLoadingStats; if (!mounted || isPageLoading) { return ( {t('title')} {t('description')}
{/* show nothing */}
); } // Render error state if (error || statsError) { return ( {t('title')} {t('description')}
{error?.message || statsError?.message}
); } return ( {t('title')} {t('description')} {/* Credits balance display */}
{/* */}
{balance.toLocaleString()}
{/* available */}
{/* Balance information */}
{/* Plan-based credits info */} {!isLoadingStats && creditStats && ( <> {/* Subscription credits (for paid plans) */} {!currentPlan?.isFree && (creditStats.subscriptionCredits.amount > 0 || creditStats.lifetimeCredits.amount > 0) && (
{currentPlan?.isLifetime ? t('lifetimeCredits', { credits: creditStats.lifetimeCredits.amount, }) : t('subscriptionCredits', { credits: creditStats.subscriptionCredits.amount, })}
)} {/* Expiring credits warning */} {creditStats.expiringCredits.amount > 0 && creditStats.expiringCredits.earliestExpiration && (
{t('expiringCredits', { credits: creditStats.expiringCredits.amount, date: formatDate( new Date( creditStats.expiringCredits.earliestExpiration ) ), })}
)} )}
{/* */}
); }