diff --git a/src/components/leads/LeadAssignmentSection.jsx b/src/components/leads/LeadAssignmentSection.jsx new file mode 100644 index 0000000..79be5db --- /dev/null +++ b/src/components/leads/LeadAssignmentSection.jsx @@ -0,0 +1,235 @@ +import React from 'react'; +import { motion } from 'framer-motion'; +import { Calendar } from 'lucide-react'; +import { useMockStore } from '../../data/mockStore'; +import { useTheme } from '../../context/ThemeContext'; + +// --------------------------------------------------------------------------- +// Shared field primitives +// --------------------------------------------------------------------------- +function FieldLabel({ children }) { + return ( + + ); +} + +// --------------------------------------------------------------------------- +// Priority pill selector — Low / Medium / High +// --------------------------------------------------------------------------- +const PRIORITY_OPTIONS = [ + { value: 'low', label: 'Low', darkColor: '#71717a', lightColor: '#71717a' }, + { value: 'medium', label: 'Medium', darkColor: '#06B6D4', lightColor: '#0891b2' }, + { value: 'high', label: 'High', darkColor: '#F59E0B', lightColor: '#d97706' }, +]; + +function PriorityPillSelector({ value, onChange }) { + const { theme } = useTheme(); + const isDark = theme === 'dark'; + + return ( +
+ {PRIORITY_OPTIONS.map(opt => { + const isSelected = value === opt.value; + return ( + + ); + })} +
+ ); +} + +// --------------------------------------------------------------------------- +// Rep selector — role-aware +// Agents: only their own name (self-assign toggle) +// Admin/Owner: scrollable list of all field agents +// --------------------------------------------------------------------------- +function RepSelector({ value, onChange, currentUser, agents }) { + const { theme } = useTheme(); + const isDark = theme === 'dark'; + const isAgent = currentUser?.role === 'FIELD_AGENT'; + + if (isAgent) { + // Simplified self-assign for field agents + const isSelf = value === currentUser.id; + return ( + + ); + } + + // Admin / Owner — scrollable agent list + return ( +
+
+ {/* Unassigned option */} + + + {agents.map(agent => { + const isSelected = value === agent.id; + const initials = agent.name.split(' ').map(n => n[0]).join('').slice(0, 2); + return ( + + ); + })} +
+
+ ); +} + +// --------------------------------------------------------------------------- +// Assignment Section — Phase 6 (Full Form only) +// --------------------------------------------------------------------------- +export default function LeadAssignmentSection({ formData, updateField, currentUser }) { + const { users } = useMockStore(); + const agents = users.filter(u => u.role === 'FIELD_AGENT'); + + return ( +
+ {/* Assign Rep */} +
+ Assign Rep + updateField('assignedTo', v)} + currentUser={currentUser} + agents={agents} + /> +
+ + {/* Priority */} +
+ Priority + updateField('priority', v)} + /> +
+ + {/* Follow-up Date */} +
+ Follow-up Date +
+ + updateField('followUpDate', e.target.value)} + className="flex-1 py-3 pl-3 pr-4 text-sm font-medium + outline-none bg-transparent + text-zinc-800 dark:text-white + [color-scheme:light] dark:[color-scheme:dark]" + /> +
+
+
+ ); +} diff --git a/src/components/leads/LeadInsuranceSection.jsx b/src/components/leads/LeadInsuranceSection.jsx new file mode 100644 index 0000000..9cbd63b --- /dev/null +++ b/src/components/leads/LeadInsuranceSection.jsx @@ -0,0 +1,138 @@ +import React from 'react'; +import { Phone } from 'lucide-react'; +import { LEAD_FORM_OPTIONS } from '../../data/mockStore'; +import LeadCustomSelect from './LeadCustomSelect'; + +// --------------------------------------------------------------------------- +// Shared field primitives +// --------------------------------------------------------------------------- +function FieldLabel({ children }) { + return ( + + ); +} + +function LeadInput({ label, value, onChange, placeholder, type = 'text' }) { + return ( +
+ {label} + onChange(e.target.value)} + placeholder={placeholder} + className="w-full rounded-xl px-4 py-3 text-sm font-medium + outline-none transition-all duration-150 + bg-white dark:bg-zinc-800/60 + border border-zinc-200 dark:border-zinc-700 + text-zinc-800 dark:text-white + placeholder-zinc-400 dark:placeholder-zinc-500 + focus:border-purple-400 dark:focus:border-purple-500 + focus:ring-2 focus:ring-purple-100 dark:focus:ring-purple-500/20" + /> +
+ ); +} + +// Phone input with (xxx) xxx-xxxx mask and purple focus ring +function AdjusterPhoneInput({ label, value, onChange }) { + const handleChange = (e) => { + const digits = e.target.value.replace(/\D/g, '').slice(0, 10); + let formatted = digits; + if (digits.length >= 7) { + formatted = `(${digits.slice(0, 3)}) ${digits.slice(3, 6)}-${digits.slice(6)}`; + } else if (digits.length >= 4) { + formatted = `(${digits.slice(0, 3)}) ${digits.slice(3)}`; + } else if (digits.length >= 1) { + formatted = `(${digits}`; + } + onChange(formatted); + }; + + return ( +
+ {label} +
+ + +
+
+ ); +} + +// --------------------------------------------------------------------------- +// Insurance Section — Phase 5 (Full Form only) +// --------------------------------------------------------------------------- +export default function LeadInsuranceSection({ formData, updateField }) { + return ( +
+ {/* Row 1: Insurance Company (full width) */} + updateField('insuranceCompany', v)} + options={LEAD_FORM_OPTIONS.insuranceCompanies} + placeholder="Select carrier…" + accent="purple" + /> + + {/* Row 2: Claim Number + Claim Status */} +
+ updateField('claimNumber', v)} + placeholder="e.g. CLM-2026-00482" + /> + updateField('claimStatus', v)} + options={LEAD_FORM_OPTIONS.claimStatuses} + placeholder="Select status…" + accent="purple" + /> +
+ + {/* Row 3: Adjuster Name + Adjuster Phone */} +
+ updateField('adjusterName', v)} + placeholder="Full name" + /> + updateField('adjusterPhone', v)} + /> +
+ + {/* Row 4: Policy Number (full width) */} + updateField('policyNumber', v)} + placeholder="e.g. POL-7734892-A" + /> +
+ ); +} diff --git a/src/pages/CreateLeadPage.jsx b/src/pages/CreateLeadPage.jsx index 5d46fc1..12374e5 100644 --- a/src/pages/CreateLeadPage.jsx +++ b/src/pages/CreateLeadPage.jsx @@ -11,6 +11,8 @@ 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'; // --------------------------------------------------------------------------- @@ -59,23 +61,6 @@ const SECTIONS = [ }, ]; -// --------------------------------------------------------------------------- -// Animation variants -// --------------------------------------------------------------------------- -const containerVariants = { - hidden: {}, - visible: { transition: { staggerChildren: 0.07 } }, -}; - -const sectionVariants = { - hidden: { opacity: 0, y: 22 }, - visible: { - opacity: 1, - y: 0, - transition: { type: 'spring', stiffness: 280, damping: 28 }, - }, -}; - // --------------------------------------------------------------------------- // Initial form state // --------------------------------------------------------------------------- @@ -144,8 +129,8 @@ export default function CreateLeadPage() { job: isQuickCapture ? !!formData.leadSource : !!(formData.leadSource && formData.leadType && formData.workType), - insurance: false, // Phase 5 - assignment: false, // Phase 6 + insurance: !!(formData.insuranceCompany && formData.claimStatus), + assignment: !!(formData.assignedTo && formData.followUpDate), }; // Progress = % of visible sections that are complete @@ -213,20 +198,24 @@ export default function CreateLeadPage() { /> ); } - // Placeholder for sections not yet built - return ( -
- -

- {section.description} -

-
- ); + ); + } + if (section.id === 'assignment') { + return ( + + ); + } + return null; }; return ( @@ -325,17 +314,14 @@ export default function CreateLeadPage() { {/* --- Sections --- */} - +
{visibleSections.map(section => ( ))} - +
{/* ----------------------------------------------------------------