feat(hail-recon): Hail Recon integration — storm intel panel, hail history, auto storm mode, address monitoring

This commit is contained in:
Satyam-Rastogi
2026-05-18 15:50:59 +05:30
parent 912967fa3a
commit 9e2173fd43
13 changed files with 893 additions and 3 deletions
+97 -1
View File
@@ -3,6 +3,7 @@ import { useParams, useNavigate } from 'react-router-dom';
import { motion } from 'framer-motion';
import { useAuth } from '../context/AuthContext';
import { useMockStore } from '../data/mockStore';
import { useHailHistory, hailSeverityColor, daysAgo } from '../hooks/useHailRecon';
import { SpotlightCard } from '../components/SpotlightCard';
import { AnimatedCounter } from '../components/AnimatedCounter';
import { toast } from 'sonner';
@@ -15,7 +16,7 @@ import {
MessageSquare, CheckCircle, ChevronDown, Activity, AlertTriangle,
TrendingUp, Calendar, DollarSign, GitPullRequest, AlertCircle,
Milestone, FolderOpen, Upload, Trash2, Eye, Pencil, X as XIcon,
File, StickyNote, Download, Image as ImageIcon
File, StickyNote, Download, Image as ImageIcon, CloudLightning
} from 'lucide-react';
// ── Neomorphic constants (mirrors OwnerProjectDetail) ─────────────────────────
@@ -209,6 +210,89 @@ const TABS_SIMPLE = [
];
// ── Main component ────────────────────────────────────────────────────────────
// ── Hail History Section ──────────────────────────────────────────────────────
function HailHistorySection({ leadId, lat, lng }) {
const { history, loading, hitCount, lastHit, maxSize } = useHailHistory(leadId, lat, lng, 12);
const summaryColor = maxSize >= 2.0 ? 'text-red-600 dark:text-red-400' :
maxSize >= 1.5 ? 'text-orange-600 dark:text-orange-400' :
maxSize >= 1.0 ? 'text-yellow-600 dark:text-yellow-400' :
'text-zinc-400';
return (
<div className="rounded-2xl border border-zinc-200 dark:border-white/[0.06] bg-white dark:bg-zinc-900/40 overflow-hidden">
{/* Header */}
<div className="flex items-center gap-3 px-5 py-4 border-b border-zinc-100 dark:border-white/[0.04]">
<div className="p-2 rounded-xl bg-amber-500/10 border border-amber-500/20">
<CloudLightning size={16} className="text-amber-500" />
</div>
<div className="flex-1">
<h3 className="text-sm font-black uppercase tracking-widest text-zinc-900 dark:text-white">Hail Impact History</h3>
<p className="text-[10px] text-zinc-500 mt-0.5">Last 12 months · Powered by Hail Recon</p>
</div>
{!loading && hitCount > 0 && (
<div className="text-right">
<p className={`text-lg font-black font-mono ${summaryColor}`}>{hitCount}x</p>
<p className="text-[10px] text-zinc-400">
{lastHit ? `Last: ${daysAgo(lastHit.date)}d ago` : ''}
</p>
</div>
)}
</div>
{/* Body */}
<div className="px-5 py-4">
{loading ? (
<div className="flex items-center gap-2 py-4">
<div className="w-4 h-4 rounded-full border-2 border-amber-500 border-t-transparent animate-spin" />
<p className="text-xs text-zinc-400">Checking hail history...</p>
</div>
) : hitCount === 0 ? (
<div className="flex items-center gap-2 py-2">
<Shield size={14} className="text-emerald-500 shrink-0" />
<p className="text-sm text-zinc-500">No hail impacts recorded in the last 12 months.</p>
</div>
) : (
<div className="space-y-2">
{history.map((event, i) => {
const textCls = hailSeverityColor(event.hailSize, 'text');
const bgCls = hailSeverityColor(event.hailSize, 'bg');
const brdCls = hailSeverityColor(event.hailSize, 'border');
const days = daysAgo(event.date);
const dateObj = new Date(event.date);
const dateLabel = dateObj.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
return (
<div key={i} className="flex items-center gap-3 py-2.5 border-b border-zinc-100 dark:border-white/[0.04] last:border-0">
{/* Date */}
<div className="w-28 shrink-0">
<p className="text-xs font-semibold text-zinc-700 dark:text-zinc-200">{dateLabel}</p>
<p className="text-[10px] text-zinc-400">{days === 0 ? 'Today' : `${days}d ago`}</p>
</div>
{/* Size badge */}
<span className={`inline-flex items-center px-2 py-0.5 rounded-lg text-xs font-black border ${bgCls} ${brdCls} ${textCls}`}>
{event.hailSize?.toFixed(2)}"
</span>
{/* Severity label */}
<span className={`text-[11px] font-semibold ${textCls}`}>
{event.hailSize >= 2.0 ? 'Severe' :
event.hailSize >= 1.5 ? 'Significant' :
event.hailSize >= 1.0 ? 'Moderate' : 'Trace'}
</span>
{i === 0 && (
<span className="ml-auto text-[10px] font-bold px-2 py-0.5 rounded-full bg-amber-100 dark:bg-amber-500/15 text-amber-600 dark:text-amber-400 border border-amber-200 dark:border-amber-500/20">
Most Recent
</span>
)}
</div>
);
})}
</div>
)}
</div>
</div>
);
}
export default function LeadProjectPage() {
const { leadId } = useParams();
const navigate = useNavigate();
@@ -468,11 +552,17 @@ export default function LeadProjectPage() {
)}
</NeoCard>
</div>
<HailHistorySection
leadId={leadId}
lat={lead.lat ?? 33.055}
lng={lead.lng ?? -96.752}
/>
</div>
)}
{/* Overview for simple (no project data) */}
{activeTab === 'overview' && !proj && (
<div className="flex flex-col gap-6">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
<NeoCard spotlightColor="rgba(59,130,246,0.08)">
<div className="flex items-center gap-3 mb-5">
@@ -528,6 +618,12 @@ export default function LeadProjectPage() {
</div>
</NeoCard>
</div>
<HailHistorySection
leadId={leadId}
lat={lead.lat ?? 33.055}
lng={lead.lng ?? -96.752}
/>
</div>
)}
{/* ── CONTACT (simple only) ── */}