Tank Alert Dual Threshold Design
Overview
Add a second alert threshold at 5,000L (Critical) alongside the existing 15,000L (Warning). Each threshold triggers an independent email notification with its own lifecycle.
Requirements
- Warning: tank < 15,000L → send warning email
- Critical: tank < 5,000L → send critical email
- Each level triggers independently (two separate emails at two different time points)
- Each level resolves independently (warning resolves at ≥ 15,000L, critical resolves at ≥ 5,000L)
- Same recipient list for both levels
- Each level only notifies once per alert cycle (no repeated emails until resolved and re-triggered)
- Email title and styling are the same for both levels; only the body text and threshold number differ
Database Changes
New migration on tank_level_alerts table:
sql
-- Add alert_level column
ALTER TABLE tank_level_alerts
ADD COLUMN alert_level text NOT NULL DEFAULT 'warning';
-- Backfill existing data
UPDATE tank_level_alerts SET alert_level = 'warning' WHERE alert_level = 'warning';
-- Drop old unique index (per asset only)
DROP INDEX idx_tank_alerts_unique_active;
-- Recreate unique index: per asset + per level
CREATE UNIQUE INDEX idx_tank_alerts_unique_active
ON tank_level_alerts (asset_id, alert_level)
WHERE resolved_at IS NULL;This allows one tank to have both a warning and critical active alert simultaneously.
Alert Detection (process-tank-alerts Edge Function)
Constants:
DEFAULT_THRESHOLD_LITRES = 15000(warning, unchanged)CRITICAL_THRESHOLD_LITRES = 5000(new)
Processing flow:
- Calculate all tank levels (unchanged)
- Pass 1: check < 15,000L → create
alert_level = 'warning'if no active warning exists - Pass 2: check < 5,000L → create
alert_level = 'critical'if no active critical exists - Resolve pass 1: ≥ 15,000L → resolve warning alerts
- Resolve pass 2: ≥ 5,000L → resolve critical alerts
activeAlertMap changes from Map<string, AlertRow> to Map<string, Map<string, AlertRow>> (outer key: asset_id, inner key: alert_level).
Request body optionally accepts critical_threshold_litres (default 5000).
Email Sending (send-email Edge Function)
fetchTankLevelAlertData changes:
- Query pending alerts for today, grouped by
alert_level - If warning pending alerts exist → generate warning email data
- If critical pending alerts exist → generate critical email data
- Send two separate emails if both levels have pending alerts
- Each email marks its own alerts as
notified = true
TankLevelAlertData type adds alert_level: 'warning' | 'critical'.
Email content differences:
- Warning body: "...falling below the minimum threshold level of 15,000 litres"
- Critical body: "...falling below the critical threshold level of 5,000 litres"
- Key Summary "Alert Threshold" card shows the corresponding threshold number
- All other structure, colors, layout remain identical
Client-Side Changes
Types (src/features/flow-meter/types.ts)
TankLevelAlertaddsalertLevel: 'warning' | 'critical'TankLevelAlertDataaddsalertLevel: 'warning' | 'critical'
tankAlertService.ts
- New constant
CRITICAL_THRESHOLD_LITRES = 5000 createAlert(assetId, scheduledSendDate?, alertLevel?)— writesalert_levelcolumnhasActiveAlert(assetId, alertLevel?)— optional level filterprocessAlerts()— two-pass detection (warning + critical)resolveRefilledTanks()— resolve by level (warning at ≥ 15,000L, critical at ≥ 5,000L)getActiveAlerts()— returns alerts withalertLevelfield populated
tankAlertService.ts (database row mapping)
TankLevelAlertRowaddsalert_level: stringmapAlertRowmapsalert_level→alertLevel
Tank Alert Management UI
- Alert list shows level badge (Warning / Critical) per row
Cron Script
No changes needed. Both thresholds are handled internally by the Edge Function.
Files to Modify
supabase/migrations/new— new migration foralert_levelcolumnsupabase/functions/process-tank-alerts/index.ts— dual threshold detectionsupabase/functions/send-email/index.ts— split emails by levelsrc/features/flow-meter/types.ts— type additionssrc/features/flow-meter/services/tankAlertService.ts— dual threshold logicsrc/features/tank-alerts/components/TankAlertManagement.tsx— level badge displaysrc/features/tank-alerts/types.ts— if separate types existsrc/features/flow-meter/services/tankAlertService.test.ts— update tests