/** * Shared registration helpers used by both the signup and join pages. * Consolidates member creation and welcome email logic. */ import { supabaseAdmin } from '$lib/server/supabase'; import { sendTemplatedEmail } from '$lib/server/email'; import type { SupabaseClient } from '@supabase/supabase-js'; // ──────────────────────────────────────────────────────────────── // Types // ──────────────────────────────────────────────────────────────── export interface RegistrationData { userId: string; email: string; firstName: string; lastName: string; phone?: string; dateOfBirth?: string; address?: string; nationality?: string[]; } export interface CreateMemberResult { success: boolean; error?: string; memberId?: string; } // ──────────────────────────────────────────────────────────────── // Member Creation // ──────────────────────────────────────────────────────────────── /** * Create a member record in the database after auth user creation. * * Handles: * - Looking up the default/pending membership status and type * - Generating a sequential MUSA-YYYY-XXXX member ID * - Inserting the member record * * @param data Core registration data. * @param supabase The Supabase client to use for DB operations. * @param options Additional options for how the member is created. */ export async function createMemberRecord( data: RegistrationData, supabase: SupabaseClient, options?: { /** Look up status by name instead of is_default. Defaults to undefined (uses is_default). */ statusName?: string; /** Whether to generate a MUSA-YYYY-XXXX member ID. Defaults to true. */ generateMemberId?: boolean; } ): Promise { const generateMemberId = options?.generateMemberId ?? true; const statusName = options?.statusName; // Look up the membership status let statusQuery; if (statusName) { statusQuery = supabase .from('membership_statuses') .select('id') .eq('name', statusName) .single(); } else { statusQuery = supabase .from('membership_statuses') .select('id') .eq('is_default', true) .single(); } const { data: statusData, error: statusError } = await statusQuery; if (statusError || !statusData?.id) { console.error('No membership status found:', statusError); return { success: false, error: 'System configuration error. Please contact support.' }; } // Look up the default membership type const { data: typeData, error: typeError } = await supabase .from('membership_types') .select('id') .eq('is_default', true) .single(); if (typeError || !typeData?.id) { console.error('No default membership type found:', typeError); return { success: false, error: 'System configuration error. Please contact support.' }; } // Generate member ID if requested let memberId: string | undefined; if (generateMemberId) { const year = new Date().getFullYear(); const { count } = await supabase .from('members') .select('*', { count: 'exact', head: true }); const memberNumber = String((count || 0) + 1).padStart(4, '0'); memberId = `MUSA-${year}-${memberNumber}`; } // Create the member profile const insertPayload: Record = { id: data.userId, first_name: data.firstName, last_name: data.lastName, email: data.email, phone: data.phone || null, date_of_birth: data.dateOfBirth || null, address: data.address || null, nationality: data.nationality || [], role: 'member', membership_status_id: statusData.id, membership_type_id: typeData.id }; if (memberId) { insertPayload.member_id = memberId; } const { error: memberError } = await supabase.from('members').insert(insertPayload); if (memberError) { console.error('Failed to create member profile:', memberError); return { success: false, error: 'Failed to create member profile. Please try again or contact support.' }; } return { success: true, memberId }; } /** * Clean up auth user on registration failure. * Uses supabaseAdmin to ensure we can always delete the user. */ export async function cleanupAuthUser(userId: string): Promise { try { await supabaseAdmin.auth.admin.deleteUser(userId); } catch (deleteError) { console.error('Failed to clean up auth user:', deleteError); } } // ──────────────────────────────────────────────────────────────── // Welcome Email // ──────────────────────────────────────────────────────────────── /** * Send the onboarding welcome email with payment instructions. * * @param member Basic member info for the email template. * @param paymentSettings Payment account details from app_settings. * @param duesAmount The annual dues amount. * @param paymentDeadline The payment deadline date. */ export async function sendWelcomeEmail( member: { id: string; first_name: string; email: string; member_id?: string; }, paymentSettings: Record, duesAmount: number, paymentDeadline: Date ): Promise<{ success: boolean; error?: string }> { try { const result = await sendTemplatedEmail( 'onboarding_welcome', member.email, { first_name: member.first_name, member_id: member.member_id || 'N/A', amount: `\u20AC${duesAmount}`, payment_deadline: paymentDeadline.toLocaleDateString('en-US', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' }), account_holder: paymentSettings.account_holder || 'Monaco USA', bank_name: paymentSettings.bank_name || 'Credit Foncier de Monaco', iban: paymentSettings.iban || 'Contact for details' }, { recipientId: member.id, recipientName: member.first_name, sentBy: 'system' } ); return result; } catch (emailError) { console.error('Failed to send welcome email:', emailError); return { success: false, error: 'Failed to send welcome email' }; } }