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.
+
+ )}
+
+
+ );
+}