const express = require('express'); const router = express.Router(); const { getTenantDb, requireAuth } = require('./auth'); const eventBus = require('./eventBus'); // 1. GET /api/leads - Fetch all leads scoped by tenant organization via RLS router.get('/', requireAuth, async (req, res) => { try { const db = getTenantDb(req); // RLS in Supabase PostgreSQL automatically handles tenant scoping & role boundaries const { data: leads, error } = await db .from('leads') .select(` *, counselors ( name ), admissions ( course, fees, payment_status ) `) .order('created_at', { ascending: false }); if (error) throw error; const formattedLeads = (leads || []).map(lead => { const hasAdmission = lead.admissions && lead.admissions.length > 0; const admissionRecord = hasAdmission ? lead.admissions[0] : null; let followupTime = 'One Day'; if (lead.notes) { try { const parsed = JSON.parse(lead.notes); if (parsed.followup_time) { followupTime = parsed.followup_time; } } catch (e) {} } return { id: lead.id, student_name: lead.name, phone_number: lead.phone, email: lead.email || '', interested_course: lead.course_interested, lead_source: lead.source, counselor_name: lead.counselors ? lead.counselors.name : '', followup_status: lead.status || 'Pending', followup_time: followupTime, admission_status: hasAdmission ? 'Admitted' : 'Not Admitted', fees: admissionRecord ? parseFloat(admissionRecord.fees || 0) : 0, lead_score: lead.lead_score || 50, created_date: lead.created_at }; }); res.json(formattedLeads); } catch (error) { console.error('Error fetching leads:', error); res.status(500).json({ error: 'Internal Server Error' }); } }); // 2. PUT /api/update-lead - Update a lead's follow-up or admission status router.put('/update-lead', requireAuth, async (req, res) => { const { phone_number, followup_status, admission_status, followup_time } = req.body; if (!phone_number) { return res.status(400).json({ error: 'Phone number is required' }); } try { const db = getTenantDb(req); // Find the lead first const { data: leads, error: findError } = await db .from('leads') .select('id, name, course_interested, status, counselor_id, notes, organization_id, branch_id') .eq('phone', phone_number); if (findError) throw findError; if (!leads || leads.length === 0) { return res.status(404).json({ error: 'Lead not found' }); } const lead = leads[0]; // Parse metadata notes JSON let metadata = {}; try { if (lead.notes && lead.notes.startsWith('{')) { metadata = JSON.parse(lead.notes); } else if (lead.notes) { metadata = { text_notes: lead.notes }; } } catch (e) {} let notesUpdated = false; if (followup_time) { metadata.followup_time = followup_time; notesUpdated = true; } // Prepare lead updates const leadUpdates = {}; const oldStatus = lead.status; if (followup_status && followup_status !== lead.status) { leadUpdates.status = followup_status; } if (notesUpdated) { leadUpdates.notes = JSON.stringify(metadata); } if (Object.keys(leadUpdates).length > 0) { const { error: updateLeadError } = await db .from('leads') .update(leadUpdates) .eq('id', lead.id); if (updateLeadError) throw updateLeadError; // Log followup details if status changed if (followup_status && followup_status !== oldStatus) { await db.from('follow_ups').insert([{ lead_id: lead.id, organization_id: lead.organization_id, branch_id: lead.branch_id, followup_date: new Date().toISOString(), followup_type: 'Call', status: followup_status === 'Pending' ? 'Pending' : 'Completed', remarks: `Status updated via CRM to ${followup_status}`, created_by: lead.counselor_id }]); // Publish event to decoupled Event Bus eventBus.publish('lead.status_changed', { lead_id: lead.id, student_name: lead.name, phone: phone_number, old_status: oldStatus, new_status: followup_status, followup_time: followup_time || 'One Day', organization_id: lead.organization_id, branch_id: lead.branch_id }); } } // Update admissions status if (admission_status) { const { data: existingAdmissions, error: admSelectError } = await db .from('admissions') .select('id') .eq('lead_id', lead.id); if (admSelectError) throw admSelectError; const hasAdmissionRecord = existingAdmissions && existingAdmissions.length > 0; if (admission_status === 'Admitted' && !hasAdmissionRecord) { // Trigger Lead Converted Event Bus sequence eventBus.publish('lead.converted', { lead_id: lead.id, student_name: lead.name, phone: phone_number, course: lead.course_interested || 'Default Course', counselor_id: lead.counselor_id, organization_id: lead.organization_id, branch_id: lead.branch_id }); } else if (admission_status === 'Not Admitted' && hasAdmissionRecord) { // Remove admission details const { error: deleteAdmError } = await db .from('admissions') .delete() .eq('lead_id', lead.id); if (deleteAdmError) throw deleteAdmError; } } res.json({ message: 'Lead updated successfully' }); } catch (error) { console.error('Error updating lead:', error); res.status(500).json({ error: 'Internal Server Error' }); } }); module.exports = router;