release: NX9-WG v1.0.0
This commit is contained in:
1 parent
c8a9b7cde6
commit
4dfe42fe68
42 files changed
+4689
-336
No files matched your search
@@ -38,9 +38,10 @@ impl PeerRowView {
|
||||
let is_expired =
|
||||
peer.state == PeerState::Expired || peer.expires_at.is_some_and(|exp| exp <= now);
|
||||
|
||||
let maybe_hs = last_handshake.or(peer.last_handshake_at);
|
||||
let is_online = if is_expired || peer.state != PeerState::Active {
|
||||
false
|
||||
} else if let Some(hs) = last_handshake.or(peer.last_handshake_at) {
|
||||
} else if let Some(hs) = maybe_hs {
|
||||
let diff = now.signed_duration_since(hs);
|
||||
diff.num_seconds() >= 0 && diff.num_seconds() < 180
|
||||
} else {
|
||||
@@ -53,9 +54,14 @@ impl PeerRowView {
|
||||
match peer.state {
|
||||
PeerState::Active => {
|
||||
if is_online {
|
||||
("Online".to_string(), "status-pass".to_string())
|
||||
("Connected".to_string(), "status-pass".to_string())
|
||||
} else if maybe_hs.is_some() {
|
||||
("Disconnected".to_string(), "status-fail".to_string())
|
||||
} else {
|
||||
("Offline".to_string(), "status-neutral".to_string())
|
||||
(
|
||||
"Awaiting Handshake".to_string(),
|
||||
"status-warning".to_string(),
|
||||
)
|
||||
}
|
||||
}
|
||||
PeerState::Disabled => ("Disabled".to_string(), "status-warning".to_string()),
|
||||
@@ -216,10 +222,18 @@ mod tests {
|
||||
|
||||
let row = PeerRowView::from_peer_and_telemetry(&peer, 1024, 2048, None);
|
||||
assert_eq!(row.name, "alice-laptop");
|
||||
assert_eq!(row.status_label, "Offline");
|
||||
assert_eq!(row.status_label, "Awaiting Handshake");
|
||||
assert_eq!(row.rx_bytes_formatted, "1.0 KB");
|
||||
assert_eq!(row.tx_bytes_formatted, "2.0 KB");
|
||||
|
||||
let recent_hs = Utc::now().naive_utc() - chrono::Duration::seconds(30);
|
||||
let row_online = PeerRowView::from_peer_and_telemetry(&peer, 1024, 2048, Some(recent_hs));
|
||||
assert_eq!(row_online.status_label, "Connected");
|
||||
|
||||
let stale_hs = Utc::now().naive_utc() - chrono::Duration::seconds(300);
|
||||
let row_stale = PeerRowView::from_peer_and_telemetry(&peer, 1024, 2048, Some(stale_hs));
|
||||
assert_eq!(row_stale.status_label, "Disconnected");
|
||||
|
||||
let filter = PeerTableFilter {
|
||||
search_query: "alice".to_string(),
|
||||
status_filter: None,
|
||||
|
||||
@@ -33,6 +33,64 @@ a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* ── Authentication & Login View ─────────────────────────────────────────────── */
|
||||
#login-view {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background-color: var(--bg-base);
|
||||
padding: 24px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.login-card {
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
background-color: var(--bg-surface);
|
||||
border: 1px solid var(--border-subtle);
|
||||
border-radius: var(--radius-lg);
|
||||
padding: 32px;
|
||||
box-shadow: var(--shadow-lg);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.login-header {
|
||||
text-align: center;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.login-header .brand-mark {
|
||||
font-size: 14px;
|
||||
padding: 4px 10px;
|
||||
display: inline-block;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.login-header h2 {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.login-header p {
|
||||
font-size: 13px;
|
||||
color: var(--text-secondary);
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.login-error {
|
||||
background-color: var(--status-fail-bg);
|
||||
color: var(--status-fail-text);
|
||||
border: 1px solid var(--status-fail-border);
|
||||
padding: 10px 14px;
|
||||
border-radius: var(--radius-md);
|
||||
font-size: 13px;
|
||||
margin-bottom: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* ── App Shell Layout ────────────────────────────────────────────────────────── */
|
||||
#app-layout {
|
||||
display: flex;
|
||||
@@ -634,10 +692,30 @@ table.data-table tr:hover td {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
padding: 24px;
|
||||
background-color: #ffffff;
|
||||
border-radius: var(--radius-lg);
|
||||
margin: 16px 0;
|
||||
border: 1px solid var(--border-subtle);
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.qr-target-box {
|
||||
width: 240px;
|
||||
height: 240px;
|
||||
max-width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.qr-target-box svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
max-width: 240px;
|
||||
max-height: 240px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.qr-image {
|
||||
|
||||
Reference in new issue
Block a user