feat: Complete app-wide error coverage

This commit is contained in:
gpt-engineer-app[bot]
2025-11-04 19:58:20 +00:00
parent d9bd7c1616
commit a649906b61
22 changed files with 111 additions and 57 deletions

View File

@@ -6,7 +6,7 @@
*/
import { z } from 'zod';
import { logger } from '@/lib/logger';
import { handleError } from '@/lib/errorHandler';
// Profile schema (matches database JSONB structure)
const ProfileSchema = z.object({
@@ -101,8 +101,11 @@ export function validateModerationItems(data: unknown): {
const result = ModerationItemArraySchema.safeParse(data);
if (!result.success) {
logger.error('❌ Data validation failed', {
errors: result.error.issues.slice(0, 5) // Log first 5 issues
handleError(result.error, {
action: 'Data validation failed',
metadata: {
errors: result.error.issues.slice(0, 5)
}
});
return {

View File

@@ -5,7 +5,7 @@
*/
import { supabase } from '@/lib/supabaseClient';
import { logger } from './logger';
import { handleError, handleNonCriticalError } from './errorHandler';
export interface SubmissionMetadataInsert {
submission_id: string;
@@ -37,7 +37,10 @@ export async function writeSubmissionMetadata(
.insert(entries);
if (error) {
logger.error('Failed to write submission metadata', { error, submissionId });
handleError(error, {
action: 'Write submission metadata',
metadata: { submissionId }
});
throw error;
}
}
@@ -56,7 +59,10 @@ export async function readSubmissionMetadata(
.order('display_order');
if (error) {
logger.error('Failed to read submission metadata', { error, submissionId });
handleNonCriticalError(error, {
action: 'Read submission metadata',
metadata: { submissionId }
});
return {};
}

View File

@@ -1,6 +1,6 @@
import { supabase } from '@/lib/supabaseClient';
import type { ParkSubmissionData, RideSubmissionData, CompanySubmissionData, RideModelSubmissionData } from '@/types/submission-data';
import { logger } from './logger';
import { handleNonCriticalError } from './errorHandler';
import {
randomInt,
randomFloat,
@@ -352,12 +352,18 @@ export async function clearTestData(): Promise<{ deleted: number }> {
.neq('id', '00000000-0000-0000-0000-000000000000'); // Delete all records
if (registryError) {
logger.error('Error clearing test data registry', { error: registryError });
handleNonCriticalError(registryError, {
action: 'Clear test data registry',
metadata: { operation: 'clearTestData' }
});
}
return { deleted: submissionCount };
} catch (error: unknown) {
logger.error('Error clearing test data', { error: error instanceof Error ? error.message : String(error) });
handleNonCriticalError(error, {
action: 'Clear test data',
metadata: { operation: 'clearTestData' }
});
throw error;
}
}

View File

@@ -11,7 +11,7 @@
import { supabase } from '@/lib/supabaseClient';
import type { EntityType } from '@/types/versioning';
import { createTableQuery } from './supabaseHelpers';
import { logger } from './logger';
import { handleNonCriticalError } from './errorHandler';
/**
* Manually trigger cleanup of old versions for a specific entity type
@@ -38,7 +38,10 @@ export async function cleanupVersions(
});
if (error) {
logger.error('Version cleanup failed', { error, entityType, keepCount });
handleNonCriticalError(error, {
action: 'Version cleanup',
metadata: { entityType, keepCount }
});
return 0;
}
@@ -98,7 +101,10 @@ export async function getVersionStats(
const { data, error } = result;
if (error || !data) {
logger.error('Failed to fetch version stats', { error, entityType, entityId });
handleNonCriticalError(error || new Error('No data returned'), {
action: 'Fetch version stats',
metadata: { entityType, entityId }
});
return null;
}

View File

@@ -1,5 +1,5 @@
import { supabase } from '@/lib/supabaseClient';
import { logger } from './logger';
import { handleNonCriticalError } from './errorHandler';
// Generate anonymous session hash (no PII)
function getSessionHash(): string {
@@ -41,6 +41,9 @@ export async function trackPageView(
});
} catch (error: unknown) {
// Fail silently - don't break the page if tracking fails
logger.error('Failed to track page view', { entityType, entityId });
handleNonCriticalError(error, {
action: 'Track page view',
metadata: { entityType, entityId }
});
}
}

View File

@@ -12,7 +12,7 @@ import { ArrowLeft, MapPin, Star, Globe, Calendar, Edit, Ruler } from 'lucide-re
import { Company } from '@/types/database';
import { supabase } from '@/lib/supabaseClient';
import { DesignerPhotoGallery } from '@/components/companies/DesignerPhotoGallery';
import { logger } from '@/lib/logger';
import { handleNonCriticalError } from '@/lib/errorHandler';
// Lazy load admin form
const DesignerForm = lazy(() => import('@/components/admin/DesignerForm').then(m => ({ default: m.DesignerForm })));
@@ -82,7 +82,10 @@ export default function DesignerDetail() {
fetchStatistics(data.id);
}
} catch (error) {
logger.error('Error fetching designer', { error });
handleNonCriticalError(error, {
action: 'Fetch designer',
metadata: { slug }
});
} finally {
setLoading(false);
}
@@ -109,7 +112,10 @@ export default function DesignerDetail() {
if (photosError) throw photosError;
setTotalPhotos(photosCount || 0);
} catch (error) {
logger.error('Error fetching statistics', { error });
handleNonCriticalError(error, {
action: 'Fetch designer statistics',
metadata: { designerId }
});
} finally {
setStatsLoading(false);
}

View File

@@ -17,7 +17,7 @@ import { toast } from '@/hooks/use-toast';
import { useAuthModal } from '@/hooks/useAuthModal';
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
import { useOpenGraph } from '@/hooks/useOpenGraph';
import { logger } from '@/lib/logger';
import { handleNonCriticalError } from '@/lib/errorHandler';
export default function DesignerRides() {
const { designerSlug } = useParams<{ designerSlug: string }>();
@@ -91,7 +91,10 @@ export default function DesignerRides() {
setRides((ridesData || []) as any);
}
} catch (error) {
logger.error('Error fetching data', { error });
handleNonCriticalError(error, {
action: 'Fetch designer rides',
metadata: { designerSlug }
});
} finally {
setLoading(false);
}

View File

@@ -18,7 +18,7 @@ import { DesignerCard } from '@/components/designers/DesignerCard';
import { DesignerListView } from '@/components/designers/DesignerListView';
import { DesignerForm } from '@/components/admin/DesignerForm';
import { useAuth } from '@/hooks/useAuth';
import { logger } from '@/lib/logger';
import { handleNonCriticalError } from '@/lib/errorHandler';
import { useUserRole } from '@/hooks/useUserRole';
import { toast } from '@/hooks/use-toast';
import { submitCompanyCreation } from '@/lib/companyHelpers';
@@ -89,7 +89,10 @@ export default function Designers() {
const { data } = await query;
setCompanies(data || []);
} catch (error) {
logger.error('Error fetching companies', { error });
handleNonCriticalError(error, {
action: 'Fetch designers',
metadata: { page: 'designers' }
});
} finally {
setLoading(false);
}

View File

@@ -3,7 +3,7 @@ import { useNavigate } from "react-router-dom";
import { supabase } from "@/lib/supabaseClient";
import { authStorage } from "@/lib/authStorage";
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
import { logger } from '@/lib/logger';
import { handleError } from '@/lib/errorHandler';
/**
* ForceLogout - Hidden endpoint for completely clearing auth session
@@ -16,26 +16,23 @@ const ForceLogout = () => {
useEffect(() => {
const performFullLogout = async () => {
logger.info('[ForceLogout] Starting complete auth cleanup');
try {
// 1. Sign out from Supabase
logger.info('[ForceLogout] Signing out from Supabase');
await supabase.auth.signOut();
// 2. Clear all auth-related storage
logger.info('[ForceLogout] Clearing all auth storage');
authStorage.clearAll();
// 3. Brief delay to ensure cleanup completes
await new Promise(resolve => setTimeout(resolve, 500));
logger.info('[ForceLogout] Auth cleanup complete, redirecting to home');
// 4. Redirect to home page
navigate('/', { replace: true });
} catch (error) {
logger.error('[ForceLogout] Error during logout', { error });
handleError(error, {
action: 'Force logout',
metadata: { operation: 'forceLogout' }
});
// Still redirect even if there's an error
navigate('/', { replace: true });
}

View File

@@ -13,7 +13,7 @@ import { ArrowLeft, MapPin, Star, Globe, Calendar, Edit, Factory, FerrisWheel }
import { Company } from '@/types/database';
import { supabase } from '@/lib/supabaseClient';
import { ManufacturerPhotoGallery } from '@/components/companies/ManufacturerPhotoGallery';
import { logger } from '@/lib/logger';
import { handleNonCriticalError } from '@/lib/errorHandler';
// Lazy load admin form
const ManufacturerForm = lazy(() => import('@/components/admin/ManufacturerForm').then(m => ({ default: m.ManufacturerForm })));
@@ -83,7 +83,10 @@ export default function ManufacturerDetail() {
fetchStatistics(data.id);
}
} catch (error) {
logger.error('Error fetching manufacturer', { error });
handleNonCriticalError(error, {
action: 'Fetch manufacturer',
metadata: { slug }
});
} finally {
setLoading(false);
}
@@ -119,7 +122,10 @@ export default function ManufacturerDetail() {
if (photosError) throw photosError;
setTotalPhotos(photosCount || 0);
} catch (error) {
logger.error('Error fetching statistics', { error });
handleNonCriticalError(error, {
action: 'Fetch manufacturer statistics',
metadata: { manufacturerId }
});
} finally {
setStatsLoading(false);
}

View File

@@ -17,7 +17,7 @@ import { toast } from '@/hooks/use-toast';
import { useAuthModal } from '@/hooks/useAuthModal';
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
import { useOpenGraph } from '@/hooks/useOpenGraph';
import { logger } from '@/lib/logger';
import { handleNonCriticalError } from '@/lib/errorHandler';
interface RideModelWithCount extends RideModel {
ride_count: number;
@@ -86,7 +86,10 @@ export default function ManufacturerModels() {
setModels(modelsWithCounts);
}
} catch (error) {
logger.error('Error fetching data', { error });
handleNonCriticalError(error, {
action: 'Fetch manufacturer models',
metadata: { manufacturerSlug }
});
} finally {
setLoading(false);
}

View File

@@ -17,7 +17,7 @@ import { toast } from '@/hooks/use-toast';
import { useAuthModal } from '@/hooks/useAuthModal';
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
import { useOpenGraph } from '@/hooks/useOpenGraph';
import { logger } from '@/lib/logger';
import { handleNonCriticalError } from '@/lib/errorHandler';
export default function ManufacturerRides() {
const { manufacturerSlug } = useParams<{ manufacturerSlug: string }>();
@@ -91,7 +91,10 @@ export default function ManufacturerRides() {
setRides((ridesData || []) as any);
}
} catch (error) {
logger.error('Error fetching data', { error });
handleNonCriticalError(error, {
action: 'Fetch manufacturer rides',
metadata: { manufacturerSlug }
});
} finally {
setLoading(false);
}

View File

@@ -18,7 +18,7 @@ import { ManufacturerCard } from '@/components/manufacturers/ManufacturerCard';
import { ManufacturerListView } from '@/components/manufacturers/ManufacturerListView';
import { ManufacturerForm } from '@/components/admin/ManufacturerForm';
import { useAuth } from '@/hooks/useAuth';
import { logger } from '@/lib/logger';
import { handleNonCriticalError } from '@/lib/errorHandler';
import { useUserRole } from '@/hooks/useUserRole';
import { toast } from '@/hooks/use-toast';
import { submitCompanyCreation } from '@/lib/companyHelpers';
@@ -76,7 +76,10 @@ export default function Manufacturers() {
const { data } = await query;
setCompanies(data || []);
} catch (error) {
logger.error('Error fetching companies', { error });
handleNonCriticalError(error, {
action: 'Fetch manufacturers',
metadata: { page: 'manufacturers' }
});
} finally {
setLoading(false);
}

View File

@@ -14,7 +14,7 @@ import { Company, Park } from '@/types/database';
import { supabase } from '@/lib/supabaseClient';
import { OperatorPhotoGallery } from '@/components/companies/OperatorPhotoGallery';
import { ParkCard } from '@/components/parks/ParkCard';
import { logger } from '@/lib/logger';
import { handleNonCriticalError } from '@/lib/errorHandler';
// Lazy load admin form
const OperatorForm = lazy(() => import('@/components/admin/OperatorForm').then(m => ({ default: m.OperatorForm })));

View File

@@ -17,7 +17,7 @@ import { Grid3X3, List } from 'lucide-react';
import { FilterState, SortState } from './Parks';
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
import { useOpenGraph } from '@/hooks/useOpenGraph';
import { logger } from '@/lib/logger';
import { handleNonCriticalError } from '@/lib/errorHandler';
const initialFilters: FilterState = {
search: '',

View File

@@ -17,7 +17,7 @@ import { Grid3X3, List } from 'lucide-react';
import { FilterState, SortState } from './Parks';
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
import { useOpenGraph } from '@/hooks/useOpenGraph';
import { logger } from '@/lib/logger';
import { handleNonCriticalError } from '@/lib/errorHandler';
const initialFilters: FilterState = {
search: '',

View File

@@ -14,8 +14,7 @@ import { RideSubmissionData } from '@/types/submission-data';
import { supabase } from '@/lib/supabaseClient';
import { useAuth } from '@/hooks/useAuth';
import { toast } from '@/hooks/use-toast';
import { getErrorMessage } from '@/lib/errorHandler';
import { logger } from '@/lib/logger';
import { getErrorMessage, handleNonCriticalError } from '@/lib/errorHandler';
import { useAuthModal } from '@/hooks/useAuthModal';
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
import { useOpenGraph } from '@/hooks/useOpenGraph';

View File

@@ -23,8 +23,7 @@ import { User, MapPin, Calendar, Star, Trophy, Settings, Camera, Edit3, Save, X,
import { Profile as ProfileType } from '@/types/database';
import { supabase } from '@/lib/supabaseClient';
import { useToast } from '@/hooks/use-toast';
import { logger } from '@/lib/logger';
import { getErrorMessage } from '@/lib/errorHandler';
import { getErrorMessage, handleNonCriticalError } from '@/lib/errorHandler';
import { PhotoUpload } from '@/components/upload/PhotoUpload';
import { profileEditSchema } from '@/lib/validation';
import { LocationDisplay } from '@/components/profile/LocationDisplay';

View File

@@ -14,7 +14,7 @@ import { Company, Park } from '@/types/database';
import { supabase } from '@/lib/supabaseClient';
import { PropertyOwnerPhotoGallery } from '@/components/companies/PropertyOwnerPhotoGallery';
import { ParkCard } from '@/components/parks/ParkCard';
import { logger } from '@/lib/logger';
import { handleNonCriticalError } from '@/lib/errorHandler';
// Lazy load admin form
const PropertyOwnerForm = lazy(() => import('@/components/admin/PropertyOwnerForm').then(m => ({ default: m.PropertyOwnerForm })));

View File

@@ -15,8 +15,7 @@ import { getCloudflareImageUrl } from '@/lib/cloudflareImageUtils';
import { useAuthModal } from '@/hooks/useAuthModal';
import { useAuth } from '@/hooks/useAuth';
import { toast } from '@/hooks/use-toast';
import { getErrorMessage } from '@/lib/errorHandler';
import { logger } from '@/lib/logger';
import { getErrorMessage, handleNonCriticalError } from '@/lib/errorHandler';
import { ManufacturerPhotoGallery } from '@/components/companies/ManufacturerPhotoGallery';
// Lazy load admin form

View File

@@ -14,8 +14,7 @@ import { RideForm } from '@/components/admin/RideForm';
import { useAuth } from '@/hooks/useAuth';
import { useAuthModal } from '@/hooks/useAuthModal';
import { toast } from '@/hooks/use-toast';
import { getErrorMessage } from '@/lib/errorHandler';
import { logger } from '@/lib/logger';
import { getErrorMessage, handleNonCriticalError } from '@/lib/errorHandler';
import type { Ride, Company, RideModel } from "@/types/database";
import { useDocumentTitle } from '@/hooks/useDocumentTitle';

View File

@@ -75,8 +75,7 @@ import { DialogFooter } from '@/components/ui/dialog';
import { useTheme } from '@/components/theme/ThemeProvider';
import { useUserRole } from '@/hooks/useUserRole';
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
import { handleError, handleSuccess } from '@/lib/errorHandler';
import { logger } from '@/lib/logger';
import { handleError, handleSuccess, handleNonCriticalError } from '@/lib/errorHandler';
import { contactCategories } from '@/lib/contactValidation';
import { invokeWithTracking } from '@/lib/edgeFunctionTracking';
import { AdminLayout } from '@/components/layout/AdminLayout';
@@ -199,7 +198,10 @@ export default function AdminContact() {
const { data, error } = await query;
if (error) {
logger.error('Failed to fetch contact submissions', { error: error.message });
handleNonCriticalError(error, {
action: 'Fetch contact submissions',
metadata: { error: error.message }
});
throw error;
}
@@ -219,7 +221,10 @@ export default function AdminContact() {
.order('created_at', { ascending: true })
.then(({ data, error }) => {
if (error) {
logger.error('Failed to fetch email threads', { error });
handleNonCriticalError(error, {
action: 'Fetch email threads',
metadata: { submissionId: selectedSubmission.id }
});
setEmailThreads([]);
} else {
setEmailThreads((data as EmailThread[]) || []);
@@ -263,7 +268,10 @@ export default function AdminContact() {
.eq('id', submissionId);
if (statusError) {
logger.error('Failed to update status', { error: statusError });
handleError(statusError, {
action: 'Update contact status',
metadata: { submissionId, newStatus }
});
throw statusError;
}
}
@@ -465,8 +473,10 @@ export default function AdminContact() {
.order('created_at', { ascending: true })
.then(({ data, error }) => {
if (error) {
logger.error('Failed to refresh email threads', { error });
handleError(error, { action: 'Refresh Email Threads' });
handleError(error, {
action: 'Refresh Email Threads',
metadata: { submissionId: selectedSubmission.id }
});
} else {
setEmailThreads((data as EmailThread[]) || []);
handleSuccess('Refreshed', 'Email thread updated');