diff --git a/supabase/functions/notify-moderators-submission/index.ts b/supabase/functions/notify-moderators-submission/index.ts index 1ffa0911..e3123e66 100644 --- a/supabase/functions/notify-moderators-submission/index.ts +++ b/supabase/functions/notify-moderators-submission/index.ts @@ -11,6 +11,11 @@ interface NotificationPayload { submission_type: string; submitter_name: string; action: string; + content_preview: string; + submitted_at: string; + has_photos: boolean; + item_count: number; + is_escalated: boolean; } serve(async (req) => { @@ -25,9 +30,44 @@ serve(async (req) => { const supabase = createClient(supabaseUrl, supabaseServiceKey); const payload: NotificationPayload = await req.json(); - const { submission_id, submission_type, submitter_name, action } = payload; + const { + submission_id, + submission_type, + submitter_name, + action, + content_preview, + submitted_at, + has_photos, + item_count, + is_escalated + } = payload; - console.log('Notifying moderators about submission via topic:', { submission_id, submission_type }); + console.log('Notifying moderators about submission via topic:', { + submission_id, + submission_type, + content_preview + }); + + // Calculate relative time and priority + const submittedDate = new Date(submitted_at); + const now = new Date(); + const waitTimeMs = now.getTime() - submittedDate.getTime(); + const waitTimeHours = waitTimeMs / (1000 * 60 * 60); + + // Format relative time + const relativeTime = (() => { + const minutes = Math.floor(waitTimeMs / (1000 * 60)); + const hours = Math.floor(waitTimeMs / (1000 * 60 * 60)); + const days = Math.floor(waitTimeMs / (1000 * 60 * 60 * 24)); + + if (minutes < 1) return 'just now'; + if (minutes < 60) return `${minutes} minute${minutes !== 1 ? 's' : ''} ago`; + if (hours < 24) return `${hours} hour${hours !== 1 ? 's' : ''} ago`; + return `${days} day${days !== 1 ? 's' : ''} ago`; + })(); + + // Determine priority based on wait time + const priority = waitTimeHours >= 24 ? 'urgent' : 'normal'; // Get the moderation-alert workflow const { data: workflow, error: workflowError } = await supabase @@ -51,13 +91,27 @@ serve(async (req) => { ); } - // Prepare notification payload + // Prepare enhanced notification payload const notificationPayload = { + // Basic info itemType: submission_type, submitterName: submitter_name, submissionId: submission_id, action: action || 'create', moderationUrl: `https://ydvtmnrszybqnbcqbdcy.supabase.co/admin/moderation`, + + // Enhanced content + contentPreview: content_preview, + + // Timing information + submittedAt: submitted_at, + relativeTime: relativeTime, + priority: priority, + + // Additional metadata + hasPhotos: has_photos, + itemCount: item_count, + isEscalated: is_escalated, }; // Send ONE notification to the moderation-submissions topic diff --git a/supabase/migrations/20251012182739_473eb6c7-8b1a-4cfa-9a1c-931f1a6a38d0.sql b/supabase/migrations/20251012182739_473eb6c7-8b1a-4cfa-9a1c-931f1a6a38d0.sql new file mode 100644 index 00000000..a72842bf --- /dev/null +++ b/supabase/migrations/20251012182739_473eb6c7-8b1a-4cfa-9a1c-931f1a6a38d0.sql @@ -0,0 +1,107 @@ +-- Update trigger function to include enhanced notification data +CREATE OR REPLACE FUNCTION public.notify_moderators_on_new_submission() +RETURNS TRIGGER +LANGUAGE plpgsql +SECURITY DEFINER +SET search_path = public +AS $$ +DECLARE + submitter_profile record; + function_url text; + anon_key text; + content_preview text; + has_photos boolean := false; + item_count integer := 0; +BEGIN + -- Get submitter's username or display name + SELECT username, display_name INTO submitter_profile + FROM public.profiles + WHERE user_id = NEW.user_id; + + -- Build content preview based on submission type + content_preview := CASE NEW.submission_type + WHEN 'park' THEN + COALESCE(NEW.content->>'name', 'Unnamed') || ' - ' || + COALESCE(NEW.content->>'park_type', 'Park') || + CASE + WHEN NEW.content->>'location' IS NOT NULL + THEN ' in ' || (NEW.content->>'location') + ELSE '' + END + WHEN 'ride' THEN + COALESCE(NEW.content->>'name', 'Unnamed') || ' - ' || + COALESCE(NEW.content->>'category', 'Ride') || + CASE + WHEN NEW.content->>'park_name' IS NOT NULL + THEN ' at ' || (NEW.content->>'park_name') + ELSE '' + END + WHEN 'company' THEN + COALESCE(NEW.content->>'name', 'Unnamed') || ' - ' || + COALESCE(NEW.content->>'company_type', 'Company') + WHEN 'ride_model' THEN + COALESCE(NEW.content->>'name', 'Unnamed') || ' - ' || + 'Ride Model' || + CASE + WHEN NEW.content->>'manufacturer_name' IS NOT NULL + THEN ' by ' || (NEW.content->>'manufacturer_name') + ELSE '' + END + WHEN 'photo' THEN + 'Photo submission for ' || COALESCE(NEW.content->>'entity_type', 'entity') + ELSE + 'Submission' + END; + + -- Truncate preview to 200 characters + content_preview := LEFT(content_preview, 200); + + -- Check if this submission has photos + has_photos := EXISTS ( + SELECT 1 FROM photo_submissions ps + WHERE ps.submission_id = NEW.id + ); + + -- Count submission items + SELECT COUNT(*) INTO item_count + FROM submission_items + WHERE submission_id = NEW.id; + + -- Build the function URL + function_url := 'https://ydvtmnrszybqnbcqbdcy.supabase.co/functions/v1/notify-moderators-submission'; + + -- Use the public anon key + anon_key := 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InlkdnRtbnJzenlicW5iY3FiZGN5Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTgzMjYzNTYsImV4cCI6MjA3MzkwMjM1Nn0.DM3oyapd_omP5ZzIlrT0H9qBsiQBxBRgw2tYuqgXKX4'; + + -- Call edge function asynchronously with enhanced data + PERFORM net.http_post( + url := function_url, + headers := jsonb_build_object( + 'Content-Type', 'application/json', + 'Authorization', 'Bearer ' || anon_key, + 'apikey', anon_key + ), + body := jsonb_build_object( + 'submission_id', NEW.id, + 'submission_type', NEW.submission_type, + 'submitter_name', COALESCE(submitter_profile.display_name, submitter_profile.username, 'Anonymous'), + 'action', COALESCE((NEW.content->>'action')::text, 'create'), + 'content_preview', content_preview, + 'submitted_at', NEW.submitted_at, + 'has_photos', has_photos, + 'item_count', item_count, + 'is_escalated', COALESCE(NEW.escalated, false) + ) + ); + + RETURN NEW; +EXCEPTION + WHEN OTHERS THEN + -- Log error but don't fail the submission + RAISE WARNING 'Failed to notify moderators: %', SQLERRM; + RETURN NEW; +END; +$$; + +COMMENT ON FUNCTION public.notify_moderators_on_new_submission() IS + 'Sends enhanced notifications to moderators via Novu topics when new submissions are created, including content preview, timing, and metadata'; \ No newline at end of file