"use client"; /** * ModelAvailabilityBadge — compact inline status indicator * * Shows green when all models are operational, or amber/red when there are * issues, with a hover popover for details and cooldown clearing. */ import { useState, useEffect, useCallback, useRef } from "react"; import { Button } from "@/shared/components"; import { useNotificationStore } from "@/store/notificationStore"; const STATUS_CONFIG = { available: { icon: "check_circle", color: "#22c55e", label: "Available" }, cooldown: { icon: "schedule", color: "#f59e0b", label: "Cooldown" }, unavailable: { icon: "error", color: "#ef4444", label: "Unavailable" }, unknown: { icon: "help", color: "#6b7280", label: "Unknown" }, }; export default function ModelAvailabilityBadge() { const [data, setData] = useState(null); const [loading, setLoading] = useState(true); const [expanded, setExpanded] = useState(false); const [clearing, setClearing] = useState(null); const ref = useRef(null); const notify = useNotificationStore(); const fetchStatus = useCallback(async () => { try { const res = await fetch("/api/models/availability"); if (res.ok) { const json = await res.json(); setData(json); } } catch { // silent fail — will retry } finally { setLoading(false); } }, []); useEffect(() => { fetchStatus(); const interval = setInterval(fetchStatus, 30000); return () => clearInterval(interval); }, [fetchStatus]); // Close popover on outside click useEffect(() => { const handleClick = (e) => { if (ref.current && !ref.current.contains(e.target)) setExpanded(false); }; if (expanded) document.addEventListener("mousedown", handleClick); return () => document.removeEventListener("mousedown", handleClick); }, [expanded]); const handleClearCooldown = async (provider, model) => { setClearing(`${provider}:${model}`); try { const res = await fetch("/api/models/availability", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ action: "clearCooldown", provider, model }), }); if (res.ok) { notify.success(`Cooldown cleared for ${model}`); await fetchStatus(); } else { notify.error("Failed to clear cooldown"); } } catch { notify.error("Failed to clear cooldown"); } finally { setClearing(null); } }; if (loading) return null; const models = data?.models || []; const unavailableCount = data?.unavailableCount || models.filter((m) => m.status !== "available").length; const isHealthy = unavailableCount === 0; // Group unhealthy models by provider const byProvider = {}; models.forEach((m) => { if (m.status === "available") return; const key = m.provider || "unknown"; if (!byProvider[key]) byProvider[key] = []; byProvider[key].push(m); }); return (
All models are responding normally.
) : ({provider}