diff --git a/package.json b/package.json index a6f6f43..cf5b050 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev", + "dev": "NEXT_PUBLIC_API_URL=http://localhost:3001 next dev", "build": "next build", "start": "next start", "lint": "next lint" diff --git a/src/components/dashboard/dashboard-client.jsx b/src/components/dashboard/dashboard-client.jsx index 51d3733..c095e0c 100644 --- a/src/components/dashboard/dashboard-client.jsx +++ b/src/components/dashboard/dashboard-client.jsx @@ -6,7 +6,7 @@ import { Button } from '@/components/ui/button'; import { Skeleton } from '@/components/ui/skeleton'; import ExtensionDetails from './extension-details'; import ApiKeys from './api-keys'; -import CallHistory from './call-history'; +import { RegisteredDevices } from './registered-devices'; import { LogOut, User, Phone, Voicemail } from 'lucide-react'; import { ActiveCalls } from './active-calls'; import { SipCredentialsCard } from './SipCredentialsCard'; @@ -227,7 +227,7 @@ export default function DashboardClient() { {/* Main Content */}
- +
diff --git a/src/components/dashboard/registered-devices.jsx b/src/components/dashboard/registered-devices.jsx new file mode 100644 index 0000000..7f78713 --- /dev/null +++ b/src/components/dashboard/registered-devices.jsx @@ -0,0 +1,114 @@ +"use client"; + +import { useState, useEffect, useCallback } from 'react'; +import { useAuth } from '@/contexts/AuthContext'; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Skeleton } from '@/components/ui/skeleton'; +import { RefreshCw, Smartphone, Server, Wifi } from 'lucide-react'; +import { TbPlugConnectedX } from 'react-icons/tb'; + +function DeviceCard({ device }) { + const ping = parseFloat(device.pingMs); + let pingColor = 'text-green-400'; + if (ping > 150) pingColor = 'text-yellow-400'; + if (ping > 300) pingColor = 'text-red-400'; + + return ( +
+
+ + {device.useragent} +
+
+
+ + {device.ip}:{device.port} +
+
+ + {ping.toFixed(2)}ms +
+
+
+ ); +} + +export function RegisteredDevices() { + const { token } = useAuth(); + const [devices, setDevices] = useState([]); + const [loading, setLoading] = useState(true); + const [isInitialLoad, setIsInitialLoad] = useState(true); + + const fetchDevices = useCallback(async (isManualRefresh = false) => { + if (!token) return; + if (isManualRefresh || isInitialLoad) { + setLoading(true); + } + try { + const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001'}/extensions/me/endpoint`, { + headers: { 'Authorization': `Bearer ${token}` } + }); + if (res.ok) { + const data = await res.json(); + setDevices(Array.isArray(data) ? data : []); + } else { + setDevices([]); + } + } catch (error) { + console.error("Failed to fetch registered devices", error); + setDevices([]); + } finally { + if (isInitialLoad || isManualRefresh) { + setLoading(false); + } + if (isInitialLoad) { + setIsInitialLoad(false); + } + } + }, [token, isInitialLoad]); + + useEffect(() => { + if (token) { + fetchDevices(); + const interval = setInterval(() => fetchDevices(false), 10000); // Refresh every 10 seconds + return () => clearInterval(interval); + } else { + setLoading(false); + } + }, [token, fetchDevices]); + + return ( + + +
+ Registered Devices + A list of your currently connected devices. +
+ +
+ + {loading ? ( +
+ + +
+ ) : devices.length > 0 ? ( +
+ {devices.map(device => ( + + ))} +
+ ) : ( +
+ +

No Registered Devices

+

When you connect a SIP client, it will appear here.

+
+ )} +
+
+ ); +}