Refine device traffic display and sorting
Build and Deploy Gateway / build-and-push (push) Successful in 14s
Build and Deploy Gateway / deploy (push) Successful in 7s

This commit is contained in:
2026-08-07 17:51:53 +03:00
parent c430557dc0
commit 0a37121dbe
5 changed files with 17 additions and 64 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
export const HARBOR_VERSIONS = Object.freeze({
macClient: '0.13.1',
gatewayClient: '0.14.1',
macClient: '0.13.2',
gatewayClient: '0.14.2',
gatewayBackend: '0.14.0',
});
+3 -13
View File
@@ -275,15 +275,10 @@ export function DevicesPanel({ open, panelRef, closeRef, onClose }) {
const saving = savingId === device.id;
const seen = formatLastSeen(device.lastSeenAt);
const online = device.status === 'online';
const download = formatByteString(device.downloadBytes);
const upload = formatByteString(device.uploadBytes);
const proxyDownload = formatByteString(device.proxyDownloadBytes);
const proxyUpload = formatByteString(device.proxyUploadBytes);
const gatewayTotal = byteString(device.downloadBytes) + byteString(device.uploadBytes);
const proxyTotal = byteString(device.proxyDownloadBytes) + byteString(device.proxyUploadBytes);
const gatewayTraffic = formatByteString(gatewayTotal.toString());
const proxyTraffic = formatByteString(proxyTotal.toString());
const trafficLabel = `Gateway: получено ${download}, отдано ${upload}. Прокси: получено ${proxyDownload}, отдано ${proxyUpload}`;
const copied = copyFeedback?.id === device.id;
const policyBusy = device.policyStatus === 'applying';
const policyFailed = device.policyStatus === 'failed';
@@ -344,7 +339,7 @@ export function DevicesPanel({ open, panelRef, closeRef, onClose }) {
<button type="button" aria-label="Отменить изменение" disabled={saving} onClick={() => setEditingId('')}>×</button>
</form>
) : (
<div className="client-device-title">
<>
{device.ip ? <h3 className="client-device-name-heading">
<button
className={`client-device-name${copied ? copyFeedback.failed ? ' is-copy-error' : ' is-copied' : ''}`}
@@ -375,7 +370,7 @@ export function DevicesPanel({ open, panelRef, closeRef, onClose }) {
</button>
<Tooltip>Изменить название</Tooltip>
</span>
</div>
</>
)}
<span className={`client-device-last-seen${online ? ' is-online' : ''}`} tabIndex="0">
<time
@@ -387,16 +382,11 @@ export function DevicesPanel({ open, panelRef, closeRef, onClose }) {
</span>
</div>
<span
className="client-device-traffic client-tooltip-anchor"
tabIndex="0"
aria-label={trafficLabel}
>
<span className="client-device-traffic" role="group" aria-label={`Gateway ${gatewayTraffic}, Прокси ${proxyTraffic}`}>
<span className="client-device-traffic-sources" aria-hidden="true">
<span><b>Gateway</b><strong>{gatewayTraffic}</strong></span>
<span className="is-proxy"><b>Прокси</b><strong>{proxyTraffic}</strong></span>
</span>
<Tooltip>{trafficLabel}</Tooltip>
</span>
<span className="client-device-policy-wrap client-tooltip-anchor">
+2 -43
View File
@@ -903,20 +903,9 @@ p {
gap: 7px;
}
.client-device-title {
flex: 1 1 auto;
.client-device-main > h3 {
flex: 0 1 auto;
min-width: 0;
display: flex;
align-items: center;
gap: 4px;
}
.client-device-title h3 {
flex: 1 1 auto;
min-width: 0;
}
.client-device-title h3 {
overflow: hidden;
margin: 0;
font-size: 14px;
@@ -944,32 +933,23 @@ p {
grid-area: 1 / 1;
overflow: hidden;
opacity: 1;
filter: blur(0);
text-overflow: ellipsis;
white-space: nowrap;
transform: translateY(0);
transition: opacity 220ms ease, filter 320ms ease, transform 320ms cubic-bezier(0.16, 1, 0.3, 1), color 220ms ease;
}
.client-device-name-ip,
.client-device-name-feedback {
opacity: 0;
filter: blur(5px);
transform: translateY(2px);
}
.client-device-name:hover .client-device-name-primary,
.client-device-name:focus-visible .client-device-name-primary {
opacity: 0;
filter: blur(5px);
transform: translateY(-2px);
}
.client-device-name:hover .client-device-name-ip,
.client-device-name:focus-visible .client-device-name-ip {
opacity: 1;
filter: blur(0);
transform: translateY(0);
}
.client-device-name.is-copied .client-device-name-primary,
@@ -977,14 +957,11 @@ p {
.client-device-name.is-copy-error .client-device-name-primary,
.client-device-name.is-copy-error .client-device-name-ip {
opacity: 0;
filter: blur(5px);
}
.client-device-name.is-copied .client-device-name-feedback,
.client-device-name.is-copy-error .client-device-name-feedback {
opacity: 1;
filter: blur(0);
transform: translateY(0);
}
.client-device-name.is-copied .client-device-name-feedback {
@@ -1198,23 +1175,6 @@ p {
color: color-mix(in oklch, var(--client-accent) 72%, var(--client-muted));
}
.client-device-traffic:focus-visible {
border-radius: 3px;
outline: 2px solid var(--client-accent);
outline-offset: 3px;
}
.client-device-traffic.client-tooltip-anchor > .client-tooltip {
right: 0;
left: auto;
transform: translate(0, 2px);
}
.client-device-traffic.client-tooltip-anchor:hover > .client-tooltip,
.client-device-traffic.client-tooltip-anchor:focus-visible > .client-tooltip {
transform: translate(0, 0);
}
.client-device-policy-wrap {
width: 32px;
position: relative;
@@ -4272,7 +4232,6 @@ p {
.client-device-pin svg,
.client-device-policy,
.client-device-policy svg,
.client-device-name > span,
.client-device-edit,
.client-device-edit svg,
.client-device-edit-wrap,
+4 -2
View File
@@ -34,7 +34,7 @@ export function formatByteString(value) {
export function sortDevicesByTraffic(devices, direction = 'desc') {
const factor = direction === 'asc' ? 1 : -1;
return (Array.isArray(devices) ? devices : [])
.map((device, index) => ({ device, index }))
.map((device) => ({ device }))
.sort((left, right) => {
const pinned = Number(right.device.pinned === true) - Number(left.device.pinned === true);
if (pinned) return pinned;
@@ -42,7 +42,9 @@ export function sortDevicesByTraffic(devices, direction = 'desc') {
+ byteString(left.device.proxyUploadBytes) + byteString(left.device.proxyDownloadBytes);
const rightTotal = byteString(right.device.uploadBytes) + byteString(right.device.downloadBytes)
+ byteString(right.device.proxyUploadBytes) + byteString(right.device.proxyDownloadBytes);
if (leftTotal === rightTotal) return left.index - right.index;
if (leftTotal === rightTotal) {
return String(left.device.id || '').localeCompare(String(right.device.id || ''));
}
return (leftTotal < rightTotal ? -1 : 1) * factor;
})
.map(({ device }) => device);