refactor: enhance credit balance and stats hooks with improved error handling and logging
This commit is contained in:
parent
13c23dab56
commit
1fb89a2a05
@ -15,8 +15,8 @@ export function QueryProvider({ children }: QueryProviderProps) {
|
|||||||
new QueryClient({
|
new QueryClient({
|
||||||
defaultOptions: {
|
defaultOptions: {
|
||||||
queries: {
|
queries: {
|
||||||
staleTime: 5 * 60 * 1000, // 5 minutes
|
staleTime: 5 * 60 * 1000, // 5 minutes - default stale time
|
||||||
gcTime: 10 * 60 * 1000, // 10 minutes (formerly cacheTime)
|
gcTime: 10 * 60 * 1000, // 10 minutes - default garbage collection time
|
||||||
retry: 1,
|
retry: 1,
|
||||||
refetchOnWindowFocus: false,
|
refetchOnWindowFocus: false,
|
||||||
},
|
},
|
||||||
|
@ -11,7 +11,11 @@ import {
|
|||||||
} from '@/components/ui/card';
|
} from '@/components/ui/card';
|
||||||
import { Skeleton } from '@/components/ui/skeleton';
|
import { Skeleton } from '@/components/ui/skeleton';
|
||||||
import { websiteConfig } from '@/config/website';
|
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 { useMounted } from '@/hooks/use-mounted';
|
||||||
import { useCurrentPlan } from '@/hooks/use-payment';
|
import { useCurrentPlan } from '@/hooks/use-payment';
|
||||||
import { useLocaleRouter } from '@/i18n/navigation';
|
import { useLocaleRouter } from '@/i18n/navigation';
|
||||||
@ -19,6 +23,7 @@ import { authClient } from '@/lib/auth-client';
|
|||||||
import { formatDate } from '@/lib/formatter';
|
import { formatDate } from '@/lib/formatter';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import { Routes } from '@/routes';
|
import { Routes } from '@/routes';
|
||||||
|
import { useQueryClient } from '@tanstack/react-query';
|
||||||
import { RefreshCwIcon } from 'lucide-react';
|
import { RefreshCwIcon } from 'lucide-react';
|
||||||
import { useTranslations } from 'next-intl';
|
import { useTranslations } from 'next-intl';
|
||||||
import { useSearchParams } from 'next/navigation';
|
import { useSearchParams } from 'next/navigation';
|
||||||
@ -37,6 +42,7 @@ export default function CreditsBalanceCard() {
|
|||||||
const t = useTranslations('Dashboard.settings.credits.balance');
|
const t = useTranslations('Dashboard.settings.credits.balance');
|
||||||
const searchParams = useSearchParams();
|
const searchParams = useSearchParams();
|
||||||
const localeRouter = useLocaleRouter();
|
const localeRouter = useLocaleRouter();
|
||||||
|
const queryClient = useQueryClient();
|
||||||
const hasHandledSession = useRef(false);
|
const hasHandledSession = useRef(false);
|
||||||
const mounted = useMounted();
|
const mounted = useMounted();
|
||||||
|
|
||||||
@ -44,8 +50,8 @@ export default function CreditsBalanceCard() {
|
|||||||
const {
|
const {
|
||||||
data: balance = 0,
|
data: balance = 0,
|
||||||
isLoading: isLoadingBalance,
|
isLoading: isLoadingBalance,
|
||||||
error,
|
error: balanceError,
|
||||||
refetch: refetchCredits,
|
refetch: refetchBalance,
|
||||||
} = useCreditBalance();
|
} = useCreditBalance();
|
||||||
|
|
||||||
// Get payment info to check plan type
|
// Get payment info to check plan type
|
||||||
@ -58,7 +64,7 @@ export default function CreditsBalanceCard() {
|
|||||||
data: creditStats,
|
data: creditStats,
|
||||||
isLoading: isLoadingStats,
|
isLoading: isLoadingStats,
|
||||||
error: statsError,
|
error: statsError,
|
||||||
refetch: refetchCreditStats,
|
refetch: refetchStats,
|
||||||
} = useCreditStats();
|
} = useCreditStats();
|
||||||
|
|
||||||
// Check for payment success and show success message
|
// Check for payment success and show success message
|
||||||
@ -67,35 +73,48 @@ export default function CreditsBalanceCard() {
|
|||||||
if (sessionId && !hasHandledSession.current) {
|
if (sessionId && !hasHandledSession.current) {
|
||||||
hasHandledSession.current = true;
|
hasHandledSession.current = true;
|
||||||
|
|
||||||
setTimeout(() => {
|
// Clean up URL parameters first
|
||||||
// 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);
|
const url = new URL(window.location.href);
|
||||||
url.searchParams.delete('credits_session_id');
|
url.searchParams.delete('credits_session_id');
|
||||||
localeRouter.replace(Routes.SettingsCredits + url.search);
|
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
|
// Retry all data fetching
|
||||||
const handleRetry = useCallback(() => {
|
const handleRetry = useCallback(() => {
|
||||||
// console.log('handleRetry, refetch credits data');
|
// console.log('handleRetry, refetch credits data');
|
||||||
// Force refresh credits balance (ignore cache)
|
// Force invalidate cache to ensure fresh data
|
||||||
refetchCredits();
|
queryClient.invalidateQueries({
|
||||||
// Refresh credit stats
|
queryKey: creditsKeys.balance(),
|
||||||
refetchCreditStats();
|
});
|
||||||
}, [refetchCredits, refetchCreditStats]);
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: creditsKeys.stats(),
|
||||||
|
});
|
||||||
|
}, [queryClient]);
|
||||||
|
|
||||||
// Render loading skeleton
|
// Render loading skeleton
|
||||||
const isPageLoading = isLoadingBalance || isLoadingStats;
|
if (!mounted || isLoadingBalance || isLoadingStats) {
|
||||||
if (!mounted || isPageLoading) {
|
|
||||||
return (
|
return (
|
||||||
<Card className={cn('w-full overflow-hidden pt-6 pb-0 flex flex-col')}>
|
<Card className={cn('w-full overflow-hidden pt-6 pb-0 flex flex-col')}>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
@ -116,7 +135,7 @@ export default function CreditsBalanceCard() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Render error state
|
// Render error state
|
||||||
if (error || statsError) {
|
if (balanceError || statsError) {
|
||||||
return (
|
return (
|
||||||
<Card className={cn('w-full overflow-hidden pt-6 pb-0 flex flex-col')}>
|
<Card className={cn('w-full overflow-hidden pt-6 pb-0 flex flex-col')}>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
@ -125,7 +144,7 @@ export default function CreditsBalanceCard() {
|
|||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent className="space-y-4 flex-1">
|
<CardContent className="space-y-4 flex-1">
|
||||||
<div className="text-destructive text-sm">
|
<div className="text-destructive text-sm">
|
||||||
{error?.message || statsError?.message}
|
{balanceError?.message || statsError?.message}
|
||||||
</div>
|
</div>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
<CardFooter className="mt-2 px-6 py-4 flex justify-end items-center bg-background rounded-none">
|
<CardFooter className="mt-2 px-6 py-4 flex justify-end items-center bg-background rounded-none">
|
||||||
|
@ -19,31 +19,39 @@ export const creditsKeys = {
|
|||||||
}) => [...creditsKeys.transactions(), filters] as const,
|
}) => [...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
|
// Hook to fetch credit balance
|
||||||
export function useCreditBalance() {
|
export function useCreditBalance() {
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: creditsKeys.balance(),
|
queryKey: creditsKeys.balance(),
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
|
console.log('Fetching credit balance...');
|
||||||
const result = await getCreditBalanceAction();
|
const result = await getCreditBalanceAction();
|
||||||
if (!result?.data?.success) {
|
if (!result?.data?.success) {
|
||||||
throw new Error('Failed to fetch credit balance');
|
throw new Error('Failed to fetch credit balance');
|
||||||
}
|
}
|
||||||
|
console.log('Credit balance fetched:', result.data.credits);
|
||||||
return result.data.credits || 0;
|
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
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
Loading…
Reference in New Issue
Block a user