mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 14:11:13 -05:00
Fix composite submission error logging
This commit is contained in:
@@ -8,6 +8,7 @@ import type { CompanyDatabaseRecord, TimelineEventDatabaseRecord } from '@/types
|
||||
import { logger } from './logger';
|
||||
import { handleError } from './errorHandler';
|
||||
import type { TimelineEventFormData, EntityType } from '@/types/timeline';
|
||||
import { breadcrumb } from './errorBreadcrumbs';
|
||||
import {
|
||||
validateParkCreateFields,
|
||||
validateRideCreateFields,
|
||||
@@ -202,57 +203,106 @@ async function submitCompositeCreation(
|
||||
dependencies: CompositeSubmissionDependency[],
|
||||
userId: string
|
||||
): Promise<{ submitted: boolean; submissionId: string }> {
|
||||
// Check if user is banned
|
||||
const { data: profile } = await supabase
|
||||
.from('profiles')
|
||||
.select('banned')
|
||||
.eq('user_id', userId)
|
||||
.single();
|
||||
try {
|
||||
breadcrumb.userAction('Start composite submission', 'submitCompositeCreation', {
|
||||
primaryType: primaryEntity.type,
|
||||
dependencyCount: dependencies.length,
|
||||
userId
|
||||
});
|
||||
|
||||
if (profile?.banned) {
|
||||
throw new Error('Account suspended. Contact support for assistance.');
|
||||
}
|
||||
// Check if user is banned
|
||||
breadcrumb.apiCall('profiles', 'SELECT');
|
||||
try {
|
||||
const { data: profile, error } = await supabase
|
||||
.from('profiles')
|
||||
.select('banned')
|
||||
.eq('user_id', userId)
|
||||
.single();
|
||||
|
||||
// Upload all pending images for all entities
|
||||
const uploadedEntities = await Promise.all([
|
||||
...dependencies.map(async (dep) => {
|
||||
if (dep.data.images?.uploaded && dep.data.images.uploaded.length > 0) {
|
||||
const uploadedImages = await uploadPendingImages(dep.data.images.uploaded);
|
||||
return {
|
||||
...dep,
|
||||
data: {
|
||||
...dep.data,
|
||||
images: { ...dep.data.images, uploaded: uploadedImages }
|
||||
}
|
||||
};
|
||||
if (error) {
|
||||
throw new Error(`Failed to check user status: ${error.message}`);
|
||||
}
|
||||
return dep;
|
||||
}),
|
||||
(async () => {
|
||||
if (primaryEntity.data.images?.uploaded && primaryEntity.data.images.uploaded.length > 0) {
|
||||
const uploadedImages = await uploadPendingImages(primaryEntity.data.images.uploaded);
|
||||
return {
|
||||
...primaryEntity,
|
||||
data: {
|
||||
...primaryEntity.data,
|
||||
images: { ...primaryEntity.data.images, uploaded: uploadedImages }
|
||||
}
|
||||
};
|
||||
|
||||
if (profile?.banned) {
|
||||
throw new Error('Account suspended. Contact support for assistance.');
|
||||
}
|
||||
return primaryEntity;
|
||||
})()
|
||||
]);
|
||||
} catch (error) {
|
||||
throw error instanceof Error ? error : new Error(`User check failed: ${String(error)}`);
|
||||
}
|
||||
|
||||
const uploadedDependencies = uploadedEntities.slice(0, -1) as CompositeSubmissionDependency[];
|
||||
const uploadedPrimary = uploadedEntities[uploadedEntities.length - 1] as typeof primaryEntity;
|
||||
// Upload all pending images for all entities
|
||||
breadcrumb.userAction('Upload images', 'submitCompositeCreation', {
|
||||
totalImages: dependencies.reduce((sum, dep) => sum + (dep.data.images?.uploaded?.length || 0), 0) +
|
||||
(primaryEntity.data.images?.uploaded?.length || 0)
|
||||
});
|
||||
|
||||
// Build submission items array with dependencies first
|
||||
const submissionItems: any[] = [];
|
||||
const tempIdMap = new Map<string, number>(); // Maps tempId to order_index
|
||||
const uploadedEntities = await Promise.all([
|
||||
...dependencies.map(async (dep, index) => {
|
||||
try {
|
||||
if (dep.data.images?.uploaded && dep.data.images.uploaded.length > 0) {
|
||||
const uploadedImages = await uploadPendingImages(dep.data.images.uploaded);
|
||||
return {
|
||||
...dep,
|
||||
data: {
|
||||
...dep.data,
|
||||
images: { ...dep.data.images, uploaded: uploadedImages }
|
||||
}
|
||||
};
|
||||
}
|
||||
return dep;
|
||||
} catch (error) {
|
||||
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||
throw new Error(
|
||||
`Failed to upload images for ${dep.type} "${dep.data.name || 'unnamed'}": ${errorMsg}`
|
||||
);
|
||||
}
|
||||
}),
|
||||
(async () => {
|
||||
try {
|
||||
if (primaryEntity.data.images?.uploaded && primaryEntity.data.images.uploaded.length > 0) {
|
||||
const uploadedImages = await uploadPendingImages(primaryEntity.data.images.uploaded);
|
||||
return {
|
||||
...primaryEntity,
|
||||
data: {
|
||||
...primaryEntity.data,
|
||||
images: { ...primaryEntity.data.images, uploaded: uploadedImages }
|
||||
}
|
||||
};
|
||||
}
|
||||
return primaryEntity;
|
||||
} catch (error) {
|
||||
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||
throw new Error(
|
||||
`Failed to upload images for ${primaryEntity.type} "${primaryEntity.data.name || 'unnamed'}": ${errorMsg}`
|
||||
);
|
||||
}
|
||||
})()
|
||||
]);
|
||||
|
||||
// Add dependency items (companies, models) first
|
||||
let orderIndex = 0;
|
||||
for (const dep of uploadedDependencies) {
|
||||
const uploadedDependencies = uploadedEntities.slice(0, -1) as CompositeSubmissionDependency[];
|
||||
const uploadedPrimary = uploadedEntities[uploadedEntities.length - 1] as typeof primaryEntity;
|
||||
|
||||
// Validate dependencies structure
|
||||
breadcrumb.stateChange('Validating dependencies', {
|
||||
dependencyCount: uploadedDependencies.length
|
||||
});
|
||||
|
||||
for (const dep of uploadedDependencies) {
|
||||
if (!dep.type) throw new Error('Dependency missing type');
|
||||
if (!dep.tempId) throw new Error('Dependency missing tempId');
|
||||
if (!dep.data) throw new Error('Dependency missing data');
|
||||
if (dep.type === 'company' && !dep.companyType) {
|
||||
throw new Error(`Company dependency "${dep.data.name || 'unnamed'}" missing companyType`);
|
||||
}
|
||||
}
|
||||
|
||||
// Build submission items array with dependencies first
|
||||
const submissionItems: any[] = [];
|
||||
const tempIdMap = new Map<string, number>(); // Maps tempId to order_index
|
||||
|
||||
// Add dependency items (companies, models) first
|
||||
let orderIndex = 0;
|
||||
for (const dep of uploadedDependencies) {
|
||||
const itemType = dep.type === 'company' ? dep.companyType : dep.type;
|
||||
tempIdMap.set(dep.tempId, orderIndex);
|
||||
|
||||
@@ -370,11 +420,12 @@ async function submitCompositeCreation(
|
||||
}
|
||||
}
|
||||
|
||||
// Use RPC to create submission with items atomically with retry logic
|
||||
const { withRetry } = await import('./retryHelpers');
|
||||
const { toast } = await import('@/hooks/use-toast');
|
||||
|
||||
const result = await withRetry(
|
||||
// Use RPC to create submission with items atomically with retry logic
|
||||
breadcrumb.apiCall('create_submission_with_items', 'RPC');
|
||||
const { withRetry } = await import('./retryHelpers');
|
||||
const { toast } = await import('@/hooks/use-toast');
|
||||
|
||||
const result = await withRetry(
|
||||
async () => {
|
||||
const { data, error } = await supabase.rpc('create_submission_with_items', {
|
||||
p_user_id: userId,
|
||||
@@ -446,24 +497,41 @@ async function submitCompositeCreation(
|
||||
return isRetryableError(error);
|
||||
}
|
||||
}
|
||||
).catch((error) => {
|
||||
// Final failure - log and throw
|
||||
handleError(error, {
|
||||
action: 'Composite submission',
|
||||
metadata: {
|
||||
primaryType: uploadedPrimary.type,
|
||||
dependencyCount: dependencies.length,
|
||||
supabaseCode: (error as any).supabaseCode,
|
||||
supabaseDetails: (error as any).supabaseDetails,
|
||||
supabaseHint: (error as any).supabaseHint,
|
||||
retriesExhausted: true
|
||||
},
|
||||
).catch((error) => {
|
||||
// Final failure - log and throw
|
||||
handleError(error, {
|
||||
action: 'Composite submission',
|
||||
metadata: {
|
||||
primaryType: uploadedPrimary.type,
|
||||
dependencyCount: dependencies.length,
|
||||
supabaseCode: (error as any).supabaseCode,
|
||||
supabaseDetails: (error as any).supabaseDetails,
|
||||
supabaseHint: (error as any).supabaseHint,
|
||||
retriesExhausted: true
|
||||
},
|
||||
});
|
||||
|
||||
throw error;
|
||||
});
|
||||
|
||||
throw error;
|
||||
});
|
||||
|
||||
return { submitted: true, submissionId: result };
|
||||
breadcrumb.stateChange('Composite submission successful', {
|
||||
submissionId: result
|
||||
});
|
||||
|
||||
return { submitted: true, submissionId: result };
|
||||
} catch (error) {
|
||||
// Ensure error is always an Error instance with context
|
||||
const enrichedError = error instanceof Error
|
||||
? error
|
||||
: new Error(`Composite submission failed: ${String(error)}`);
|
||||
|
||||
// Attach metadata for better debugging
|
||||
(enrichedError as any).originalError = error;
|
||||
(enrichedError as any).primaryType = primaryEntity?.type;
|
||||
(enrichedError as any).dependencyCount = dependencies?.length;
|
||||
|
||||
throw enrichedError;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user