import React, { useState, useEffect, useRef } from 'react'; import ReactDOM from 'react-dom'; import { useNavigate } from 'react-router-dom'; import MapBg from '../assets/images/intel_map_dfw_twilight.webp'; import IntelligenceSidePanel from './dashboard/IntelligenceSidePanel'; import gsap from 'gsap'; import { ScrollTrigger } from 'gsap/ScrollTrigger'; gsap.registerPlugin(ScrollTrigger); // ── 10 Hotspots — 5 Commercial + 5 Residential, DFW Metro ── const hotspots = [ // Commercial { id: 1, top: '22%', left: '58%', address: '8333 Douglas Ave, Dallas', type: 'Commercial', problem: 'Roof Membrane Uplift', gain: '+$8.5M', weather: 'Hail Expected', status: 'Urgent', specs: { built: '1998', maintenance: '5 yrs since last coating' }, management: { name: 'Sterling Properties', contact: 'Sign in for contact details' }, photos: [ { id: 'c1-1', url: '/assets/images/properties/Apartment_Complex.jpg', caption: 'Exterior View' }, { id: 'c1-2', url: '/assets/images/properties/Office_Roof_Top_View.jpg', caption: 'Roof Aerial' }, { id: 'c1-3', url: '/assets/images/properties/Broken_Roof_Surface.jpg', caption: 'Membrane Damage' }, ] }, { id: 2, top: '35%', left: '38%', address: '5600 Tennyson Pkwy, Plano', type: 'Commercial', problem: 'Critical Membrane Failure', gain: '+$22.5M', weather: 'Clear', status: 'Monitoring', specs: { built: '1982', maintenance: '20 yrs since roof replacement' }, management: { name: 'Goldman Group', contact: 'Sign in for contact details' }, photos: [ { id: 'c2-1', url: '/assets/images/properties/Warehouse_Roof_Aerial.jpg', caption: 'Aerial Survey' }, { id: 'c2-2', url: '/assets/images/properties/Mall_Rooftop_HVAC_Units.jpg', caption: 'HVAC Systems' }, { id: 'c2-3', url: '/assets/images/properties/Storm_Worn_Roof.jpg', caption: 'Surface Wear' }, { id: 'c2-4', url: '/assets/images/properties/Cracked_Asphalt_Shingles.jpg', caption: 'Substrate Cracking' }, ] }, { id: 3, top: '48%', left: '72%', address: '1900 Preston Rd, Frisco', type: 'Commercial', problem: 'Ponding Water Accumulation', gain: '+$4.2M', weather: 'Storm Watch', status: 'Action Required', specs: { built: '2005', maintenance: '3 yrs since drainage check' }, management: { name: 'Hillcrest Capital', contact: 'Sign in for contact details' }, photos: [ { id: 'c3-1', url: '/assets/images/properties/Apartment_Complex.jpg', caption: 'Building Exterior' }, { id: 'c3-2', url: '/assets/images/properties/Mall_Rooftop_HVAC_Units.jpg', caption: 'Rooftop Overview' }, { id: 'c3-3', url: '/assets/images/properties/Broken_Roof_Surface.jpg', caption: 'Ponding Damage' }, ] }, { id: 4, top: '62%', left: '28%', address: '2100 N Hwy 360, Grand Prairie', type: 'Commercial', problem: 'HVAC Penetration Leak', gain: '+$6.8M', weather: 'Windy', status: 'Monitoring', specs: { built: '1995', maintenance: '8 yrs since waterproofing' }, management: { name: 'Apex Industrial Partners', contact: 'Sign in for contact details' }, photos: [ { id: 'c4-1', url: '/assets/images/properties/Warehouse_Roof_Aerial.jpg', caption: 'Facility Aerial' }, { id: 'c4-2', url: '/assets/images/properties/Mall_Rooftop_HVAC_Units.jpg', caption: 'HVAC Penetration' }, { id: 'c4-3', url: '/assets/images/properties/Cracked_Storm_Shingles.jpg', caption: 'Seal Failure' }, ] }, { id: 5, top: '15%', left: '20%', address: '401 Commerce St, Fort Worth', type: 'Commercial', problem: 'Flashing Deterioration', gain: '+$11.3M', weather: 'Hail Expected', status: 'Urgent', specs: { built: '1990', maintenance: '12 yrs since seal replacement' }, management: { name: 'Tarrant Realty Holdings', contact: 'Sign in for contact details' }, photos: [ { id: 'c5-1', url: '/assets/images/properties/Office_Roof_Top_View.jpg', caption: 'Roof Survey' }, { id: 'c5-2', url: '/assets/images/properties/Apartment_Complex.jpg', caption: 'Building Facade' }, { id: 'c5-3', url: '/assets/images/properties/Curled_Aging_Shingles.jpg', caption: 'Flashing Wear' }, { id: 'c5-4', url: '/assets/images/properties/Storm_Worn_Roof.jpg', caption: 'Wind Exposure' }, ] }, // Residential { id: 6, top: '55%', left: '50%', address: '4200 Beverly Dr, Highland Park', type: 'Residential', problem: 'Severe Hail Impact', gain: '+$1.1M', weather: 'Rain Soon', status: 'Action Required', specs: { roofSize: '62 sq', eventHistory: '8 yrs since last hail event' }, owner: { fico: '715', profile: 'Early Adopter / Long-term value' }, photos: [ { id: 'r6-1', url: '/assets/images/properties/Luxury_Driveway_Residence.jpg', caption: 'Front Exterior' }, { id: 'r6-2', url: '/assets/images/properties/Hail_Damaged_Shingles.jpg', caption: 'Hail Damage' }, { id: 'r6-3', url: '/assets/images/properties/Cracked_Storm_Shingles.jpg', caption: 'Storm Damage' }, ] }, { id: 7, top: '72%', left: '65%', address: '3505 Lexington Ave, University Park', type: 'Residential', problem: 'Asphalt Shingle Loss', gain: '+$950K', weather: 'Windy', status: 'Low Priority', specs: { roofSize: '45 sq', eventHistory: 'High Wind Zone' }, owner: { fico: '680', profile: 'First-time Buyer' }, photos: [ { id: 'r7-1', url: '/assets/images/properties/Modern_White_Suburban_Home.jpg', caption: 'Front View' }, { id: 'r7-2', url: '/assets/images/properties/Cracked_Asphalt_Shingles.jpg', caption: 'Shingle Loss' }, { id: 'r7-3', url: '/assets/images/properties/Roof_Inspection_Angle.jpg', caption: 'Drone Inspection' }, ] }, { id: 8, top: '40%', left: '15%', address: '7212 Covewood Dr, Garland', type: 'Residential', problem: 'Aging Composite Shingles', gain: '+$420K', weather: 'Clear', status: 'Low Priority', specs: { roofSize: '38 sq', eventHistory: 'No recent events' }, owner: { fico: '740', profile: 'Long-term Owner' }, photos: [ { id: 'r8-1', url: '/assets/images/properties/Beige_Two_Story_House.jpg', caption: 'Street View' }, { id: 'r8-2', url: '/assets/images/properties/Curled_Aging_Shingles.jpg', caption: 'Aging Shingles' }, ] }, { id: 9, top: '75%', left: '42%', address: '1520 Creekside Dr, Allen', type: 'Residential', problem: 'Ridge Cap Separation', gain: '+$780K', weather: 'Hail Expected', status: 'Urgent', specs: { roofSize: '55 sq', eventHistory: '2 hail events in 3 years' }, owner: { fico: '695', profile: 'Insurance-motivated' }, photos: [ { id: 'r9-1', url: '/assets/images/properties/Brick_Front_Porch_Home.jpg', caption: 'Entryway' }, { id: 'r9-2', url: '/assets/images/properties/Overlapping_Roof_Shingles.jpg', caption: 'Ridge Detail' }, { id: 'r9-3', url: '/assets/images/properties/Hail_Damaged_Shingles.jpg', caption: 'Hail Damage' }, { id: 'r9-4', url: '/assets/images/properties/Storm_Worn_Roof.jpg', caption: 'Worn Surface' }, ] }, { id: 10, top: '30%', left: '82%', address: '905 W Main St, Richardson', type: 'Residential', problem: 'Granule Loss Pattern', gain: '+$560K', weather: 'Storm Watch', status: 'Action Required', specs: { roofSize: '42 sq', eventHistory: '5 yrs since installation' }, owner: { fico: '720', profile: 'Recently Purchased' }, photos: [ { id: 'r10-1', url: '/assets/images/properties/Red_Shingle_Cottage.jpg', caption: 'Cottage Exterior' }, { id: 'r10-2', url: '/assets/images/properties/Residential_Roof_Texture.jpg', caption: 'Roof Texture' }, { id: 'r10-3', url: '/assets/images/properties/Cracked_Asphalt_Shingles.jpg', caption: 'Granule Loss' }, ] }, ]; const FILTERS = ['All', 'Commercial', 'Residential', 'Storm Path']; const STORM_WEATHER = ['Hail Expected', 'Storm Watch', 'Rain Soon']; const metrics = [ { label: 'Total Pipeline', value: 57.1, prefix: '$', suffix: 'M', color: 'text-blue-500', stamp: '\u25CF Active', stampClass: 'permit-stamp--accent' }, { label: 'Active Alerts', value: 7, prefix: '', suffix: '', color: 'text-amber-500', stamp: '\u25B2 Monitoring', stampClass: 'permit-stamp--accent' }, { label: 'Urgent', value: 3, prefix: '', suffix: '', color: 'text-red-500', stamp: '\u2715 Critical', stampClass: 'permit-stamp--danger' }, { label: 'Avg Roof Age', value: 18, prefix: '', suffix: ' yrs', color: 'text-emerald-500', stamp: '\u2713 Assessed', stampClass: 'permit-stamp--verified' }, ]; const isSpotVisible = (spot, filter) => { if (filter === 'All') return true; if (filter === 'Commercial') return spot.type === 'Commercial'; if (filter === 'Residential') return spot.type === 'Residential'; if (filter === 'Storm Path') return STORM_WEATHER.includes(spot.weather); return true; }; const IntelligenceMap = () => { const navigate = useNavigate(); const [activeSpot, setActiveSpot] = useState(null); const [activeFilter, setActiveFilter] = useState('All'); const [hoveredSpot, setHoveredSpot] = useState(null); const [showRescheduleDialog, setShowRescheduleDialog] = useState(false); const sectionRef = useRef(null); const pinRefs = useRef([]); const metricRefs = useRef([]); const mapGlowRef = useRef(null); // Reschedule dialog timer useEffect(() => { const timer = setTimeout(() => setShowRescheduleDialog(true), 2500); return () => clearTimeout(timer); }, []); // GSAP: Staggered pin entrance + metric counters useEffect(() => { const ctx = gsap.context(() => { // Pin entrance const pins = pinRefs.current.filter(Boolean); pins.forEach(pin => gsap.set(pin, { opacity: 0, scale: 0, y: 20 })); gsap.to(pins, { opacity: 1, scale: 1, y: 0, duration: 0.6, stagger: 0.08, ease: 'back.out(1.7)', scrollTrigger: { trigger: sectionRef.current, start: 'top 70%', toggleActions: 'play none none none', }, onComplete: () => { // Clear GSAP inline styles so CSS transitions take over pins.forEach(pin => { if (pin) gsap.set(pin, { clearProps: 'opacity,transform' }); }); }, }); // Metric counters — proxy object approach metricRefs.current.forEach((el) => { if (!el) return; const target = parseFloat(el.dataset.target); const suffix = el.dataset.suffix || ''; const prefix = el.dataset.prefix || ''; const counter = { value: 0 }; el.innerText = prefix + '0' + suffix; gsap.to(counter, { value: target, duration: 2, ease: 'power1.out', scrollTrigger: { trigger: el, start: 'top 85%', toggleActions: 'play none none none', }, onUpdate: () => { const display = target % 1 !== 0 ? counter.value.toFixed(1) : Math.ceil(counter.value).toString(); el.innerText = prefix + display + suffix; }, }); }); }, sectionRef); return () => ctx.revert(); }, []); // Mouse-follow glow on map const handleMapMouseMove = (e) => { if (!mapGlowRef.current) return; const rect = e.currentTarget.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; mapGlowRef.current.style.background = `radial-gradient(350px circle at ${x}px ${y}px, rgba(6,182,212,0.08), transparent 60%)`; }; const handleMapMouseLeave = () => { if (mapGlowRef.current) mapGlowRef.current.style.background = 'transparent'; }; return (
{/* Blueprint grid bg */}
{/* ── Header ── */}
Live CRM Data Feed

