diff --git a/src/components/dispatch/DispatchLeadQueue.jsx b/src/components/dispatch/DispatchLeadQueue.jsx index 5e7e65a..10eee9e 100644 --- a/src/components/dispatch/DispatchLeadQueue.jsx +++ b/src/components/dispatch/DispatchLeadQueue.jsx @@ -56,23 +56,30 @@ const getAgingState = (arrivedMinsAgo, status) => { }; // Fake leads that "drop in" every 20s during demo — cycled through pool +// Keep these fully enriched so Lead Quick View shows complete detail const DROP_POOL = [ { source: 'call_center', leadType: 'roof_inspection', urgency: 'high', - customer: { name: 'Marcus Webb' }, + customer: { name: 'Marcus Webb', phone: '(972) 555-2201', email: 'marcus.webb@gmail.com' }, property: { address: '1620 Mapleshade Ln', city: 'Plano', state: 'TX', zip: '75075', lat: 33.0545, lng: -96.7634 }, + callCenter: { id: 'CC-02', name: 'DFW Ops Hub' }, + notes: 'Homeowner noticed missing shingles after last week\'s storm. No active leak reported but wants inspection before weekend rain.', estimatedDuration: 45, status: 'unassigned', arrivedMinsAgo: 0, assignedRepId: null, }, { source: 'website_form', leadType: 'emergency_tarp', urgency: 'emergency', - customer: { name: 'Sandra Kim' }, + customer: { name: 'Sandra Kim', phone: '(972) 555-2202', email: 'sandra.kim@icloud.com' }, property: { address: '3488 Midway Rd', city: 'Plano', state: 'TX', zip: '75093', lat: 33.0712, lng: -96.8090 }, + webSource: { campaign: 'Google Ads — Emergency Storm Response' }, + notes: 'Tree limb through garage roof — active water ingress reported. Customer is extremely stressed. Dispatch immediately.', estimatedDuration: 90, status: 'unassigned', arrivedMinsAgo: 0, assignedRepId: null, }, { source: 'canvassing', leadType: 'insurance_claim', urgency: 'high', - customer: { name: 'David Okonkwo' }, + customer: { name: 'David Okonkwo', phone: '(972) 555-2203', email: 'd.okonkwo@outlook.com' }, property: { address: '892 McDermott Dr', city: 'Plano', state: 'TX', zip: '75024', lat: 33.0921, lng: -96.7812 }, + canvassedBy: { agentId: 'U-09', name: 'Kenji Flores' }, + notes: null, estimatedDuration: 60, status: 'unassigned', arrivedMinsAgo: 0, assignedRepId: null, }, ]; diff --git a/src/components/dispatch/DispatchMapPanel.jsx b/src/components/dispatch/DispatchMapPanel.jsx index 216d28b..35151f5 100644 --- a/src/components/dispatch/DispatchMapPanel.jsx +++ b/src/components/dispatch/DispatchMapPanel.jsx @@ -113,13 +113,19 @@ const MapController = ({ selectedLeadId, leadPos, repPos, routePath }) => { // Invalidate size on mount and on every window resize so Leaflet fills // the full flex container — prevents the blank tile area on the right. + // On mobile the map panel is inside a CollapsePanel that animates + // height 0→auto over 220ms, so we need to wait past that before calling + // invalidateSize, otherwise Leaflet initialises at near-zero height. useEffect(() => { const invalidate = () => map.invalidateSize({ animate: false }); - // Defer one frame so the flex layout has fully settled - const t = setTimeout(invalidate, 50); + // 50ms covers desktop (no animation). 320ms covers the 220ms collapse + // animation with margin so tiles fill correctly on mobile/tablet. + const t1 = setTimeout(invalidate, 50); + const t2 = setTimeout(invalidate, 320); window.addEventListener('resize', invalidate); return () => { - clearTimeout(t); + clearTimeout(t1); + clearTimeout(t2); window.removeEventListener('resize', invalidate); }; }, [map]); @@ -186,7 +192,7 @@ const MAP_STYLES = ` // --------------------------------------------------------------------------- // Main Component // --------------------------------------------------------------------------- -const DispatchMapPanel = ({ selectedLead, dispatchLeads, reps = DISPATCH_REPS, stormMode, accent, isDesktop }) => { +const DispatchMapPanel = ({ selectedLead, dispatchLeads, reps = DISPATCH_REPS, stormMode, accent, isDesktop, mapHeight }) => { const { theme } = useTheme(); // Top recommendation for the selected lead @@ -213,13 +219,15 @@ const DispatchMapPanel = ({ selectedLead, dispatchLeads, reps = DISPATCH_REPS, s const routeColor = stormMode ? '#F59E0B' : accent; - const containerStyle = isDesktop ? { height: 'calc(72rem + 2.375rem)' } : undefined; - const containerClass = !isDesktop - ? 'min-h-[280px] sm:min-h-[360px] md:min-h-[420px]' - : ''; + // Explicit pixel height on both the wrapper div and MapContainer. + // Do NOT use h-full / height:100% — the CollapsePanel ancestor has + // height:auto so the percentage chain resolves to 0. Inline px values + // bypass the chain entirely and give Leaflet a reliable offsetHeight. + // mapHeight prop is computed responsively in LynkDispatchPage (300–520px). + const mapH = isDesktop ? 'calc(72rem + 2.375rem)' : (mapHeight || '300px'); return ( -