update
This commit is contained in:
1 parent
da8fb57bfb
commit
bba75c7dc6
35 files changed
+92592
-500
No files matched your search
+69
-18
@@ -2,14 +2,16 @@
|
||||
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/Card";
|
||||
import { DataTable, Column } from "@/components/ui/DataTable";
|
||||
import { DeviceDetailModal } from "@/components/ui/DeviceDetailModal";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useDevices, DeviceStat } from "@/lib/api";
|
||||
import { fmtBytes } from "@/lib/utils";
|
||||
import { Loader2 } from "lucide-react";
|
||||
import { Loader2, ChevronRight } from "lucide-react";
|
||||
|
||||
export default function DevicesPage() {
|
||||
const [mounted, setMounted] = useState(false);
|
||||
const devices = useDevices(100);
|
||||
const [selectedDevice, setSelectedDevice] = useState<DeviceStat | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
setMounted(true);
|
||||
@@ -19,13 +21,45 @@ export default function DevicesPage() {
|
||||
|
||||
const columns: Column<DeviceStat>[] = [
|
||||
{ header: "#", accessor: (row, i) => i + 1 },
|
||||
{ header: "IP Address", accessor: (row) => <span className="font-medium text-blue-400">{row.ip_address || '-'}</span> },
|
||||
{ header: "MAC Address", accessor: (row) => row.mac_address ? <code className="text-xs bg-secondary/50 px-1 py-0.5 rounded text-muted-foreground">{row.mac_address}</code> : '-' },
|
||||
{ header: "Type", accessor: (row) => row.device_type || '-' },
|
||||
{ header: "OS", accessor: (row) => row.os_label || '-' },
|
||||
{ header: "Manufacturer", accessor: (row) => row.manufacturer || '-' },
|
||||
{ header: "Download", accessor: (row) => <span className="text-cyan-400">{fmtBytes(row.download)}</span>, className: "text-right" },
|
||||
{ header: "Upload", accessor: (row) => <span className="text-green-400">{fmtBytes(row.upload)}</span>, className: "text-right" },
|
||||
{
|
||||
header: "IP Address",
|
||||
accessor: (row) => (
|
||||
<button
|
||||
onClick={() => setSelectedDevice(row)}
|
||||
className="flex items-center gap-1.5 group text-left"
|
||||
title="Click to view device details"
|
||||
>
|
||||
<span className="font-medium text-blue-400 group-hover:text-blue-300 transition-colors font-mono text-sm">
|
||||
{row.ip_address || "-"}
|
||||
</span>
|
||||
<ChevronRight className="w-3 h-3 text-slate-600 group-hover:text-blue-400 transition-colors" />
|
||||
</button>
|
||||
),
|
||||
},
|
||||
{
|
||||
header: "MAC Address",
|
||||
accessor: (row) =>
|
||||
row.mac_address ? (
|
||||
<code className="text-xs bg-secondary/50 px-1 py-0.5 rounded text-muted-foreground">
|
||||
{row.mac_address}
|
||||
</code>
|
||||
) : (
|
||||
"-"
|
||||
),
|
||||
},
|
||||
{ header: "Type", accessor: (row) => row.device_type || "-" },
|
||||
{ header: "OS", accessor: (row) => row.os_label || "-" },
|
||||
{ header: "Manufacturer", accessor: (row) => row.manufacturer || "-" },
|
||||
{
|
||||
header: "Download",
|
||||
accessor: (row) => <span className="text-cyan-400">{fmtBytes(row.download)}</span>,
|
||||
className: "text-right",
|
||||
},
|
||||
{
|
||||
header: "Upload",
|
||||
accessor: (row) => <span className="text-green-400">{fmtBytes(row.upload)}</span>,
|
||||
className: "text-right",
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
@@ -36,29 +70,46 @@ export default function DevicesPage() {
|
||||
Devices
|
||||
{devices.isLoading && <Loader2 className="w-5 h-5 animate-spin text-primary" />}
|
||||
</h2>
|
||||
<p className="text-muted-foreground mt-1">Monitor and manage connected network devices.</p>
|
||||
<p className="text-muted-foreground mt-1">
|
||||
Monitor and manage connected network devices.{" "}
|
||||
<span className="text-slate-500 text-xs">Click an IP to see app usage & flows.</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Card className="bg-card/80 backdrop-blur-xl border-border/50 animate-slide-up" style={{ animationDelay: '100ms' }}>
|
||||
<Card className="bg-card/80 backdrop-blur-xl border-border/50 animate-slide-up" style={{ animationDelay: "100ms" }}>
|
||||
<CardHeader className="border-b border-border/50 pb-4 mb-4">
|
||||
<CardTitle className="text-lg text-white">Active Devices {devices.data ? `(${devices.data.length})` : ''}</CardTitle>
|
||||
<CardTitle className="text-lg text-white">
|
||||
Active Devices {devices.data ? `(${devices.data.length})` : ""}
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<DataTable
|
||||
data={devices.data || []}
|
||||
columns={columns}
|
||||
<DataTable
|
||||
data={devices.data || []}
|
||||
columns={columns}
|
||||
searchPlaceholder="Search devices by IP, MAC, OS..."
|
||||
searchFilter={(row, query) => {
|
||||
const q = query.toLowerCase();
|
||||
return (row.ip_address || "").toLowerCase().includes(q) ||
|
||||
(row.mac_address || "").toLowerCase().includes(q) ||
|
||||
(row.os_label || "").toLowerCase().includes(q) ||
|
||||
(row.device_type || "").toLowerCase().includes(q);
|
||||
return (
|
||||
(row.ip_address || "").toLowerCase().includes(q) ||
|
||||
(row.mac_address || "").toLowerCase().includes(q) ||
|
||||
(row.os_label || "").toLowerCase().includes(q) ||
|
||||
(row.device_type || "").toLowerCase().includes(q)
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Device Detail Modal */}
|
||||
{selectedDevice && (
|
||||
<DeviceDetailModal
|
||||
ip={selectedDevice.ip_address}
|
||||
deviceLabel={selectedDevice.device_label}
|
||||
macAddress={selectedDevice.mac_address}
|
||||
onClose={() => setSelectedDevice(null)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user