diff --git a/src/components/admin/DesignerForm.tsx b/src/components/admin/DesignerForm.tsx index 330cb9e2..29df72a5 100644 --- a/src/components/admin/DesignerForm.tsx +++ b/src/components/admin/DesignerForm.tsx @@ -99,11 +99,16 @@ export function DesignerForm({ onSubmit, onCancel, initialData }: DesignerFormPr setIsSubmitting(true); try { + const formData = { + ...data, + founded_year: data.founded_year ? parseInt(String(data.founded_year)) : undefined, + }; + if (initialData?.id) { - await submitDesignerUpdate(initialData.id, data, user.id); + await submitDesignerUpdate(initialData.id, formData, user.id); toast.success('Designer update submitted for review'); } else { - await submitDesignerCreation(data, user.id); + await submitDesignerCreation(formData, user.id); toast.success('Designer submitted for review'); } onCancel(); diff --git a/src/components/admin/ManufacturerForm.tsx b/src/components/admin/ManufacturerForm.tsx index 2765236d..9ce7727b 100644 --- a/src/components/admin/ManufacturerForm.tsx +++ b/src/components/admin/ManufacturerForm.tsx @@ -101,11 +101,16 @@ export function ManufacturerForm({ onSubmit, onCancel, initialData }: Manufactur setIsSubmitting(true); try { + const formData = { + ...data, + founded_year: data.founded_year ? parseInt(String(data.founded_year)) : undefined, + }; + if (initialData?.id) { - await submitManufacturerUpdate(initialData.id, data, user.id); + await submitManufacturerUpdate(initialData.id, formData, user.id); toast.success('Manufacturer update submitted for review'); } else { - await submitManufacturerCreation(data, user.id); + await submitManufacturerCreation(formData, user.id); toast.success('Manufacturer submitted for review'); } onCancel(); diff --git a/src/components/admin/OperatorForm.tsx b/src/components/admin/OperatorForm.tsx index b12a8377..367b77a6 100644 --- a/src/components/admin/OperatorForm.tsx +++ b/src/components/admin/OperatorForm.tsx @@ -99,11 +99,16 @@ export function OperatorForm({ onSubmit, onCancel, initialData }: OperatorFormPr setIsSubmitting(true); try { + const formData = { + ...data, + founded_year: data.founded_year ? parseInt(String(data.founded_year)) : undefined, + }; + if (initialData?.id) { - await submitOperatorUpdate(initialData.id, data, user.id); + await submitOperatorUpdate(initialData.id, formData, user.id); toast.success('Operator update submitted for review'); } else { - await submitOperatorCreation(data, user.id); + await submitOperatorCreation(formData, user.id); toast.success('Operator submitted for review'); } onCancel(); diff --git a/src/components/admin/PropertyOwnerForm.tsx b/src/components/admin/PropertyOwnerForm.tsx index 6123e917..a15b76f5 100644 --- a/src/components/admin/PropertyOwnerForm.tsx +++ b/src/components/admin/PropertyOwnerForm.tsx @@ -125,13 +125,18 @@ export function PropertyOwnerForm({ onSubmit, onCancel, initialData }: PropertyO setIsSubmitting(true); try { - if (initialData?.id) { - await submitPropertyOwnerUpdate(initialData.id, data, user.id); - toast.success('Property owner update submitted for review'); - } else { - await submitPropertyOwnerCreation(data, user.id); - toast.success('Property owner submitted for review'); - } + const formData = { + ...data, + founded_year: data.founded_year ? parseInt(String(data.founded_year)) : undefined, + }; + + if (initialData?.id) { + await submitPropertyOwnerUpdate(initialData.id, formData, user.id); + toast.success('Property owner update submitted for review'); + } else { + await submitPropertyOwnerCreation(formData, user.id); + toast.success('Property owner submitted for review'); + } onCancel(); } catch (error) { console.error('Submission error:', error); diff --git a/src/lib/notificationService.ts b/src/lib/notificationService.ts index 3ed9b4f1..4d91113f 100644 --- a/src/lib/notificationService.ts +++ b/src/lib/notificationService.ts @@ -327,6 +327,40 @@ class NotificationService { } } + /** + * Notify all moderators about a new submission + */ + async notifyModerators(payload: { + submission_id: string; + submission_type: string; + submitter_name: string; + action: string; + }): Promise<{ success: boolean; count: number; error?: string }> { + try { + const { data, error } = await supabase.functions.invoke('notify-moderators-submission', { + body: payload, + }); + + if (error) { + console.error('Edge function error notifying moderators:', error); + throw error; + } + + console.log('Moderators notified successfully:', data); + return { + success: true, + count: data?.count || 0 + }; + } catch (error: unknown) { + console.error('Error notifying moderators:', error); + return { + success: false, + count: 0, + error: this.extractErrorMessage(error) + }; + } + } + /** * Check if Novu is enabled */ diff --git a/supabase/functions/notify-moderators-submission/index.ts b/supabase/functions/notify-moderators-submission/index.ts new file mode 100644 index 00000000..69f59201 --- /dev/null +++ b/supabase/functions/notify-moderators-submission/index.ts @@ -0,0 +1,130 @@ +import { serve } from "https://deno.land/std@0.168.0/http/server.ts"; +import { createClient } from "https://esm.sh/@supabase/supabase-js@2.57.4"; + +const corsHeaders = { + 'Access-Control-Allow-Origin': '*', + 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', +}; + +interface NotificationPayload { + submission_id: string; + submission_type: string; + submitter_name: string; + action: string; +} + +serve(async (req) => { + if (req.method === 'OPTIONS') { + return new Response(null, { headers: corsHeaders }); + } + + try { + const supabaseUrl = Deno.env.get('SUPABASE_URL')!; + const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')!; + + const supabase = createClient(supabaseUrl, supabaseServiceKey); + + const payload: NotificationPayload = await req.json(); + const { submission_id, submission_type, submitter_name, action } = payload; + + console.log('Notifying moderators about submission:', { submission_id, submission_type }); + + // Get all moderators, admins, and superusers + const { data: moderators, error: moderatorsError } = await supabase + .from('user_roles') + .select('user_id') + .in('role', ['moderator', 'admin', 'superuser']); + + if (moderatorsError) { + console.error('Error fetching moderators:', moderatorsError); + throw moderatorsError; + } + + if (!moderators || moderators.length === 0) { + console.log('No moderators found to notify'); + return new Response( + JSON.stringify({ success: true, count: 0, message: 'No moderators to notify' }), + { headers: { ...corsHeaders, 'Content-Type': 'application/json' }, status: 200 } + ); + } + + console.log(`Found ${moderators.length} moderators to notify`); + + // Get the moderation-alert template + const { data: template } = await supabase + .from('notification_templates') + .select('workflow_id') + .eq('workflow_id', 'moderation-alert') + .single(); + + if (!template) { + console.warn('moderation-alert workflow not configured in notification_templates'); + } + + // Prepare notification data + const moderationUrl = `${supabaseUrl.replace('.supabase.co', '')}/admin/moderation`; + const notificationPayload = { + itemType: submission_type, + submissionId: submission_id, + submitterName: submitter_name, + action: action || 'create', + moderationUrl, + }; + + // Send notifications to all moderators in parallel + const notificationPromises = moderators.map(async (moderator) => { + try { + const { error: notifyError } = await supabase.functions.invoke('trigger-notification', { + body: { + workflowId: 'moderation-alert', + subscriberId: moderator.user_id, + payload: notificationPayload, + }, + }); + + if (notifyError) { + console.error(`Failed to notify moderator ${moderator.user_id}:`, notifyError); + return { success: false, userId: moderator.user_id, error: notifyError }; + } + + console.log(`Successfully notified moderator ${moderator.user_id}`); + return { success: true, userId: moderator.user_id }; + } catch (error) { + console.error(`Exception notifying moderator ${moderator.user_id}:`, error); + return { success: false, userId: moderator.user_id, error }; + } + }); + + const results = await Promise.all(notificationPromises); + const successCount = results.filter(r => r.success).length; + const failCount = results.filter(r => !r.success).length; + + console.log(`Notification summary: ${successCount} sent, ${failCount} failed`); + + return new Response( + JSON.stringify({ + success: true, + count: successCount, + failed: failCount, + details: results, + }), + { + headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + status: 200, + } + ); + } catch (error: any) { + console.error('Error in notify-moderators-submission:', error); + + return new Response( + JSON.stringify({ + success: false, + error: error.message, + }), + { + headers: { ...corsHeaders, 'Content-Type': 'application/json' }, + status: 500, + } + ); + } +});