235 lines
12 KiB
React
235 lines
12 KiB
React
import React, { useState, useEffect } from 'react';
|
|
import { useNavigate } from 'react-router-dom';
|
|
import { Icons } from './landing/constants';
|
|
import MapBg from '../assets/images/hyper_intelligence_bg_image.jpg';
|
|
import IntelligenceSidePanel from './dashboard/IntelligenceSidePanel';
|
|
|
|
// Updated with Residential & Commercial mix
|
|
const hotspots = [
|
|
{
|
|
id: 1,
|
|
top: '45%',
|
|
left: '65%',
|
|
address: 'Preston Royal Village',
|
|
type: 'Commercial',
|
|
problem: 'Roof Membrane Uplift',
|
|
gain: '+$8.5M Value',
|
|
weather: 'Hail Expected',
|
|
status: 'Urgent',
|
|
specs: { built: '1998', maintenance: '5 years since last coating' },
|
|
management: { name: 'Sterling Properties', contact: 'Sign in for contact details' },
|
|
photos: [
|
|
{ id: 1, url: '/assets/images/properties/Warehouse_Roof_Aerial.jpg', caption: 'Warehouse Roof Aerial' },
|
|
{ id: 2, url: '/assets/images/properties/Office_Roof_Top_View.jpg', caption: 'Office Roof Top' }
|
|
]
|
|
},
|
|
{
|
|
id: 2,
|
|
top: '28%',
|
|
left: '42%',
|
|
address: 'Highland Park Place',
|
|
type: 'Commercial',
|
|
problem: 'Critical Membrane Failure',
|
|
gain: '$22.5 Million',
|
|
weather: 'Clear',
|
|
status: 'Monitoring',
|
|
specs: { built: '1982', maintenance: '20 years since last roof replacement permit' },
|
|
management: { name: 'Goldman Group', contact: 'Sign in for contact details' },
|
|
photos: [
|
|
{ id: 3, url: '/assets/images/properties/Mall_Rooftop_HVAC_Units.jpg', caption: 'Rooftop HVAC Units' }
|
|
]
|
|
},
|
|
{
|
|
id: 3,
|
|
top: '55%',
|
|
left: '25%',
|
|
address: '4200 Beverly Dr',
|
|
type: 'Residential',
|
|
problem: 'Severe Hail Impact',
|
|
gain: '$1.1M',
|
|
weather: 'Rain Soon',
|
|
status: 'Action Required',
|
|
specs: { roofSize: '62 sq Roof', eventHistory: '8 years since last known hail event' },
|
|
owner: { fico: '715', profile: 'Early Adopter / Long-term value' },
|
|
photos: [
|
|
{ id: 4, url: '/assets/images/properties/Hail_Damaged_Shingles.jpg', caption: 'Hail Damage Close-up' },
|
|
{ id: 5, url: '/assets/images/properties/Broken_Roof_Surface.jpg', caption: 'Broken Surface' }
|
|
]
|
|
},
|
|
{
|
|
id: 4,
|
|
top: '70%',
|
|
left: '48%',
|
|
address: '3505 Lexington Ave',
|
|
type: 'Residential',
|
|
problem: 'Asphalt Shingle Loss',
|
|
gain: '+$950k Home',
|
|
weather: 'Windy',
|
|
status: 'Low Priority',
|
|
specs: { roofSize: '45 sq Roof', eventHistory: 'High Wind Zone' },
|
|
owner: { fico: '680', profile: 'First-time Buyer' },
|
|
photos: [
|
|
{ id: 6, url: '/assets/images/properties/Cracked_Asphalt_Shingles.jpg', caption: 'Cracked Shingles' },
|
|
{ id: 7, url: '/assets/images/properties/Curled_Aging_Shingles.jpg', caption: 'Aging Shingles' }
|
|
]
|
|
},
|
|
];
|
|
|
|
const IntelligenceMap = () => {
|
|
const navigate = useNavigate();
|
|
const [activeSpot, setActiveSpot] = useState(null);
|
|
const [showRescheduleDialog, setShowRescheduleDialog] = useState(false);
|
|
|
|
useEffect(() => {
|
|
// Simulate incoming weather alert
|
|
const timer = setTimeout(() => {
|
|
setShowRescheduleDialog(true);
|
|
}, 2500);
|
|
return () => clearTimeout(timer);
|
|
}, []);
|
|
|
|
return (
|
|
<section className="py-24 bg-gray-900 border-t border-white/5 relative overflow-hidden">
|
|
<div className="container mx-auto px-4 relative z-10">
|
|
<div className="mb-12 text-center max-w-4xl mx-auto">
|
|
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-cyan-400/10 border border-cyan-400/20 mb-4">
|
|
<div className="w-1.5 h-1.5 bg-cyan-400 rounded-full animate-pulse"></div>
|
|
<span className="text-cyan-400 text-xs font-mono font-bold uppercase tracking-widest">Live CRM Data Feed</span>
|
|
</div>
|
|
<h2 className="text-3xl md:text-5xl font-heading font-bold text-white mb-6">Hyper-Local Sales Intelligence</h2>
|
|
<p className="text-gray-400 text-lg max-w-2xl mx-auto">
|
|
Our systems overlay live NOAA weather data with property value insights. Maximize revenue by targeting neighborhoods directly in the storm path.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Map Container Frame */}
|
|
<div className="relative w-full h-[650px] bg-black rounded-2xl border border-white/20 shadow-2xl overflow-hidden group isolate ring-1 ring-white/10">
|
|
|
|
{/* Map Background - DFW Satellite Style */}
|
|
<div className="absolute inset-0 bg-[#0f172a]">
|
|
{/* Aerial/Satellite View of a Grid City */}
|
|
<img
|
|
src={MapBg}
|
|
alt="DFW Metroplex Map Overlay"
|
|
className="w-full h-full object-cover opacity-60 mix-blend-luminosity hover:scale-105 transition-transform duration-[20s]"
|
|
/>
|
|
|
|
{/* Map Grid Overlay */}
|
|
<div className="absolute inset-0 bg-[url('https://grainy-gradients.vercel.app/noise.svg')] opacity-20"></div>
|
|
<div className="absolute inset-0 bg-grid-pattern opacity-10 bg-[length:40px_40px] border-t border-white/5"></div>
|
|
|
|
{/* Region Indicator - HUD Style */}
|
|
<div className="absolute top-6 left-6 z-30">
|
|
<div className="bg-slate-900/80 backdrop-blur-md text-white pl-4 pr-6 py-2 rounded-r-lg border-l-4 border-cyan-400 shadow-[0_0_15px_rgba(0,229,255,0.2)] flex items-center gap-3">
|
|
<div>
|
|
<div className="text-[10px] text-cyan-400 font-bold uppercase tracking-widest mb-0.5">Active Region</div>
|
|
<div className="font-heading font-bold text-lg leading-none">DFW Zone 2</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Weather Overlay Widget */}
|
|
<div className="absolute top-0 right-0 w-full h-full bg-gradient-to-bl from-slate-900/80 via-transparent to-transparent pointer-events-none"></div>
|
|
<div className="absolute top-6 right-6 p-4 bg-black/60 backdrop-blur-md rounded-lg border border-white/10 max-w-xs z-30">
|
|
<div className="flex items-center gap-4">
|
|
<div className="bg-white/10 p-2 rounded-full text-white">
|
|
<span className="text-2xl">⛈</span>
|
|
</div>
|
|
<div>
|
|
<div className="font-bold text-white text-sm">Severe Hail Watch</div>
|
|
<div className="text-xs text-cyan-400 animate-pulse">Approaching Tarrant County</div>
|
|
<div className="text-[10px] text-gray-500 font-mono mt-1">ETA: 15 MINS</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Interactive Pins */}
|
|
{hotspots.map((spot) => (
|
|
<div
|
|
key={spot.id}
|
|
className="absolute group/pin z-20 cursor-pointer"
|
|
style={{ top: spot.top, left: spot.left }}
|
|
onClick={() => setActiveSpot(spot)}
|
|
>
|
|
{/* Radar Ripple Effect */}
|
|
<div className={`absolute -inset-4 rounded-full animate-ping opacity-75 ${spot.type === 'Commercial' ? 'bg-purple-500/20' : 'bg-cyan-400/20'}`}></div>
|
|
|
|
{/* Price Tag Pin */}
|
|
<div className="relative flex flex-col items-center transform group-hover/pin:scale-110 transition-transform duration-200">
|
|
<div className={`bg-slate-900/90 backdrop-blur text-[10px] font-mono font-bold px-2 py-1 rounded border shadow-[0_0_10px_rgba(0,0,0,0.5)] whitespace-nowrap mb-1 opacity-0 group-hover/pin:opacity-100 transition-opacity ${spot.type === 'Commercial' ? 'text-purple-400 border-purple-400/30' : 'text-cyan-400 border-cyan-400/30'}`}>
|
|
{spot.gain}
|
|
</div>
|
|
|
|
{/* Pin Head */}
|
|
<div className={`w-3 h-3 rounded-full border-2 border-white shadow-lg relative z-10 ${spot.type === 'Commercial' ? 'bg-purple-500 shadow-[0_0_15px_rgba(168,85,247,0.6)]' : 'bg-cyan-400 shadow-[0_0_15px_rgba(0,229,255,0.6)]'}`}></div>
|
|
|
|
{/* Pin Line */}
|
|
<div className="w-[1px] h-8 bg-gradient-to-b from-white to-transparent opacity-50"></div>
|
|
|
|
{/* Ground Shadow */}
|
|
<div className="absolute bottom-0 w-6 h-1.5 bg-black/60 blur-sm rounded-[100%] translate-y-1"></div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
|
|
{/* Reschedule Dialog Overlay */}
|
|
{showRescheduleDialog && (
|
|
<div className="absolute bottom-8 left-1/2 transform -translate-x-1/2 md:left-8 md:translate-x-0 z-50 w-[90%] md:w-96">
|
|
<div className="bg-white/95 backdrop-blur-xl rounded-xl shadow-2xl p-6 border-l-4 border-red-600 animate-[slideUp_0.5s_ease-out]">
|
|
<div className="flex justify-between items-start mb-3">
|
|
<div className="flex items-center gap-3">
|
|
<div className="w-8 h-8 bg-red-100 rounded-full flex items-center justify-center text-red-600 font-bold text-lg">!</div>
|
|
<div>
|
|
<h4 className="font-bold text-slate-900 text-sm uppercase tracking-wide">Schedule Conflict</h4>
|
|
</div>
|
|
</div>
|
|
<button onClick={() => setShowRescheduleDialog(false)} className="text-gray-400 hover:text-black">✕</button>
|
|
</div>
|
|
|
|
<p className="text-gray-600 text-sm mb-4 leading-relaxed">
|
|
<strong>Heavy rainfall expected in Plano at 2:00 PM.</strong> <br />
|
|
This conflicts with the inspection at <span className="text-slate-900 font-semibold">Legacy West</span>.
|
|
</p>
|
|
|
|
<div className="flex gap-3">
|
|
<button onClick={() => navigate('/login')} className="flex-1 bg-slate-900 text-white text-xs font-bold py-2.5 rounded hover:bg-slate-800 transition-colors uppercase tracking-wider">
|
|
Reschedule
|
|
</button>
|
|
<button onClick={() => setShowRescheduleDialog(false)} className="flex-1 bg-gray-100 text-gray-600 text-xs font-bold py-2.5 rounded hover:bg-gray-200 transition-colors uppercase tracking-wider">
|
|
Dismiss
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Right Sidebar Panel (Glass Detail View) */}
|
|
{activeSpot && (
|
|
<IntelligenceSidePanel
|
|
data={activeSpot}
|
|
onClose={() => setActiveSpot(null)}
|
|
/>
|
|
)}
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<style>{`
|
|
@keyframes slideUp {
|
|
from { opacity: 0; transform: translate(-50%, 20px); }
|
|
to { opacity: 1; transform: translate(-50%, 0); }
|
|
}
|
|
@media (min-width: 768px) {
|
|
@keyframes slideUp {
|
|
from { opacity: 0; transform: translateY(20px); }
|
|
to { opacity: 1; transform: translateY(0); }
|
|
}
|
|
}
|
|
`}</style>
|
|
</section>
|
|
);
|
|
};
|
|
|
|
export default IntelligenceMap;
|