feat(storm-intel): Phase 4+5 — Tomorrow.io forecast overlay and canvasser zone assignment

- Tomorrow.io 7-day forecast strip with weather icons, storm score bar, and per-day risk color coding
- High-risk alert banner in forecast strip when any day scores >=65
- api/storm-forecast.js: Vercel proxy that reads TOMORROW_API_KEY server-side; graceful empty response when key unset
- useStormForecast hook: USE_MOCK=false with realistic MOCK_FORECAST fallback
- ZoneDrawingLayer: click-to-place polygon vertices using react-leaflet useMapEvents; disables double-click zoom while active; shows live vertex dots + dashed preview polygon
- ZoneLayer: renders stormZones as colored semi-transparent polygons with agent initials at centroid
- ZoneAssignmentModal: name + canvasser picker + color selector + optional storm link; portaled modal
- StormIntelPage: Draw Zone button (Admin/Owner only); drawing state/cancel flow; stormZones displayed on map; zone summary strip in sidebar; StormDetailDrawer has working "Draw Canvasser Zone" action
- FieldStormZonePage: canvasser mobile view at /field/storm-zones showing assigned zones with map, linked storm details, conversion window badge, and peak window alert
- Layout: Storm Zones entry in FIELD_AGENT nav
- App: /field/storm-zones route (FIELD_AGENT + Admin + Owner)
This commit is contained in:
Satyam-Rastogi
2026-05-18 18:06:17 +05:30
parent df50f27ba6
commit 12bd2fe0de
8 changed files with 1199 additions and 115 deletions
+9
View File
@@ -37,6 +37,7 @@ import EstimatesPage from './pages/EstimatesPage';
import KanbanPage from './pages/KanbanPage';
import LeadProjectPage from './pages/LeadProjectPage';
import StormIntelPage from './pages/StormIntelPage';
import FieldStormZonePage from './pages/FieldStormZonePage';
// ... (existing imports)
const ProtectedRoute = ({ children, allowedRoles }) => {
@@ -279,6 +280,14 @@ function App() {
</ProtectedRoute>
}
/>
<Route
path="/field/storm-zones"
element={
<ProtectedRoute allowedRoles={['FIELD_AGENT', 'ADMIN', 'OWNER']}>
<FieldStormZonePage />
</ProtectedRoute>
}
/>
<Route
path="/admin/maps"
element={