diff --git a/src/data/mockStore.jsx b/src/data/mockStore.jsx index 63d4858..9ee5028 100644 --- a/src/data/mockStore.jsx +++ b/src/data/mockStore.jsx @@ -5598,151 +5598,7 @@ export const LEAD_FORM_OPTIONS = { ], }; -// ── Storm Events — mock history of severe weather events in Plano TX area ────── -// Each polygon is an array of [lat, lng] pairs forming the storm impact boundary. -// severity scale: trace (<1"), moderate (1–1.5"), significant (1.5–2"), severe (2"+) -// conversionWindow: days 7–60 = hot, 61–120 = warm, >120 = cold -export const MOCK_STORM_EVENTS = [ - { - id: 'SE-001', - date: '2026-04-28', - type: 'hail', - severity: 'severe', - maxHailSize: 2.5, - areaName: 'NE Plano / Spring Creek Pkwy', - county: 'Collin', - estimatedHomes: 1240, - description: 'Golf ball–sized hail (2.5") swept through northeast Plano, generating dozens of immediate roof damage reports. Highest claim density in the Spring Creek / Custer Rd corridor.', - source: 'NOAA', - // Severe — large irregular blob, WSW→ENE track, jagged edges like real NWS swath - polygon: [ - [33.082, -96.748], [33.086, -96.741], [33.081, -96.731], - [33.089, -96.720], [33.093, -96.712], [33.090, -96.703], - [33.099, -96.695], [33.106, -96.687], [33.112, -96.680], - [33.116, -96.672], [33.119, -96.665], [33.114, -96.659], - [33.108, -96.665], [33.112, -96.674], [33.105, -96.681], - [33.098, -96.691], [33.091, -96.702], [33.085, -96.713], - [33.078, -96.726], [33.075, -96.739], - ], - }, - { - id: 'SE-002', - date: '2026-03-15', - type: 'hail', - severity: 'moderate', - maxHailSize: 1.2, - areaName: 'Central Plano / W 15th St', - county: 'Collin', - estimatedHomes: 870, - description: 'Quarter-sized hail moved through central Plano neighborhoods. Minor granule loss on older roofs; several shingle bruising reports filed.', - source: 'NWS', - // Moderate — compact irregular blob, slight ENE drift, concave S edge - polygon: [ - [33.064, -96.763], [33.068, -96.754], [33.065, -96.744], - [33.071, -96.736], [33.067, -96.727], [33.059, -96.723], - [33.051, -96.727], [33.046, -96.736], [33.048, -96.747], - [33.053, -96.757], [33.060, -96.764], - ], - }, - { - id: 'SE-003', - date: '2026-02-03', - type: 'hail', - severity: 'significant', - maxHailSize: 1.75, - areaName: 'SE Plano / Parker Rd Corridor', - county: 'Collin', - estimatedHomes: 960, - description: 'Half-dollar sized hail impacted the Parker Rd corridor. High claim conversion probability — many homes in this zone have 15–20 year old roofs.', - source: 'NOAA', - // Significant — elongated jagged swath with notch mid-south edge - polygon: [ - [33.007, -96.739], [33.012, -96.729], [33.008, -96.719], - [33.017, -96.709], [33.024, -96.701], [33.031, -96.694], - [33.037, -96.686], [33.041, -96.679], [33.038, -96.671], - [33.032, -96.677], [33.036, -96.686], [33.029, -96.694], - [33.021, -96.704], [33.014, -96.715], [33.006, -96.729], - [33.004, -96.740], - ], - }, - { - id: 'SE-004', - date: '2026-01-20', - type: 'hail', - severity: 'moderate', - maxHailSize: 1.0, - areaName: 'W Plano / Legacy West', - county: 'Collin', - estimatedHomes: 680, - description: 'Quarter-sized hail with sustained winds up to 45 mph. Mixed residential and commercial impact around the Legacy West district.', - source: 'NWS', - // Moderate — roughly E-W oriented compact blob, irregular N edge - polygon: [ - [33.075, -96.841], [33.080, -96.831], [33.077, -96.819], - [33.083, -96.810], [33.079, -96.802], [33.070, -96.801], - [33.062, -96.805], [33.057, -96.814], [33.060, -96.826], - [33.066, -96.836], [33.073, -96.843], - ], - }, - { - id: 'SE-005', - date: '2025-12-10', - type: 'hail', - severity: 'trace', - maxHailSize: 0.8, - areaName: 'Allen / N Plano Border', - county: 'Collin', - estimatedHomes: 430, - description: 'Marble-sized hail, minimal visible damage. Worth a light canvassing pass on homes with 10+ year roofs in this zone.', - source: 'NWS', - // Trace — small amoeba-like blob, limited extent - polygon: [ - [33.113, -96.669], [33.117, -96.661], [33.115, -96.652], - [33.109, -96.646], [33.102, -96.648], [33.098, -96.657], - [33.101, -96.666], [33.108, -96.672], - ], - }, - { - id: 'SE-006', - date: '2025-11-05', - type: 'hail', - severity: 'significant', - maxHailSize: 1.6, - areaName: 'S Plano / Haggard Park Area', - county: 'Collin', - estimatedHomes: 810, - description: 'Half-dollar hail combined with strong gusts. Shingle bruising confirmed across multiple streets. This zone responded well to canvassing in Q4.', - source: 'NOAA', - // Significant — ragged WSW→ENE swath with NE hook (supercell right turn) - polygon: [ - [33.015, -96.793], [33.021, -96.782], [33.017, -96.771], - [33.026, -96.761], [33.034, -96.752], [33.040, -96.743], - [33.048, -96.737], [33.051, -96.730], [33.047, -96.722], - [33.040, -96.731], [33.032, -96.743], [33.024, -96.756], - [33.016, -96.769], [33.011, -96.782], [33.012, -96.793], - ], - }, - { - id: 'SE-007', - date: '2025-09-18', - type: 'hail', - severity: 'moderate', - maxHailSize: 1.1, - areaName: 'Frisco / N Plano', - county: 'Collin', - estimatedHomes: 530, - description: 'Storm cell crossed from Frisco into north Plano. Roofs 10 years or older in this zone are likely showing impact marks.', - source: 'NOAA', - // Moderate — enters from W, ragged NE exit, concave mid-south - polygon: [ - [33.112, -96.808], [33.117, -96.797], [33.113, -96.786], - [33.121, -96.776], [33.129, -96.764], [33.135, -96.753], - [33.140, -96.743], [33.136, -96.736], [33.129, -96.742], - [33.133, -96.751], [33.127, -96.762], [33.120, -96.773], - [33.113, -96.785], [33.108, -96.798], [33.110, -96.809], - ], - }, -]; +export const MOCK_STORM_EVENTS = []; // ── Storm-sourced leads — canvasser door-knock results linked to storm events ─ // Each lead has a stormSource { id, areaName, date, maxHailSize, severity } and diff --git a/src/hooks/useStormEvents.js b/src/hooks/useStormEvents.js index ec59afb..4343750 100644 --- a/src/hooks/useStormEvents.js +++ b/src/hooks/useStormEvents.js @@ -101,7 +101,7 @@ function featureToEvent(feature, idx) { async function fetchIEMEvents() { const now = new Date(); - const cutoff = new Date(now.getTime() - 36 * 30.44 * 86400000); + const cutoff = new Date(now.getTime() - 6 * 30.44 * 86400000); const url = `${IEM_SBW}?sts=${encodeURIComponent(cutoff.toISOString())}&ets=${encodeURIComponent(now.toISOString())}&wfo=${WFO}`; const res = await fetch(url); diff --git a/src/pages/StormIntelPage.jsx b/src/pages/StormIntelPage.jsx index a76bcec..683bc1d 100644 --- a/src/pages/StormIntelPage.jsx +++ b/src/pages/StormIntelPage.jsx @@ -242,12 +242,10 @@ const PinDropLayer = ({ onDrop }) => { return null; }; -const PIN_RADIUS_M = 8047; // 5 miles in metres - const StormMap = memo(({ events, selectedId, pinnedIds, onSelect, theme, flyCount, selectedStorm, stormZones, isDrawing, draftPolygon, onAddPoint, onFinishPolygon, - pinDropMode, pins, onDropPin, + pinDropMode, pins, onDropPin, pinRadius = 5, }) => { return (
@@ -334,7 +332,7 @@ const StormMap = memo(({ -

Pin {i + 1} · 5 mi radius

+

Pin {i + 1} · {pinRadius} mi radius

@@ -978,7 +976,7 @@ const StormIntelPage = () => { const canManageZones = ['ADMIN', 'OWNER'].includes(user?.role); const [activeTab, setActiveTab] = useState('map'); - const [dateRange, setDateRange] = useState('12m'); + const [dateRange, setDateRange] = useState('6m'); const [customStart, setCustomStart] = useState(''); const [customEnd, setCustomEnd] = useState(''); const [typeFilter, setTypeFilter] = useState('all'); @@ -993,6 +991,7 @@ const StormIntelPage = () => { // Pin drop — up to 5 pins const [pinDropMode, setPinDropMode] = useState(false); const [pins, setPins] = useState([]); // array of [lat,lng] + const [pinRadius, setPinRadius] = useState(5); // miles const MAX_PINS = 5; // Zone drawing state @@ -1026,13 +1025,13 @@ const StormIntelPage = () => { if (!event.polygon?.length) continue; const [cLat, cLon] = polygonCentroid(event.polygon); const minDist = Math.min(...pins.map(p => haversine(p[0], p[1], cLat, cLon))); - if (minDist <= 5 && !seen.has(event.id)) { + if (minDist <= pinRadius && !seen.has(event.id)) { seen.add(event.id); results.push({ ...event, _distanceMiles: minDist }); } } return results.sort((a, b) => a._distanceMiles - b._distanceMiles); - }, [displayEvents, pins]); + }, [displayEvents, pins, pinRadius]); // Sidebar shows nearby events when pins exist, otherwise full filtered list const sidebarEvents = pins.length > 0 ? nearbyEvents : displayEvents; @@ -1335,33 +1334,9 @@ const StormIntelPage = () => {

Failed to load storm data

{error}

- ) : pins.length === 0 ? ( - /* Show base map with pin prompt overlay */ - - ) : mapEvents.length === 0 ? ( -
- -

No storms within 5 miles of your pins

-
) : ( { pinDropMode={pinDropMode} pins={pins} onDropPin={handleDropPin} + pinRadius={pinRadius} /> )} @@ -1386,7 +1362,7 @@ const StormIntelPage = () => {

{pins.length > 0 - ? <>{nearbyEvents.length} Nearby{pinnedIds.size > 0 && · {pinnedIds.size} pinned} + ? <>{nearbyEvents.length} within {pinRadius} mi{pinnedIds.size > 0 && · {pinnedIds.size} pinned} : <>{displayEvents.length} Event{displayEvents.length !== 1 ? 's' : ''}{pinnedIds.size > 0 && · {pinnedIds.size} pinned} }

@@ -1395,7 +1371,7 @@ const StormIntelPage = () => { +
+ ))} + {pins.length < MAX_PINS && ( + - - ))} - {pins.length < MAX_PINS && ( - - )} + )} + + {/* Radius control */} +
+ Radius + { + const v = parseInt(e.target.value, 10); + if (!isNaN(v) && v >= 1) setPinRadius(Math.min(v, 100)); + }} + className="w-14 text-center text-[11px] font-bold rounded-md border border-violet-200 dark:border-violet-500/30 bg-white dark:bg-violet-500/10 text-violet-700 dark:text-violet-300 outline-none focus:ring-1 focus:ring-violet-400 px-1 py-0.5" + /> + mi +
)} @@ -1510,7 +1504,7 @@ const StormIntelPage = () => {

Drop a pin to explore

-

Click the crosshair button and place a pin on the map to find storm events within 5 miles.

+

Click the crosshair button and place a pin on the map to find storm events within your chosen radius.