feat(leads): Lead Creation Phase 3 — Contact section with dynamic phone and email rows
- LeadContactSection: First/Last name grid, dynamic phone list, dynamic email list (Full Form only), all with spring height animations and LayoutGroup for coordinated layout shifts - PhoneEntryRow: phone input with (555) 000-0000 mask, type selector (Mobile/Home/Work/Other), amber star primary toggle, red X remove — type+star hidden in Quick Capture mode - EmailEntryRow: email input, type selector, star primary toggle, remove button - Add Phone / Add Email buttons animate in when switching to Full Form; rows slide in/out with spring height collapse - CreateLeadPage: sectionCompletion.contact now live (firstName + lastName + 10-digit phone), progress bar switched to section-completion-based percentage (completed sections / visible sections)
This commit is contained in:
@@ -9,6 +9,7 @@ import {
|
||||
} from 'lucide-react';
|
||||
import LeadSectionWrapper from '../components/leads/LeadSectionWrapper';
|
||||
import LeadJobSection from '../components/leads/LeadJobSection';
|
||||
import LeadContactSection from '../components/leads/LeadContactSection';
|
||||
import gsap from 'gsap';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -85,10 +86,10 @@ const INITIAL_FORM = {
|
||||
tradeType: '',
|
||||
urgency: 'standard',
|
||||
notes: '',
|
||||
// Contact (Phase 3)
|
||||
// Contact
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
phones: [],
|
||||
phones: [{ id: '1', number: '', type: 'Mobile', isPrimary: true }],
|
||||
emails: [],
|
||||
// Property (Phase 4)
|
||||
address: '',
|
||||
@@ -130,14 +131,26 @@ export default function CreateLeadPage() {
|
||||
|
||||
const progressBarRef = useRef(null);
|
||||
|
||||
// Derived: which fields must be filled for this mode
|
||||
const requiredFields = isQuickCapture
|
||||
? ['leadSource']
|
||||
: ['leadSource', 'leadType', 'workType'];
|
||||
// Section completion map
|
||||
const sectionCompletion = {
|
||||
contact: !!(
|
||||
formData.firstName &&
|
||||
formData.lastName &&
|
||||
formData.phones.some(p => p.number.replace(/\D/g, '').length >= 10)
|
||||
),
|
||||
property: false, // Phase 4
|
||||
job: isQuickCapture
|
||||
? !!formData.leadSource
|
||||
: !!(formData.leadSource && formData.leadType && formData.workType),
|
||||
insurance: false, // Phase 5
|
||||
assignment: false, // Phase 6
|
||||
};
|
||||
|
||||
const filledCount = requiredFields.filter(f => formData[f]).length;
|
||||
const progress = requiredFields.length > 0
|
||||
? Math.round((filledCount / requiredFields.length) * 100)
|
||||
// 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
|
||||
@@ -151,17 +164,6 @@ export default function CreateLeadPage() {
|
||||
}
|
||||
}, [progress]);
|
||||
|
||||
// Section completion map — grows as phases add fields
|
||||
const sectionCompletion = {
|
||||
contact: false, // Phase 3
|
||||
property: false, // Phase 4
|
||||
job: isQuickCapture
|
||||
? !!formData.leadSource
|
||||
: !!(formData.leadSource && formData.leadType && formData.workType),
|
||||
insurance: false, // Phase 5
|
||||
assignment: false, // Phase 6
|
||||
};
|
||||
|
||||
const updateField = (field, value) => {
|
||||
setFormData(prev => ({ ...prev, [field]: value }));
|
||||
};
|
||||
@@ -170,9 +172,7 @@ export default function CreateLeadPage() {
|
||||
setOpenSections(prev => ({ ...prev, [id]: !prev[id] }));
|
||||
};
|
||||
|
||||
const visibleSections = isQuickCapture
|
||||
? SECTIONS.filter(s => s.quickCapture)
|
||||
: SECTIONS;
|
||||
const visibleSections = isQuickCapture ? SECTIONS.filter(s => s.quickCapture) : SECTIONS;
|
||||
|
||||
const modeSwitchTo = (quick) => {
|
||||
if (quick === isQuickCapture) return;
|
||||
@@ -184,6 +184,15 @@ export default function CreateLeadPage() {
|
||||
|
||||
// Render the content for a given section (placeholder until its phase is built)
|
||||
const renderSectionContent = (section) => {
|
||||
if (section.id === 'contact') {
|
||||
return (
|
||||
<LeadContactSection
|
||||
formData={formData}
|
||||
updateField={updateField}
|
||||
isQuickCapture={isQuickCapture}
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (section.id === 'job') {
|
||||
return (
|
||||
<LeadJobSection
|
||||
|
||||
Reference in New Issue
Block a user