Reposition Gateway traffic summary
Build and Deploy Gateway / build-and-push (push) Successful in 11s
Build and Deploy Gateway / deploy (push) Successful in 6s

This commit is contained in:
2026-08-08 00:43:28 +03:00
parent 4c61a04dc7
commit 3cdb0c735e
5 changed files with 63 additions and 57 deletions
+28 -25
View File
@@ -1338,31 +1338,6 @@ export function ClientOverviewPage({
</button>
</nav>}
<main className={`client-panel${showPower ? '' : ' is-setup'}${!isGateway && hasSubscription ? ' has-subscription' : ''}${isGateway ? ' is-gateway-home' : ''}`}>
{isGateway && <section className="client-gateway-summary" aria-labelledby="gateway-summary-title">
<span className="client-gateway-summary-kicker">Сейчас</span>
<h2 id="gateway-summary-title">
{appliedServer?.label || 'VPN-сервер не используется'}
</h2>
<div className="client-gateway-route-slot" role="status" aria-live="polite">
{switchingServer && <span>Переключаем на {desiredServer.label}</span>}
</div>
<div className="client-gateway-traffic-heading">
<span>Учтено Harbor</span>
<strong>{formatByteString(globalTraffic?.totalBytes || '0')}</strong>
</div>
<div className="client-gateway-traffic-chart">
<TrafficChart
samples={globalTraffic?.history || []}
capacity={deviceSnapshot?.trafficHistoryCapacity || 120}
routeLabel="Gateway"
/>
</div>
<div className={`client-gateway-traffic-freshness${trafficSourceError ? ' is-stale' : ''}`} role="status" aria-live="polite">
{trafficSourceError
? `Трафик не обновляется · последние данные ${trafficFreshness}`
: globalTraffic?.observedAt ? `Обновлено ${trafficFreshness}` : 'Ожидаем первые данные трафика'}
</div>
</section>}
{showPower && (
<section className="client-power-section" aria-labelledby="connection-title">
{isGateway ? <span
@@ -1390,6 +1365,15 @@ export function ClientOverviewPage({
<span className={connected && !gatewayDirect ? 'is-active' : ''} aria-hidden="true">VPN включён</span>
<span className={connected && gatewayDirect ? 'is-active' : ''} aria-hidden="true">Gateway подключён</span>
</h2>
{isGateway && <div className="client-gateway-route-summary" aria-labelledby="gateway-summary-title">
<span className="client-gateway-summary-kicker">Сейчас</span>
<strong id="gateway-summary-title">
{appliedServer?.label || 'VPN-сервер не используется'}
</strong>
<div className="client-gateway-route-slot">
{switchingServer && <span>Переключаем на {desiredServer.label}</span>}
</div>
</div>}
<div className="client-state-detail">
{connected ? (
<button
@@ -1442,6 +1426,25 @@ export function ClientOverviewPage({
</div>
</div>
{isGateway && <section className="client-gateway-summary" aria-label="Общий трафик Harbor">
<div className="client-gateway-traffic-heading">
<span>Учтено Harbor</span>
<strong>{formatByteString(globalTraffic?.totalBytes || '0')}</strong>
</div>
<div className="client-gateway-traffic-chart">
<TrafficChart
samples={globalTraffic?.history || []}
capacity={deviceSnapshot?.trafficHistoryCapacity || 120}
routeLabel="Gateway"
/>
</div>
<div className={`client-gateway-traffic-freshness${trafficSourceError ? ' is-stale' : ''}`} role="status" aria-live="polite">
{trafficSourceError
? `Трафик не обновляется · последние данные ${trafficFreshness}`
: globalTraffic?.observedAt ? `Обновлено ${trafficFreshness}` : 'Ожидаем первые данные трафика'}
</div>
</section>}
<section className={`client-proxies${isGateway ? ' is-gateway' : ''}`} aria-label={isGateway ? 'Gateway и Gateway Proxy' : 'Локальный прокси'}>
<div className="client-access-point">
{!isGateway && (
+28 -26
View File
@@ -2616,15 +2616,10 @@ p {
}
.client-gateway-summary {
grid-column: 1;
align-self: center;
justify-self: end;
width: min(360px, 100%);
width: min(300px, 100%);
display: grid;
gap: 8px;
padding-right: clamp(20px, 4vw, 64px);
text-align: left;
animation: client-power-arrive 850ms cubic-bezier(0.16, 1, 0.3, 1) both;
gap: 6px;
text-align: center;
}
.client-gateway-summary-kicker,
@@ -2636,22 +2631,34 @@ p {
text-transform: uppercase;
}
.client-gateway-summary h2 {
min-height: 29px;
margin: 0;
.client-gateway-route-summary {
min-height: 54px;
display: grid;
justify-items: center;
gap: 2px;
margin-top: 6px;
}
.client-gateway-route-summary strong {
max-width: min(300px, 100%);
min-height: 20px;
overflow: hidden;
color: var(--client-text);
font-size: 22px;
letter-spacing: -0.045em;
font-size: 15px;
letter-spacing: -0.035em;
text-overflow: ellipsis;
white-space: nowrap;
}
.client-gateway-route-slot {
min-height: 18px;
width: 100%;
min-height: 16px;
overflow: hidden;
color: var(--client-accent);
font-size: 9px;
font-weight: 700;
text-overflow: ellipsis;
white-space: nowrap;
}
.client-gateway-traffic-heading {
@@ -2659,7 +2666,6 @@ p {
align-items: baseline;
justify-content: space-between;
gap: 12px;
margin-top: 10px;
}
.client-gateway-traffic-heading strong {
@@ -2671,18 +2677,18 @@ p {
.client-gateway-traffic-chart {
width: 100%;
min-height: 118px;
min-height: 88px;
}
.client-gateway-traffic-chart .client-device-traffic-chart {
grid-column: auto;
grid-row: auto;
width: 100%;
height: 118px;
height: 88px;
}
.client-gateway-traffic-freshness {
min-height: 28px;
min-height: 26px;
color: var(--client-muted);
font-size: 8.5px;
line-height: 1.5;
@@ -2764,6 +2770,10 @@ p {
min-height: 76px;
}
.client-panel.is-gateway-home .client-state-copy {
min-height: 136px;
}
.client-power-control {
width: 96px;
height: 96px;
@@ -4532,7 +4542,6 @@ p {
}
.client-power-section,
.client-gateway-summary,
.client-form,
.client-panel.is-setup .client-form {
grid-column: 1;
@@ -4544,12 +4553,6 @@ p {
order: 1;
}
.client-gateway-summary {
order: 2;
justify-self: center;
padding-right: 0;
}
.client-form {
height: auto;
max-height: none;
@@ -5176,7 +5179,6 @@ p {
.client-subscription-refresh,
.client-subscription-delete,
.client-power-section,
.client-gateway-summary,
.client-gateway-traffic-chart,
.client-gateway-traffic-freshness,
.client-subscription-drawer,