({ organizationId }: BillingAlertsProps)
| 33 | } |
| 34 | |
| 35 | export function BillingAlerts({ organizationId }: BillingAlertsProps) { |
| 36 | const [alerts, setAlerts] = useState<BillingAlert[]>([]) |
| 37 | const [loading, setLoading] = useState(true) |
| 38 | |
| 39 | useEffect(() => { |
| 40 | fetchAlerts() |
| 41 | |
| 42 | // Poll for new alerts every 30 seconds |
| 43 | const interval = setInterval(fetchAlerts, 30000) |
| 44 | return () => clearInterval(interval) |
| 45 | }, [organizationId]) |
| 46 | |
| 47 | const fetchAlerts = async () => { |
| 48 | try { |
| 49 | const response = await fetch(`/api/orgs/${organizationId}/alerts`) |
| 50 | |
| 51 | if (response.ok) { |
| 52 | const data = await response.json() |
| 53 | setAlerts(data.alerts || []) |
| 54 | } |
| 55 | } catch (error) { |
| 56 | console.error('Error fetching billing alerts:', error) |
| 57 | } finally { |
| 58 | setLoading(false) |
| 59 | } |
| 60 | } |
| 61 | |
| 62 | const dismissAlert = async (alertId: string) => { |
| 63 | try { |
| 64 | await fetch(`/api/orgs/${organizationId}/alerts/${alertId}/dismiss`, { |
| 65 | method: 'POST', |
| 66 | }) |
| 67 | |
| 68 | setAlerts(alerts.filter((alert) => alert.id !== alertId)) |
| 69 | } catch (error) { |
| 70 | console.error('Error dismissing alert:', error) |
| 71 | } |
| 72 | } |
| 73 | |
| 74 | const getAlertIcon = (type: string, severity: string) => { |
| 75 | if (severity === 'critical') { |
| 76 | return <AlertTriangle className="h-4 w-4 text-red-600" /> |
| 77 | } else if (severity === 'warning') { |
| 78 | return <AlertTriangle className="h-4 w-4 text-yellow-600" /> |
| 79 | } else if (type === 'high_usage') { |
| 80 | return <TrendingUp className="h-4 w-4 text-blue-600" /> |
| 81 | } else if (type === 'auto_topup_failed') { |
| 82 | return <CreditCard className="h-4 w-4 text-red-600" /> |
| 83 | } |
| 84 | return <CheckCircle className="h-4 w-4 text-green-600" /> |
| 85 | } |
| 86 | |
| 87 | const getAlertColor = (severity: string) => { |
| 88 | switch (severity) { |
| 89 | case 'critical': |
| 90 | return 'destructive' |
| 91 | case 'warning': |
| 92 | return 'secondary' |
nothing calls this directly
no test coverage detected