Podium values now count up via AnimatedNumber with per-metric formatting
(currency/percent/deals). Podium bars grow from a fixed baseline at constant
speed so bronze settles first, then silver, then gold last; reserve row height
so the baseline doesn't shift mid-animation. Remove unused formatSales/formatCv.
Tomorrow.io 7xxx codes are ice pellets, not snow — remapped from Snowflake
to CloudHail. Added hailRisk override so any day flagged as hail risk by
the API always renders the hail icon regardless of weatherCode. Also added
missing 6xxx (freezing rain) codes to the icon table.
Mock Sunday entry corrected to weatherCode 8000 (thunderstorm) — 7102 at
76°F made no meteorological sense.
Hail Recon fetch now checks Content-Type before parsing JSON — when running
under pnpm dev (Vite only), the proxy JS file is served as-is; the check
catches the non-JSON response and flags it as proxy_unavailable, surfacing
"Not available in dev mode" instead of the raw parse-error string.
Est. Homes header now shows "—" when no events are event-pinned. The
previous totalHomes sum across all 37 NWS warning polygons (each 100–500 sq mi)
produced a nonsensical 19.7M figure. The number is only meaningful per-event
or when the user explicitly pins events to compare.
useStormEvents:
- Extract windSpeed (windtag), expireTime, warningDurationMins, phenomenaCode/Label
- Calculate polygonAreaSqMi via shoelace formula; estimate homes at 1800/sq mi
StormDetailDrawer — new sections:
- Weather Metrics: hail size with damage descriptor, wind speed with severity label
- Impact Zone: warning area sq miles + estimated homes in zone
- Warning Timeline: issued time, expired time, duration, issuing NWS office
- NWS Boundary note: explains the shape is a meteorologist-drawn warning zone
Map tooltip:
- Shows phenomenaLabel, wind speed, area sq mi
- Footer section labeled "NWS Warning Boundary" with shape explanation
- Location pins filter the sidebar only; map no longer auto-renders all
nearby polygons (NWS warnings span entire counties, cluttering the view)
- Map shows event polygons only for explicitly selected or event-pinned items
- "Show all on map" toggle in sidebar renders the full sidebar event list
- FitToPins auto-zooms to the pin radius circle when a new pin is dropped;
clears back to Plano center when all pins are removed
- Fix ReferenceError: hailIn was used before declaration in featureToEvent,
causing every SV event to throw and fall back to empty mock data
- Restore IEM fetch window to 36 months so events are available for all range chips
- Radius input now has explicit Go button (Enter key also applies); draft value
shows hint until applied so accidental keystrokes don't retrigger search
- Map always renders (removed blank overlay when no nearby events)
- Pin radius is now user-configurable (default 5 mi, input shown in pin strip)
- Default date range changed to 6 months for faster initial load
- IEM fetch window narrowed to 6 months to match default
- MOCK_STORM_EVENTS cleared (IEM provides real NWS polygon data)
Search: text filter across areaName/type/severity in sidebar
Pin drop (up to 5 pins):
- Crosshair button activates drop mode; next map click places a pin
- 5-mile radius circle drawn on map for each pin
- Sidebar shows only events within 5mi of any pin, sorted by distance
- Distance label (e.g. "1.2 mi") shown on each card
- Individual pin removal + clear all; + Add pin shortcut in pin strip
- Map shows blank + drop-prompt until at least one pin is placed
More event types:
- Hook now fetches WS/BZ/WW/IS (winter storm, blizzard, ice) from IEM
- SV without hailtag maps to wind; SV with hailtag maps to hail
- New icons: CloudHail, CloudSnow, CloudRain per type
- Type filter row added to filter bar: All/Hail/Tornado/Flood/Wind/Snow/Rain
Map now shows only the currently selected event + any pinned events.
If nothing is selected or pinned, all events are shown (default state).
- Pin button (MapPin icon) on each card — click to persist that event
on the map independently of selection changes
- Card click = select (amber border, opens drawer, flies to zone)
- Pinned cards get a subtle amber tint + filled pin icon
- Pinned events on map: tighter dash + slightly elevated opacity vs unselected
- List header shows "X pinned" count + "Clear pins" button
- Selection clear button still works independently of pins
IEM geojson/sbw.geojson has CORS open to all origins so the browser can
fetch it directly. Moves all transformation logic into useStormEvents.js,
eliminating the need for vercel dev or any server-side proxy. Works with
plain pnpm run dev on localhost. Falls back to MOCK_STORM_EVENTS if IEM
is unreachable. api/storm-polygons.js kept as optional edge cache layer.
api/storm-polygons.js — Vercel proxy that fetches historical NWS storm-based
warnings (severe thunderstorm + tornado) from IEM for WFO FWD (Fort Worth),
covering Collin County / Plano TX. Filters to local bounding box, derives
severity from polygon area (area2163 km²), estimates impacted homes from
density, derives area name from centroid. Cached 12h on Vercel edge.
useStormEvents.js — switched to real IEM data (USE_MOCK=false). Fetches full
36-month dataset once on mount; all range/type/severity filtering stays
client-side in useMemo for instant UI response. Falls back to
MOCK_STORM_EVENTS when /api routes are unavailable (local vite dev).
Updated all 7 MOCK_STORM_EVENTS polygons to use irregular, amoeba-like
swath outlines matching real hail storm paths from NWS reports. Also
renamed project to LynkedUpPro-CRM in package.json.
- Alert/warn forecast cards: day label, temp, and rain% now use high-contrast red/orange text instead of muted gray — readable in both light and dark mode
- Alert card background slightly more opaque (bg-red-100/dark:bg-red-500/15) for better definition
- Range chips: 12 Mo / 18 Mo / 24 Mo / 36 Mo replacing old 1m/3m/6m/1y options
- Custom range: CalendarRange chip reveals two date inputs (from/to) with amber focus ring
- useStormEvents: DATE_RANGE_DAYS updated for new keys; custom range filters by customStart/customEnd; allEvents now uses 36m baseline
- ZoneAssignmentModal: portal to document.body with z-9999 so it renders above Leaflet map stacking context
- ZoneLayer: clicking any zone polygon or centroid circle fits map bounds to that zone
- handleFlyTo: closes detail drawer after zooming so the map is unobstructed
- StormDetailDrawer: pl-5 on header, body, and footer for consistent left padding
- 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)
Adds an Attribution tab to Storm Intel showing full ROI breakdown of storm-sourced canvassing.
- MOCK_STORM_ATTRIBUTION_LEADS: 35 realistic leads across 7 storms, with addresses, statuses
(New / Contacted / Appointed / Closed) and contractValues for closed deals — $218,900 total revenue
- useStormAttribution hook: merges historical mock leads with any session-created storm leads,
aggregates per-storm stats (leads, closed, conversion rate, revenue, pipeline estimate, avg deal size)
- Attribution tab in StormIntelPage:
- 6 summary stat cards: Total Leads, Closed, Conversion Rate, Revenue, Pipeline, Avg Deal
- Grouped bar chart (Recharts) — leads generated vs closed per storm, bars colored by severity
- Per-storm revenue cards ranked by revenue: severity badge, stats row, animated revenue
progress bar relative to top performer
- "Top Performing Zone" insight card at bottom
- Tab switcher (Map / Attribution) added to header; filter bar only shows on Map tab
When a canvasser taps "Create Lead from Zone" in the Storm Intel detail drawer,
router state carries the storm context into CreateLeadPage:
- CreateLeadPage reads stormSource from location.state; initialises the form with
city=Plano, state=TX, leadSource=Door Knock, urgency=high for significant/severe
storms, and a pre-filled notes field with zone name, hail size, and date
- Amber storm banner renders below the page header showing zone name, hail size,
and a note that urgency was auto-set; dismissible with X
- stormSource is saved on the lead object via the existing addLead spread
- LeadsListPage LeadCard shows an amber "Storm Zone" badge when lead.stormSource exists
- KanbanCard footer shows a compact amber "Storm" label for storm-sourced leads
Adds a new Storm Intel section to the sidebar and routes for ADMIN and OWNER roles.
- StormIntelPage: full-height layout with Leaflet map + storm polygon overlays,
severity-coded color system, horizontal filter bar (date range / severity / sort),
and a scrollable storm event list
- Storm detail drawer slides in from right with hail size, est. homes, days ago,
conversion window indicator (Hot Zone / Warm / Cold / Too Fresh), intel text,
and quick-action buttons (Create Lead, Open in Dispatch, Assign Canvassers stub)
- Clicking a storm card or map polygon zooms the map to that polygon and opens the drawer
- useStormEvents hook with USE_MOCK flag, date-range cutoff filter, type/severity filter,
sort by date or severity, and conversionWindow() helper
- api/storm-events.js: NWS api.weather.gov proxy (free, no auth needed), parses
GeoJSON polygons and hail size from alert descriptions
- MOCK_STORM_EVENTS: 7 realistic Plano TX storm events with accurate polygon coords
- Sidebar: CloudLightning "Storm Intel" entry added for OWNER and ADMIN nav