"use client"; import { useState } from "react"; import { Activity } from "lucide-react"; import { fmtBytes, formatAppLabel } from "@/lib/utils"; import { Pagination } from "./Pagination"; import { UniversalFilters, useUniversalFilterState, FilterConfig } from "./UniversalFilters"; interface Props { flows: any[]; ip: string; } interface ColDef { header: string; width: string; align?: "center" | "right"; } function TableWrap({ children, cols }: { children: React.ReactNode; cols: ColDef[] }) { return (
{cols.map((c) => ( ))} {children}
{c.header}
); } function Cell({ value, className = "", center = true, right = false }: { value: string | number; className?: string; center?: boolean; right?: boolean }) { const str = String(value ?? "—"); const align = center ? "text-center" : right ? "text-right" : "text-center"; return (
{str || "—"}
); } export default function DeviceFlowsTab({ flows, ip }: Props) { const [page, setPage] = useState(1); const pageSize = 50; const { filters, handleFilterChange, handleReset, activeCount } = useUniversalFilterState(); const opts = (key: string) => { const raw = Array.from(new Set(flows.map((r: any) => String(r[key] || "")).filter(Boolean))); return raw.sort(); }; const filterConfigs: FilterConfig[] = [ { id: "app_name", label: "App", type: "select", value: filters.app_name, options: opts("app_name"), formatter: formatAppLabel }, { id: "protocol_name", label: "Protocol", type: "select", value: filters.protocol_name, options: opts("protocol_name"), formatter: formatAppLabel }, ]; const filteredData = flows.filter((row: any) => { if (filters.app_name && filters.app_name !== "All" && row.app_name !== filters.app_name) return false; if (filters.protocol_name && filters.protocol_name !== "All" && row.protocol_name !== filters.protocol_name) return false; return true; }); const totalPages = Math.ceil(filteredData.length / pageSize); const startIndex = (page - 1) * pageSize; const paginated = filteredData.slice(startIndex, startIndex + pageSize); if (flows.length === 0) { return (

No Flow Data Yet

Network flows for this IP haven't been captured or are currently being ingested. If you just started the system, please wait a few moments as data is actively being synchronized.

); } const COLS: ColDef[] = [ { header: "#", width: "w-[5%]", align: "center" }, { header: "Time", width: "w-[15%]", align: "center" }, { header: "Source", width: "w-[15%]", align: "center" }, { header: "Destination", width: "w-[15%]", align: "center" }, { header: "Port", width: "w-[10%]", align: "center" }, { header: "App / Protocol", width: "w-[20%]", align: "center" }, { header: "Download", width: "w-[10%]", align: "center" }, { header: "Upload", width: "w-[10%]", align: "center" }, ]; return (
{ handleFilterChange(id, val); setPage(1); }} showDateRange={false} activeFilterCount={activeCount} onReset={() => { handleReset(); setPage(1); }} /> {paginated.map((f: any, i: number) => { const isOutbound = f.src_ip === ip; const timeStr = f.last_seen ? new Date(f.last_seen).toLocaleString() : '-'; return (
{f.app_label || f.protocol || '-'}
); })}
{totalPages > 1 && (
)}
); }