mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-28 09:46:59 -05:00
Compare commits
5 Commits
972505f53b
...
898f838862
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
898f838862 | ||
|
|
b326252138 | ||
|
|
d62b3c2412 | ||
|
|
303853ff94 | ||
|
|
b036fb4785 |
@@ -4718,6 +4718,38 @@ export type Database = {
|
||||
}
|
||||
Relationships: []
|
||||
}
|
||||
submission_item_temp_refs: {
|
||||
Row: {
|
||||
created_at: string
|
||||
id: string
|
||||
ref_order_index: number
|
||||
ref_type: string
|
||||
submission_item_id: string
|
||||
}
|
||||
Insert: {
|
||||
created_at?: string
|
||||
id?: string
|
||||
ref_order_index: number
|
||||
ref_type: string
|
||||
submission_item_id: string
|
||||
}
|
||||
Update: {
|
||||
created_at?: string
|
||||
id?: string
|
||||
ref_order_index?: number
|
||||
ref_type?: string
|
||||
submission_item_id?: string
|
||||
}
|
||||
Relationships: [
|
||||
{
|
||||
foreignKeyName: "submission_item_temp_refs_submission_item_id_fkey"
|
||||
columns: ["submission_item_id"]
|
||||
isOneToOne: false
|
||||
referencedRelation: "submission_items"
|
||||
referencedColumns: ["id"]
|
||||
},
|
||||
]
|
||||
}
|
||||
submission_items: {
|
||||
Row: {
|
||||
action_type: string | null
|
||||
|
||||
@@ -359,6 +359,40 @@ serve(withRateLimit(async (req) => {
|
||||
throw new Error(`Failed to fetch items: ${fetchError.message}`);
|
||||
}
|
||||
|
||||
// Query temporary references for all submission items
|
||||
const { data: tempRefs, error: tempRefsError } = await supabase
|
||||
.from('submission_item_temp_refs')
|
||||
.select('submission_item_id, ref_type, ref_order_index')
|
||||
.in('submission_item_id', itemIds);
|
||||
|
||||
if (tempRefsError) {
|
||||
edgeLogger.warn('Failed to fetch temp refs', {
|
||||
action: 'approval_fetch_temp_refs',
|
||||
submissionId,
|
||||
error: tempRefsError.message,
|
||||
requestId: tracking.requestId
|
||||
});
|
||||
// Don't throw - continue with empty temp refs (backwards compatibility)
|
||||
}
|
||||
|
||||
// Build a map: itemId -> { _temp_operator_ref: 0, _temp_park_ref: 1, ... }
|
||||
const tempRefsByItemId = new Map<string, Record<string, number>>();
|
||||
for (const ref of tempRefs || []) {
|
||||
if (!tempRefsByItemId.has(ref.submission_item_id)) {
|
||||
tempRefsByItemId.set(ref.submission_item_id, {});
|
||||
}
|
||||
const fieldName = `_temp_${ref.ref_type}_ref`;
|
||||
tempRefsByItemId.get(ref.submission_item_id)![fieldName] = ref.ref_order_index;
|
||||
}
|
||||
|
||||
edgeLogger.info('Loaded temp refs', {
|
||||
action: 'approval_temp_refs_loaded',
|
||||
submissionId,
|
||||
itemsWithTempRefs: tempRefsByItemId.size,
|
||||
totalTempRefs: tempRefs?.length || 0,
|
||||
requestId: tracking.requestId
|
||||
});
|
||||
|
||||
// Get the submitter's user_id from the submission
|
||||
const { data: submission, error: submissionError } = await supabase
|
||||
.from('content_submissions')
|
||||
@@ -423,25 +457,39 @@ serve(withRateLimit(async (req) => {
|
||||
let itemData: any;
|
||||
switch (item.item_type) {
|
||||
case 'park':
|
||||
itemData = (item as any).park_submission;
|
||||
itemData = {
|
||||
...(item as any).park_submission,
|
||||
// Merge temp refs for this item
|
||||
...(tempRefsByItemId.get(item.id) || {})
|
||||
};
|
||||
break;
|
||||
case 'ride':
|
||||
itemData = (item as any).ride_submission;
|
||||
itemData = {
|
||||
...(item as any).ride_submission,
|
||||
...(tempRefsByItemId.get(item.id) || {})
|
||||
};
|
||||
break;
|
||||
case 'manufacturer':
|
||||
case 'operator':
|
||||
case 'property_owner':
|
||||
case 'designer':
|
||||
itemData = (item as any).company_submission;
|
||||
itemData = {
|
||||
...(item as any).company_submission,
|
||||
...(tempRefsByItemId.get(item.id) || {})
|
||||
};
|
||||
break;
|
||||
case 'ride_model':
|
||||
itemData = (item as any).ride_model_submission;
|
||||
itemData = {
|
||||
...(item as any).ride_model_submission,
|
||||
...(tempRefsByItemId.get(item.id) || {})
|
||||
};
|
||||
break;
|
||||
case 'photo':
|
||||
// Combine photo_submission with its photo_items array
|
||||
itemData = {
|
||||
...(item as any).photo_submission,
|
||||
photos: (item as any).photo_submission?.photo_items || []
|
||||
photos: (item as any).photo_submission?.photo_items || [],
|
||||
...(tempRefsByItemId.get(item.id) || {})
|
||||
};
|
||||
break;
|
||||
default:
|
||||
@@ -454,6 +502,17 @@ serve(withRateLimit(async (req) => {
|
||||
itemData = item.item_data;
|
||||
}
|
||||
|
||||
// Log if temp refs were found for this item
|
||||
if (tempRefsByItemId.has(item.id)) {
|
||||
edgeLogger.info('Item has temp refs', {
|
||||
action: 'approval_item_temp_refs',
|
||||
itemId: item.id,
|
||||
itemType: item.item_type,
|
||||
tempRefs: tempRefsByItemId.get(item.id),
|
||||
requestId: tracking.requestId
|
||||
});
|
||||
}
|
||||
|
||||
// Validate entity data with strict validation, passing original_data for edits
|
||||
const validation = validateEntityDataStrict(item.item_type, itemData, item.original_data);
|
||||
|
||||
@@ -632,6 +691,37 @@ serve(withRateLimit(async (req) => {
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ CLEANUP: Delete temporary references for approved items
|
||||
const approvedItemIds = approvalResults.filter(r => r.success).map(r => r.itemId);
|
||||
if (approvedItemIds.length > 0) {
|
||||
try {
|
||||
const { error: cleanupError } = await supabase
|
||||
.from('submission_item_temp_refs')
|
||||
.delete()
|
||||
.in('submission_item_id', approvedItemIds);
|
||||
|
||||
if (cleanupError) {
|
||||
edgeLogger.warn('Failed to cleanup temp refs for approved items', {
|
||||
requestId: tracking.requestId,
|
||||
approvedItemIds,
|
||||
error: cleanupError.message
|
||||
});
|
||||
// Don't throw - cleanup failure shouldn't block approval
|
||||
} else {
|
||||
edgeLogger.info('Cleaned up temp refs for approved items', {
|
||||
requestId: tracking.requestId,
|
||||
count: approvedItemIds.length
|
||||
});
|
||||
}
|
||||
} catch (cleanupErr) {
|
||||
edgeLogger.warn('Exception during temp ref cleanup', {
|
||||
requestId: tracking.requestId,
|
||||
error: cleanupErr instanceof Error ? cleanupErr.message : 'Unknown error'
|
||||
});
|
||||
// Continue - don't let cleanup errors affect approval
|
||||
}
|
||||
}
|
||||
|
||||
// Batch update all rejected items
|
||||
const rejectedItemIds = approvalResults.filter(r => !r.success).map(r => r.itemId);
|
||||
if (rejectedItemIds.length > 0) {
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
-- =====================================================
|
||||
-- Automatic Cleanup of Temporary References
|
||||
-- =====================================================
|
||||
-- Trigger to delete submission_item_temp_refs when items are approved
|
||||
-- This acts as a safety net if edge function cleanup fails
|
||||
|
||||
-- Function to cleanup temp refs on approval
|
||||
CREATE OR REPLACE FUNCTION public.cleanup_temp_refs_on_approval()
|
||||
RETURNS TRIGGER
|
||||
LANGUAGE plpgsql
|
||||
SECURITY DEFINER
|
||||
SET search_path TO 'public'
|
||||
AS $function$
|
||||
DECLARE
|
||||
v_deleted_count INTEGER;
|
||||
BEGIN
|
||||
-- Only cleanup when status changes to approved
|
||||
IF NEW.status = 'approved' AND OLD.status != 'approved' THEN
|
||||
-- Delete all temp refs for this submission item
|
||||
DELETE FROM public.submission_item_temp_refs
|
||||
WHERE submission_item_id = NEW.id;
|
||||
|
||||
GET DIAGNOSTICS v_deleted_count = ROW_COUNT;
|
||||
|
||||
IF v_deleted_count > 0 THEN
|
||||
RAISE NOTICE 'Cleaned up % temp refs for submission_item %', v_deleted_count, NEW.id;
|
||||
END IF;
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
EXCEPTION WHEN OTHERS THEN
|
||||
-- Log but don't block the approval
|
||||
RAISE NOTICE 'Failed to cleanup temp refs for item %: %', NEW.id, SQLERRM;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$function$;
|
||||
|
||||
-- Create trigger on submission_items
|
||||
CREATE TRIGGER trigger_cleanup_temp_refs_on_approval
|
||||
AFTER UPDATE OF status ON public.submission_items
|
||||
FOR EACH ROW
|
||||
WHEN (NEW.status = 'approved' AND OLD.status IS DISTINCT FROM 'approved')
|
||||
EXECUTE FUNCTION public.cleanup_temp_refs_on_approval();
|
||||
|
||||
COMMENT ON FUNCTION public.cleanup_temp_refs_on_approval() IS
|
||||
'Automatically deletes temporary reference records when submission items are approved. Acts as safety net for edge function cleanup.';
|
||||
|
||||
COMMENT ON TRIGGER trigger_cleanup_temp_refs_on_approval ON public.submission_items IS
|
||||
'Ensures temp refs are cleaned up even if edge function cleanup fails';
|
||||
@@ -0,0 +1,85 @@
|
||||
-- =====================================================
|
||||
-- Temporary Reference Storage for Composite Submissions
|
||||
-- =====================================================
|
||||
-- Stores _temp_*_ref fields extracted from submission items
|
||||
-- These are used during approval to resolve dependencies
|
||||
|
||||
CREATE TABLE IF NOT EXISTS public.submission_item_temp_refs (
|
||||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||||
submission_item_id UUID NOT NULL REFERENCES public.submission_items(id) ON DELETE CASCADE,
|
||||
ref_type TEXT NOT NULL CHECK (ref_type IN (
|
||||
'operator', 'property_owner', 'manufacturer', 'designer',
|
||||
'park', 'ride', 'ride_model', 'location'
|
||||
)),
|
||||
ref_order_index INTEGER NOT NULL,
|
||||
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||
);
|
||||
|
||||
-- Indexes for performance
|
||||
CREATE INDEX idx_submission_item_temp_refs_item_id
|
||||
ON public.submission_item_temp_refs(submission_item_id);
|
||||
|
||||
CREATE INDEX idx_submission_item_temp_refs_type
|
||||
ON public.submission_item_temp_refs(ref_type);
|
||||
|
||||
-- Composite index for fast lookups during approval
|
||||
CREATE INDEX idx_submission_item_temp_refs_item_type
|
||||
ON public.submission_item_temp_refs(submission_item_id, ref_type);
|
||||
|
||||
-- Enable RLS
|
||||
ALTER TABLE public.submission_item_temp_refs ENABLE ROW LEVEL SECURITY;
|
||||
|
||||
-- RLS Policy: Moderators can view all temp refs
|
||||
CREATE POLICY "Moderators can view all temp refs"
|
||||
ON public.submission_item_temp_refs
|
||||
FOR SELECT
|
||||
USING (
|
||||
is_moderator(auth.uid()) AND
|
||||
((NOT has_mfa_enabled(auth.uid())) OR has_aal2())
|
||||
);
|
||||
|
||||
-- RLS Policy: Users can view their own temp refs via submission ownership
|
||||
CREATE POLICY "Users can view their own temp refs"
|
||||
ON public.submission_item_temp_refs
|
||||
FOR SELECT
|
||||
USING (
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM public.submission_items si
|
||||
JOIN public.content_submissions cs ON cs.id = si.submission_id
|
||||
WHERE si.id = submission_item_temp_refs.submission_item_id
|
||||
AND cs.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
-- RLS Policy: System can insert temp refs during submission creation
|
||||
CREATE POLICY "System can insert temp refs"
|
||||
ON public.submission_item_temp_refs
|
||||
FOR INSERT
|
||||
WITH CHECK (
|
||||
EXISTS (
|
||||
SELECT 1
|
||||
FROM public.submission_items si
|
||||
JOIN public.content_submissions cs ON cs.id = si.submission_id
|
||||
WHERE si.id = submission_item_temp_refs.submission_item_id
|
||||
AND cs.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
-- RLS Policy: Moderators can delete temp refs (manual cleanup if needed)
|
||||
CREATE POLICY "Moderators can delete temp refs"
|
||||
ON public.submission_item_temp_refs
|
||||
FOR DELETE
|
||||
USING (
|
||||
is_moderator(auth.uid()) AND
|
||||
((NOT has_mfa_enabled(auth.uid())) OR has_aal2())
|
||||
);
|
||||
|
||||
COMMENT ON TABLE public.submission_item_temp_refs IS
|
||||
'Stores temporary reference fields (_temp_operator_ref, etc.) from submission items for composite submissions. Cleaned up automatically when items are approved.';
|
||||
|
||||
COMMENT ON COLUMN public.submission_item_temp_refs.ref_type IS
|
||||
'The entity type being referenced (e.g., operator, park, manufacturer). Extracted from _temp_operator_ref -> operator.';
|
||||
|
||||
COMMENT ON COLUMN public.submission_item_temp_refs.ref_order_index IS
|
||||
'The array index of the referenced item in the submission order. Matches the order_index of the target submission_item.';
|
||||
@@ -0,0 +1,278 @@
|
||||
-- =====================================================
|
||||
-- Update create_submission_with_items to Extract Temp Refs
|
||||
-- =====================================================
|
||||
-- Extracts _temp_*_ref fields and stores them in submission_item_temp_refs
|
||||
|
||||
CREATE OR REPLACE FUNCTION public.create_submission_with_items(
|
||||
p_user_id uuid,
|
||||
p_submission_type text,
|
||||
p_content jsonb,
|
||||
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;
|
||||
v_timeline_event_submission_id UUID;
|
||||
v_submission_item_id UUID;
|
||||
v_temp_ref_key TEXT;
|
||||
v_temp_ref_value TEXT;
|
||||
v_ref_type TEXT;
|
||||
v_ref_order_index INTEGER;
|
||||
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 := NULL;
|
||||
v_company_submission_id := NULL;
|
||||
v_ride_submission_id := NULL;
|
||||
v_ride_model_submission_id := NULL;
|
||||
v_photo_submission_id := NULL;
|
||||
v_timeline_event_submission_id := NULL;
|
||||
|
||||
-- Create specialized submission records based on item_type
|
||||
IF v_item_type = 'park' THEN
|
||||
INSERT INTO park_submissions (
|
||||
submission_id, name, slug, description, park_type, status,
|
||||
opening_date, opening_date_precision, closing_date, closing_date_precision,
|
||||
location_id, operator_id, property_owner_id,
|
||||
website_url, phone, email,
|
||||
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->>'opening_date_precision',
|
||||
(v_item_data->>'closing_date')::DATE,
|
||||
v_item_data->>'closing_date_precision',
|
||||
(v_item_data->>'location_id')::UUID,
|
||||
(v_item_data->>'operator_id')::UUID,
|
||||
(v_item_data->>'property_owner_id')::UUID,
|
||||
v_item_data->>'website_url',
|
||||
v_item_data->>'phone',
|
||||
v_item_data->>'email',
|
||||
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 ('manufacturer', 'operator', 'property_owner', '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_type,
|
||||
COALESCE(v_item_data->>'person_type', 'company'),
|
||||
(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, opening_date_precision, closing_date, 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, image_url,
|
||||
ride_sub_type, coaster_type, seating_type, intensity_level
|
||||
) 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->>'opening_date_precision',
|
||||
(v_item_data->>'closing_date')::DATE,
|
||||
v_item_data->>'closing_date_precision',
|
||||
(v_item_data->>'height_requirement_cm')::NUMERIC,
|
||||
(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',
|
||||
v_item_data->>'image_url',
|
||||
v_item_data->>'ride_sub_type',
|
||||
v_item_data->>'coaster_type',
|
||||
v_item_data->>'seating_type',
|
||||
v_item_data->>'intensity_level'
|
||||
) RETURNING id INTO v_ride_submission_id;
|
||||
|
||||
ELSIF v_item_type = 'ride_model' THEN
|
||||
INSERT INTO ride_model_submissions (
|
||||
submission_id, name, slug, manufacturer_id, category, ride_type, description,
|
||||
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->>'manufacturer_id')::UUID,
|
||||
v_item_data->>'category',
|
||||
v_item_data->>'ride_type',
|
||||
v_item_data->>'description',
|
||||
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_model_submission_id;
|
||||
|
||||
ELSIF v_item_type = 'photo' THEN
|
||||
INSERT INTO photo_submissions (
|
||||
submission_id, entity_type, entity_id, title
|
||||
) VALUES (
|
||||
v_submission_id,
|
||||
v_item_data->>'entity_type',
|
||||
(v_item_data->>'entity_id')::UUID,
|
||||
v_item_data->>'title'
|
||||
) RETURNING id INTO v_photo_submission_id;
|
||||
|
||||
ELSIF v_item_type IN ('timeline_event', 'milestone') THEN
|
||||
INSERT INTO timeline_event_submissions (
|
||||
submission_id, entity_type, entity_id, event_type, event_date,
|
||||
event_date_precision, title, description
|
||||
) VALUES (
|
||||
v_submission_id,
|
||||
v_item_data->>'entity_type',
|
||||
(v_item_data->>'entity_id')::UUID,
|
||||
v_item_data->>'event_type',
|
||||
(v_item_data->>'event_date')::DATE,
|
||||
v_item_data->>'event_date_precision',
|
||||
v_item_data->>'title',
|
||||
v_item_data->>'description'
|
||||
) RETURNING id INTO v_timeline_event_submission_id;
|
||||
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,
|
||||
timeline_event_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,
|
||||
v_timeline_event_submission_id,
|
||||
'pending',
|
||||
COALESCE((v_item->>'order_index')::INTEGER, 0),
|
||||
(v_item->>'depends_on')::UUID
|
||||
) RETURNING id INTO v_submission_item_id;
|
||||
|
||||
-- Extract and store temp refs from item_data
|
||||
IF v_item_data IS NOT NULL AND jsonb_typeof(v_item_data) = 'object' THEN
|
||||
FOR v_temp_ref_key, v_temp_ref_value IN
|
||||
SELECT key, value::text
|
||||
FROM jsonb_each_text(v_item_data)
|
||||
WHERE key LIKE '_temp_%_ref'
|
||||
AND value ~ '^\d+$'
|
||||
LOOP
|
||||
BEGIN
|
||||
-- Extract ref_type from key (e.g., "_temp_operator_ref" -> "operator")
|
||||
v_ref_type := substring(v_temp_ref_key from '_temp_(.+)_ref');
|
||||
v_ref_order_index := v_temp_ref_value::INTEGER;
|
||||
|
||||
-- Insert temp ref record
|
||||
INSERT INTO submission_item_temp_refs (
|
||||
submission_item_id,
|
||||
ref_type,
|
||||
ref_order_index
|
||||
) VALUES (
|
||||
v_submission_item_id,
|
||||
v_ref_type,
|
||||
v_ref_order_index
|
||||
);
|
||||
|
||||
RAISE NOTICE 'Stored temp ref: item_id=%, ref_type=%, order_index=%',
|
||||
v_submission_item_id, v_ref_type, v_ref_order_index;
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN
|
||||
RAISE WARNING 'Failed to store temp ref % for item %: %',
|
||||
v_temp_ref_key, v_submission_item_id, SQLERRM;
|
||||
END;
|
||||
END LOOP;
|
||||
END IF;
|
||||
|
||||
END LOOP;
|
||||
|
||||
RETURN v_submission_id;
|
||||
|
||||
EXCEPTION
|
||||
WHEN OTHERS THEN
|
||||
RAISE NOTICE 'Submission creation failed for user % (type=%): %', p_user_id, p_submission_type, SQLERRM;
|
||||
RAISE;
|
||||
END;
|
||||
$$;
|
||||
Reference in New Issue
Block a user