import React, { useState, useRef, useEffect } from 'react'; import { motion, AnimatePresence } from 'framer-motion'; import { useNavigate, useLocation } from 'react-router-dom'; import { useTheme } from '../context/ThemeContext'; import { useAuth } from '../context/AuthContext'; import { useMockStore } from '../data/mockStore'; import { toast } from 'sonner'; import { useRegisterAddress } from '../hooks/useHailRecon'; import { User, MapPin, Briefcase, Shield, Users, ChevronLeft, Zap, FileText, Save, AlertCircle, CloudLightning, X, } from 'lucide-react'; import LeadSectionWrapper from '../components/leads/LeadSectionWrapper'; import LeadJobSection from '../components/leads/LeadJobSection'; import LeadContactSection from '../components/leads/LeadContactSection'; import LeadPropertySection from '../components/leads/LeadPropertySection'; import LeadInsuranceSection from '../components/leads/LeadInsuranceSection'; import LeadAssignmentSection from '../components/leads/LeadAssignmentSection'; import gsap from 'gsap'; // --------------------------------------------------------------------------- // Section definitions // --------------------------------------------------------------------------- const SECTIONS = [ { id: 'contact', title: 'Contact', icon: User, accentColor: '#3B82F6', quickCapture: true, description: 'Name, phone numbers, and email addresses', }, { id: 'property', title: 'Property', icon: MapPin, accentColor: '#10B981', quickCapture: true, description: 'Property address, type, and site photos', }, { id: 'job', title: 'Job Details', icon: Briefcase, accentColor: '#F59E0B', quickCapture: true, description: 'Lead source, work type, urgency, and notes', }, { id: 'insurance', title: 'Insurance', icon: Shield, accentColor: '#8B5CF6', quickCapture: false, description: 'Carrier, claim number, adjuster, and policy details', }, { id: 'assignment', title: 'Assignment', icon: Users, accentColor: '#06B6D4', quickCapture: false, description: 'Assign a rep, set priority, and schedule follow-up', }, ]; // --------------------------------------------------------------------------- // Initial form state // --------------------------------------------------------------------------- const INITIAL_FORM = { // Job Details leadSource: '', canvasserId: '', canvasserName: '', referralNote: '', leadType: '', workType: '', tradeType: '', urgency: 'standard', notes: '', // Contact firstName: '', lastName: '', phones: [{ id: '1', number: '', type: 'Mobile', isPrimary: true }], emails: [], // Property address: '', city: '', state: 'TX', zip: '', propertyType: '', propertyPhotos: [], // Insurance (Phase 5) insuranceCompany: '', claimNumber: '', claimStatus: '', adjusterName: '', adjusterPhone: '', policyNumber: '', // Assignment (Phase 6) assignedTo: '', priority: 'medium', followUpDate: '', }; // --------------------------------------------------------------------------- // Component // --------------------------------------------------------------------------- export default function CreateLeadPage() { const { theme } = useTheme(); const { user } = useAuth(); const { addLead } = useMockStore(); const registerAddress = useRegisterAddress(); const isDark = theme === 'dark'; const navigate = useNavigate(); const location = useLocation(); const [validationError, setValidationError] = useState(''); // Storm zone context — passed from StormIntelPage via router state const stormSource = location.state?.stormSource ?? null; const [stormBannerDismissed, setStormBannerDismissed] = useState(false); const [isQuickCapture, setIsQuickCapture] = useState(true); const [openSections, setOpenSections] = useState({ contact: true, property: false, job: false, insurance: false, assignment: false, }); // Pre-fill form when arriving from Storm Intel const [formData, setFormData] = useState(() => { if (!stormSource) return INITIAL_FORM; const urgency = (stormSource.severity === 'severe' || stormSource.severity === 'significant') ? 'high' : 'standard'; const stormDate = stormSource.date ? new Date(stormSource.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }) : ''; return { ...INITIAL_FORM, city: 'Plano', state: 'TX', leadSource: 'Canvassers', urgency, notes: `Storm zone canvass — ${stormSource.areaName}.${stormSource.maxHailSize ? ` ${stormSource.maxHailSize}" hail` : ''}${stormDate ? ` on ${stormDate}` : ''}.`, stormSource, }; }); const progressBarRef = useRef(null); // Section completion map const sectionCompletion = { contact: !!( formData.firstName && formData.lastName && formData.phones.some(p => p.number.replace(/\D/g, '').length >= 10) ), property: !!(formData.address && formData.city), job: isQuickCapture ? !!formData.leadSource : !!(formData.leadSource && formData.leadType && formData.workType), insurance: !!(formData.insuranceCompany && formData.claimStatus), assignment: !!(formData.assignedTo && formData.followUpDate), }; // Progress = % of visible sections that are complete const visibleSectionIds = (isQuickCapture ? SECTIONS.filter(s => s.quickCapture) : SECTIONS).map(s => s.id); const completedCount = visibleSectionIds.filter(id => sectionCompletion[id]).length; const progress = visibleSectionIds.length > 0 ? Math.round((completedCount / visibleSectionIds.length) * 100) : 0; // GSAP animates the progress bar whenever progress changes useEffect(() => { if (progressBarRef.current) { gsap.to(progressBarRef.current, { width: `${progress}%`, duration: 0.45, ease: 'power2.out', }); } }, [progress]); const updateField = (field, value) => { setFormData(prev => { const next = { ...prev, [field]: value }; if (field === 'leadSource') { if (value !== 'Canvassers') { next.canvasserId = ''; next.canvasserName = ''; } if (value !== 'Referral') { next.referralNote = ''; } } return next; }); }; const toggleSection = (id) => { setOpenSections(prev => { const isOpening = !prev[id]; const next = Object.keys(prev).reduce((acc, key) => { acc[key] = false; return acc; }, {}); next[id] = isOpening; return next; }); }; const handleSave = () => { // Required field validation const missingFields = []; if (!formData.firstName) missingFields.push('First Name'); if (!formData.lastName) missingFields.push('Last Name'); if (!formData.phones.some(p => p.number.replace(/\D/g, '').length >= 10)) missingFields.push('Phone Number'); if (!formData.address) missingFields.push('Street Address'); if (!formData.city) missingFields.push('City'); if (!formData.leadSource) missingFields.push('Lead Source'); if (formData.leadSource === 'Canvassers' && !formData.canvasserId) missingFields.push('Canvasser'); if (missingFields.length > 0) { setValidationError(`Required: ${missingFields.join(', ')}`); // Open the first incomplete section (close others for accordion behavior) const openOnly = (id) => setOpenSections(prev => Object.keys(prev).reduce((acc, key) => ({ ...acc, [key]: key === id }), {}) ); if (!formData.firstName || !formData.lastName || !formData.phones.some(p => p.number.replace(/\D/g, '').length >= 10)) { openOnly('contact'); } else if (!formData.address || !formData.city) { openOnly('property'); } else { openOnly('job'); } toast.error(`Missing required fields: ${missingFields.join(', ')}`); return; } setValidationError(''); const savedLead = addLead({ ...formData, createdBy: user?.id, createdByName: user?.name, isQuickCapture, }); // Silently register the address with Hail Recon for future monitoring registerAddress({ id: savedLead?.id, urgency: formData.urgency, leadType: formData.jobType, property: { address: formData.address, city: formData.city || 'Plano', state: formData.state || 'TX', zip: formData.zip || '', lat: formData.lat || null, lng: formData.lng || null, }, customer: { name: `${formData.firstName || ''} ${formData.lastName || ''}`.trim(), phone: formData.phone || '', email: formData.email || '', }, }); toast.success(`Lead saved — ${formData.firstName} ${formData.lastName}`, { description: formData.address || 'No address provided', }); navigate('/emp/fa/leads'); }; const visibleSections = isQuickCapture ? SECTIONS.filter(s => s.quickCapture) : SECTIONS; const modeSwitchTo = (quick) => { if (quick === isQuickCapture) return; setIsQuickCapture(quick); if (quick) { setOpenSections(prev => ({ ...prev, insurance: false, assignment: false })); } }; // Render the content for a given section (placeholder until its phase is built) const renderSectionContent = (section) => { if (section.id === 'contact') { return ( ); } if (section.id === 'property') { return ( ); } if (section.id === 'job') { return ( ); } if (section.id === 'insurance') { return ( ); } if (section.id === 'assignment') { return ( ); } return null; }; return (
{/* --- Progress bar --- */}
{/* ---------------------------------------------------------------- Page content ---------------------------------------------------------------- */}
{/* --- Page Header --- */}
{/* Title */}

New Lead

{isQuickCapture ? 'Essential fields only — fastest capture at the door.' : 'Full lead profile with insurance and assignment details.'}

{/* Quick / Full Form toggle */}
{/* Section indicator pills */}
{SECTIONS.map(s => (
{s.title}
))}
{/* --- Storm zone banner --- */} {stormSource && !stormBannerDismissed && (

Storm Zone Lead

{stormSource.areaName} {stormSource.maxHailSize && ( {stormSource.maxHailSize}" hail )}

Lead source, urgency, and notes pre-filled from storm data. {(stormSource.severity === 'severe' || stormSource.severity === 'significant') && ( Urgency set to High. )}

)}
{/* --- Sections --- */}
{visibleSections.map(section => ( toggleSection(section.id)} isComplete={sectionCompletion[section.id]} > {renderSectionContent(section)} ))}
{/* ---------------------------------------------------------------- Mobile sticky save footer ---------------------------------------------------------------- */}
{/* Compact mode toggle */}
{/* Save */}
{/* ---------------------------------------------------------------- Desktop save bar (hidden on mobile — mobile uses sticky footer) ---------------------------------------------------------------- */}
{/* Validation error */} {validationError && ( {validationError} )}
{progress}% complete
); }