Refactor: Implement logging and JSONB cleanup

This commit is contained in:
gpt-engineer-app[bot]
2025-11-03 18:05:58 +00:00
parent b6179372e6
commit e9b9faa3e1
18 changed files with 430 additions and 142 deletions

View File

@@ -1,5 +1,6 @@
import { Analytics } from "@vercel/analytics/react";
import { Component, ReactNode } from "react";
import { logger } from "@/lib/logger";
class AnalyticsErrorBoundary extends Component<
{ children: ReactNode },
@@ -16,7 +17,7 @@ class AnalyticsErrorBoundary extends Component<
componentDidCatch(error: Error) {
// Silently fail - analytics should never break the app
console.info('[Analytics] Failed to load, continuing without analytics');
logger.info('Analytics failed to load, continuing without analytics', { error: error.message });
}
render() {

View File

@@ -3,6 +3,7 @@ import { UserTopList, UserTopListItem, Park, Ride, Company } from "@/types/datab
import { supabase } from "@/integrations/supabase/client";
import { Link } from "react-router-dom";
import { Badge } from "@/components/ui/badge";
import { handleError } from "@/lib/errorHandler";
interface ListDisplayProps {
list: UserTopList;
@@ -31,7 +32,10 @@ export function ListDisplay({ list }: ListDisplayProps) {
.order("position", { ascending: true });
if (itemsError) {
console.error("Error fetching items:", itemsError);
handleError(itemsError, {
action: 'Fetch List Items',
metadata: { listId: list.id }
});
setLoading(false);
return;
}

View File

@@ -8,6 +8,7 @@ import { Plus, Trash2, Edit, Eye, EyeOff } from "lucide-react";
import { toast } from "sonner";
import { ListItemEditor } from "./ListItemEditor";
import { ListDisplay } from "./ListDisplay";
import { handleError } from "@/lib/errorHandler";
import {
Dialog,
DialogContent,
@@ -62,8 +63,10 @@ export function UserListManager() {
.order("created_at", { ascending: false });
if (error) {
toast.error("Failed to load lists");
console.error(error);
handleError(error, {
action: 'Load User Lists',
userId: user.id
});
} else {
// Map Supabase data to UserTopList interface
const mappedLists: UserTopList[] = (data || []).map((list: any) => ({
@@ -101,8 +104,11 @@ export function UserListManager() {
.single();
if (error) {
toast.error("Failed to create list");
console.error(error);
handleError(error, {
action: 'Create List',
userId: user.id,
metadata: { title: newListTitle }
});
} else {
toast.success("List created successfully");
const newList: UserTopList = {
@@ -132,8 +138,11 @@ export function UserListManager() {
.eq("id", listId);
if (error) {
toast.error("Failed to delete list");
console.error(error);
handleError(error, {
action: 'Delete List',
userId: user?.id,
metadata: { listId }
});
} else {
toast.success("List deleted");
setLists(lists.filter(l => l.id !== listId));
@@ -147,8 +156,11 @@ export function UserListManager() {
.eq("id", list.id);
if (error) {
toast.error("Failed to update list");
console.error(error);
handleError(error, {
action: 'Toggle List Visibility',
userId: user?.id,
metadata: { listId: list.id }
});
} else {
toast.success(`List is now ${!list.is_public ? "public" : "private"}`);
setLists(lists.map(l =>

View File

@@ -72,7 +72,6 @@ export const QueueItemActions = memo(({
() => {
// Extra guard against race conditions
if (actionLoading === item.id) {
console.warn('⚠️ Action already in progress, ignoring duplicate request');
return;
}
onApprove(item, 'approved', notes[item.id]);
@@ -84,7 +83,6 @@ export const QueueItemActions = memo(({
const handleReject = useDebouncedCallback(
() => {
if (actionLoading === item.id) {
console.warn('⚠️ Action already in progress, ignoring duplicate request');
return;
}
onApprove(item, 'rejected', notes[item.id]);
@@ -128,7 +126,6 @@ export const QueueItemActions = memo(({
const handleReverseApprove = useDebouncedCallback(
() => {
if (actionLoading === item.id) {
console.warn('⚠️ Action already in progress, ignoring duplicate request');
return;
}
onApprove(item, 'approved', notes[`reverse-${item.id}`]);
@@ -140,7 +137,6 @@ export const QueueItemActions = memo(({
const handleReverseReject = useDebouncedCallback(
() => {
if (actionLoading === item.id) {
console.warn('⚠️ Action already in progress, ignoring duplicate request');
return;
}
onApprove(item, 'rejected', notes[`reverse-${item.id}`]);

View File

@@ -1,6 +1,7 @@
import { useState, useEffect } from 'react';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { cn } from '@/lib/utils';
import { logger } from '@/lib/logger';
interface UserAvatarProps {
avatarUrl?: string | null;
@@ -43,7 +44,7 @@ export function UserAvatar({
}, [avatarUrl]);
const handleImageError = () => {
console.warn('[UserAvatar] Image load failed:', imageUrl, 'Retry count:', retryCount);
logger.debug('UserAvatar image load failed', { imageUrl, retryCount });
if (retryCount < MAX_RETRIES && avatarUrl) {
// Add cache-busting parameter and retry
@@ -52,19 +53,19 @@ export function UserAvatar({
? `${avatarUrl}&retry=${retryCount + 1}&t=${Date.now()}`
: `${avatarUrl}${cacheBuster}`;
console.log('[UserAvatar] Retrying with cache buster:', urlWithCacheBuster);
logger.debug('UserAvatar retrying with cache buster', { urlWithCacheBuster });
setRetryCount(prev => prev + 1);
setImageUrl(urlWithCacheBuster);
} else {
// All retries exhausted, show fallback
console.warn('[UserAvatar] All retries exhausted, showing fallback');
logger.debug('UserAvatar all retries exhausted, showing fallback');
setHasError(true);
setIsLoading(false);
}
};
const handleImageLoad = () => {
console.log('[UserAvatar] Image loaded successfully:', imageUrl);
logger.debug('UserAvatar image loaded successfully', { imageUrl });
setIsLoading(false);
setHasError(false);
};

View File

@@ -522,7 +522,6 @@ export function PhotoUpload({
alt={`Existing photo ${index + 1}`}
className="w-full aspect-square object-cover rounded-lg border"
onError={(e) => {
console.error('Failed to load existing image:', url);
e.currentTarget.src = 'data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjQiIGhlaWdodD0iMjQiIHZpZXdCb3g9IjAgMCAyNCAyNCIgZmlsbD0ibm9uZSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHJlY3Qgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0IiBmaWxsPSIjZjNmNGY2Ii8+CjxwYXRoIGQ9Im0xNSAxMi0zLTMtMy4wMDEgM0w2IDlsNi02aDZ2NloiIGZpbGw9IiM5Y2EzYWYiLz4KPC9zdmc+';
}}
/>

View File

@@ -2,6 +2,7 @@ import { useState, useEffect } from 'react';
import { supabase } from '@/integrations/supabase/client';
import { TechnicalSpecFilter, CoasterStatFilter } from '@/components/search/AdvancedRideFilters';
import { useDebounce } from './useDebounce';
import { handleError } from '@/lib/errorHandler';
interface AdvancedSearchOptions {
query?: string;
@@ -151,7 +152,14 @@ export function useAdvancedRideSearch(options: AdvancedSearchOptions) {
setResults(filteredResults);
} catch (err) {
console.error('Advanced search error:', err);
handleError(err, {
action: 'Advanced Ride Search',
metadata: {
query: options.query,
category: options.category,
manufacturer: options.manufacturer
}
});
setError(err instanceof Error ? err.message : 'Search failed');
setResults([]);
} finally {

View File

@@ -3,6 +3,7 @@ import { useAuth } from '@/hooks/useAuth';
import { supabase } from '@/integrations/supabase/client';
import { useNavigate } from 'react-router-dom';
import { toast } from '@/hooks/use-toast';
import { logger } from '@/lib/logger';
export function useBanCheck() {
const { user } = useAuth();
@@ -56,7 +57,7 @@ export function useBanCheck() {
navigate('/');
}
} catch (error) {
console.error('Ban check error:', error);
logger.error('Ban check error', { error, userId: user.id });
} finally {
setLoading(false);
}

View File

@@ -203,7 +203,6 @@ export function useEntityVersions(entityType: EntityType, entityId: string) {
supabase.removeChannel(channelRef.current);
} catch (error: unknown) {
logger.error('Failed to cleanup realtime subscription', { entityType, entityId, error: getErrorMessage(error) });
console.error('Error removing previous channel:', error);
} finally {
channelRef.current = null;
}
@@ -236,7 +235,6 @@ export function useEntityVersions(entityType: EntityType, entityId: string) {
if (channelRef.current) {
supabase.removeChannel(channelRef.current).catch((error) => {
logger.error('Failed to cleanup realtime subscription', { entityType, entityId, error: getErrorMessage(error) });
console.error('Error removing channel:', error);
});
channelRef.current = null;
}

View File

@@ -1,4 +1,5 @@
import { useState, useEffect } from 'react';
import { logger } from '@/lib/logger';
const STORAGE_KEY = 'queue-filter-panel-collapsed';
@@ -21,7 +22,7 @@ export function useFilterPanelState(): UseFilterPanelStateReturn {
const isMobile = window.innerWidth < 768;
return stored ? JSON.parse(stored) : isMobile;
} catch (error) {
console.error('Error reading filter panel state from localStorage:', error);
logger.warn('Error reading filter panel state from localStorage', { error });
return window.innerWidth < 768;
}
});
@@ -31,7 +32,7 @@ export function useFilterPanelState(): UseFilterPanelStateReturn {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(isCollapsed));
} catch (error) {
console.error('Error saving filter panel state to localStorage:', error);
logger.warn('Error saving filter panel state to localStorage', { error });
}
}, [isCollapsed]);

View File

@@ -64,7 +64,6 @@ export function usePhotoSubmissionItems(
setPhotos(data || []);
} catch (error: unknown) {
const errorMsg = getErrorMessage(error);
console.error('Error fetching photo submission items:', errorMsg);
setError(errorMsg);
setPhotos([]);
} finally {

View File

@@ -2,6 +2,7 @@ import { useQuery } from '@tanstack/react-query';
import { supabase } from '@/integrations/supabase/client';
import { useAuth } from '@/hooks/useAuth';
import { queryKeys } from '@/lib/queryKeys';
import { logger } from '@/lib/logger';
export type UserRole = 'admin' | 'moderator' | 'user' | 'superuser';
@@ -29,7 +30,7 @@ export function useUserRole() {
.eq('user_id', user.id);
if (error) {
console.error('Error fetching user roles:', error);
logger.error('Error fetching user roles', { error, userId: user.id });
return [];
}
@@ -50,7 +51,7 @@ export function useUserRole() {
.rpc('get_user_management_permissions', { _user_id: user.id });
if (error) {
console.error('Error fetching user permissions:', error);
logger.error('Error fetching user permissions', { error, userId: user.id });
return null;
}

View File

@@ -1,6 +1,7 @@
import { useState, useEffect } from 'react';
import { supabase } from '@/integrations/supabase/client';
import type { EntityType, VersionDiff } from '@/types/versioning';
import { handleError } from '@/lib/errorHandler';
/**
* Hook to compare two versions of an entity and get the diff
@@ -37,7 +38,10 @@ export function useVersionComparison(
setDiff(data as VersionDiff);
} catch (err) {
console.error('Error fetching version diff:', err);
handleError(err, {
action: 'Compare Versions',
metadata: { entityType, fromVersionId, toVersionId }
});
setError(err instanceof Error ? err.message : 'Failed to compare versions');
setDiff(null);
} finally {

View File

@@ -1,23 +1,18 @@
/**
* Conditional authentication logging utility
* Logs are only shown in development mode
* Uses structured logger internally
*/
const isDevelopment = import.meta.env.DEV;
import { logger } from './logger';
export const authLog = (...args: any[]) => {
if (isDevelopment) {
console.log(...args);
}
export const authLog = (...args: unknown[]) => {
logger.info('[Auth]', ...args);
};
export const authWarn = (...args: any[]) => {
if (isDevelopment) {
console.warn(...args);
}
export const authWarn = (...args: unknown[]) => {
logger.warn('[Auth]', ...args);
};
export const authError = (...args: any[]) => {
// Always log errors
console.error(...args);
export const authError = (...args: unknown[]) => {
logger.error('[Auth] Error occurred', { error: args });
};

View File

@@ -1,5 +1,6 @@
import { supabase } from '@/integrations/supabase/client';
import type { Database } from '@/integrations/supabase/types';
import { logger } from '@/lib/logger';
type TableName = keyof Database['public']['Tables'];
@@ -78,11 +79,11 @@ export class TestDataTracker {
if (error) {
errors.push({ table, error });
console.warn(`Failed to cleanup ${table}:`, error);
logger.warn('Failed to cleanup test data table', { table, error });
}
} catch (err) {
errors.push({ table, error: err });
console.warn(`Exception cleaning up ${table}:`, err);
logger.warn('Exception cleaning up test data table', { table, error: err });
}
}
@@ -90,7 +91,7 @@ export class TestDataTracker {
this.entities.clear();
if (errors.length > 0) {
console.warn(`Cleanup completed with ${errors.length} errors:`, errors);
logger.warn('Cleanup completed with errors', { errorCount: errors.length, errors });
}
}
@@ -116,7 +117,7 @@ export class TestDataTracker {
.eq('is_test_data', true);
if (error) {
console.warn(`Failed to check ${table}:`, error);
logger.warn('Failed to check test data table', { table, error });
continue;
}
@@ -124,7 +125,7 @@ export class TestDataTracker {
remaining.push({ table, count });
}
} catch (err) {
console.warn(`Exception checking ${table}:`, err);
logger.warn('Exception checking test data table', { table, error: err });
}
}
@@ -154,13 +155,13 @@ export class TestDataTracker {
.select('id');
if (error) {
console.warn(`Failed to bulk delete from ${table}:`, error);
logger.warn('Failed to bulk delete test data', { table, error });
totalErrors++;
} else if (data) {
totalDeleted += data.length;
}
} catch (err) {
console.warn(`Exception bulk deleting from ${table}:`, err);
logger.warn('Exception bulk deleting test data', { table, error: err });
totalErrors++;
}
}