From 1fb89a2a053062549eb569a0bc69cf7afb7a63f5 Mon Sep 17 00:00:00 2001 From: javayhu Date: Thu, 21 Aug 2025 01:48:40 +0800 Subject: [PATCH] refactor: enhance credit balance and stats hooks with improved error handling and logging --- src/components/providers/query-provider.tsx | 4 +- .../settings/credits/credits-balance-card.tsx | 69 ++++++++++++------- src/hooks/use-credits.ts | 36 ++++++---- src/lib/query-client.ts | 15 ---- 4 files changed, 68 insertions(+), 56 deletions(-) delete mode 100644 src/lib/query-client.ts diff --git a/src/components/providers/query-provider.tsx b/src/components/providers/query-provider.tsx index c8e15c4..87d933d 100644 --- a/src/components/providers/query-provider.tsx +++ b/src/components/providers/query-provider.tsx @@ -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, }, diff --git a/src/components/settings/credits/credits-balance-card.tsx b/src/components/settings/credits/credits-balance-card.tsx index 54c2f21..05042e5 100644 --- a/src/components/settings/credits/credits-balance-card.tsx +++ b/src/components/settings/credits/credits-balance-card.tsx @@ -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 ( @@ -116,7 +135,7 @@ export default function CreditsBalanceCard() { } // Render error state - if (error || statsError) { + if (balanceError || statsError) { return ( @@ -125,7 +144,7 @@ export default function CreditsBalanceCard() {
- {error?.message || statsError?.message} + {balanceError?.message || statsError?.message}
diff --git a/src/hooks/use-credits.ts b/src/hooks/use-credits.ts index 8794905..b60252c 100644 --- a/src/hooks/use-credits.ts +++ b/src/hooks/use-credits.ts @@ -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 }); } diff --git a/src/lib/query-client.ts b/src/lib/query-client.ts deleted file mode 100644 index bb0486e..0000000 --- a/src/lib/query-client.ts +++ /dev/null @@ -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, - }, - }, -});