refactor: enhance credit balance and stats hooks with improved error handling and logging

This commit is contained in:
javayhu 2025-08-21 01:48:40 +08:00
parent 13c23dab56
commit 1fb89a2a05
4 changed files with 68 additions and 56 deletions

View File

@ -15,8 +15,8 @@ export function QueryProvider({ children }: QueryProviderProps) {
new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 5 minutes
gcTime: 10 * 60 * 1000, // 10 minutes (formerly cacheTime)
staleTime: 5 * 60 * 1000, // 5 minutes - default stale time
gcTime: 10 * 60 * 1000, // 10 minutes - default garbage collection time
retry: 1,
refetchOnWindowFocus: false,
},

View File

@ -11,7 +11,11 @@ import {
} from '@/components/ui/card';
import { Skeleton } from '@/components/ui/skeleton';
import { websiteConfig } from '@/config/website';
import { useCreditBalance, useCreditStats } from '@/hooks/use-credits';
import {
creditsKeys,
useCreditBalance,
useCreditStats,
} from '@/hooks/use-credits';
import { useMounted } from '@/hooks/use-mounted';
import { useCurrentPlan } from '@/hooks/use-payment';
import { useLocaleRouter } from '@/i18n/navigation';
@ -19,6 +23,7 @@ import { authClient } from '@/lib/auth-client';
import { formatDate } from '@/lib/formatter';
import { cn } from '@/lib/utils';
import { Routes } from '@/routes';
import { useQueryClient } from '@tanstack/react-query';
import { RefreshCwIcon } from 'lucide-react';
import { useTranslations } from 'next-intl';
import { useSearchParams } from 'next/navigation';
@ -37,6 +42,7 @@ export default function CreditsBalanceCard() {
const t = useTranslations('Dashboard.settings.credits.balance');
const searchParams = useSearchParams();
const localeRouter = useLocaleRouter();
const queryClient = useQueryClient();
const hasHandledSession = useRef(false);
const mounted = useMounted();
@ -44,8 +50,8 @@ export default function CreditsBalanceCard() {
const {
data: balance = 0,
isLoading: isLoadingBalance,
error,
refetch: refetchCredits,
error: balanceError,
refetch: refetchBalance,
} = useCreditBalance();
// Get payment info to check plan type
@ -58,7 +64,7 @@ export default function CreditsBalanceCard() {
data: creditStats,
isLoading: isLoadingStats,
error: statsError,
refetch: refetchCreditStats,
refetch: refetchStats,
} = useCreditStats();
// Check for payment success and show success message
@ -67,35 +73,48 @@ export default function CreditsBalanceCard() {
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
// Clean up URL parameters first
const url = new URL(window.location.href);
url.searchParams.delete('credits_session_id');
localeRouter.replace(Routes.SettingsCredits + url.search);
// Handle payment success with proper timing
const handlePaymentSuccess = async () => {
// Show success toast (must be in setTimeout to avoid errors)
setTimeout(() => {
toast.success(t('creditsAdded'));
}, 0);
// Wait for webhook to process
await new Promise((resolve) => setTimeout(resolve, 1000));
// Force refresh data
queryClient.invalidateQueries({
queryKey: creditsKeys.balance(),
});
queryClient.invalidateQueries({
queryKey: creditsKeys.stats(),
});
};
handlePaymentSuccess();
}
}, [searchParams, localeRouter, refetchCredits, refetchCreditStats, t]);
}, [searchParams, localeRouter, queryClient, 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]);
// Force invalidate cache to ensure fresh data
queryClient.invalidateQueries({
queryKey: creditsKeys.balance(),
});
queryClient.invalidateQueries({
queryKey: creditsKeys.stats(),
});
}, [queryClient]);
// Render loading skeleton
const isPageLoading = isLoadingBalance || isLoadingStats;
if (!mounted || isPageLoading) {
if (!mounted || isLoadingBalance || isLoadingStats) {
return (
<Card className={cn('w-full overflow-hidden pt-6 pb-0 flex flex-col')}>
<CardHeader>
@ -116,7 +135,7 @@ export default function CreditsBalanceCard() {
}
// Render error state
if (error || statsError) {
if (balanceError || statsError) {
return (
<Card className={cn('w-full overflow-hidden pt-6 pb-0 flex flex-col')}>
<CardHeader>
@ -125,7 +144,7 @@ export default function CreditsBalanceCard() {
</CardHeader>
<CardContent className="space-y-4 flex-1">
<div className="text-destructive text-sm">
{error?.message || statsError?.message}
{balanceError?.message || statsError?.message}
</div>
</CardContent>
<CardFooter className="mt-2 px-6 py-4 flex justify-end items-center bg-background rounded-none">

View File

@ -19,31 +19,39 @@ export const creditsKeys = {
}) => [...creditsKeys.transactions(), filters] as const,
};
// Hook to fetch credit statistics
export function useCreditStats() {
return useQuery({
queryKey: creditsKeys.stats(),
queryFn: async () => {
const result = await getCreditStatsAction();
if (!result?.data?.success) {
throw new Error(result?.data?.error || 'Failed to fetch credit stats');
}
return result.data.data;
},
});
}
// Hook to fetch credit balance
export function useCreditBalance() {
return useQuery({
queryKey: creditsKeys.balance(),
queryFn: async () => {
console.log('Fetching credit balance...');
const result = await getCreditBalanceAction();
if (!result?.data?.success) {
throw new Error('Failed to fetch credit balance');
}
console.log('Credit balance fetched:', result.data.credits);
return result.data.credits || 0;
},
staleTime: 30 * 1000, // 30 seconds - reasonable stale time
retry: 2, // Retry up to 2 times on failure
});
}
// Hook to fetch credit statistics
export function useCreditStats() {
return useQuery({
queryKey: creditsKeys.stats(),
queryFn: async () => {
console.log('Fetching credit stats...');
const result = await getCreditStatsAction();
if (!result?.data?.success) {
throw new Error(result?.data?.error || 'Failed to fetch credit stats');
}
console.log('Credit stats fetched:', result.data.data);
return result.data.data;
},
staleTime: 30 * 1000, // 30 seconds - reasonable stale time
retry: 2, // Retry up to 2 times on failure
});
}

View File

@ -1,15 +0,0 @@
import { QueryClient } from '@tanstack/react-query';
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000, // 5 minutes
gcTime: 10 * 60 * 1000, // 10 minutes (formerly cacheTime)
retry: 1,
refetchOnWindowFocus: false,
},
mutations: {
retry: 1,
},
},
});