fix(dispatch): fix Leaflet map blank/partial render on mobile and tablet
- DispatchMapPanel: replace h-full/height:100% with explicit inline px height
on both wrapper div and MapContainer — CollapsePanel uses height:auto which
makes percentage heights resolve to 0 giving Leaflet a zero-size canvas
- DispatchMapPanel: accept mapHeight prop from parent for responsive sizing
- LynkDispatchPage: compute responsive mapHeight state (300px mobile →
380px sm → 460px md → 520px lg tablet) updated on window resize; passed
as prop to DispatchMapPanel on all non-desktop renders
- LynkDispatchPage: add missing reps={effectiveReps} prop on mobile map render
so live rep status cycling is reflected on all breakpoints
- LynkDispatchPage: add missing onQuickAssign={handleAssign} on desktop lead
queue render so Quick Assign button works on desktop
- LynkDispatchPage: add whitespace-nowrap to efficiency badge to prevent
'89% AI-Dispatched' wrapping to two lines in header
- LynkDispatchPage: dispatch synthetic resize event 260ms after map panel
expands on mobile/tablet so Leaflet invalidateSize fires post-animation
- DispatchMapPanel: add second invalidateSize call at 320ms (belt-and-suspenders
alongside 50ms) to cover 220ms CollapsePanel animation on slower devices
- DispatchLeadQueue: enrich DROP_POOL leads with phone, email, source
sub-details and notes so Lead Quick View shows complete data on drop-in leads
This commit is contained in:
@@ -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,
|
||||
},
|
||||
];
|
||||
|
||||
@@ -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 (
|
||||
<div className={`relative overflow-hidden ${containerClass}`} style={containerStyle}>
|
||||
<div className="relative overflow-hidden" style={{ height: mapH }}>
|
||||
{/* Inject map animations */}
|
||||
<style>{MAP_STYLES}</style>
|
||||
|
||||
@@ -228,8 +236,8 @@ const DispatchMapPanel = ({ selectedLead, dispatchLeads, reps = DISPATCH_REPS, s
|
||||
zoom={DEFAULT_ZOOM}
|
||||
scrollWheelZoom={true}
|
||||
zoomControl={false}
|
||||
className="w-full h-full"
|
||||
style={{ height: '100%', width: '100%' }}
|
||||
className="w-full"
|
||||
style={{ height: mapH, width: '100%' }}
|
||||
>
|
||||
{/* Tile layer — dark/light aware */}
|
||||
<TileLayer
|
||||
|
||||
Reference in New Issue
Block a user