Hyper-Local Sales Intelligence

Our systems overlay live NOAA weather data with property value insights. Maximize revenue by targeting neighborhoods directly in the storm path.

{/* ── Filter Bar ── */}
{FILTERS.map((filter) => ( ))}
{/* ══════════════════════════════════════════════ */} {/* MAP CONTAINER — always dark (satellite image) */} {/* ══════════════════════════════════════════════ */}
{/* Map Background */}
DFW Metroplex Map Overlay
{/* Mouse-follow glow */}
{/* Region Indicator — HUD */}
Active Region
DFW Zone 2
{/* Weather Widget */}
{'\u26C8'}
Severe Hail Watch
Approaching Tarrant County
ETA: 15 MINS
{/* ── Interactive Pins ── */} {hotspots.map((spot, index) => { const visible = isSpotVisible(spot, activeFilter); const isDimmed = activeSpot && activeSpot.id !== spot.id; const isCommercial = spot.type === 'Commercial'; return (
{ pinRefs.current[index] = el; }} className={`absolute group/pin z-20 cursor-pointer transition-all duration-300 ease-in-out ${!visible ? 'opacity-0 scale-0 pointer-events-none' : ''} ${visible && isDimmed ? 'opacity-30' : ''} `} style={{ top: spot.top, left: spot.left }} onClick={() => setActiveSpot(activeSpot?.id === spot.id ? null : spot)} onMouseEnter={() => setHoveredSpot(spot.id)} onMouseLeave={() => setHoveredSpot(null)} > {/* Larger invisible touch target */}
{/* Urgent Status Alert Pulse */} {visible && spot.status === 'Urgent' && (
)} {/* Radar Ripple */} {visible && (
)} {/* Selected glow ring */} {activeSpot?.id === spot.id && (
)} {/* Pin cluster */}
{/* Pin Head */}
{/* Pin Line */}
{/* Ground Shadow */}
{/* Hover Tooltip */}
{spot.address}
{spot.gain}
); })} {/* Reschedule Dialog */} {showRescheduleDialog && (
!

Schedule Conflict

Heavy rainfall expected in Plano at 2:00 PM.
This conflicts with the inspection at Legacy West.

)} {/* Desktop Side Panel */} {activeSpot && (
setActiveSpot(null)} />
)}
{/* ── Mobile Bottom Sheet ── */} {activeSpot && ReactDOM.createPortal(
setActiveSpot(null)} />
{/* Sheet header */}

{activeSpot.address}

setActiveSpot(null)} variant="sheet" />
, document.body )} {/* ── Aggregate Metrics ── */}
{metrics.map((m, i) => (
{m.label}
{ metricRefs.current[i] = el; }} data-target={m.value} data-suffix={m.suffix} data-prefix={m.prefix} className={`text-3xl font-mono font-bold ${m.color} tracking-tight`} > {m.prefix}0{m.suffix}
{m.stamp}
))}
); }; export default IntelligenceMap;