Initial Commit

This commit is contained in:
Eric Gullickson
2025-09-17 16:09:15 -05:00
parent 0cdb9803de
commit a052040e3a
373 changed files with 437090 additions and 6773 deletions

View File

@@ -0,0 +1,148 @@
/**
* @ai-summary Enhanced Query Client configuration with mobile optimization
*/
import { QueryClient, QueryCache, MutationCache } from '@tanstack/react-query';
import toast from 'react-hot-toast';
// Mobile detection utility
const isMobileDevice = (): boolean => {
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ||
window.innerWidth <= 768;
};
// Enhanced error handler for mobile devices
const handleQueryError = (error: any) => {
const isMobile = isMobileDevice();
if (error?.response?.status === 401) {
// Token refresh handled by Auth0Provider
if (isMobile) {
toast.error('Refreshing session...', {
duration: 2000,
id: 'mobile-auth-refresh'
});
}
} else if (error?.response?.status >= 500) {
toast.error(isMobile ? 'Server issue, retrying...' : 'Server error occurred', {
duration: isMobile ? 3000 : 4000,
});
} else if (error?.code === 'NETWORK_ERROR') {
if (isMobile) {
toast.error('Check connection and try again', {
duration: 4000,
id: 'mobile-network-error'
});
} else {
toast.error('Network error occurred');
}
}
};
// Create enhanced query client with mobile-optimized settings
export const createEnhancedQueryClient = (): QueryClient => {
const isMobile = isMobileDevice();
return new QueryClient({
queryCache: new QueryCache({
onError: handleQueryError,
}),
mutationCache: new MutationCache({
onError: handleQueryError,
}),
defaultOptions: {
queries: {
// Mobile-optimized retry strategy
retry: (failureCount, error: any) => {
// Don't retry 4xx errors except 401 (auth issues)
if (error?.response?.status >= 400 && error?.response?.status < 500) {
return error?.response?.status === 401 && failureCount < 2;
}
// Mobile devices get more aggressive retry for network issues
if (isMobile) {
return failureCount < 3;
}
return failureCount < 2;
},
// Mobile-optimized timing
retryDelay: (attemptIndex) => {
const baseDelay = isMobile ? 1000 : 500;
return Math.min(baseDelay * (2 ** attemptIndex), 30000);
},
// Stale time optimization for mobile
staleTime: isMobile ? 1000 * 60 * 2 : 1000 * 60 * 5, // 2 min mobile, 5 min desktop
// GC time optimization
gcTime: isMobile ? 1000 * 60 * 10 : 1000 * 60 * 30, // 10 min mobile, 30 min desktop
// Refetch behavior
refetchOnWindowFocus: !isMobile, // Disable on mobile to save data
refetchOnReconnect: true,
refetchOnMount: true,
// Network mode for offline capability
networkMode: 'offlineFirst',
},
mutations: {
// Mutation retry strategy
retry: (failureCount, error: any) => {
if (error?.response?.status >= 400 && error?.response?.status < 500) {
return false; // Don't retry 4xx errors for mutations
}
return failureCount < (isMobile ? 2 : 1);
},
retryDelay: (attemptIndex) => {
const baseDelay = isMobile ? 2000 : 1000;
return Math.min(baseDelay * (2 ** attemptIndex), 30000);
},
networkMode: 'offlineFirst',
},
},
});
};
// Query key factories for consistent cache management
export const queryKeys = {
all: ['motovaultpro'] as const,
users: () => [...queryKeys.all, 'users'] as const,
user: (id: string) => [...queryKeys.users(), id] as const,
vehicles: () => [...queryKeys.all, 'vehicles'] as const,
vehicle: (id: string) => [...queryKeys.vehicles(), id] as const,
vehiclesByUser: (userId: string) => [...queryKeys.vehicles(), 'user', userId] as const,
fuelLogs: () => [...queryKeys.all, 'fuel-logs'] as const,
fuelLog: (id: string) => [...queryKeys.fuelLogs(), id] as const,
fuelLogsByVehicle: (vehicleId: string) => [...queryKeys.fuelLogs(), 'vehicle', vehicleId] as const,
settings: () => [...queryKeys.all, 'settings'] as const,
userSettings: (userId: string) => [...queryKeys.settings(), 'user', userId] as const,
} as const;
// Performance monitoring utilities
export const queryPerformanceMonitor = {
logSlowQuery: (queryKey: readonly unknown[], duration: number) => {
if (duration > 5000) { // Log queries taking more than 5 seconds
console.warn('Slow query detected:', {
queryKey,
duration: `${duration}ms`,
isMobile: isMobileDevice(),
timestamp: new Date().toISOString(),
});
}
},
logCacheHit: (queryKey: readonly unknown[], fromCache: boolean) => {
if (import.meta.env.MODE === 'development') {
console.log('Query cache:', {
queryKey,
fromCache,
isMobile: isMobileDevice(),
});
}
},
};