// DEPLOY_CONFIG: {"triggers": [{"name": "critical-alert-area-notify", "on": "collection.add", "collection": "critical_alerts", "actions": [{"type": "email", "to": "{{user.email}}", "subject": "Critical alert in your area", "body": "A new critical alert has affected your area. Please check the app for details."}, {"type": "in_app_notification", "to": "{{user.id}}", "title": "Critical alert in your area", "body": "A new critical alert has affected your area."}]}], "collections": [{"name": "alert_history", "permissions": {"read": "authenticated", "write": "server"}}]} import { useEffect, useMemo, useState } from 'react'; import { useCollection, renderChart } from '@deplixo/sdk'; import { WeatherMap } from './components/WeatherMap.jsx'; import { AlertTimeline } from './components/AlertTimeline.jsx'; import { AlertDetail } from './components/AlertDetail.jsx'; function getShareParamsFromLocation() { if (typeof window === 'undefined') return {}; const params = new URLSearchParams(window.location.search); const lat = params.get('lat'); const lng = params.get('lng'); const zoom = params.get('zoom'); const severity = params.get('severity'); const alertId = params.get('alertId'); const tab = params.get('tab'); return { lat: lat !== null ? Number(lat) : null, lng: lng !== null ? Number(lng) : null, zoom: zoom !== null ? Number(zoom) : null, severity: severity || '', alertId: alertId || '', tab: tab === 'timeline' ? 'timeline' : 'map', }; } function buildShareUrl(viewState) { if (typeof window === 'undefined') return ''; const url = new URL(window.location.href); const params = url.searchParams; params.set('tab', viewState.activeTab || 'map'); if (typeof viewState.mapCenter?.lat === 'number' && Number.isFinite(viewState.mapCenter.lat)) { params.set('lat', String(viewState.mapCenter.lat)); } else { params.delete('lat'); } if (typeof viewState.mapCenter?.lng === 'number' && Number.isFinite(viewState.mapCenter.lng)) { params.set('lng', String(viewState.mapCenter.lng)); } else { params.delete('lng'); } if (typeof viewState.mapZoom === 'number' && Number.isFinite(viewState.mapZoom)) { params.set('zoom', String(viewState.mapZoom)); } else { params.delete('zoom'); } if (viewState.alertFilterSeverity) { params.set('severity', viewState.alertFilterSeverity); } else { params.delete('severity'); } if (viewState.selectedAlert?.id) { params.set('alertId', viewState.selectedAlert.id); } else { params.delete('alertId'); } return url.toString(); } function normalizeAlertHistoryEvent(alert) { const issuedAt = alert?.sent || alert?.onset || new Date().toISOString(); const effectiveAt = alert?.onset || alert?.sent || issuedAt; const expiresAt = alert?.ends || alert?.expires || null; const severity = (alert?.severity || alert?.properties?.severity || 'Unknown').toString(); const type = (alert?.event || alert?.properties?.event || 'Alert').toString(); const area = (alert?.areaDesc || alert?.properties?.areaDesc || alert?.area || 'Unknown area').toString(); return { alertId: alert?.id || alert?.properties?.id || `${type}-${issuedAt}-${area}`, type, severity, affectedArea: area, issuedAt, effectiveAt, expiresAt, updatedAt: alert?.updated || alert?.properties?.updated || issuedAt, source: 'weather.gov', }; } function getHistoryTimestamp(item) { const raw = item?.issuedAt || item?.effectiveAt || item?.updatedAt || item?.sent || item?.createdAt || item?.timestamp; const time = raw ? new Date(raw).getTime() : NaN; return Number.isFinite(time) ? time : 0; } function getPastMonthChartData(historyItems) { const now = Date.now(); const monthAgo = now - 31 * 24 * 60 * 60 * 1000; const recentItems = (Array.isArray(historyItems) ? historyItems : []).filter(item => getHistoryTimestamp(item) >= monthAgo); const typeCounts = recentItems.reduce((acc, item) => { const label = (item?.type || item?.event || 'Alert').toString(); acc[label] = (acc[label] || 0) + 1; return acc; }, {}); const labels = Object.keys(typeCounts) .sort((a, b) => typeCounts[b] - typeCounts[a]) .slice(0, 8); return { labels, values: labels.map(label => typeCounts[label] || 0), total: recentItems.length, }; } function AlertFrequencyChart({ historyItems, loading }) { const canvasRef = useMemo(() => ({ current: null }), []); const chartData = useMemo(() => getPastMonthChartData(historyItems), [historyItems]); useEffect(() => { if (!canvasRef.current || loading || chartData.labels.length === 0) return; renderChart(canvasRef.current, { type: 'bar', data: { labels: chartData.labels, datasets: [ { label: 'Alerts in past 31 days', data: chartData.values, }, ], }, options: { responsive: true, maintainAspectRatio: false, }, }); }, [canvasRef, loading, chartData]); return (
Stored alert history from the past month