feat(source2): lock dark mode, sans-serif typography, and all recent updates

This commit is contained in:
rafif committed 2026-08-20 23:02:00 +07:00
1 parent dd4c8f6876
commit 9f24b56e97
259 files changed
+15596 -8966

No files matched your search

+49 -136
View File
@@ -1,18 +1,16 @@
"use client";
import { useState, useEffect } from "react";
import { TimestampCell } from "@/components/ui/TimestampCell";
import { useState, useEffect, useMemo } from "react";
import { useFlows, useFlowsOptions } from "@/lib/api-with-context";
import { DataTable, Column } from "@/components/ui/DataTable";
import { DataTable } from "@/components/ui/DataTable";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/Card";
import { Loader2, Download, Search } from "lucide-react";
import { fmtBytes, formatAppLabel } from "@/lib/utils";
import { IpDetails } from "@/components/ui/IpDetails";
import { formatAppLabel } from "@/lib/utils";
import { DeviceDetailModal } from "@/components/ui/DeviceDetailModal";
import { UniversalFilters, useUniversalFilterState, FilterConfig } from "@/components/ui/UniversalFilters";
import { explainAppOrPort } from "./explainFlow";
import { downloadCsv } from "@/components/ui/DataTableCsvHelper";
import { HelpTrigger } from "@/components/help/HelpTrigger";
import { getFlowColumns } from "./flowColumns";
export default function FlowsPage() {
const [mounted, setMounted] = useState(false);
@@ -53,29 +51,33 @@ export default function FlowsPage() {
document.title = "Flows | BackOne - Deep Package Inspection";
}, []);
if (!mounted) return null;
const columns = useMemo(() => getFlowColumns(setSelectedIp), []);
const opts = (key: string) => {
if (!optionsData) return [];
const mapping: Record<string, string[]> = {
protocol: optionsData.protocols, src_ip: optionsData.srcIps, dst_ip: optionsData.dstIps,
dst_port: optionsData.dstPorts, app_label: optionsData.apps, domain: optionsData.domains
const filterConfigs: FilterConfig[] = useMemo(() => {
const opts = (key: string) => {
if (!optionsData) return [];
const mapping: Record<string, string[]> = {
protocol: optionsData.protocols, src_ip: optionsData.srcIps, dst_ip: optionsData.dstIps,
dst_port: optionsData.dstPorts, app_label: optionsData.apps, domain: optionsData.domains
};
return mapping[key] || [];
};
return mapping[key] || [];
};
const filterConfigs: FilterConfig[] = [
{ id: "protocol", label: "Protocol", type: "select", value: filters.protocol, options: opts("protocol"), formatter: formatAppLabel },
{ id: "src_ip", label: "Source IP", type: "select", value: filters.src_ip, options: opts("src_ip") },
{ id: "dst_ip", label: "Dest IP", type: "select", value: filters.dst_ip, options: opts("dst_ip") },
{ id: "dst_port", label: "Dest Port", type: "select", value: filters.dst_port, options: opts("dst_port") },
{ id: "app", label: "App", type: "select", value: filters.app, options: opts("app_label"), formatter: formatAppLabel },
{ id: "domain", label: "Domain", type: "select", value: filters.domain, options: opts("domain") },
{ id: "sort_download", label: "Sort Download", type: "select", value: filters.sort_download, options: ["Descending", "Ascending"] },
{ id: "sort_upload", label: "Sort Upload", type: "select", value: filters.sort_upload, options: ["Descending", "Ascending"] },
];
return [
{ id: "protocol", label: "Protocol", type: "select", value: filters.protocol, options: opts("protocol"), formatter: formatAppLabel },
{ id: "src_ip", label: "Source IP", type: "select", value: filters.src_ip, options: opts("src_ip") },
{ id: "dst_ip", label: "Dest IP", type: "select", value: filters.dst_ip, options: opts("dst_ip") },
{ id: "dst_port", label: "Dest Port", type: "select", value: filters.dst_port, options: opts("dst_port") },
{ id: "app", label: "App", type: "select", value: filters.app, options: opts("app_label"), formatter: formatAppLabel },
{ id: "domain", label: "Domain", type: "select", value: filters.domain, options: opts("domain") },
{ id: "sort_download", label: "Sort Download", type: "select", value: filters.sort_download, options: ["Descending", "Ascending"] },
{ id: "sort_upload", label: "Sort Upload", type: "select", value: filters.sort_upload, options: ["Descending", "Ascending"] },
];
}, [optionsData, filters]);
const minDate = new Date(Date.now() - 30 * 24 * 3600 * 1000).toISOString().slice(0, 10);
const minDate = useMemo(() => {
return new Date(Date.now() - 30 * 24 * 3600 * 1000).toISOString().slice(0, 10);
}, []);
const handleExport = () => {
const headers = ["Flow ID", "Src IP", "Dst IP", "Dst Port", "Protocol", "App / Domain", "Download (bytes)", "Upload (bytes)"];
@@ -83,120 +85,31 @@ export default function FlowsPage() {
downloadCsv(`flows-${new Date().toISOString().slice(0,10)}.csv`, rows);
};
const columns: Column<any>[] = [
{
header: "Last Seen",
accessor: (row) => (
<TimestampCell
timestamp={row.last_seen}
firstSeen={row.first_seen}
showActiveStatus={true}
/>
),
className: "w-[14%] text-center",
tooltip: (row) => new Date(row.last_seen).toLocaleString()
},
{
header: "#",
accessor: (row, i) => i + 1,
className: "w-[4%] text-center"
},
{
header: "Flow ID",
accessor: (row) => row.flow_id,
className: "w-[11%] text-center font-mono text-xs truncate"
},
{
header: "Src IP",
accessor: (row) => (
<div className="flex flex-col items-center justify-center text-center">
<button
onClick={() => setSelectedIp(row.src_ip)}
className="text-blue-400 hover:text-blue-300 hover:underline font-mono text-center focus:outline-none truncate w-full"
>
{row.src_ip}
</button>
{row.src_label && (
<span className="text-[10px] text-cyan-400 font-semibold tracking-wide mt-0.5 truncate w-full block">
{row.src_label}
</span>
)}
</div>
),
className: "w-[11%] text-center",
tooltip: (row) => row.src_label ? `${row.src_ip} (${row.src_label})` : row.src_ip
},
{
header: "Dst IP",
accessor: (row) => (
<div className="flex flex-col items-center justify-center">
<button
onClick={() => setSelectedIp(row.dst_ip)}
className="text-blue-400 hover:text-blue-300 hover:underline font-mono text-center focus:outline-none truncate w-full"
>
{row.dst_ip}
</button>
<IpDetails ip={row.dst_ip} />
</div>
),
className: "w-[13%] text-center",
tooltip: (row) => row.dst_ip
},
{
header: "Dst Port",
accessor: (row) => row.dst_port,
className: "w-[7%] text-center font-mono text-xs"
},
{
header: "Protocol",
accessor: (row) => row.protocol,
className: "w-[7%] text-center text-xs"
},
{
header: "App / Domain",
accessor: (row) => {
const exp = explainAppOrPort(row.app_label, row.domain, row.dst_port);
return (
<div className="flex flex-col items-center justify-center">
<span className="font-semibold text-white truncate max-w-[200px] text-center" title={exp.main}>
{exp.main}
</span>
{exp.sub && (
<span className="text-[10px] text-slate-400 italic text-center truncate max-w-[200px]">
{exp.sub}
</span>
)}
</div>
);
},
className: "w-[18%] text-center",
tooltip: (row) => {
const exp = explainAppOrPort(row.app_label, row.domain, row.dst_port);
return exp.sub ? `${exp.main} (${exp.sub})` : exp.main;
}
},
{
header: "Download",
accessor: (row) => <span className="text-blue-400">{fmtBytes(row.bytes_download)}</span>,
className: "w-[8%] text-center",
tooltip: (row) => `${fmtBytes(row.bytes_download)} (${(row.bytes_download || 0).toLocaleString()} bytes)`
},
{
header: "Upload",
accessor: (row) => <span className="text-green-500">{fmtBytes(row.bytes_upload)}</span>,
className: "w-[7%] text-center",
tooltip: (row) => `${fmtBytes(row.bytes_upload)} (${(row.bytes_upload || 0).toLocaleString()} bytes)`
}
];
if (!mounted) return null;
return (
<div className="space-y-6">
<div className="flex items-center justify-between">
<h2 className="text-2xl font-bold tracking-tight text-white flex items-center gap-3">
Flows
{isLoading && <Loader2 className="w-5 h-5 animate-spin text-muted-foreground" />}
</h2>
<HelpTrigger pageId="flows" />
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between sm:gap-4">
<div className="min-w-0 flex-1">
<div className="flex items-center justify-between gap-3 sm:justify-start sm:gap-4 w-full">
<h2 className="text-xl sm:text-2xl md:text-3xl font-bold tracking-tight text-white flex items-center gap-2 sm:gap-3 truncate">
<span>Flows</span>
{isLoading && <Loader2 className="w-4 h-4 sm:w-5 sm:h-5 animate-spin text-primary flex-shrink-0" />}
</h2>
{/* Mobile Actions: Inline next to Title */}
<div className="flex items-center gap-1.5 flex-shrink-0 sm:hidden">
<HelpTrigger pageId="flows" />
</div>
</div>
<p className="text-xs sm:text-sm text-slate-400 mt-1 leading-normal sm:leading-relaxed">
Real-time packet inspection and active network connection flows.
</p>
</div>
{/* Laptop/Tablet Actions: Floats on the right */}
<div className="hidden sm:flex items-center gap-2 sm:gap-3 flex-shrink-0">
<HelpTrigger pageId="flows" />
</div>
</div>
<UniversalFilters