mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 17:11:13 -05:00
feat: Complete app-wide error coverage
This commit is contained in:
@@ -6,7 +6,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
import { logger } from '@/lib/logger';
|
import { handleError } from '@/lib/errorHandler';
|
||||||
|
|
||||||
// Profile schema (matches database JSONB structure)
|
// Profile schema (matches database JSONB structure)
|
||||||
const ProfileSchema = z.object({
|
const ProfileSchema = z.object({
|
||||||
@@ -101,8 +101,11 @@ export function validateModerationItems(data: unknown): {
|
|||||||
const result = ModerationItemArraySchema.safeParse(data);
|
const result = ModerationItemArraySchema.safeParse(data);
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
logger.error('❌ Data validation failed', {
|
handleError(result.error, {
|
||||||
errors: result.error.issues.slice(0, 5) // Log first 5 issues
|
action: 'Data validation failed',
|
||||||
|
metadata: {
|
||||||
|
errors: result.error.issues.slice(0, 5)
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { supabase } from '@/lib/supabaseClient';
|
import { supabase } from '@/lib/supabaseClient';
|
||||||
import { logger } from './logger';
|
import { handleError, handleNonCriticalError } from './errorHandler';
|
||||||
|
|
||||||
export interface SubmissionMetadataInsert {
|
export interface SubmissionMetadataInsert {
|
||||||
submission_id: string;
|
submission_id: string;
|
||||||
@@ -37,7 +37,10 @@ export async function writeSubmissionMetadata(
|
|||||||
.insert(entries);
|
.insert(entries);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
logger.error('Failed to write submission metadata', { error, submissionId });
|
handleError(error, {
|
||||||
|
action: 'Write submission metadata',
|
||||||
|
metadata: { submissionId }
|
||||||
|
});
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -56,7 +59,10 @@ export async function readSubmissionMetadata(
|
|||||||
.order('display_order');
|
.order('display_order');
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
logger.error('Failed to read submission metadata', { error, submissionId });
|
handleNonCriticalError(error, {
|
||||||
|
action: 'Read submission metadata',
|
||||||
|
metadata: { submissionId }
|
||||||
|
});
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { supabase } from '@/lib/supabaseClient';
|
import { supabase } from '@/lib/supabaseClient';
|
||||||
import type { ParkSubmissionData, RideSubmissionData, CompanySubmissionData, RideModelSubmissionData } from '@/types/submission-data';
|
import type { ParkSubmissionData, RideSubmissionData, CompanySubmissionData, RideModelSubmissionData } from '@/types/submission-data';
|
||||||
import { logger } from './logger';
|
import { handleNonCriticalError } from './errorHandler';
|
||||||
import {
|
import {
|
||||||
randomInt,
|
randomInt,
|
||||||
randomFloat,
|
randomFloat,
|
||||||
@@ -352,12 +352,18 @@ export async function clearTestData(): Promise<{ deleted: number }> {
|
|||||||
.neq('id', '00000000-0000-0000-0000-000000000000'); // Delete all records
|
.neq('id', '00000000-0000-0000-0000-000000000000'); // Delete all records
|
||||||
|
|
||||||
if (registryError) {
|
if (registryError) {
|
||||||
logger.error('Error clearing test data registry', { error: registryError });
|
handleNonCriticalError(registryError, {
|
||||||
|
action: 'Clear test data registry',
|
||||||
|
metadata: { operation: 'clearTestData' }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return { deleted: submissionCount };
|
return { deleted: submissionCount };
|
||||||
} catch (error: unknown) {
|
} 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;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
import { supabase } from '@/lib/supabaseClient';
|
import { supabase } from '@/lib/supabaseClient';
|
||||||
import type { EntityType } from '@/types/versioning';
|
import type { EntityType } from '@/types/versioning';
|
||||||
import { createTableQuery } from './supabaseHelpers';
|
import { createTableQuery } from './supabaseHelpers';
|
||||||
import { logger } from './logger';
|
import { handleNonCriticalError } from './errorHandler';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Manually trigger cleanup of old versions for a specific entity type
|
* Manually trigger cleanup of old versions for a specific entity type
|
||||||
@@ -38,7 +38,10 @@ export async function cleanupVersions(
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
logger.error('Version cleanup failed', { error, entityType, keepCount });
|
handleNonCriticalError(error, {
|
||||||
|
action: 'Version cleanup',
|
||||||
|
metadata: { entityType, keepCount }
|
||||||
|
});
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,7 +101,10 @@ export async function getVersionStats(
|
|||||||
const { data, error } = result;
|
const { data, error } = result;
|
||||||
|
|
||||||
if (error || !data) {
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { supabase } from '@/lib/supabaseClient';
|
import { supabase } from '@/lib/supabaseClient';
|
||||||
import { logger } from './logger';
|
import { handleNonCriticalError } from './errorHandler';
|
||||||
|
|
||||||
// Generate anonymous session hash (no PII)
|
// Generate anonymous session hash (no PII)
|
||||||
function getSessionHash(): string {
|
function getSessionHash(): string {
|
||||||
@@ -41,6 +41,9 @@ export async function trackPageView(
|
|||||||
});
|
});
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
// Fail silently - don't break the page if tracking fails
|
// 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 }
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import { ArrowLeft, MapPin, Star, Globe, Calendar, Edit, Ruler } from 'lucide-re
|
|||||||
import { Company } from '@/types/database';
|
import { Company } from '@/types/database';
|
||||||
import { supabase } from '@/lib/supabaseClient';
|
import { supabase } from '@/lib/supabaseClient';
|
||||||
import { DesignerPhotoGallery } from '@/components/companies/DesignerPhotoGallery';
|
import { DesignerPhotoGallery } from '@/components/companies/DesignerPhotoGallery';
|
||||||
import { logger } from '@/lib/logger';
|
import { handleNonCriticalError } from '@/lib/errorHandler';
|
||||||
|
|
||||||
// Lazy load admin form
|
// Lazy load admin form
|
||||||
const DesignerForm = lazy(() => import('@/components/admin/DesignerForm').then(m => ({ default: m.DesignerForm })));
|
const DesignerForm = lazy(() => import('@/components/admin/DesignerForm').then(m => ({ default: m.DesignerForm })));
|
||||||
@@ -82,7 +82,10 @@ export default function DesignerDetail() {
|
|||||||
fetchStatistics(data.id);
|
fetchStatistics(data.id);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error fetching designer', { error });
|
handleNonCriticalError(error, {
|
||||||
|
action: 'Fetch designer',
|
||||||
|
metadata: { slug }
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@@ -109,7 +112,10 @@ export default function DesignerDetail() {
|
|||||||
if (photosError) throw photosError;
|
if (photosError) throw photosError;
|
||||||
setTotalPhotos(photosCount || 0);
|
setTotalPhotos(photosCount || 0);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error fetching statistics', { error });
|
handleNonCriticalError(error, {
|
||||||
|
action: 'Fetch designer statistics',
|
||||||
|
metadata: { designerId }
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setStatsLoading(false);
|
setStatsLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { toast } from '@/hooks/use-toast';
|
|||||||
import { useAuthModal } from '@/hooks/useAuthModal';
|
import { useAuthModal } from '@/hooks/useAuthModal';
|
||||||
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
||||||
import { useOpenGraph } from '@/hooks/useOpenGraph';
|
import { useOpenGraph } from '@/hooks/useOpenGraph';
|
||||||
import { logger } from '@/lib/logger';
|
import { handleNonCriticalError } from '@/lib/errorHandler';
|
||||||
|
|
||||||
export default function DesignerRides() {
|
export default function DesignerRides() {
|
||||||
const { designerSlug } = useParams<{ designerSlug: string }>();
|
const { designerSlug } = useParams<{ designerSlug: string }>();
|
||||||
@@ -91,7 +91,10 @@ export default function DesignerRides() {
|
|||||||
setRides((ridesData || []) as any);
|
setRides((ridesData || []) as any);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error fetching data', { error });
|
handleNonCriticalError(error, {
|
||||||
|
action: 'Fetch designer rides',
|
||||||
|
metadata: { designerSlug }
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { DesignerCard } from '@/components/designers/DesignerCard';
|
|||||||
import { DesignerListView } from '@/components/designers/DesignerListView';
|
import { DesignerListView } from '@/components/designers/DesignerListView';
|
||||||
import { DesignerForm } from '@/components/admin/DesignerForm';
|
import { DesignerForm } from '@/components/admin/DesignerForm';
|
||||||
import { useAuth } from '@/hooks/useAuth';
|
import { useAuth } from '@/hooks/useAuth';
|
||||||
import { logger } from '@/lib/logger';
|
import { handleNonCriticalError } from '@/lib/errorHandler';
|
||||||
import { useUserRole } from '@/hooks/useUserRole';
|
import { useUserRole } from '@/hooks/useUserRole';
|
||||||
import { toast } from '@/hooks/use-toast';
|
import { toast } from '@/hooks/use-toast';
|
||||||
import { submitCompanyCreation } from '@/lib/companyHelpers';
|
import { submitCompanyCreation } from '@/lib/companyHelpers';
|
||||||
@@ -89,7 +89,10 @@ export default function Designers() {
|
|||||||
const { data } = await query;
|
const { data } = await query;
|
||||||
setCompanies(data || []);
|
setCompanies(data || []);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error fetching companies', { error });
|
handleNonCriticalError(error, {
|
||||||
|
action: 'Fetch designers',
|
||||||
|
metadata: { page: 'designers' }
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import { useNavigate } from "react-router-dom";
|
|||||||
import { supabase } from "@/lib/supabaseClient";
|
import { supabase } from "@/lib/supabaseClient";
|
||||||
import { authStorage } from "@/lib/authStorage";
|
import { authStorage } from "@/lib/authStorage";
|
||||||
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
||||||
import { logger } from '@/lib/logger';
|
import { handleError } from '@/lib/errorHandler';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ForceLogout - Hidden endpoint for completely clearing auth session
|
* ForceLogout - Hidden endpoint for completely clearing auth session
|
||||||
@@ -16,26 +16,23 @@ const ForceLogout = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const performFullLogout = async () => {
|
const performFullLogout = async () => {
|
||||||
logger.info('[ForceLogout] Starting complete auth cleanup');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1. Sign out from Supabase
|
// 1. Sign out from Supabase
|
||||||
logger.info('[ForceLogout] Signing out from Supabase');
|
|
||||||
await supabase.auth.signOut();
|
await supabase.auth.signOut();
|
||||||
|
|
||||||
// 2. Clear all auth-related storage
|
// 2. Clear all auth-related storage
|
||||||
logger.info('[ForceLogout] Clearing all auth storage');
|
|
||||||
authStorage.clearAll();
|
authStorage.clearAll();
|
||||||
|
|
||||||
// 3. Brief delay to ensure cleanup completes
|
// 3. Brief delay to ensure cleanup completes
|
||||||
await new Promise(resolve => setTimeout(resolve, 500));
|
await new Promise(resolve => setTimeout(resolve, 500));
|
||||||
|
|
||||||
logger.info('[ForceLogout] Auth cleanup complete, redirecting to home');
|
|
||||||
|
|
||||||
// 4. Redirect to home page
|
// 4. Redirect to home page
|
||||||
navigate('/', { replace: true });
|
navigate('/', { replace: true });
|
||||||
} catch (error) {
|
} 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
|
// Still redirect even if there's an error
|
||||||
navigate('/', { replace: true });
|
navigate('/', { replace: true });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import { ArrowLeft, MapPin, Star, Globe, Calendar, Edit, Factory, FerrisWheel }
|
|||||||
import { Company } from '@/types/database';
|
import { Company } from '@/types/database';
|
||||||
import { supabase } from '@/lib/supabaseClient';
|
import { supabase } from '@/lib/supabaseClient';
|
||||||
import { ManufacturerPhotoGallery } from '@/components/companies/ManufacturerPhotoGallery';
|
import { ManufacturerPhotoGallery } from '@/components/companies/ManufacturerPhotoGallery';
|
||||||
import { logger } from '@/lib/logger';
|
import { handleNonCriticalError } from '@/lib/errorHandler';
|
||||||
|
|
||||||
// Lazy load admin form
|
// Lazy load admin form
|
||||||
const ManufacturerForm = lazy(() => import('@/components/admin/ManufacturerForm').then(m => ({ default: m.ManufacturerForm })));
|
const ManufacturerForm = lazy(() => import('@/components/admin/ManufacturerForm').then(m => ({ default: m.ManufacturerForm })));
|
||||||
@@ -83,7 +83,10 @@ export default function ManufacturerDetail() {
|
|||||||
fetchStatistics(data.id);
|
fetchStatistics(data.id);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error fetching manufacturer', { error });
|
handleNonCriticalError(error, {
|
||||||
|
action: 'Fetch manufacturer',
|
||||||
|
metadata: { slug }
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
@@ -119,7 +122,10 @@ export default function ManufacturerDetail() {
|
|||||||
if (photosError) throw photosError;
|
if (photosError) throw photosError;
|
||||||
setTotalPhotos(photosCount || 0);
|
setTotalPhotos(photosCount || 0);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error fetching statistics', { error });
|
handleNonCriticalError(error, {
|
||||||
|
action: 'Fetch manufacturer statistics',
|
||||||
|
metadata: { manufacturerId }
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setStatsLoading(false);
|
setStatsLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { toast } from '@/hooks/use-toast';
|
|||||||
import { useAuthModal } from '@/hooks/useAuthModal';
|
import { useAuthModal } from '@/hooks/useAuthModal';
|
||||||
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
||||||
import { useOpenGraph } from '@/hooks/useOpenGraph';
|
import { useOpenGraph } from '@/hooks/useOpenGraph';
|
||||||
import { logger } from '@/lib/logger';
|
import { handleNonCriticalError } from '@/lib/errorHandler';
|
||||||
|
|
||||||
interface RideModelWithCount extends RideModel {
|
interface RideModelWithCount extends RideModel {
|
||||||
ride_count: number;
|
ride_count: number;
|
||||||
@@ -86,7 +86,10 @@ export default function ManufacturerModels() {
|
|||||||
setModels(modelsWithCounts);
|
setModels(modelsWithCounts);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error fetching data', { error });
|
handleNonCriticalError(error, {
|
||||||
|
action: 'Fetch manufacturer models',
|
||||||
|
metadata: { manufacturerSlug }
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { toast } from '@/hooks/use-toast';
|
|||||||
import { useAuthModal } from '@/hooks/useAuthModal';
|
import { useAuthModal } from '@/hooks/useAuthModal';
|
||||||
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
||||||
import { useOpenGraph } from '@/hooks/useOpenGraph';
|
import { useOpenGraph } from '@/hooks/useOpenGraph';
|
||||||
import { logger } from '@/lib/logger';
|
import { handleNonCriticalError } from '@/lib/errorHandler';
|
||||||
|
|
||||||
export default function ManufacturerRides() {
|
export default function ManufacturerRides() {
|
||||||
const { manufacturerSlug } = useParams<{ manufacturerSlug: string }>();
|
const { manufacturerSlug } = useParams<{ manufacturerSlug: string }>();
|
||||||
@@ -91,7 +91,10 @@ export default function ManufacturerRides() {
|
|||||||
setRides((ridesData || []) as any);
|
setRides((ridesData || []) as any);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error fetching data', { error });
|
handleNonCriticalError(error, {
|
||||||
|
action: 'Fetch manufacturer rides',
|
||||||
|
metadata: { manufacturerSlug }
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import { ManufacturerCard } from '@/components/manufacturers/ManufacturerCard';
|
|||||||
import { ManufacturerListView } from '@/components/manufacturers/ManufacturerListView';
|
import { ManufacturerListView } from '@/components/manufacturers/ManufacturerListView';
|
||||||
import { ManufacturerForm } from '@/components/admin/ManufacturerForm';
|
import { ManufacturerForm } from '@/components/admin/ManufacturerForm';
|
||||||
import { useAuth } from '@/hooks/useAuth';
|
import { useAuth } from '@/hooks/useAuth';
|
||||||
import { logger } from '@/lib/logger';
|
import { handleNonCriticalError } from '@/lib/errorHandler';
|
||||||
import { useUserRole } from '@/hooks/useUserRole';
|
import { useUserRole } from '@/hooks/useUserRole';
|
||||||
import { toast } from '@/hooks/use-toast';
|
import { toast } from '@/hooks/use-toast';
|
||||||
import { submitCompanyCreation } from '@/lib/companyHelpers';
|
import { submitCompanyCreation } from '@/lib/companyHelpers';
|
||||||
@@ -76,7 +76,10 @@ export default function Manufacturers() {
|
|||||||
const { data } = await query;
|
const { data } = await query;
|
||||||
setCompanies(data || []);
|
setCompanies(data || []);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
logger.error('Error fetching companies', { error });
|
handleNonCriticalError(error, {
|
||||||
|
action: 'Fetch manufacturers',
|
||||||
|
metadata: { page: 'manufacturers' }
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { Company, Park } from '@/types/database';
|
|||||||
import { supabase } from '@/lib/supabaseClient';
|
import { supabase } from '@/lib/supabaseClient';
|
||||||
import { OperatorPhotoGallery } from '@/components/companies/OperatorPhotoGallery';
|
import { OperatorPhotoGallery } from '@/components/companies/OperatorPhotoGallery';
|
||||||
import { ParkCard } from '@/components/parks/ParkCard';
|
import { ParkCard } from '@/components/parks/ParkCard';
|
||||||
import { logger } from '@/lib/logger';
|
import { handleNonCriticalError } from '@/lib/errorHandler';
|
||||||
|
|
||||||
// Lazy load admin form
|
// Lazy load admin form
|
||||||
const OperatorForm = lazy(() => import('@/components/admin/OperatorForm').then(m => ({ default: m.OperatorForm })));
|
const OperatorForm = lazy(() => import('@/components/admin/OperatorForm').then(m => ({ default: m.OperatorForm })));
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { Grid3X3, List } from 'lucide-react';
|
|||||||
import { FilterState, SortState } from './Parks';
|
import { FilterState, SortState } from './Parks';
|
||||||
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
||||||
import { useOpenGraph } from '@/hooks/useOpenGraph';
|
import { useOpenGraph } from '@/hooks/useOpenGraph';
|
||||||
import { logger } from '@/lib/logger';
|
import { handleNonCriticalError } from '@/lib/errorHandler';
|
||||||
|
|
||||||
const initialFilters: FilterState = {
|
const initialFilters: FilterState = {
|
||||||
search: '',
|
search: '',
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ import { Grid3X3, List } from 'lucide-react';
|
|||||||
import { FilterState, SortState } from './Parks';
|
import { FilterState, SortState } from './Parks';
|
||||||
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
||||||
import { useOpenGraph } from '@/hooks/useOpenGraph';
|
import { useOpenGraph } from '@/hooks/useOpenGraph';
|
||||||
import { logger } from '@/lib/logger';
|
import { handleNonCriticalError } from '@/lib/errorHandler';
|
||||||
|
|
||||||
const initialFilters: FilterState = {
|
const initialFilters: FilterState = {
|
||||||
search: '',
|
search: '',
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ import { RideSubmissionData } from '@/types/submission-data';
|
|||||||
import { supabase } from '@/lib/supabaseClient';
|
import { supabase } from '@/lib/supabaseClient';
|
||||||
import { useAuth } from '@/hooks/useAuth';
|
import { useAuth } from '@/hooks/useAuth';
|
||||||
import { toast } from '@/hooks/use-toast';
|
import { toast } from '@/hooks/use-toast';
|
||||||
import { getErrorMessage } from '@/lib/errorHandler';
|
import { getErrorMessage, handleNonCriticalError } from '@/lib/errorHandler';
|
||||||
import { logger } from '@/lib/logger';
|
|
||||||
import { useAuthModal } from '@/hooks/useAuthModal';
|
import { useAuthModal } from '@/hooks/useAuthModal';
|
||||||
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
||||||
import { useOpenGraph } from '@/hooks/useOpenGraph';
|
import { useOpenGraph } from '@/hooks/useOpenGraph';
|
||||||
|
|||||||
@@ -23,8 +23,7 @@ import { User, MapPin, Calendar, Star, Trophy, Settings, Camera, Edit3, Save, X,
|
|||||||
import { Profile as ProfileType } from '@/types/database';
|
import { Profile as ProfileType } from '@/types/database';
|
||||||
import { supabase } from '@/lib/supabaseClient';
|
import { supabase } from '@/lib/supabaseClient';
|
||||||
import { useToast } from '@/hooks/use-toast';
|
import { useToast } from '@/hooks/use-toast';
|
||||||
import { logger } from '@/lib/logger';
|
import { getErrorMessage, handleNonCriticalError } from '@/lib/errorHandler';
|
||||||
import { getErrorMessage } from '@/lib/errorHandler';
|
|
||||||
import { PhotoUpload } from '@/components/upload/PhotoUpload';
|
import { PhotoUpload } from '@/components/upload/PhotoUpload';
|
||||||
import { profileEditSchema } from '@/lib/validation';
|
import { profileEditSchema } from '@/lib/validation';
|
||||||
import { LocationDisplay } from '@/components/profile/LocationDisplay';
|
import { LocationDisplay } from '@/components/profile/LocationDisplay';
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { Company, Park } from '@/types/database';
|
|||||||
import { supabase } from '@/lib/supabaseClient';
|
import { supabase } from '@/lib/supabaseClient';
|
||||||
import { PropertyOwnerPhotoGallery } from '@/components/companies/PropertyOwnerPhotoGallery';
|
import { PropertyOwnerPhotoGallery } from '@/components/companies/PropertyOwnerPhotoGallery';
|
||||||
import { ParkCard } from '@/components/parks/ParkCard';
|
import { ParkCard } from '@/components/parks/ParkCard';
|
||||||
import { logger } from '@/lib/logger';
|
import { handleNonCriticalError } from '@/lib/errorHandler';
|
||||||
|
|
||||||
// Lazy load admin form
|
// Lazy load admin form
|
||||||
const PropertyOwnerForm = lazy(() => import('@/components/admin/PropertyOwnerForm').then(m => ({ default: m.PropertyOwnerForm })));
|
const PropertyOwnerForm = lazy(() => import('@/components/admin/PropertyOwnerForm').then(m => ({ default: m.PropertyOwnerForm })));
|
||||||
|
|||||||
@@ -15,8 +15,7 @@ import { getCloudflareImageUrl } from '@/lib/cloudflareImageUtils';
|
|||||||
import { useAuthModal } from '@/hooks/useAuthModal';
|
import { useAuthModal } from '@/hooks/useAuthModal';
|
||||||
import { useAuth } from '@/hooks/useAuth';
|
import { useAuth } from '@/hooks/useAuth';
|
||||||
import { toast } from '@/hooks/use-toast';
|
import { toast } from '@/hooks/use-toast';
|
||||||
import { getErrorMessage } from '@/lib/errorHandler';
|
import { getErrorMessage, handleNonCriticalError } from '@/lib/errorHandler';
|
||||||
import { logger } from '@/lib/logger';
|
|
||||||
import { ManufacturerPhotoGallery } from '@/components/companies/ManufacturerPhotoGallery';
|
import { ManufacturerPhotoGallery } from '@/components/companies/ManufacturerPhotoGallery';
|
||||||
|
|
||||||
// Lazy load admin form
|
// Lazy load admin form
|
||||||
|
|||||||
@@ -14,8 +14,7 @@ import { RideForm } from '@/components/admin/RideForm';
|
|||||||
import { useAuth } from '@/hooks/useAuth';
|
import { useAuth } from '@/hooks/useAuth';
|
||||||
import { useAuthModal } from '@/hooks/useAuthModal';
|
import { useAuthModal } from '@/hooks/useAuthModal';
|
||||||
import { toast } from '@/hooks/use-toast';
|
import { toast } from '@/hooks/use-toast';
|
||||||
import { getErrorMessage } from '@/lib/errorHandler';
|
import { getErrorMessage, handleNonCriticalError } from '@/lib/errorHandler';
|
||||||
import { logger } from '@/lib/logger';
|
|
||||||
import type { Ride, Company, RideModel } from "@/types/database";
|
import type { Ride, Company, RideModel } from "@/types/database";
|
||||||
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
||||||
|
|
||||||
|
|||||||
@@ -75,8 +75,7 @@ import { DialogFooter } from '@/components/ui/dialog';
|
|||||||
import { useTheme } from '@/components/theme/ThemeProvider';
|
import { useTheme } from '@/components/theme/ThemeProvider';
|
||||||
import { useUserRole } from '@/hooks/useUserRole';
|
import { useUserRole } from '@/hooks/useUserRole';
|
||||||
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
import { useDocumentTitle } from '@/hooks/useDocumentTitle';
|
||||||
import { handleError, handleSuccess } from '@/lib/errorHandler';
|
import { handleError, handleSuccess, handleNonCriticalError } from '@/lib/errorHandler';
|
||||||
import { logger } from '@/lib/logger';
|
|
||||||
import { contactCategories } from '@/lib/contactValidation';
|
import { contactCategories } from '@/lib/contactValidation';
|
||||||
import { invokeWithTracking } from '@/lib/edgeFunctionTracking';
|
import { invokeWithTracking } from '@/lib/edgeFunctionTracking';
|
||||||
import { AdminLayout } from '@/components/layout/AdminLayout';
|
import { AdminLayout } from '@/components/layout/AdminLayout';
|
||||||
@@ -199,7 +198,10 @@ export default function AdminContact() {
|
|||||||
const { data, error } = await query;
|
const { data, error } = await query;
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
logger.error('Failed to fetch contact submissions', { error: error.message });
|
handleNonCriticalError(error, {
|
||||||
|
action: 'Fetch contact submissions',
|
||||||
|
metadata: { error: error.message }
|
||||||
|
});
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,7 +221,10 @@ export default function AdminContact() {
|
|||||||
.order('created_at', { ascending: true })
|
.order('created_at', { ascending: true })
|
||||||
.then(({ data, error }) => {
|
.then(({ data, error }) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
logger.error('Failed to fetch email threads', { error });
|
handleNonCriticalError(error, {
|
||||||
|
action: 'Fetch email threads',
|
||||||
|
metadata: { submissionId: selectedSubmission.id }
|
||||||
|
});
|
||||||
setEmailThreads([]);
|
setEmailThreads([]);
|
||||||
} else {
|
} else {
|
||||||
setEmailThreads((data as EmailThread[]) || []);
|
setEmailThreads((data as EmailThread[]) || []);
|
||||||
@@ -263,7 +268,10 @@ export default function AdminContact() {
|
|||||||
.eq('id', submissionId);
|
.eq('id', submissionId);
|
||||||
|
|
||||||
if (statusError) {
|
if (statusError) {
|
||||||
logger.error('Failed to update status', { error: statusError });
|
handleError(statusError, {
|
||||||
|
action: 'Update contact status',
|
||||||
|
metadata: { submissionId, newStatus }
|
||||||
|
});
|
||||||
throw statusError;
|
throw statusError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -465,8 +473,10 @@ export default function AdminContact() {
|
|||||||
.order('created_at', { ascending: true })
|
.order('created_at', { ascending: true })
|
||||||
.then(({ data, error }) => {
|
.then(({ data, error }) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
logger.error('Failed to refresh email threads', { error });
|
handleError(error, {
|
||||||
handleError(error, { action: 'Refresh Email Threads' });
|
action: 'Refresh Email Threads',
|
||||||
|
metadata: { submissionId: selectedSubmission.id }
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
setEmailThreads((data as EmailThread[]) || []);
|
setEmailThreads((data as EmailThread[]) || []);
|
||||||
handleSuccess('Refreshed', 'Email thread updated');
|
handleSuccess('Refreshed', 'Email thread updated');
|
||||||
|
|||||||
Reference in New Issue
Block a user