- {/* identity */}
-
- {c.login}
+ {c.interfaceName}
+ {IFACE_TYPE_LABEL[c.interfaceType]}
{showServer && (
-
+
{c.serverSite}
)}
-
{c.description}
+
{c.comment || c.userLogin}
-
- {/* live RX / TX */}
{fmtRate(c.rxNow)}
@@ -471,19 +453,13 @@ function GreClientRow({ c, showServer = false }: { c: GreClientTraffic; showServ
{fmtRate(c.txNow)}
-
- {/* totals today */}
{fmtGB(c.rxTotal)}
{fmtGB(c.txTotal)}
-
- {/* status dot — badge style */}
{c.status === "online" ? "online" : "offline"}
@@ -491,12 +467,12 @@ function GreClientRow({ c, showServer = false }: { c: GreClientTraffic; showServ
)
}
-function GreClientsList({ clients, showServer = false }: { clients: GreClientTraffic[]; showServer?: boolean }) {
+function BoundIfacesList({ clients, showServer = false }: { clients: BoundIfaceTraffic[]; showServer?: boolean }) {
if (clients.length === 0)
- return
Нет GRE-клиентов
+ return
Нет привязанных интерфейсов
return (
- {clients.map(c => )}
+ {clients.map((c) => )}
)
}
@@ -504,7 +480,7 @@ function GreClientsList({ clients, showServer = false }: { clients: GreClientTra
// ─── detail panel helpers ─────────────────────────────────────────────────────
function DetailHeader({ range, setRange, children }: {
- range: Range; setRange: (r: Range) => void; children: React.ReactNode
+ range: Range; setRange: (r: Range) => void; children: ReactNode
}) {
return (
@@ -617,14 +593,14 @@ function ServerDetail({
/>
- {sel.greClients.length > 0 && (
+ {sel.boundIfaces.length > 0 && (
- GRE-клиенты
- ({sel.greClients.length})
+ Привязанные интерфейсы
+ ({sel.boundIfaces.length})
-
+
)}
>
@@ -632,7 +608,7 @@ function ServerDetail({
}
function UserDetail({ sel, range, setRange }: { sel: UserTraffic; range: Range; setRange: (r: Range) => void }) {
- const hasClients = sel.greClients.length > 0
+ const hasClients = sel.interfaces.length > 0
return (
<>
@@ -645,7 +621,7 @@ function UserDetail({ sel, range, setRange }: { sel: UserTraffic; range: Range;
{!hasClients ? (
-