diff --git a/src/components/admin/DesignerForm.tsx b/src/components/admin/DesignerForm.tsx index 6a1bb78d..3c9a3066 100644 --- a/src/components/admin/DesignerForm.tsx +++ b/src/components/admin/DesignerForm.tsx @@ -19,6 +19,7 @@ import { FlexibleDateInput, type DatePrecision } from '@/components/ui/flexible- import { submitDesignerCreation, submitDesignerUpdate } from '@/lib/entitySubmissionHelpers'; import { useAuth } from '@/hooks/useAuth'; import { toast } from 'sonner'; +import { handleError } from '@/lib/errorHandler'; import { useNavigate } from 'react-router-dom'; import type { UploadedImage } from '@/types/company'; @@ -113,9 +114,11 @@ export function DesignerForm({ onSubmit, onCancel, initialData }: DesignerFormPr toast.success('Designer submitted for review'); onCancel(); } - } catch (error) { - console.error('Submission error:', error); - toast.error(error instanceof Error ? error.message : 'Failed to submit designer'); + } catch (error: unknown) { + handleError(error, { + action: initialData?.id ? 'Update Designer' : 'Create Designer', + metadata: { companyName: data.name } + }); } finally { setIsSubmitting(false); } diff --git a/src/components/admin/LocationSearch.tsx b/src/components/admin/LocationSearch.tsx index b150a714..7185e2aa 100644 --- a/src/components/admin/LocationSearch.tsx +++ b/src/components/admin/LocationSearch.tsx @@ -122,10 +122,9 @@ export function LocationSearch({ onLocationSelect, initialLocationId, className setResults(data); setShowResults(true); setSearchError(null); - } catch (error) { - const errorMsg = error instanceof Error ? error.message : 'Failed to search locations. Please check your connection.'; - console.error('Error searching locations:', error); - setSearchError(errorMsg); + } catch (error: unknown) { + logger.error('Location search failed', { query: searchQuery }); + setSearchError('Failed to search locations. Please check your connection.'); setResults([]); setShowResults(false); } finally { diff --git a/src/components/admin/ManufacturerForm.tsx b/src/components/admin/ManufacturerForm.tsx index 0a6a8b2e..d9b22f40 100644 --- a/src/components/admin/ManufacturerForm.tsx +++ b/src/components/admin/ManufacturerForm.tsx @@ -19,6 +19,7 @@ import { FlexibleDateInput, type DatePrecision } from '@/components/ui/flexible- import { submitManufacturerCreation, submitManufacturerUpdate } from '@/lib/entitySubmissionHelpers'; import { useAuth } from '@/hooks/useAuth'; import { toast } from 'sonner'; +import { handleError } from '@/lib/errorHandler'; import { useNavigate } from 'react-router-dom'; import { toDateOnly } from '@/lib/dateUtils'; import type { UploadedImage } from '@/types/company'; @@ -116,9 +117,11 @@ export function ManufacturerForm({ onSubmit, onCancel, initialData }: Manufactur toast.success('Manufacturer submitted for review'); onCancel(); } - } catch (error) { - console.error('Submission error:', error); - toast.error(error instanceof Error ? error.message : 'Failed to submit manufacturer'); + } catch (error: unknown) { + handleError(error, { + action: initialData?.id ? 'Update Manufacturer' : 'Create Manufacturer', + metadata: { companyName: data.name } + }); } finally { setIsSubmitting(false); } diff --git a/src/components/admin/OperatorForm.tsx b/src/components/admin/OperatorForm.tsx index 68bbf9e7..b1a40d46 100644 --- a/src/components/admin/OperatorForm.tsx +++ b/src/components/admin/OperatorForm.tsx @@ -19,6 +19,7 @@ import { FlexibleDateInput, type DatePrecision } from '@/components/ui/flexible- import { submitOperatorCreation, submitOperatorUpdate } from '@/lib/entitySubmissionHelpers'; import { useAuth } from '@/hooks/useAuth'; import { toast } from 'sonner'; +import { handleError } from '@/lib/errorHandler'; import { useNavigate } from 'react-router-dom'; import type { UploadedImage } from '@/types/company'; @@ -113,9 +114,11 @@ export function OperatorForm({ onSubmit, onCancel, initialData }: OperatorFormPr toast.success('Operator submitted for review'); onCancel(); } - } catch (error) { - console.error('Submission error:', error); - toast.error(error instanceof Error ? error.message : 'Failed to submit operator'); + } catch (error: unknown) { + handleError(error, { + action: initialData?.id ? 'Update Operator' : 'Create Operator', + metadata: { companyName: data.name } + }); } finally { setIsSubmitting(false); } diff --git a/src/components/admin/ParkForm.tsx b/src/components/admin/ParkForm.tsx index 0c3e0e22..51ef6a59 100644 --- a/src/components/admin/ParkForm.tsx +++ b/src/components/admin/ParkForm.tsx @@ -15,7 +15,7 @@ import { DatePicker } from '@/components/ui/date-picker'; import { FlexibleDateInput, type DatePrecision } from '@/components/ui/flexible-date-input'; import { SlugField } from '@/components/ui/slug-field'; import { toast } from '@/hooks/use-toast'; -import { getErrorMessage } from '@/lib/errorHandler'; +import { handleError } from '@/lib/errorHandler'; import { MapPin, Save, X, Plus } from 'lucide-react'; import { toDateOnly } from '@/lib/dateUtils'; import { Badge } from '@/components/ui/badge'; @@ -193,12 +193,16 @@ export function ParkForm({ onSubmit, onCancel, initialData, isEditing = false }: ? "The park information has been updated successfully." : "The new park has been created successfully." }); - } catch (error) { - const errorMsg = getErrorMessage(error); - toast({ - title: "Error", - description: errorMsg, - variant: "destructive" + } catch (error: unknown) { + handleError(error, { + action: isEditing ? 'Update Park' : 'Create Park', + userId: user?.id, + metadata: { + parkName: data.name, + hasLocation: !!data.location_id, + hasNewOperator: !!tempNewOperator, + hasNewOwner: !!tempNewPropertyOwner + } }); } finally { setSubmitting(false); diff --git a/src/components/admin/PropertyOwnerForm.tsx b/src/components/admin/PropertyOwnerForm.tsx index d55d76d6..a0d98226 100644 --- a/src/components/admin/PropertyOwnerForm.tsx +++ b/src/components/admin/PropertyOwnerForm.tsx @@ -19,6 +19,7 @@ import { FlexibleDateInput, type DatePrecision } from '@/components/ui/flexible- import { submitPropertyOwnerCreation, submitPropertyOwnerUpdate } from '@/lib/entitySubmissionHelpers'; import { useAuth } from '@/hooks/useAuth'; import { toast } from 'sonner'; +import { handleError } from '@/lib/errorHandler'; import { useNavigate } from 'react-router-dom'; import type { UploadedImage } from '@/types/company'; @@ -113,9 +114,11 @@ export function PropertyOwnerForm({ onSubmit, onCancel, initialData }: PropertyO toast.success('Property owner submitted for review'); onCancel(); } - } catch (error) { - console.error('Submission error:', error); - toast.error(error instanceof Error ? error.message : 'Failed to submit property owner'); + } catch (error: unknown) { + handleError(error, { + action: initialData?.id ? 'Update Property Owner' : 'Create Property Owner', + metadata: { companyName: data.name } + }); } finally { setIsSubmitting(false); } diff --git a/src/components/admin/RideForm.tsx b/src/components/admin/RideForm.tsx index c83adf3b..1e256a92 100644 --- a/src/components/admin/RideForm.tsx +++ b/src/components/admin/RideForm.tsx @@ -20,7 +20,7 @@ import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } f import { Combobox } from '@/components/ui/combobox'; import { SlugField } from '@/components/ui/slug-field'; import { toast } from '@/hooks/use-toast'; -import { getErrorMessage } from '@/lib/errorHandler'; +import { handleError } from '@/lib/errorHandler'; import { Plus, Zap, Save, X } from 'lucide-react'; import { toDateOnly } from '@/lib/dateUtils'; import { useUnitPreferences } from '@/hooks/useUnitPreferences'; @@ -264,12 +264,14 @@ export function RideForm({ onSubmit, onCancel, initialData, isEditing = false }: ? "Ride, manufacturer, and model submitted for review" : "Ride submitted for review" }); - } catch (error) { - const errorMsg = getErrorMessage(error); - toast({ - title: "Error", - description: errorMsg, - variant: "destructive" + } catch (error: unknown) { + handleError(error, { + action: isEditing ? 'Update Ride' : 'Create Ride', + metadata: { + rideName: data.name, + hasNewManufacturer: !!tempNewManufacturer, + hasNewModel: !!tempNewRideModel + } }); } finally { setSubmitting(false); diff --git a/src/components/auth/AuthModal.tsx b/src/components/auth/AuthModal.tsx index 7d05bf07..2a556352 100644 --- a/src/components/auth/AuthModal.tsx +++ b/src/components/auth/AuthModal.tsx @@ -8,7 +8,7 @@ import { Separator } from '@/components/ui/separator'; import { Zap, Mail, Lock, User, Eye, EyeOff } from 'lucide-react'; import { supabase } from '@/integrations/supabase/client'; import { useToast } from '@/hooks/use-toast'; -import { getErrorMessage } from '@/lib/errorHandler'; +import { handleError } from '@/lib/errorHandler'; import { TurnstileCaptcha } from './TurnstileCaptcha'; import { notificationService } from '@/lib/notificationService'; import { useCaptchaBypass } from '@/hooks/useCaptchaBypass'; @@ -121,12 +121,15 @@ export function AuthModal({ open, onOpenChange, defaultTab = 'signin' }: AuthMod await new Promise(resolve => setTimeout(resolve, 100)); onOpenChange(false); - } catch (error) { + } catch (error: unknown) { setSignInCaptchaKey(prev => prev + 1); - toast({ - variant: "destructive", - title: "Sign in failed", - description: getErrorMessage(error) + handleError(error, { + action: 'Sign In', + metadata: { + method: 'password', + hasCaptcha: !!tokenToUse + // ⚠️ NEVER log: email, password, tokens + } }); } finally { setLoading(false); @@ -249,12 +252,15 @@ export function AuthModal({ open, onOpenChange, defaultTab = 'signin' }: AuthMod description: "Please check your email to verify your account." }); onOpenChange(false); - } catch (error) { + } catch (error: unknown) { setCaptchaKey(prev => prev + 1); - toast({ - variant: "destructive", - title: "Sign up failed", - description: getErrorMessage(error) + handleError(error, { + action: 'Sign Up', + metadata: { + hasCaptcha: !!tokenToUse, + hasUsername: !!formData.username + // ⚠️ NEVER log: email, password, username + } }); } finally { setLoading(false); @@ -288,11 +294,13 @@ export function AuthModal({ open, onOpenChange, defaultTab = 'signin' }: AuthMod description: "Check your email for a sign-in link." }); onOpenChange(false); - } catch (error) { - toast({ - variant: "destructive", - title: "Failed to send magic link", - description: getErrorMessage(error) + } catch (error: unknown) { + handleError(error, { + action: 'Send Magic Link', + metadata: { + method: 'magic_link' + // ⚠️ NEVER log: email, link + } }); } finally { setMagicLinkLoading(false); @@ -312,11 +320,13 @@ export function AuthModal({ open, onOpenChange, defaultTab = 'signin' }: AuthMod } }); if (error) throw error; - } catch (error) { - toast({ - variant: "destructive", - title: "Social sign in failed", - description: getErrorMessage(error) + } catch (error: unknown) { + handleError(error, { + action: 'Social Sign In', + metadata: { + provider, + method: 'oauth' + } }); } }; diff --git a/src/components/moderation/SubmissionReviewManager.tsx b/src/components/moderation/SubmissionReviewManager.tsx index d4a4f80b..7dd481cc 100644 --- a/src/components/moderation/SubmissionReviewManager.tsx +++ b/src/components/moderation/SubmissionReviewManager.tsx @@ -2,7 +2,7 @@ import { useState, useEffect } from 'react'; import { useToast } from '@/hooks/use-toast'; import { useUserRole } from '@/hooks/useUserRole'; import { useAuth } from '@/hooks/useAuth'; -import { getErrorMessage } from '@/lib/errorHandler'; +import { handleError } from '@/lib/errorHandler'; import { fetchSubmissionItems, buildDependencyTree, @@ -108,11 +108,11 @@ export function SubmissionReviewManager({ .filter(item => item.status === 'pending') .map(item => item.id); setSelectedItemIds(new Set(pendingIds)); - } catch (error) { - toast({ - title: 'Error', - description: getErrorMessage(error), - variant: 'destructive', + } catch (error: unknown) { + handleError(error, { + action: 'Load Submission Items', + userId: user?.id, + metadata: { submissionId, submissionType } }); } finally { setLoading(false); @@ -147,11 +147,11 @@ export function SubmissionReviewManager({ // No conflicts, proceed with approval handleApprove(); } - } catch (error) { - toast({ - title: 'Error', - description: getErrorMessage(error), - variant: 'destructive', + } catch (error: unknown) { + handleError(error, { + action: 'Check Dependency Conflicts', + userId: user?.id, + metadata: { submissionId, selectedCount: selectedItemIds.size } }); } finally { setLoading(false); @@ -253,12 +253,16 @@ export function SubmissionReviewManager({ onComplete(); onOpenChange(false); - } catch (error) { - console.error('Error approving items:', error); - toast({ - title: 'Error', - description: getErrorMessage(error), - variant: 'destructive', + } catch (error: unknown) { + handleError(error, { + action: 'Approve Submission Items', + userId: user?.id, + metadata: { + submissionId, + itemCount: selectedItemIds.size, + hasWarnings: userConfirmedWarnings, + hasBlockingErrors + } }); } finally { setLoading(false); @@ -308,12 +312,16 @@ export function SubmissionReviewManager({ onComplete(); onOpenChange(false); - } catch (error) { - console.error('Error rejecting items:', error); - toast({ - title: 'Error', - description: getErrorMessage(error), - variant: 'destructive', + } catch (error: unknown) { + handleError(error, { + action: 'Reject Submission Items', + userId: user?.id, + metadata: { + submissionId, + itemCount: selectedItemIds.size, + cascade, + reason: reason.substring(0, 100) + } }); } finally { setLoading(false); @@ -361,12 +369,14 @@ export function SubmissionReviewManager({ onComplete(); onOpenChange(false); - } catch (error) { - console.error('Error escalating submission:', error); - toast({ - title: 'Error', - description: getErrorMessage(error), - variant: 'destructive', + } catch (error: unknown) { + handleError(error, { + action: 'Escalate Submission', + userId: user?.id, + metadata: { + submissionId, + reason: reason.substring(0, 100) + } }); } finally { setLoading(false); @@ -429,12 +439,15 @@ export function SubmissionReviewManager({ } await loadSubmissionItems(); - } catch (error) { - console.error('Error changing item status:', error); - toast({ - title: 'Error', - description: getErrorMessage(error), - variant: 'destructive', + } catch (error: unknown) { + handleError(error, { + action: `${status === 'approved' ? 'Approve' : 'Reject'} Item`, + userId: user?.id, + metadata: { + submissionId, + itemId, + status + } }); } finally { setLoading(false); @@ -557,11 +570,11 @@ export function SubmissionReviewManager({ }); onComplete(); onOpenChange(false); - } catch (error) { - toast({ - title: 'Error', - description: getErrorMessage(error), - variant: 'destructive', + } catch (error: unknown) { + handleError(error, { + action: 'Archive Corrupted Submission', + userId: user?.id, + metadata: { submissionId } }); } }} diff --git a/src/components/profile/RideCreditsManager.tsx b/src/components/profile/RideCreditsManager.tsx index b17e0e14..71e16ef2 100644 --- a/src/components/profile/RideCreditsManager.tsx +++ b/src/components/profile/RideCreditsManager.tsx @@ -5,7 +5,7 @@ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@ import { Plus, LayoutGrid, List, GripVertical } from 'lucide-react'; import { supabase } from '@/integrations/supabase/client'; import { toast } from 'sonner'; -import { getErrorMessage } from '@/lib/errorHandler'; +import { handleError } from '@/lib/errorHandler'; import { AddRideCreditDialog } from './AddRideCreditDialog'; import { RideCreditCard } from './RideCreditCard'; import { SortableRideCreditCard } from './SortableRideCreditCard'; @@ -135,9 +135,12 @@ export function RideCreditsManager({ userId }: RideCreditsManagerProps) { } setCredits(processedData); - } catch (error) { - console.error('Error fetching ride credits:', error); - toast.error(getErrorMessage(error)); + } catch (error: unknown) { + handleError(error, { + action: 'Fetch Ride Credits', + userId, + metadata: { sortBy } + }); } finally { setLoading(false); } @@ -196,9 +199,12 @@ export function RideCreditsManager({ userId }: RideCreditsManagerProps) { // Add to existing array setCredits(prev => [...prev, newCredit]); setIsAddDialogOpen(false); - } catch (error) { - console.error('Error fetching new credit:', error); - toast.error(getErrorMessage(error)); + } catch (error: unknown) { + handleError(error, { + action: 'Add Ride Credit', + userId, + metadata: { creditId: newCreditId } + }); } }; @@ -226,9 +232,12 @@ export function RideCreditsManager({ userId }: RideCreditsManagerProps) { if (error) throw error; toast.success('Ride credit removed'); - } catch (error) { - console.error('Error deleting credit:', error); - toast.error(getErrorMessage(error)); + } catch (error: unknown) { + handleError(error, { + action: 'Delete Ride Credit', + userId, + metadata: { creditId } + }); // Rollback on error if (deletedCredit) { @@ -247,8 +256,12 @@ export function RideCreditsManager({ userId }: RideCreditsManagerProps) { if (error) throw error; // No refetch - optimistic update is already applied - } catch (error) { - console.error('Error reordering credit:', error); + } catch (error: unknown) { + handleError(error, { + action: 'Reorder Ride Credit', + userId, + metadata: { creditId, newPosition } + }); throw error; } }; @@ -273,8 +286,17 @@ export function RideCreditsManager({ userId }: RideCreditsManagerProps) { try { await handleReorder(String(active.id), newIndex + 1); toast.success('Order updated'); - } catch (error) { - toast.error(getErrorMessage(error)); + } catch (error: unknown) { + handleError(error, { + action: 'Drag Reorder Ride Credit', + userId, + metadata: { + activeId: String(active.id), + overId: String(over.id), + oldIndex, + newIndex + } + }); // Rollback to old order setCredits(oldCredits); }