diff --git a/src/components/leads/LeadPropertySection.jsx b/src/components/leads/LeadPropertySection.jsx
new file mode 100644
index 0000000..ece6314
--- /dev/null
+++ b/src/components/leads/LeadPropertySection.jsx
@@ -0,0 +1,271 @@
+import React, { useRef } from 'react';
+import { motion, AnimatePresence } from 'framer-motion';
+import { MapPin, Camera, X, ChevronDown, Image } from 'lucide-react';
+import { useTheme } from '../../context/ThemeContext';
+import { LEAD_FORM_OPTIONS } from '../../data/mockStore';
+
+// ---------------------------------------------------------------------------
+// Shared field primitives (local)
+// ---------------------------------------------------------------------------
+function FieldLabel({ children }) {
+ return (
+
+ );
+}
+
+function LeadInput({ label, value, onChange, placeholder, type = 'text', autoComplete, inputMode }) {
+ const { theme } = useTheme();
+ const isDark = theme === 'dark';
+ return (
+
+ {label && {label}}
+ onChange(e.target.value)}
+ placeholder={placeholder}
+ autoComplete={autoComplete}
+ className={`w-full rounded-xl px-4 py-3 text-sm font-medium
+ outline-none transition-all duration-150
+ ${isDark
+ ? 'bg-zinc-800/60 border border-zinc-700 text-white placeholder-zinc-500 focus:border-emerald-500 focus:ring-2 focus:ring-emerald-500/20'
+ : 'bg-white border border-zinc-200 text-zinc-800 placeholder-zinc-400 focus:border-emerald-400 focus:ring-2 focus:ring-emerald-100'
+ }
+ `}
+ />
+
+ );
+}
+
+function LeadSelect({ label, value, onChange, options, placeholder }) {
+ const { theme } = useTheme();
+ const isDark = theme === 'dark';
+ return (
+
+ {label &&
{label}}
+
+
+
+
+
+ );
+}
+
+// Collapse transition shared config
+const collapseTransition = {
+ type: 'spring',
+ stiffness: 300,
+ damping: 32,
+ opacity: { duration: 0.15 },
+};
+
+// ---------------------------------------------------------------------------
+// Property Section
+// ---------------------------------------------------------------------------
+export default function LeadPropertySection({ formData, updateField, isQuickCapture }) {
+ const { theme } = useTheme();
+ const isDark = theme === 'dark';
+ const fileInputRef = useRef(null);
+
+ const handlePhotoAdd = (e) => {
+ const files = Array.from(e.target.files || []);
+ if (!files.length) return;
+ const newPhotos = files.map(file => ({
+ id: `${Date.now()}-${Math.random().toString(36).slice(2)}`,
+ url: URL.createObjectURL(file),
+ name: file.name,
+ }));
+ updateField('propertyPhotos', [...(formData.propertyPhotos || []), ...newPhotos]);
+ // Reset input so the same file can be re-added after removal
+ e.target.value = '';
+ };
+
+ const removePhoto = (id) => {
+ updateField('propertyPhotos', (formData.propertyPhotos || []).filter(p => p.id !== id));
+ };
+
+ return (
+
+ {/* ---- Street Address ---- */}
+
+
Street Address
+
+
+ updateField('address', e.target.value)}
+ placeholder="123 Main St"
+ autoComplete="street-address"
+ className={`w-full rounded-xl pl-9 pr-4 py-3 text-sm font-medium
+ outline-none transition-all duration-150
+ ${isDark
+ ? 'bg-zinc-800/60 border border-zinc-700 text-white placeholder-zinc-500 focus:border-emerald-500 focus:ring-2 focus:ring-emerald-500/20'
+ : 'bg-white border border-zinc-200 text-zinc-800 placeholder-zinc-400 focus:border-emerald-400 focus:ring-2 focus:ring-emerald-100'
+ }
+ `}
+ />
+
+
+
+ {/* ---- City / State / ZIP ---- */}
+
+ {/* City — flex-1 */}
+
+ updateField('city', v)}
+ placeholder="Plano"
+ autoComplete="address-level2"
+ />
+
+
+ {/* State — fixed narrow */}
+
+ updateField('state', v)}
+ options={LEAD_FORM_OPTIONS.usStates}
+ />
+
+
+ {/* ZIP — fixed narrow */}
+
+ updateField('zip', v.replace(/\D/g, '').slice(0, 5))}
+ placeholder="75023"
+ type="text"
+ inputMode="numeric"
+ autoComplete="postal-code"
+ />
+
+
+
+ {/* ---- Property Type + Photo Upload — Full Form only ---- */}
+
+ {!isQuickCapture && (
+
+
+ updateField('propertyType', v)}
+ options={LEAD_FORM_OPTIONS.leadTypes}
+ placeholder="Residential, Commercial…"
+ />
+
+
+ {/* ---- Photo Upload ---- */}
+
+
Site Photos
+
+ {/* Upload zone */}
+
+
+ {/* Hidden file input */}
+
+
+ {/* Photo thumbnails */}
+ {(formData.propertyPhotos || []).length > 0 && (
+
+
+ {(formData.propertyPhotos || []).map(photo => (
+
+
+ {/* Remove overlay */}
+
+
+ ))}
+
+
+ )}
+
+
+ )}
+
+
+ );
+}
diff --git a/src/pages/CreateLeadPage.jsx b/src/pages/CreateLeadPage.jsx
index b438bda..ebad923 100644
--- a/src/pages/CreateLeadPage.jsx
+++ b/src/pages/CreateLeadPage.jsx
@@ -10,6 +10,7 @@ import {
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 gsap from 'gsap';
// ---------------------------------------------------------------------------
@@ -91,12 +92,13 @@ const INITIAL_FORM = {
lastName: '',
phones: [{ id: '1', number: '', type: 'Mobile', isPrimary: true }],
emails: [],
- // Property (Phase 4)
+ // Property
address: '',
city: '',
state: 'TX',
zip: '',
propertyType: '',
+ propertyPhotos: [],
// Insurance (Phase 5)
insuranceCompany: '',
claimNumber: '',
@@ -138,7 +140,7 @@ export default function CreateLeadPage() {
formData.lastName &&
formData.phones.some(p => p.number.replace(/\D/g, '').length >= 10)
),
- property: false, // Phase 4
+ property: !!(formData.address && formData.city),
job: isQuickCapture
? !!formData.leadSource
: !!(formData.leadSource && formData.leadType && formData.workType),
@@ -193,6 +195,15 @@ export default function CreateLeadPage() {
/>
);
}
+ if (section.id === 'property') {
+ return (
+
+ );
+ }
if (section.id === 'job') {
return (