fix: extract data.devices in UnifiWidget to prevent e.filter crash

API returns {devices, health, totalClients} object, not an array.
setDevices(data) was setting the whole object, causing .filter() to crash.
This commit is contained in:
2026-02-17 08:45:30 -05:00
parent 152ff4a3ad
commit 19134924bd

View File

@@ -1,75 +1,77 @@
"use client"; "use client";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Wifi, WifiOff } from "lucide-react"; import { Wifi, WifiOff } from "lucide-react";
export default function UnifiWidget() { export default function UnifiWidget() {
const [devices, setDevices] = useState<any[]>([]); const [devices, setDevices] = useState<any[]>([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [error, setError] = useState(false); const [error, setError] = useState(false);
useEffect(() => { useEffect(() => {
fetchDevices(); fetchDevices();
const interval = setInterval(fetchDevices, 30000); const interval = setInterval(fetchDevices, 30000);
return () => clearInterval(interval); return () => clearInterval(interval);
}, []); }, []);
const fetchDevices = async () => { const fetchDevices = async () => {
try { try {
const response = await fetch("/api/unifi"); const response = await fetch("/api/unifi");
if (response.ok) { if (response.ok) {
const data = await response.json(); const data = await response.json();
setDevices(data); // API returns { devices, health, totalClients } — extract the array
setError(false); const devArr = Array.isArray(data) ? data : Array.isArray(data?.devices) ? data.devices : [];
} else { setDevices(devArr);
setError(true); setError(false);
} } else {
setLoading(false); setError(true);
} catch (err) { }
setError(true); setLoading(false);
setLoading(false); } catch (err) {
} setError(true);
}; setLoading(false);
}
const onlineDevices = devices.filter((d) => d.state === 1).length; };
const totalClients = devices.reduce((sum, d) => sum + (d.clients || 0), 0);
const onlineDevices = devices.filter((d) => d.state === 1).length;
return ( const totalClients = devices.reduce((sum, d) => sum + (d.clients || 0), 0);
<div className="bg-gray-800/40 backdrop-blur-sm rounded-lg border border-gray-700 p-6">
<div className="flex items-center justify-between mb-4"> return (
<h3 className="text-lg font-semibold text-white flex items-center gap-2"> <div className="bg-gray-800/40 backdrop-blur-sm rounded-lg border border-gray-700 p-6">
<Wifi className="w-5 h-5 text-blue-500" /> <div className="flex items-center justify-between mb-4">
UniFi Network <h3 className="text-lg font-semibold text-white flex items-center gap-2">
</h3> <Wifi className="w-5 h-5 text-blue-500" />
</div> UniFi Network
</h3>
{loading ? ( </div>
<div className="flex justify-center py-8">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div> {loading ? (
</div> <div className="flex justify-center py-8">
) : error ? ( <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-500"></div>
<div className="text-center py-8"> </div>
<WifiOff className="w-12 h-12 text-gray-600 mx-auto mb-2" /> ) : error ? (
<p className="text-sm text-gray-400"> <div className="text-center py-8">
Configure UniFi credentials in .env <WifiOff className="w-12 h-12 text-gray-600 mx-auto mb-2" />
</p> <p className="text-sm text-gray-400">
</div> Configure UniFi credentials in .env
) : ( </p>
<div className="space-y-4"> </div>
<div className="grid grid-cols-2 gap-4"> ) : (
<div className="bg-gray-900/50 rounded-lg p-3"> <div className="space-y-4">
<p className="text-xs text-gray-400">Devices Online</p> <div className="grid grid-cols-2 gap-4">
<p className="text-2xl font-bold text-green-500"> <div className="bg-gray-900/50 rounded-lg p-3">
{onlineDevices}/{devices.length} <p className="text-xs text-gray-400">Devices Online</p>
</p> <p className="text-2xl font-bold text-green-500">
</div> {onlineDevices}/{devices.length}
<div className="bg-gray-900/50 rounded-lg p-3"> </p>
<p className="text-xs text-gray-400">Connected Clients</p> </div>
<p className="text-2xl font-bold text-blue-500">{totalClients}</p> <div className="bg-gray-900/50 rounded-lg p-3">
</div> <p className="text-xs text-gray-400">Connected Clients</p>
</div> <p className="text-2xl font-bold text-blue-500">{totalClients}</p>
</div> </div>
)} </div>
</div> </div>
); )}
} </div>
);
}