Compare commits

...

3 Commits

Author SHA1 Message Date
gpt-engineer-app[bot]
ae22a48ce2 Fix null safety issues 2025-11-04 22:57:49 +00:00
gpt-engineer-app[bot]
c15efd7907 Fix composite submission RPC 2025-11-04 22:49:39 +00:00
gpt-engineer-app[bot]
2a287b0d48 Fix composite submission data linkage 2025-11-04 22:49:01 +00:00
5 changed files with 212 additions and 10 deletions

View File

@@ -56,12 +56,19 @@ export const SubmissionItemsList = memo(function SubmissionItemsList({
if (itemsError) throw itemsError;
// Transform to expected format
const transformedItems = (itemsData || []).map((item: any) => ({
...item,
item_data: item.entity_data || {},
entity_data: item.entity_data
}));
// Transform to expected format with better null handling
const transformedItems = (itemsData || []).map((item: any) => {
// Ensure entity_data is at least an empty object, never null
const safeEntityData = item.entity_data && typeof item.entity_data === 'object'
? item.entity_data
: {};
return {
...item,
item_data: safeEntityData,
entity_data: item.entity_data // Keep original for debugging
};
});
// Check for photo submissions (using array query to avoid 406)
const { data: photoData, error: photoError } = await supabase

View File

@@ -30,9 +30,12 @@ export function ValidationSummary({ item, onValidationChange, compact = false, v
// Helper to extract the correct entity ID based on entity type
const getEntityId = (
itemType: string,
itemData: SubmissionItemData,
itemData: SubmissionItemData | null | undefined,
fallbackId?: string
): string | undefined => {
// Guard against null/undefined itemData
if (!itemData) return fallbackId;
// Try entity-specific ID fields first
const entityIdField = `${itemType}_id`;
const typedData = itemData as unknown as Record<string, unknown>;
@@ -84,7 +87,7 @@ export function ValidationSummary({ item, onValidationChange, compact = false, v
const result = await validateEntityData(
item.item_type as ValidEntityType,
{
...item.item_data,
...(item.item_data || {}), // Add null coalescing
id: getEntityId(item.item_type, item.item_data, item.id)
}
);

View File

@@ -17,6 +17,9 @@ export function RichParkDisplay({ data, actionType, showAllFields = true }: Rich
const [propertyOwner, setPropertyOwner] = useState<string | null>(null);
useEffect(() => {
// Guard against null/undefined data
if (!data) return;
const fetchRelatedData = async () => {
// Fetch location
if (data.location_id) {
@@ -52,7 +55,8 @@ export function RichParkDisplay({ data, actionType, showAllFields = true }: Rich
fetchRelatedData();
}, [data.location_id, data.operator_id, data.property_owner_id]);
const getStatusColor = (status: string) => {
const getStatusColor = (status: string | undefined) => {
if (!status) return 'bg-gray-500';
switch (status.toLowerCase()) {
case 'operating': return 'bg-green-500';
case 'closed': return 'bg-red-500';

View File

@@ -22,6 +22,9 @@ export function RichRideDisplay({ data, actionType, showAllFields = true }: Rich
const [showTechnical, setShowTechnical] = useState(false);
useEffect(() => {
// Guard against null/undefined data
if (!data) return;
const fetchRelatedData = async () => {
if (data.park_id) {
const { data: parkData } = await supabase
@@ -63,7 +66,8 @@ export function RichRideDisplay({ data, actionType, showAllFields = true }: Rich
fetchRelatedData();
}, [data.park_id, data.manufacturer_id, data.designer_id, data.ride_model_id]);
const getStatusColor = (status: string) => {
const getStatusColor = (status: string | undefined) => {
if (!status) return 'bg-gray-500';
switch (status.toLowerCase()) {
case 'operating': return 'bg-green-500';
case 'closed': return 'bg-red-500';

View File

@@ -0,0 +1,184 @@
-- Fix create_submission_with_items to properly handle composite submissions
-- Creates specialized records (park_submissions, company_submissions, etc.) and links them
CREATE OR REPLACE FUNCTION public.create_submission_with_items(
p_user_id uuid,
p_submission_type text,
p_content jsonb, -- Keep for backward compatibility
p_items jsonb[]
)
RETURNS uuid
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path TO 'public'
AS $$
DECLARE
v_submission_id UUID;
v_item JSONB;
v_item_data JSONB;
v_item_type TEXT;
v_action_type TEXT;
v_park_submission_id UUID;
v_company_submission_id UUID;
v_ride_submission_id UUID;
v_ride_model_submission_id UUID;
v_photo_submission_id UUID;
BEGIN
-- Create main submission
INSERT INTO content_submissions (user_id, submission_type, status, approval_mode)
VALUES (p_user_id, p_submission_type, 'pending', 'full')
RETURNING id INTO v_submission_id;
-- Validate items array
IF array_length(p_items, 1) IS NULL OR array_length(p_items, 1) = 0 THEN
RAISE EXCEPTION 'Cannot create submission without items';
END IF;
-- Process each item
FOREACH v_item IN ARRAY p_items
LOOP
v_item_type := (v_item->>'item_type')::TEXT;
v_action_type := (v_item->>'action_type')::TEXT;
v_item_data := v_item->'item_data';
-- Reset IDs for this iteration
v_park_submission_id := (v_item->>'park_submission_id')::UUID;
v_company_submission_id := (v_item->>'company_submission_id')::UUID;
v_ride_submission_id := (v_item->>'ride_submission_id')::UUID;
v_ride_model_submission_id := (v_item->>'ride_model_submission_id')::UUID;
v_photo_submission_id := (v_item->>'photo_submission_id')::UUID;
-- Create specialized submission if item_data exists (for new entities)
IF v_item_data IS NOT NULL AND jsonb_typeof(v_item_data) = 'object' THEN
IF v_item_type = 'park' THEN
INSERT INTO park_submissions (
submission_id, name, slug, description, park_type, status,
opening_date, closing_date, opening_date_precision, closing_date_precision,
website_url, phone, email, location_id,
operator_id, property_owner_id,
banner_image_url, banner_image_id, card_image_url, card_image_id
) VALUES (
v_submission_id,
v_item_data->>'name',
v_item_data->>'slug',
v_item_data->>'description',
v_item_data->>'park_type',
v_item_data->>'status',
(v_item_data->>'opening_date')::DATE,
(v_item_data->>'closing_date')::DATE,
v_item_data->>'opening_date_precision',
v_item_data->>'closing_date_precision',
v_item_data->>'website_url',
v_item_data->>'phone',
v_item_data->>'email',
(v_item_data->>'location_id')::UUID,
(v_item_data->>'operator_id')::UUID,
(v_item_data->>'property_owner_id')::UUID,
v_item_data->>'banner_image_url',
v_item_data->>'banner_image_id',
v_item_data->>'card_image_url',
v_item_data->>'card_image_id'
) RETURNING id INTO v_park_submission_id;
ELSIF v_item_type IN ('operator', 'property_owner', 'manufacturer', 'designer') THEN
INSERT INTO company_submissions (
submission_id, name, slug, description, company_type, person_type,
founded_year, founded_date, founded_date_precision,
headquarters_location, website_url,
logo_url, banner_image_url, banner_image_id,
card_image_url, card_image_id
) VALUES (
v_submission_id,
v_item_data->>'name',
v_item_data->>'slug',
v_item_data->>'description',
v_item_data->>'company_type',
v_item_data->>'person_type',
(v_item_data->>'founded_year')::INTEGER,
(v_item_data->>'founded_date')::DATE,
v_item_data->>'founded_date_precision',
v_item_data->>'headquarters_location',
v_item_data->>'website_url',
v_item_data->>'logo_url',
v_item_data->>'banner_image_url',
v_item_data->>'banner_image_id',
v_item_data->>'card_image_url',
v_item_data->>'card_image_id'
) RETURNING id INTO v_company_submission_id;
ELSIF v_item_type = 'ride' THEN
INSERT INTO ride_submissions (
submission_id, name, slug, description, category, status,
park_id, manufacturer_id, designer_id, ride_model_id,
opening_date, closing_date, opening_date_precision, closing_date_precision,
height_requirement_cm, age_requirement,
max_speed_kmh, duration_seconds, capacity_per_hour,
gforce_max, inversions_count,
length_meters, height_meters, drop_meters,
banner_image_url, banner_image_id, card_image_url, card_image_id
) VALUES (
v_submission_id,
v_item_data->>'name',
v_item_data->>'slug',
v_item_data->>'description',
v_item_data->>'category',
v_item_data->>'status',
(v_item_data->>'park_id')::UUID,
(v_item_data->>'manufacturer_id')::UUID,
(v_item_data->>'designer_id')::UUID,
(v_item_data->>'ride_model_id')::UUID,
(v_item_data->>'opening_date')::DATE,
(v_item_data->>'closing_date')::DATE,
v_item_data->>'opening_date_precision',
v_item_data->>'closing_date_precision',
(v_item_data->>'height_requirement_cm')::INTEGER,
(v_item_data->>'age_requirement')::INTEGER,
(v_item_data->>'max_speed_kmh')::NUMERIC,
(v_item_data->>'duration_seconds')::INTEGER,
(v_item_data->>'capacity_per_hour')::INTEGER,
(v_item_data->>'gforce_max')::NUMERIC,
(v_item_data->>'inversions_count')::INTEGER,
(v_item_data->>'length_meters')::NUMERIC,
(v_item_data->>'height_meters')::NUMERIC,
(v_item_data->>'drop_meters')::NUMERIC,
v_item_data->>'banner_image_url',
v_item_data->>'banner_image_id',
v_item_data->>'card_image_url',
v_item_data->>'card_image_id'
) RETURNING id INTO v_ride_submission_id;
END IF;
END IF;
-- Insert submission_item with proper foreign key linkage
INSERT INTO submission_items (
submission_id,
item_type,
action_type,
park_submission_id,
company_submission_id,
ride_submission_id,
ride_model_submission_id,
photo_submission_id,
status,
order_index,
depends_on
) VALUES (
v_submission_id,
v_item_type,
v_action_type,
v_park_submission_id,
v_company_submission_id,
v_ride_submission_id,
v_ride_model_submission_id,
v_photo_submission_id,
'pending',
COALESCE((v_item->>'order_index')::INTEGER, 0),
(v_item->>'depends_on')::UUID
);
END LOOP;
RETURN v_submission_id;
END;
$$;