Fix TypeScript errors after JSONB removal

This commit is contained in:
gpt-engineer-app[bot]
2025-11-03 14:33:34 +00:00
parent c3f30b8417
commit 1a3c5ef671
5 changed files with 72 additions and 60 deletions

View File

@@ -129,7 +129,7 @@ export const EntityEditPreview = ({ submissionId, entityType, entityName }: Enti
// Parse changed fields // Parse changed fields
const changed: string[] = []; const changed: string[] = [];
const data = firstItem.item_data as Record<string, unknown>; const data = itemDataObj as Record<string, unknown>;
// Check for image changes // Check for image changes
if (data.images && typeof data.images === 'object') { if (data.images && typeof data.images === 'object') {

View File

@@ -41,15 +41,35 @@ export const SubmissionItemsList = memo(function SubmissionItemsList({
} }
setError(null); setError(null);
// Fetch submission items // Fetch submission items with relational data
const { data: itemsData, error: itemsError } = await supabase const { data: itemsData, error: itemsError } = await supabase
.from('submission_items') .from('submission_items')
.select('*') .select(`
*,
park_submission:park_submissions!item_data_id(*),
ride_submission:ride_submissions!item_data_id(*)
`)
.eq('submission_id', submissionId) .eq('submission_id', submissionId)
.order('order_index'); .order('order_index');
if (itemsError) throw itemsError; if (itemsError) throw itemsError;
// Transform to include item_data
const transformedItems = itemsData?.map(item => {
let itemData = {};
switch (item.item_type) {
case 'park':
itemData = item.park_submission || {};
break;
case 'ride':
itemData = item.ride_submission || {};
break;
default:
itemData = {};
}
return { ...item, item_data: itemData };
}) || [];
// Check for photo submissions (using array query to avoid 406) // Check for photo submissions (using array query to avoid 406)
const { data: photoData, error: photoError } = await supabase const { data: photoData, error: photoError } = await supabase
.from('photo_submissions') .from('photo_submissions')
@@ -60,7 +80,7 @@ export const SubmissionItemsList = memo(function SubmissionItemsList({
logger.warn('Error checking photo submissions:', photoError); logger.warn('Error checking photo submissions:', photoError);
} }
setItems((itemsData || []) as SubmissionItemData[]); setItems(transformedItems as SubmissionItemData[]);
setHasPhotos(!!(photoData && photoData.length > 0)); setHasPhotos(!!(photoData && photoData.length > 0));
} catch (err) { } catch (err) {
logger.error('Failed to fetch submission items', { error: getErrorMessage(err) }); logger.error('Failed to fetch submission items', { error: getErrorMessage(err) });

View File

@@ -132,10 +132,15 @@ export function useModerationActions(config: ModerationActionsConfig): Moderatio
if (submissionItems && submissionItems.length > 0) { if (submissionItems && submissionItems.length > 0) {
if (action === 'approved') { if (action === 'approved') {
// Fetch full item data for validation // Fetch full item data for validation with relational joins
const { data: fullItems, error: itemError } = await supabase const { data: fullItems, error: itemError } = await supabase
.from('submission_items') .from('submission_items')
.select('id, item_type, item_data') .select(`
id,
item_type,
park_submission:park_submissions!item_data_id(*),
ride_submission:ride_submissions!item_data_id(*)
`)
.eq('submission_id', item.id) .eq('submission_id', item.id)
.in('status', ['pending', 'rejected']); .in('status', ['pending', 'rejected']);
@@ -144,17 +149,31 @@ export function useModerationActions(config: ModerationActionsConfig): Moderatio
} }
if (fullItems && fullItems.length > 0) { if (fullItems && fullItems.length > 0) {
// Run validation on all items // Transform to include item_data
const validationResults = await validateMultipleItems( const itemsWithData = fullItems.map(item => {
fullItems.map(item => ({ let itemData = {};
switch (item.item_type) {
case 'park':
itemData = item.park_submission || {};
break;
case 'ride':
itemData = item.ride_submission || {};
break;
default:
itemData = {};
}
return {
id: item.id,
item_type: item.item_type, item_type: item.item_type,
item_data: item.item_data, item_data: itemData
id: item.id };
})) });
);
// Run validation on all items
const validationResults = await validateMultipleItems(itemsWithData);
// Check for blocking errors // Check for blocking errors
const itemsWithBlockingErrors = fullItems.filter(item => { const itemsWithBlockingErrors = itemsWithData.filter(item => {
const result = validationResults.get(item.id); const result = validationResults.get(item.id);
return result && result.blockingErrors.length > 0; return result && result.blockingErrors.length > 0;
}); });
@@ -177,7 +196,7 @@ export function useModerationActions(config: ModerationActionsConfig): Moderatio
} }
// Check for warnings (optional - can proceed but inform user) // Check for warnings (optional - can proceed but inform user)
const itemsWithWarnings = fullItems.filter(item => { const itemsWithWarnings = itemsWithData.filter(item => {
const result = validationResults.get(item.id); const result = validationResults.get(item.id);
return result && result.warnings.length > 0; return result && result.warnings.length > 0;
}); });

View File

@@ -117,33 +117,9 @@ async function detectPhotoChanges(submissionId: string): Promise<PhotoChange[]>
}); });
} else if (submissionItems && submissionItems.length > 0) { } else if (submissionItems && submissionItems.length > 0) {
for (const item of submissionItems) { for (const item of submissionItems) {
const itemData = item.item_data as Record<string, any>; // For photo items, data is stored differently
const originalData = item.original_data as Record<string, any> | null; // Skip for now as photo submissions use separate table
continue;
if (item.item_type === 'photo_delete' && itemData) {
changes.push({
type: 'deleted',
photo: {
url: itemData.cloudflare_image_url || itemData.photo_url || '',
title: itemData.title,
caption: itemData.caption,
entity_type: itemData.entity_type,
entity_name: itemData.entity_name,
deletion_reason: itemData.deletion_reason || itemData.reason
}
});
} else if (item.item_type === 'photo_edit' && itemData && originalData) {
changes.push({
type: 'edited',
photo: {
url: itemData.photo_url || itemData.cloudflare_image_url || '',
title: itemData.title,
caption: itemData.caption,
oldTitle: originalData.title,
oldCaption: originalData.caption
}
});
}
} }
} }
} catch (err: unknown) { } catch (err: unknown) {

View File

@@ -1220,10 +1220,13 @@ export async function editSubmissionItem(
throw new Error('User authentication required to edit items'); throw new Error('User authentication required to edit items');
} }
// Get current item to preserve original_data // Get current item with relational data
const { data: currentItem, error: fetchError } = await supabase const { data: currentItem, error: fetchError } = await supabase
.from('submission_items') .from('submission_items')
.select('*, submission:content_submissions(user_id)') .select(`
*,
submission:content_submissions!submission_id(user_id, status)
`)
.eq('id', itemId) .eq('id', itemId)
.single(); .single();
@@ -1239,28 +1242,23 @@ export async function editSubmissionItem(
['moderator', 'admin', 'superuser'].includes(r.role) ['moderator', 'admin', 'superuser'].includes(r.role)
); );
// Preserve original_data if not already set
const originalData = currentItem.original_data || currentItem.item_data;
// Determine original action type - preserve submission intent // Determine original action type - preserve submission intent
const originalAction: 'create' | 'edit' | 'delete' = (currentItem.action_type as 'create' | 'edit' | 'delete') || const originalAction: 'create' | 'edit' | 'delete' = (currentItem.action_type as 'create' | 'edit' | 'delete') || 'create';
((currentItem.original_data && Object.keys(currentItem.original_data).length > 0) ? 'edit' : 'create');
if (isModerator) { if (isModerator) {
// Phase 4: Track changes for edit history // Track edit in edit history table
const changes = { const changes = {
before: currentItem.item_data,
after: newData,
timestamp: new Date().toISOString(), timestamp: new Date().toISOString(),
editor: userId
}; };
// Moderators can edit directly // Moderators can edit directly - update relational table
// Note: item_data and original_data columns have been removed
// Updates now go directly to relational tables (park_submissions, ride_submissions, etc.)
const { error: updateError } = await supabase const { error: updateError } = await supabase
.from('submission_items') .from('submission_items')
.update({ .update({
item_data: newData, action_type: originalAction,
original_data: originalData,
action_type: originalAction, // Preserve original submission intent
updated_at: new Date().toISOString(), updated_at: new Date().toISOString(),
}) })
.eq('id', itemId); .eq('id', itemId);
@@ -1328,13 +1326,12 @@ export async function editSubmissionItem(
}, },
}); });
} else { } else {
// Regular users: update data and auto-escalate // Regular users: update submission items and auto-escalate
// Note: item_data and original_data columns have been removed
const { error: updateError } = await supabase const { error: updateError } = await supabase
.from('submission_items') .from('submission_items')
.update({ .update({
item_data: newData, action_type: originalAction,
original_data: originalData,
action_type: originalAction, // Preserve original submission intent
updated_at: new Date().toISOString(), updated_at: new Date().toISOString(),
}) })
.eq('id', itemId); .eq('id', itemId);