diff --git a/src/app/call/[id].tsx b/src/app/call/[id].tsx index 172bb3d1..0bcc6b2f 100644 --- a/src/app/call/[id].tsx +++ b/src/app/call/[id].tsx @@ -657,7 +657,7 @@ export default function CallDetail() { {/* Tabs */} - + setIsNotesModalOpen(false)} callId={callId} /> diff --git a/src/app/weather-alert/[id].tsx b/src/app/weather-alert/[id].tsx index b511780f..e368792c 100644 --- a/src/app/weather-alert/[id].tsx +++ b/src/app/weather-alert/[id].tsx @@ -37,7 +37,7 @@ export default function WeatherAlertDetail() { }, [alertId, fetchAlertDetail]); const severityColor = useMemo(() => (alert ? getSeverityColor(alert.Severity) : '#9E9E9E'), [alert]); - const CategoryIcon = useMemo(() => (alert ? getCategoryIcon(alert.Category) : null), [alert]); + const CategoryIcon = useMemo(() => (alert ? getCategoryIcon(alert.AlertCategory) : null), [alert]); const formatDate = (dateStr: string) => { if (!dateStr) return t('call_detail.not_available'); @@ -90,19 +90,19 @@ export default function WeatherAlertDetail() { {alert.EffectiveUtc ? ( {t('weather_alerts.detail.effective')} - {formatDate(alert.EffectiveUtc)} + {formatDate(alert.EffectiveOnUtc || alert.EffectiveUtc)} ) : null} {alert.OnsetUtc ? ( {t('weather_alerts.detail.onset')} - {formatDate(alert.OnsetUtc)} + {formatDate(alert.OnsetOnUtc || alert.OnsetUtc)} ) : null} {alert.ExpiresUtc ? ( {t('weather_alerts.detail.expires')} - {formatDate(alert.ExpiresUtc)} + {formatDate(alert.ExpiresOnUtc || alert.ExpiresUtc)} ) : null} @@ -125,10 +125,10 @@ export default function WeatherAlertDetail() { ) : null} {/* Instructions */} - {alert.Instructions ? ( + {alert.Instruction ? ( {t('weather_alerts.detail.instructions')} - {alert.Instructions} + {alert.Instruction} ) : null} @@ -143,10 +143,10 @@ export default function WeatherAlertDetail() { {/* Metadata */} - {alert.SenderName ? ( + {alert.Sender ? ( {t('weather_alerts.detail.sender')} - {alert.SenderName} + {alert.Sender} ) : null} diff --git a/src/components/weather-alerts/__tests__/weather-alert-banner.test.tsx b/src/components/weather-alerts/__tests__/weather-alert-banner.test.tsx index ee35bc66..d54a4a39 100644 --- a/src/components/weather-alerts/__tests__/weather-alert-banner.test.tsx +++ b/src/components/weather-alerts/__tests__/weather-alert-banner.test.tsx @@ -19,27 +19,27 @@ const createMockAlert = (overrides = {}) => ({ Event: 'Tornado Warning', Headline: 'Tornado Warning for County', Description: '', - Instructions: '', + Instruction: '', Severity: 0, - Category: 0, + AlertCategory: 0, Urgency: 0, Certainty: 0, Status: 0, - SourceType: 0, - SourceAlertId: '', - SenderName: '', + Sender: '', AreaDescription: '', Polygon: '', CenterGeoLocation: '', EffectiveUtc: '', OnsetUtc: '', ExpiresUtc: '', - Ends: '', - ReceivedOnUtc: '', - UpdatedOnUtc: '', - WebUrl: '', - ZoneCode: '', - MessageType: '', + WeatherAlertSourceId: '', + ExternalId: '', + Geocodes: '', + SentUtc: '', + FirstSeenUtc: '', + LastUpdatedUtc: '', + ReferencesExternalId: '', + NotificationSent: false, ...overrides, }); diff --git a/src/components/weather-alerts/__tests__/weather-alert-card.test.tsx b/src/components/weather-alerts/__tests__/weather-alert-card.test.tsx index 8a543b3c..34988450 100644 --- a/src/components/weather-alerts/__tests__/weather-alert-card.test.tsx +++ b/src/components/weather-alerts/__tests__/weather-alert-card.test.tsx @@ -29,27 +29,27 @@ const createMockAlert = (overrides = {}) => ({ Event: 'Tornado Warning', Headline: 'Tornado Warning for County', Description: 'A tornado has been spotted.', - Instructions: 'Take shelter.', + Instruction: 'Take shelter.', Severity: 1, - Category: 0, + AlertCategory: 0, Urgency: 0, Certainty: 0, Status: 0, - SourceType: 0, - SourceAlertId: '', - SenderName: 'NWS', + Sender: 'NWS', AreaDescription: 'County A', Polygon: '', CenterGeoLocation: '', EffectiveUtc: '2026-04-15T10:00:00Z', OnsetUtc: '', ExpiresUtc: '2026-04-15T14:00:00Z', - Ends: '', - ReceivedOnUtc: '', - UpdatedOnUtc: '', - WebUrl: '', - ZoneCode: '', - MessageType: '', + WeatherAlertSourceId: '', + ExternalId: '', + Geocodes: '', + SentUtc: '', + FirstSeenUtc: '', + LastUpdatedUtc: '', + ReferencesExternalId: '', + NotificationSent: false, ...overrides, }); diff --git a/src/components/weather-alerts/weather-alert-card.tsx b/src/components/weather-alerts/weather-alert-card.tsx index bca69cb4..6d083e40 100644 --- a/src/components/weather-alerts/weather-alert-card.tsx +++ b/src/components/weather-alerts/weather-alert-card.tsx @@ -17,9 +17,11 @@ interface WeatherAlertCardProps { const WeatherAlertCardComponent: React.FC = ({ alert }) => { const { t } = useTranslation(); const severityColor = getSeverityColor(alert.Severity); - const CategoryIcon = getCategoryIcon(alert.Category); - const effectiveDate = parseWeatherAlertDate(alert.EffectiveUtc); - const expiresDate = parseWeatherAlertDate(alert.ExpiresUtc); + const CategoryIcon = getCategoryIcon(alert.AlertCategory); + // Prefer the real UTC instants; the *Utc strings are department-local display values that + // read hours off when treated as timestamps. Older servers only send the display strings. + const effectiveDate = parseWeatherAlertDate(alert.EffectiveOnUtc || alert.EffectiveUtc); + const expiresDate = parseWeatherAlertDate(alert.ExpiresOnUtc || alert.ExpiresUtc); return ( diff --git a/src/lib/weather-alert-utils.ts b/src/lib/weather-alert-utils.ts index 9ebdc0ed..86333806 100644 --- a/src/lib/weather-alert-utils.ts +++ b/src/lib/weather-alert-utils.ts @@ -200,16 +200,16 @@ export const parseWeatherAlertDate = (dateStr: string): Date | null => { export const sortAlertsBySeverity = (alerts: WeatherAlertResultData[]): WeatherAlertResultData[] => { return [...alerts].sort((a, b) => { if (a.Severity !== b.Severity) return a.Severity - b.Severity; - const bEffectiveTime = parseWeatherAlertDate(b.EffectiveUtc)?.getTime() ?? 0; - const aEffectiveTime = parseWeatherAlertDate(a.EffectiveUtc)?.getTime() ?? 0; + const bEffectiveTime = parseWeatherAlertDate(b.EffectiveOnUtc || b.EffectiveUtc)?.getTime() ?? 0; + const aEffectiveTime = parseWeatherAlertDate(a.EffectiveOnUtc || a.EffectiveUtc)?.getTime() ?? 0; return bEffectiveTime - aEffectiveTime; }); }; export const isAlertActive = (alert: WeatherAlertResultData): boolean => { if (alert.Status !== WeatherAlertStatus.Active) return false; - if (alert.ExpiresUtc) { - const expiration = parseWeatherAlertDate(alert.ExpiresUtc); + if (alert.ExpiresOnUtc || alert.ExpiresUtc) { + const expiration = parseWeatherAlertDate(alert.ExpiresOnUtc || alert.ExpiresUtc); return expiration ? expiration.getTime() > Date.now() : true; } return true; diff --git a/src/models/v4/weatherAlerts/weatherAlertResultData.ts b/src/models/v4/weatherAlerts/weatherAlertResultData.ts index 4aa0596a..b943e093 100644 --- a/src/models/v4/weatherAlerts/weatherAlertResultData.ts +++ b/src/models/v4/weatherAlerts/weatherAlertResultData.ts @@ -1,28 +1,37 @@ +// Mirrors Resgrid Core's v4 WeatherAlertResultData (Web/Resgrid.Web.Services/Models/v4/WeatherAlerts). +// Enum-valued fields are ints (see WeatherAlertSeverity/Category/Urgency/Certainty/Status in Core). export class WeatherAlertResultData { public WeatherAlertId: string = ''; public DepartmentId: number = 0; + public WeatherAlertSourceId: string = ''; + public ExternalId: string = ''; + public Sender: string = ''; public Event: string = ''; + public AlertCategory: number = 4; // Other + public Severity: number = 4; // Unknown + public Urgency: number = 4; // Unknown + public Certainty: number = 4; // Unknown + public Status: number = 0; // Active public Headline: string = ''; public Description: string = ''; - public Instructions: string = ''; - public Severity: number = 4; - public Category: number = 4; - public Urgency: number = 4; - public Certainty: number = 4; - public Status: number = 0; - public SourceType: number = 0; - public SourceAlertId: string = ''; - public SenderName: string = ''; + public Instruction: string = ''; public AreaDescription: string = ''; public Polygon: string = ''; + public Geocodes: string = ''; public CenterGeoLocation: string = ''; - public EffectiveUtc: string = ''; + // Department-local display strings (despite the Utc names) — render verbatim only. public OnsetUtc: string = ''; public ExpiresUtc: string = ''; - public Ends: string = ''; - public ReceivedOnUtc: string = ''; - public UpdatedOnUtc: string = ''; - public WebUrl: string = ''; - public ZoneCode: string = ''; - public MessageType: string = ''; + public EffectiveUtc: string = ''; + public SentUtc: string = ''; + public FirstSeenUtc: string = ''; + public LastUpdatedUtc: string = ''; + // Real UTC instants with explicit "Z" — use these for any date math. Empty on older servers. + public EffectiveOnUtc?: string = ''; + public OnsetOnUtc?: string = ''; + public ExpiresOnUtc?: string = ''; + public SentOnUtc?: string = ''; + public ReferencesExternalId: string = ''; + public NotificationSent: boolean = false; + public SystemMessageId?: number; } diff --git a/src/stores/weather-alerts/__tests__/store.test.ts b/src/stores/weather-alerts/__tests__/store.test.ts index c5705e82..3e92081b 100644 --- a/src/stores/weather-alerts/__tests__/store.test.ts +++ b/src/stores/weather-alerts/__tests__/store.test.ts @@ -13,27 +13,27 @@ const createMockAlert = (overrides = {}) => ({ Event: 'Tornado Warning', Headline: 'Tornado Warning for County', Description: 'A tornado has been spotted.', - Instructions: 'Take shelter immediately.', + Instruction: 'Take shelter immediately.', Severity: 0, - Category: 0, + AlertCategory: 0, Urgency: 0, Certainty: 0, Status: 0, - SourceType: 0, - SourceAlertId: 'nws-1', - SenderName: 'NWS', + Sender: 'NWS', AreaDescription: 'County A', Polygon: '', CenterGeoLocation: '35.0,-97.0', EffectiveUtc: '2026-04-15T10:00:00Z', OnsetUtc: '2026-04-15T10:00:00Z', ExpiresUtc: '2026-04-15T14:00:00Z', - Ends: '', - ReceivedOnUtc: '2026-04-15T10:00:00Z', - UpdatedOnUtc: '', - WebUrl: '', - ZoneCode: 'OKC001', - MessageType: 'Alert', + WeatherAlertSourceId: '', + ExternalId: '', + Geocodes: '', + SentUtc: '', + FirstSeenUtc: '', + LastUpdatedUtc: '', + ReferencesExternalId: '', + NotificationSent: false, ...overrides, });