mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-20 10:31:13 -05:00
Refactor submission items
This commit is contained in:
@@ -4147,6 +4147,7 @@ export type Database = {
|
|||||||
id: string
|
id: string
|
||||||
is_test_data: boolean | null
|
is_test_data: boolean | null
|
||||||
item_data: Json
|
item_data: Json
|
||||||
|
item_data_id: string | null
|
||||||
item_type: string
|
item_type: string
|
||||||
order_index: number | null
|
order_index: number | null
|
||||||
original_data: Json | null
|
original_data: Json | null
|
||||||
@@ -4163,6 +4164,7 @@ export type Database = {
|
|||||||
id?: string
|
id?: string
|
||||||
is_test_data?: boolean | null
|
is_test_data?: boolean | null
|
||||||
item_data: Json
|
item_data: Json
|
||||||
|
item_data_id?: string | null
|
||||||
item_type: string
|
item_type: string
|
||||||
order_index?: number | null
|
order_index?: number | null
|
||||||
original_data?: Json | null
|
original_data?: Json | null
|
||||||
@@ -4179,6 +4181,7 @@ export type Database = {
|
|||||||
id?: string
|
id?: string
|
||||||
is_test_data?: boolean | null
|
is_test_data?: boolean | null
|
||||||
item_data?: Json
|
item_data?: Json
|
||||||
|
item_data_id?: string | null
|
||||||
item_type?: string
|
item_type?: string
|
||||||
order_index?: number | null
|
order_index?: number | null
|
||||||
original_data?: Json | null
|
original_data?: Json | null
|
||||||
|
|||||||
@@ -465,20 +465,48 @@ export async function submitParkCreation(
|
|||||||
|
|
||||||
if (submissionError) throw submissionError;
|
if (submissionError) throw submissionError;
|
||||||
|
|
||||||
// Create the submission item with actual park data
|
// Extract images for assignment
|
||||||
|
const changedFields = extractChangedFields(data, {});
|
||||||
|
|
||||||
|
// Insert into relational park_submissions table
|
||||||
|
const { data: parkSubmission, error: parkSubmissionError } = await supabase
|
||||||
|
.from('park_submissions' as any)
|
||||||
|
.insert({
|
||||||
|
submission_id: submissionData.id,
|
||||||
|
name: data.name,
|
||||||
|
slug: data.slug,
|
||||||
|
description: data.description || null,
|
||||||
|
park_type: data.park_type,
|
||||||
|
status: data.status,
|
||||||
|
opening_date: data.opening_date ? new Date(data.opening_date).toISOString().split('T')[0] : null,
|
||||||
|
closing_date: data.closing_date ? new Date(data.closing_date).toISOString().split('T')[0] : null,
|
||||||
|
website_url: data.website_url || null,
|
||||||
|
phone: data.phone || null,
|
||||||
|
email: data.email || null,
|
||||||
|
operator_id: data.operator_id || null,
|
||||||
|
property_owner_id: data.property_owner_id || null,
|
||||||
|
location_id: data.location_id || null,
|
||||||
|
banner_image_url: changedFields.banner_image_url as string || null,
|
||||||
|
banner_image_id: changedFields.banner_image_id as string || null,
|
||||||
|
card_image_url: changedFields.card_image_url as string || null,
|
||||||
|
card_image_id: changedFields.card_image_id as string || null
|
||||||
|
} as any)
|
||||||
|
.select('id')
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (parkSubmissionError) throw parkSubmissionError;
|
||||||
|
|
||||||
|
// Create submission_items record linking to park_submissions
|
||||||
const { error: itemError } = await supabase
|
const { error: itemError } = await supabase
|
||||||
.from('submission_items')
|
.from('submission_items')
|
||||||
.insert({
|
.insert({
|
||||||
submission_id: submissionData.id,
|
submission_id: submissionData.id,
|
||||||
item_type: 'park',
|
item_type: 'park',
|
||||||
action_type: 'create',
|
action_type: 'create',
|
||||||
item_data: {
|
item_data_id: (parkSubmission as any).id,
|
||||||
...extractChangedFields(data, {}),
|
|
||||||
images: processedImages as unknown as Json
|
|
||||||
},
|
|
||||||
status: 'pending' as const,
|
status: 'pending' as const,
|
||||||
order_index: 0
|
order_index: 0
|
||||||
});
|
} as any);
|
||||||
|
|
||||||
if (itemError) throw itemError;
|
if (itemError) throw itemError;
|
||||||
|
|
||||||
@@ -748,20 +776,61 @@ export async function submitRideCreation(
|
|||||||
|
|
||||||
if (submissionError) throw submissionError;
|
if (submissionError) throw submissionError;
|
||||||
|
|
||||||
// Create the submission item with actual ride data
|
// Extract images for assignment
|
||||||
|
const changedFields = extractChangedFields(data, {});
|
||||||
|
|
||||||
|
// Insert into relational ride_submissions table
|
||||||
|
const { data: rideSubmission, error: rideSubmissionError } = await supabase
|
||||||
|
.from('ride_submissions' as any)
|
||||||
|
.insert({
|
||||||
|
submission_id: submissionData.id,
|
||||||
|
park_id: data.park_id || null,
|
||||||
|
name: data.name,
|
||||||
|
slug: data.slug,
|
||||||
|
description: data.description || null,
|
||||||
|
category: data.category,
|
||||||
|
ride_sub_type: data.ride_sub_type || null,
|
||||||
|
status: data.status,
|
||||||
|
opening_date: data.opening_date ? new Date(data.opening_date).toISOString().split('T')[0] : null,
|
||||||
|
closing_date: data.closing_date ? new Date(data.closing_date).toISOString().split('T')[0] : null,
|
||||||
|
manufacturer_id: data.manufacturer_id || null,
|
||||||
|
designer_id: data.designer_id || null,
|
||||||
|
ride_model_id: data.ride_model_id || null,
|
||||||
|
height_requirement: data.height_requirement || null,
|
||||||
|
age_requirement: data.age_requirement || null,
|
||||||
|
capacity_per_hour: data.capacity_per_hour || null,
|
||||||
|
duration_seconds: data.duration_seconds || null,
|
||||||
|
max_speed_kmh: data.max_speed_kmh || null,
|
||||||
|
max_height_meters: data.max_height_meters || null,
|
||||||
|
length_meters: data.length_meters || null,
|
||||||
|
drop_height_meters: data.drop_height_meters || null,
|
||||||
|
inversions: data.inversions || 0,
|
||||||
|
max_g_force: data.max_g_force || null,
|
||||||
|
coaster_type: data.coaster_type || null,
|
||||||
|
seating_type: data.seating_type || null,
|
||||||
|
intensity_level: data.intensity_level || null,
|
||||||
|
banner_image_url: changedFields.banner_image_url as string || null,
|
||||||
|
banner_image_id: changedFields.banner_image_id as string || null,
|
||||||
|
card_image_url: changedFields.card_image_url as string || null,
|
||||||
|
card_image_id: changedFields.card_image_id as string || null,
|
||||||
|
image_url: null
|
||||||
|
} as any)
|
||||||
|
.select('id')
|
||||||
|
.single();
|
||||||
|
|
||||||
|
if (rideSubmissionError) throw rideSubmissionError;
|
||||||
|
|
||||||
|
// Create submission_items record linking to ride_submissions
|
||||||
const { error: itemError } = await supabase
|
const { error: itemError } = await supabase
|
||||||
.from('submission_items')
|
.from('submission_items')
|
||||||
.insert({
|
.insert({
|
||||||
submission_id: submissionData.id,
|
submission_id: submissionData.id,
|
||||||
item_type: 'ride',
|
item_type: 'ride',
|
||||||
action_type: 'create',
|
action_type: 'create',
|
||||||
item_data: {
|
item_data_id: (rideSubmission as any).id,
|
||||||
...extractChangedFields(data, {}),
|
|
||||||
images: processedImages as unknown as Json
|
|
||||||
},
|
|
||||||
status: 'pending' as const,
|
status: 'pending' as const,
|
||||||
order_index: 0
|
order_index: 0
|
||||||
});
|
} as any);
|
||||||
|
|
||||||
if (itemError) throw itemError;
|
if (itemError) throw itemError;
|
||||||
|
|
||||||
|
|||||||
@@ -335,10 +335,16 @@ serve(async (req) => {
|
|||||||
|
|
||||||
edgeLogger.info('Processing selective approval', { action: 'approval_start', itemCount: itemIds.length, userId: authenticatedUserId, submissionId });
|
edgeLogger.info('Processing selective approval', { action: 'approval_start', itemCount: itemIds.length, userId: authenticatedUserId, submissionId });
|
||||||
|
|
||||||
// Fetch all items for the submission
|
// Fetch all items with relational data for the submission
|
||||||
const { data: items, error: fetchError } = await supabase
|
const { data: items, error: fetchError } = await supabase
|
||||||
.from('submission_items')
|
.from('submission_items')
|
||||||
.select('*')
|
.select(`
|
||||||
|
*,
|
||||||
|
park_submission:park_submissions!item_data_id(*),
|
||||||
|
ride_submission:ride_submissions!item_data_id(*),
|
||||||
|
company_submission:company_submissions!item_data_id(*),
|
||||||
|
ride_model_submission:ride_model_submissions!item_data_id(*)
|
||||||
|
`)
|
||||||
.in('id', itemIds);
|
.in('id', itemIds);
|
||||||
|
|
||||||
if (fetchError) {
|
if (fetchError) {
|
||||||
@@ -405,8 +411,36 @@ serve(async (req) => {
|
|||||||
try {
|
try {
|
||||||
edgeLogger.info('Processing item', { action: 'approval_process_item', itemId: item.id, itemType: item.item_type });
|
edgeLogger.info('Processing item', { action: 'approval_process_item', itemId: item.id, itemType: item.item_type });
|
||||||
|
|
||||||
|
// Extract data from relational tables based on item_type
|
||||||
|
let itemData: any;
|
||||||
|
switch (item.item_type) {
|
||||||
|
case 'park':
|
||||||
|
itemData = (item as any).park_submission;
|
||||||
|
break;
|
||||||
|
case 'ride':
|
||||||
|
itemData = (item as any).ride_submission;
|
||||||
|
break;
|
||||||
|
case 'manufacturer':
|
||||||
|
case 'operator':
|
||||||
|
case 'property_owner':
|
||||||
|
case 'designer':
|
||||||
|
itemData = (item as any).company_submission;
|
||||||
|
break;
|
||||||
|
case 'ride_model':
|
||||||
|
itemData = (item as any).ride_model_submission;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// For photo/timeline items, fall back to item_data (these still use JSONB)
|
||||||
|
itemData = item.item_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!itemData && item.item_data) {
|
||||||
|
// Fallback to item_data if relational data not found (for backwards compatibility)
|
||||||
|
itemData = item.item_data;
|
||||||
|
}
|
||||||
|
|
||||||
// Validate entity data with strict validation, passing original_data for edits
|
// Validate entity data with strict validation, passing original_data for edits
|
||||||
const validation = validateEntityDataStrict(item.item_type, item.item_data, item.original_data);
|
const validation = validateEntityDataStrict(item.item_type, itemData, item.original_data);
|
||||||
|
|
||||||
if (validation.blockingErrors.length > 0) {
|
if (validation.blockingErrors.length > 0) {
|
||||||
edgeLogger.error('Blocking validation errors', {
|
edgeLogger.error('Blocking validation errors', {
|
||||||
@@ -467,7 +501,7 @@ serve(async (req) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Resolve dependencies in item data
|
// Resolve dependencies in item data
|
||||||
const resolvedData = resolveDependencies(item.item_data, dependencyMap, sortedItems);
|
const resolvedData = resolveDependencies(itemData, dependencyMap, sortedItems);
|
||||||
|
|
||||||
// Add submitter ID to the data for photo tracking
|
// Add submitter ID to the data for photo tracking
|
||||||
resolvedData._submitter_id = submitterId;
|
resolvedData._submitter_id = submitterId;
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
-- ============================================================================
|
||||||
|
-- ELIMINATE JSONB FROM submission_items
|
||||||
|
-- ============================================================================
|
||||||
|
-- Problem: submission_items.item_data stores entire submission payloads as JSONB
|
||||||
|
-- Solution: Use existing relational tables (*_submissions) and link via foreign key
|
||||||
|
--
|
||||||
|
-- CRITICAL PROJECT RULE: NO JSON OR JSONB IN SQL COLUMNS FOR RELATIONAL DATA
|
||||||
|
-- ============================================================================
|
||||||
|
|
||||||
|
-- Step 1: Add foreign key column to link to relational submission tables
|
||||||
|
ALTER TABLE submission_items
|
||||||
|
ADD COLUMN item_data_id UUID;
|
||||||
|
|
||||||
|
-- Step 2: Create index for performance
|
||||||
|
CREATE INDEX idx_submission_items_item_data_id ON submission_items(item_data_id);
|
||||||
|
|
||||||
|
-- Step 3: Add comments explaining the architecture
|
||||||
|
COMMENT ON COLUMN submission_items.item_data_id IS
|
||||||
|
'Foreign key to *_submissions tables based on item_type. park -> park_submissions, ride -> ride_submissions, company types -> company_submissions, ride_model -> ride_model_submissions. Replaces the deprecated item_data JSONB column.';
|
||||||
|
|
||||||
|
COMMENT ON COLUMN submission_items.item_data IS
|
||||||
|
'DEPRECATED: Use item_data_id to reference relational *_submissions tables. This JSONB column violates the NO JSON IN DATABASE rule and will be dropped.';
|
||||||
|
|
||||||
|
COMMENT ON COLUMN submission_items.original_data IS
|
||||||
|
'DEPRECATED: Use item_data_id to reference relational *_submissions tables. This JSONB column violates the NO JSON IN DATABASE rule and will be dropped.';
|
||||||
Reference in New Issue
Block a user