refactor: streamline dashboard layout by consolidating hero metrics and removing redundant stats grid
Remove four-card stats grid section and consolidate key metrics into hero banner. Add Policies mini-stat showing active and service-based policy counts. Replace revoked device count with total traffic summary in Devices mini-stat. Update hero grid layout from 3-column to 2-column metrics display. Add owner_username to traffic leaders and recent devices lists for better user context. Remove dashboard
This commit is contained in:
@@ -1,9 +1,7 @@
|
||||
import { useMemo } from "react";
|
||||
import { useQueries } from "@tanstack/react-query";
|
||||
import {
|
||||
Activity,
|
||||
ArrowUpRight,
|
||||
FileClock,
|
||||
Network,
|
||||
TrendingUp,
|
||||
Shield,
|
||||
@@ -147,56 +145,21 @@ export function DashboardPage() {
|
||||
<div className="dashboard-mini-stat">
|
||||
<span className="dashboard-mini-label">Devices</span>
|
||||
<strong>{summary.activeDeviceCount}</strong>
|
||||
<span>{summary.revokedDeviceCount} revoked</span>
|
||||
<span>{formatBytes(summary.totalRX + summary.totalTX)} traffic</span>
|
||||
</div>
|
||||
<div className="dashboard-mini-stat">
|
||||
<span className="dashboard-mini-label">Gateways</span>
|
||||
<strong>{summary.activeGatewayCount}</strong>
|
||||
<span>{summary.gatewayHealth}</span>
|
||||
</div>
|
||||
<div className="dashboard-mini-stat">
|
||||
<span className="dashboard-mini-label">Policies</span>
|
||||
<strong>{summary.activePolicyCount}</strong>
|
||||
<span>{summary.servicePolicyCount} service-based</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="dashboard-stats-grid">
|
||||
<Card>
|
||||
<div className="dashboard-stat-head">
|
||||
<div className="dashboard-stat-icon"><Users size={18} strokeWidth={2} /></div>
|
||||
<p className="metric-label">Identity coverage</p>
|
||||
</div>
|
||||
<strong className="metric-value">{summary.activeUserCount}</strong>
|
||||
<p className="dashboard-stat-support">{summary.adminUserCount} administrators currently active in the control plane.</p>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<div className="dashboard-stat-head">
|
||||
<div className="dashboard-stat-icon"><Activity size={18} strokeWidth={2} /></div>
|
||||
<p className="metric-label">Endpoint activity</p>
|
||||
</div>
|
||||
<strong className="metric-value">{summary.activeDeviceCount}</strong>
|
||||
<p className="dashboard-stat-support">{formatBytes(summary.totalRX + summary.totalTX)} total observed tunnel traffic.</p>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<div className="dashboard-stat-head">
|
||||
<div className="dashboard-stat-icon"><Network size={18} strokeWidth={2} /></div>
|
||||
<p className="metric-label">Gateway posture</p>
|
||||
</div>
|
||||
<strong className="metric-value">{summary.gatewayHealth}</strong>
|
||||
<p className="dashboard-stat-support">{summary.activeGatewayCount} active gateway nodes enforcing policy.</p>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<div className="dashboard-stat-head">
|
||||
<div className="dashboard-stat-icon"><Shield size={18} strokeWidth={2} /></div>
|
||||
<p className="metric-label">Policy model</p>
|
||||
</div>
|
||||
<strong className="metric-value">{summary.activePolicyCount}</strong>
|
||||
<p className="dashboard-stat-support">
|
||||
{summary.servicePolicyCount} service-based and {summary.fullTunnelPolicyCount} full-tunnel policies active.
|
||||
</p>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<div className="dashboard-main-grid">
|
||||
<Card>
|
||||
<div className="dashboard-section-head">
|
||||
@@ -233,7 +196,7 @@ export function DashboardPage() {
|
||||
<div className="dashboard-traffic-row" key={device.id}>
|
||||
<div className="dashboard-traffic-copy">
|
||||
<strong>{device.name}</strong>
|
||||
<span>{device.assigned_ip ?? "No VPN IP"} · {formatBytes(total)}</span>
|
||||
<span>{device.owner_username || "unknown user"} · {device.assigned_ip ?? "No VPN IP"} · {formatBytes(total)}</span>
|
||||
</div>
|
||||
<div className="dashboard-traffic-bar">
|
||||
<span className="dashboard-traffic-bar-fill" style={{ width }} />
|
||||
@@ -263,7 +226,7 @@ export function DashboardPage() {
|
||||
<div className="dashboard-list-row" key={device.id}>
|
||||
<div className="dashboard-list-copy">
|
||||
<strong>{device.name}</strong>
|
||||
<span>{device.assigned_ip ?? "No VPN IP"} · {device.platform}</span>
|
||||
<span>{device.owner_username || "unknown user"} · {device.assigned_ip ?? "No VPN IP"} · {device.platform}</span>
|
||||
</div>
|
||||
<div className="dashboard-list-metric">
|
||||
<span>{formatBytes((device.rx_bytes ?? 0) + (device.tx_bytes ?? 0))}</span>
|
||||
@@ -327,22 +290,6 @@ export function DashboardPage() {
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<div className="dashboard-section-head">
|
||||
<div>
|
||||
<p className="eyebrow">Operations note</p>
|
||||
<h4>Gateway enforcement</h4>
|
||||
</div>
|
||||
<div className="dashboard-inline-icon">
|
||||
<FileClock size={16} strokeWidth={2} />
|
||||
</div>
|
||||
</div>
|
||||
<p className="dashboard-note">
|
||||
Effective WireGuard peer state and nftables enforcement are generated from active policies, selected device profiles,
|
||||
and service-level access rules. Use this dashboard to spot drift before it becomes an outage.
|
||||
</p>
|
||||
</Card>
|
||||
</div>
|
||||
</Page>
|
||||
);
|
||||
|
||||
@@ -439,8 +439,8 @@ button {
|
||||
.dashboard-hero {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1.35fr) minmax(320px, 0.9fr);
|
||||
gap: 22px;
|
||||
padding: 26px;
|
||||
gap: 18px;
|
||||
padding: 24px;
|
||||
background:
|
||||
radial-gradient(circle at top left, rgba(68, 230, 231, 0.1), transparent 32%),
|
||||
linear-gradient(180deg, rgba(18, 31, 67, 0.92), rgba(10, 18, 40, 0.92));
|
||||
@@ -467,7 +467,7 @@ button {
|
||||
|
||||
.dashboard-hero-metrics {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
@@ -490,12 +490,6 @@ button {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.dashboard-stats-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.dashboard-stat-head,
|
||||
.dashboard-section-head {
|
||||
display: flex;
|
||||
@@ -504,7 +498,6 @@ button {
|
||||
gap: 14px;
|
||||
}
|
||||
|
||||
.dashboard-stat-icon,
|
||||
.dashboard-inline-icon {
|
||||
display: grid;
|
||||
place-items: center;
|
||||
@@ -516,12 +509,6 @@ button {
|
||||
background: rgba(8, 14, 30, 0.68);
|
||||
}
|
||||
|
||||
.dashboard-stat-support {
|
||||
margin: 0;
|
||||
color: var(--muted);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.dashboard-main-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
@@ -777,7 +764,6 @@ button {
|
||||
.dashboard-hero,
|
||||
.dashboard-main-grid,
|
||||
.dashboard-bottom-grid,
|
||||
.dashboard-stats-grid,
|
||||
.dashboard-posture-grid,
|
||||
.dashboard-hero-metrics,
|
||||
.dashboard-traffic-summary {
|
||||
|
||||
Reference in New Issue
Block a user