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:
2026-03-24 18:11:53 +01:00
parent e3da49ec23
commit 9aa4a13fd5
2 changed files with 11 additions and 78 deletions

View File

@@ -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>
);

View File

@@ -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 {