From 8bb321e1f7303a99573c4f7ef3edc4470cbb45c8 Mon Sep 17 00:00:00 2001 From: Satyam <95536056+Satyam-Rastogi@users.noreply.github.com> Date: Fri, 13 Mar 2026 16:07:29 +0530 Subject: [PATCH] =?UTF-8?q?feat(leads):=20Lead=20Creation=20Phase=201=20?= =?UTF-8?q?=E2=80=94=20page=20shell,=20section=20framework,=20mock=20data?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - CreateLeadPage.jsx: full page shell with Quick Capture / Full Form toggle, Barlow Condensed header, breadcrumb nav, GSAP progress bar stub, mobile sticky save footer - LeadSectionWrapper.jsx: collapsible section component with Framer Motion height animation, spring chevron rotation, completion dot, dual light/dark header design matching GlassPanel language - 5 sections wired up: Contact (blue), Property (emerald), Job Details (amber), Insurance (violet), Assignment (cyan) — Quick Capture shows first 3, Full Form shows all 5 - Section indicator pills in header dim gracefully when hidden in Quick Capture mode - LEAD_FORM_OPTIONS added to mockStore: lead types, sources, work types, trade types, urgency/priority levels, insurance companies, claim statuses, phone/email types, US states - Route /emp/fa/leads/new added (FIELD_AGENT, ADMIN, OWNER) - New Lead nav item added to sidebar for all 3 roles --- src/App.jsx | 9 + src/components/Layout.jsx | 7 +- src/components/leads/LeadSectionWrapper.jsx | 101 +++++++ src/data/mockStore.jsx | 90 ++++++ src/pages/CreateLeadPage.jsx | 318 ++++++++++++++++++++ 5 files changed, 523 insertions(+), 2 deletions(-) create mode 100644 src/components/leads/LeadSectionWrapper.jsx create mode 100644 src/pages/CreateLeadPage.jsx diff --git a/src/App.jsx b/src/App.jsx index a1e9a5a..b420791 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -29,6 +29,7 @@ import VendorDashboard from './pages/vendor/VendorDashboard'; import VendorOrders from './pages/vendor/VendorOrders'; import ContractorDashboard from './pages/contractor/ContractorDashboard'; import SubContractorDashboard from './pages/subcontractor/SubContractorDashboard'; +import CreateLeadPage from './pages/CreateLeadPage'; // ... (existing imports) const ProtectedRoute = ({ children, allowedRoles }) => { @@ -106,6 +107,14 @@ function App() { } /> + + + + } + /> {/* Owner Routes */} diff --git a/src/components/Layout.jsx b/src/components/Layout.jsx index 97ca779..32d9210 100644 --- a/src/components/Layout.jsx +++ b/src/components/Layout.jsx @@ -5,7 +5,7 @@ import { useTheme } from '../context/ThemeContext'; import { LayoutDashboard, Map, Calendar, LogOut, User, Home, MessageSquare, ChevronLeft, ChevronRight, Sun, Moon, Trophy, Users, Briefcase, - FileText, Menu, X, Calculator + FileText, Menu, X, Calculator, PlusCircle } from 'lucide-react'; import PageTransition from './PageTransition'; @@ -137,10 +137,11 @@ const Layout = () => { { to: "/owner/snapshot", icon: LayoutDashboard, label: "Owners Box" }, { to: "/admin/dashboard", icon: LayoutDashboard, label: "Dashboard" }, { to: "/owner/projects", icon: Briefcase, label: "Projects" }, + { to: "/emp/fa/leads/new", icon: PlusCircle, label: "New Lead" }, { to: "/owner/maps", icon: Map, label: "Territory Map" }, { to: "/owner/pro-canvas", - icon: LayoutDashboard, // Placeholder icon, maybe change later + icon: LayoutDashboard, label: ProCanvas }, { to: "/owner/estimate", icon: Calculator, label: "Estimate Builder" }, @@ -153,6 +154,7 @@ const Layout = () => { { to: "/admin/dashboard", icon: LayoutDashboard, label: "Dashboard" }, { to: "/admin/schedule", icon: Calendar, label: "Schedule" }, { to: "/admin/leaderboard", icon: Trophy, label: "Leaderboard" }, + { to: "/emp/fa/leads/new", icon: PlusCircle, label: "New Lead" }, { to: "/admin/maps", icon: Map, label: "Territory Map" }, { to: "/admin/pro-canvas", @@ -179,6 +181,7 @@ const Layout = () => { return [ { to: "/emp/fa/dashboard", icon: LayoutDashboard, label: "Dashboard" }, { to: "/emp/fa/maps", icon: Map, label: "My Map" }, + { to: "/emp/fa/leads/new", icon: PlusCircle, label: "New Lead" }, { to: "/emp/fa/pro-canvas", icon: LayoutDashboard, diff --git a/src/components/leads/LeadSectionWrapper.jsx b/src/components/leads/LeadSectionWrapper.jsx new file mode 100644 index 0000000..cad15a3 --- /dev/null +++ b/src/components/leads/LeadSectionWrapper.jsx @@ -0,0 +1,101 @@ +import React from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import { ChevronDown, CheckCircle2 } from 'lucide-react'; +import { useTheme } from '../../context/ThemeContext'; + +export default function LeadSectionWrapper({ + title, + icon: Icon, + accentColor, + isOpen, + onToggle, + children, + isComplete = false, +}) { + const { theme } = useTheme(); + const isDark = theme === 'dark'; + + return ( +
+ {/* --- Header Button --- */} + + + {/* --- Collapsible Content --- */} + + {isOpen && ( + +
+ {children} +
+
+ )} +
+
+ ); +} diff --git a/src/data/mockStore.jsx b/src/data/mockStore.jsx index 649f16a..1042d82 100644 --- a/src/data/mockStore.jsx +++ b/src/data/mockStore.jsx @@ -2839,3 +2839,93 @@ export const MockStoreProvider = ({ children }) => { }; export const useMockStore = () => useContext(MockStoreContext); + +// --------------------------------------------------------------------------- +// Lead Creation Form Options +// --------------------------------------------------------------------------- +export const LEAD_FORM_OPTIONS = { + leadTypes: [ + 'Residential', + 'Commercial', + 'Multi-Family', + 'Industrial', + 'HOA / Community', + ], + leadSources: [ + 'Door Knock', + 'Referral', + 'Storm Chase', + 'Online / Web', + 'Mailer / Postcard', + 'Sign Call', + 'Insurance Agent Referral', + 'Repeat Customer', + 'Social Media', + 'Other', + ], + workTypes: [ + 'Roof Replacement', + 'Roof Repair', + 'Gutters', + 'Siding', + 'Windows', + 'Solar', + 'Inspection Only', + 'Emergency Tarping', + 'Other', + ], + tradeTypes: [ + 'Roofing', + 'Gutter', + 'Siding', + 'Windows', + 'Solar', + 'General Contractor', + ], + urgencyLevels: [ + { label: 'Standard', value: 'standard', color: '#6B7280' }, + { label: 'High', value: 'high', color: '#F59E0B' }, + { label: 'Emergency', value: 'emergency', color: '#EF4444' }, + ], + priorityLevels: [ + { label: 'Low', value: 'low', color: '#6B7280' }, + { label: 'Medium', value: 'medium', color: '#3B82F6' }, + { label: 'High', value: 'high', color: '#F59E0B' }, + { label: 'Critical', value: 'critical', color: '#EF4444' }, + ], + insuranceCompanies: [ + 'State Farm', + 'Allstate', + 'USAA', + 'Farmers Insurance', + 'Liberty Mutual', + 'Progressive', + 'Nationwide', + 'American Family', + 'Travelers', + 'GEICO', + 'Chubb', + 'Erie Insurance', + 'Auto-Owners', + 'Other', + ], + claimStatuses: [ + 'Not Filed', + 'Filed – Pending Adjuster', + 'Adjuster Scheduled', + 'Adjuster Completed', + 'Approved', + 'Denied', + 'Supplement In Progress', + 'Closed', + ], + phoneTypes: ['Mobile', 'Home', 'Work', 'Other'], + emailTypes: ['Personal', 'Work', 'Other'], + usStates: [ + 'AL','AK','AZ','AR','CA','CO','CT','DE','FL','GA', + 'HI','ID','IL','IN','IA','KS','KY','LA','ME','MD', + 'MA','MI','MN','MS','MO','MT','NE','NV','NH','NJ', + 'NM','NY','NC','ND','OH','OK','OR','PA','RI','SC', + 'SD','TN','TX','UT','VT','VA','WA','WV','WI','WY', + ], +}; diff --git a/src/pages/CreateLeadPage.jsx b/src/pages/CreateLeadPage.jsx new file mode 100644 index 0000000..5787b4e --- /dev/null +++ b/src/pages/CreateLeadPage.jsx @@ -0,0 +1,318 @@ +import React, { useState, useRef, useEffect } from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import { useNavigate } from 'react-router-dom'; +import { useTheme } from '../context/ThemeContext'; +import { useAuth } from '../context/AuthContext'; +import { + User, MapPin, Briefcase, Shield, Users, + ChevronLeft, Zap, FileText, Save, +} from 'lucide-react'; +import LeadSectionWrapper from '../components/leads/LeadSectionWrapper'; +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', + }, +]; + +// --------------------------------------------------------------------------- +// 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 }, + }, +}; + +// --------------------------------------------------------------------------- +// Component +// --------------------------------------------------------------------------- +export default function CreateLeadPage() { + const { theme } = useTheme(); + const { user } = useAuth(); + const isDark = theme === 'dark'; + const navigate = useNavigate(); + + const [isQuickCapture, setIsQuickCapture] = useState(true); + const [openSections, setOpenSections] = useState({ + contact: true, + property: false, + job: false, + insurance: false, + assignment: false, + }); + + const progressBarRef = useRef(null); + + // Animate progress bar on mount (Phase 1: stays at 0) + useEffect(() => { + if (progressBarRef.current) { + gsap.fromTo( + progressBarRef.current, + { scaleX: 0, transformOrigin: 'left' }, + { scaleX: 0, duration: 0.6, ease: 'power2.out' } + ); + } + }, []); + + const toggleSection = (id) => { + setOpenSections(prev => ({ ...prev, [id]: !prev[id] })); + }; + + const visibleSections = isQuickCapture + ? SECTIONS.filter(s => s.quickCapture) + : SECTIONS; + + const modeSwitchTo = (quick) => { + if (quick === isQuickCapture) return; + setIsQuickCapture(quick); + // Close insurance + assignment when switching to quick capture + if (quick) { + setOpenSections(prev => ({ ...prev, insurance: false, assignment: false })); + } + }; + + return ( +
+ {/* Progress bar — thin accent line at top */} +
+
+
+ + {/* ---------------------------------------------------------------- + Page content + ---------------------------------------------------------------- */} +
+ + {/* --- Page Header --- */} +
+ {/* Back breadcrumb */} + + +
+ {/* Title */} +
+

+ New Lead +

+

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

+
+ + {/* Quick Capture / Full Form toggle */} +
+ + +
+
+ + {/* Mode indicator pills */} +
+ {SECTIONS.map(s => ( +
+ + {s.title} +
+ ))} +
+
+ + {/* --- Sections --- */} + + + {visibleSections.map(section => ( + + toggleSection(section.id)} + > + {/* Placeholder — fields added in Phase 2+ */} +
+ +

+ {section.description} +

+
+
+
+ ))} +
+
+
+ + {/* ---------------------------------------------------------------- + Mobile sticky save footer + ---------------------------------------------------------------- */} +
+
+ {/* Mode toggle — compact repeat for mobile */} +
+ + +
+ + {/* Save button */} + +
+
+
+ ); +}