From 2d79ee3de4b672837bb7bfbf1308c8f09066eb8a Mon Sep 17 00:00:00 2001 From: seo Date: Mon, 20 Jul 2026 23:37:18 +0900 Subject: [PATCH] Polish DHCP inactive time display --- public/api.php | 8 +++++++- public/assets/app.js | 22 ++++++++++++++++++++-- public/index.php | 2 +- 3 files changed, 28 insertions(+), 4 deletions(-) diff --git a/public/api.php b/public/api.php index 1396abb..7f90863 100644 --- a/public/api.php +++ b/public/api.php @@ -2689,7 +2689,7 @@ function parse_live_wifi_rows(string $iface, string $band, string $text, array $ $rows[] = $cur; } - return $rows; + return array_values(array_filter($rows, static fn(array $row): bool => !wifi_client_stale($row))); } function lan_fdb_macs(string $iface = 'eth1'): array @@ -2780,6 +2780,12 @@ function wifi_time_ms(string $value): int return PHP_INT_MAX; } +function wifi_client_stale(array $client): bool +{ + $inactiveMs = wifi_time_ms((string)($client['inactive_time'] ?? '')); + return $inactiveMs >= 3600000; +} + function wifi_unknown(mixed $value): bool { $text = strtoupper(trim((string)$value)); diff --git a/public/assets/app.js b/public/assets/app.js index ebe0fee..1607c89 100644 --- a/public/assets/app.js +++ b/public/assets/app.js @@ -1226,7 +1226,7 @@ function wifiRateCells(row) { if (row?.band === 'LAN') { - return `${escapeHtml(wifiCellValue(row, 'tx_bitrate'))}`; + return `${escapeHtml(wifiCellValue(row, 'tx_bitrate'))}`; } return `${wifiMetricCell(row, 'tx_bitrate')}${wifiMetricCell(row, 'rx_bitrate')}`; @@ -1354,6 +1354,24 @@ return formatDhms(parseWifiDurationSeconds(value)) || value; } + function wifiInactiveTime(value) { + const text = String(value ?? '').trim(); + if (text === '' || text.toUpperCase() === 'N/A' || text === '-') { + return '-'; + } + + const msMatch = text.match(/^(\d+(?:\.\d+)?)\s*ms$/i); + if (msMatch) { + const ms = Number(msMatch[1]); + if (Number.isFinite(ms) && ms <= 1000) { + return `${Math.round(ms)} ms`; + } + return formatDhms(ms / 1000) || text; + } + + return formatDhms(parseWifiDurationSeconds(text)) || text; + } + function wifiCellValue(row, key) { const value = row?.[key]; const text = String(value ?? '').trim(); @@ -1440,7 +1458,7 @@ ${wifiMetricCell(row, 'signal')} ${wifiRateCells(row)} ${td(wifiConnectedCellValue(row))} - ${td(wifiCellValue(row, 'inactive_time'))} + ${td(wifiInactiveTime(row.inactive_time))} `).join('') : `${escapeHtml(t('noWifiClients'))}`; diff --git a/public/index.php b/public/index.php index d63feb8..af69110 100644 --- a/public/index.php +++ b/public/index.php @@ -127,7 +127,7 @@ html[data-theme="light"] .btn.secondary{background:#d9e2ef;color:#172033}html[da .select:focus,.slider:focus{outline:2px solid rgba(59,130,246,.65);outline-offset:2px} .btn:disabled{opacity:.55;cursor:not-allowed} .status-list{display:grid;gap:10px}.status-row{display:grid;grid-template-columns:112px minmax(0,1fr);gap:12px;align-items:baseline;padding:10px 12px;background:var(--card2);border-radius:14px}.status-key{color:var(--sub);font-size:13px}.status-value{overflow:visible;white-space:normal;word-break:break-word;font-variant-numeric:tabular-nums} -.table-wrap{max-width:100%;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;border-radius:16px;border:1px solid var(--line)}.table-wrap.compact{border-radius:12px}.wifi-table{width:100%;min-width:1080px;table-layout:fixed;border-collapse:collapse}.wifi-table th,.wifi-table td{padding:11px 13px;border-bottom:1px solid rgba(128,145,170,.18);text-align:left;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.wifi-table th{position:sticky;top:0;background:var(--table-head);z-index:1;font-weight:500}.wifi-table tr:hover{background:rgba(128,145,170,.09)}.wifi-table .col-band{width:72px}.wifi-table .col-host{width:240px}.wifi-table .col-ip{width:132px}.wifi-table .col-mac{width:170px}.wifi-table .col-signal{width:100px}.wifi-table .col-rate{width:120px}.wifi-table .col-time{width:130px} +.table-wrap{max-width:100%;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch;border-radius:16px;border:1px solid var(--line)}.table-wrap.compact{border-radius:12px}.wifi-table{width:100%;min-width:1080px;table-layout:fixed;border-collapse:collapse}.wifi-table th,.wifi-table td{padding:11px 13px;border-bottom:1px solid rgba(128,145,170,.18);text-align:left;font-size:14px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.wifi-table th{position:sticky;top:0;background:var(--table-head);z-index:1;font-weight:500}.wifi-table tr:hover{background:rgba(128,145,170,.09)}.wifi-table .lan-link-speed{text-align:center}.wifi-table .col-band{width:72px}.wifi-table .col-host{width:240px}.wifi-table .col-ip{width:132px}.wifi-table .col-mac{width:170px}.wifi-table .col-signal{width:100px}.wifi-table .col-rate{width:120px}.wifi-table .col-time{width:130px} .wifi-host-edit{appearance:none;border:0;background:transparent;color:inherit;font:inherit;max-width:100%;padding:0;margin:0;cursor:pointer;text-align:left;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;text-decoration:underline;text-decoration-style:dotted;text-underline-offset:3px}.wifi-host-edit:hover{color:var(--blue)} .wifi-signal{font-weight:700;font-variant-numeric:tabular-nums;cursor:help;text-decoration:underline;text-decoration-style:dotted;text-decoration-thickness:1px;text-underline-offset:3px}.wifi-signal.best{color:#16a34a}.wifi-signal.excellent{color:#22c55e}.wifi-signal.good{color:#38bdf8}.wifi-signal.fine{color:#0ea5e9}.wifi-signal.normal{color:#f59e0b}.wifi-signal.weak{color:#fb7185}.wifi-signal.bad{color:#ef4444}.wifi-signal.unknown{color:var(--sub);text-decoration:none;cursor:default} .chart-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:14px}.chart-box{height:280px;background:var(--input);border:1px solid rgba(84,101,128,.58);border-radius:16px;padding:12px 12px 18px}.chart-box h3{margin:0 0 8px;color:var(--sub);font-size:13px}.chart-canvas{position:relative;height:calc(100% - 27px);min-height:0}.chart-canvas canvas{width:100%!important;height:100%!important}.chart-secondary{margin-top:14px}