import { supabase } from "@/integrations/supabase/client"; export interface NotificationPayload { workflowId: string; subscriberId: string; payload: Record; overrides?: Record; } export interface SubscriberData { subscriberId: string; email?: string; firstName?: string; lastName?: string; phone?: string; avatar?: string; data?: Record; } export interface NotificationPreferences { channelPreferences: { in_app: boolean; email: boolean; push: boolean; sms: boolean; }; workflowPreferences: Record; frequencySettings: { digest: 'realtime' | 'hourly' | 'daily' | 'weekly'; max_per_hour: number; }; } class NotificationService { private readonly isNovuEnabled: boolean; constructor() { this.isNovuEnabled = !!import.meta.env.VITE_NOVU_APPLICATION_IDENTIFIER; } /** * Create or update a Novu subscriber */ async createSubscriber(subscriberData: SubscriberData): Promise<{ success: boolean; error?: string }> { if (!this.isNovuEnabled) { console.warn('Novu is not configured. Skipping subscriber creation.'); return { success: false, error: 'Novu not configured' }; } try { const { data, error } = await supabase.functions.invoke('create-novu-subscriber', { body: subscriberData, }); if (error) throw error; // Update local database with Novu subscriber ID const { error: dbError } = await supabase .from('user_notification_preferences') .upsert({ user_id: subscriberData.subscriberId, novu_subscriber_id: data.subscriberId, }); if (dbError) throw dbError; return { success: true }; } catch (error: any) { console.error('Error creating Novu subscriber:', error); return { success: false, error: error.message }; } } /** * Update subscriber preferences in Novu */ async updatePreferences( userId: string, preferences: NotificationPreferences ): Promise<{ success: boolean; error?: string }> { if (!this.isNovuEnabled) { // Save to local database only try { const { error } = await supabase .from('user_notification_preferences') .upsert({ user_id: userId, channel_preferences: preferences.channelPreferences, workflow_preferences: preferences.workflowPreferences, frequency_settings: preferences.frequencySettings, }); if (error) throw error; return { success: true }; } catch (error: any) { return { success: false, error: error.message }; } } try { const { error } = await supabase.functions.invoke('update-novu-preferences', { body: { userId, preferences, }, }); if (error) throw error; // Also update local database const { error: dbError } = await supabase .from('user_notification_preferences') .upsert({ user_id: userId, channel_preferences: preferences.channelPreferences, workflow_preferences: preferences.workflowPreferences, frequency_settings: preferences.frequencySettings, }); if (dbError) throw dbError; return { success: true }; } catch (error: any) { console.error('Error updating preferences:', error); return { success: false, error: error.message }; } } /** * Trigger a notification workflow */ async trigger(payload: NotificationPayload): Promise<{ success: boolean; transactionId?: string; error?: string }> { if (!this.isNovuEnabled) { console.warn('Novu is not configured. Notification not sent.'); return { success: false, error: 'Novu not configured' }; } try { const { data, error } = await supabase.functions.invoke('trigger-notification', { body: payload, }); if (error) throw error; // Log notification in local database await this.logNotification({ userId: payload.subscriberId, workflowId: payload.workflowId, transactionId: data.transactionId, payload: payload.payload, }); return { success: true, transactionId: data.transactionId }; } catch (error: any) { console.error('Error triggering notification:', error); return { success: false, error: error.message }; } } /** * Get user's notification preferences */ async getPreferences(userId: string): Promise { try { const { data, error } = await supabase .from('user_notification_preferences') .select('channel_preferences, workflow_preferences, frequency_settings') .eq('user_id', userId) .single(); if (error && error.code !== 'PGRST116') throw error; if (!data) { // Return default preferences return { channelPreferences: { in_app: true, email: true, push: false, sms: false, }, workflowPreferences: {}, frequencySettings: { digest: 'daily', max_per_hour: 10, }, }; } return { channelPreferences: data.channel_preferences as any, workflowPreferences: data.workflow_preferences as any, frequencySettings: data.frequency_settings as any, }; } catch (error: any) { console.error('Error fetching preferences:', error); return null; } } /** * Get notification templates */ async getTemplates(): Promise { try { const { data, error } = await supabase .from('notification_templates') .select('*') .eq('is_active', true) .order('category', { ascending: true }); if (error) throw error; return data || []; } catch (error: any) { console.error('Error fetching templates:', error); return []; } } /** * Log notification in database */ private async logNotification(log: { userId: string; workflowId: string; transactionId: string; payload: Record; }): Promise { try { // Get template ID from workflow ID const { data: template } = await supabase .from('notification_templates') .select('id') .eq('workflow_id', log.workflowId) .single(); await supabase.from('notification_logs').insert({ user_id: log.userId, template_id: template?.id, novu_transaction_id: log.transactionId, channel: 'multi', status: 'sent', payload: log.payload, }); } catch (error) { console.error('Error logging notification:', error); } } /** * Check if Novu is enabled */ isEnabled(): boolean { return this.isNovuEnabled; } } export const notificationService = new NotificationService();