feat(ui): quieten the dashboard — mute checks, hide app noise, honest sparkline (v0.23.6-beta)

Closes #101, #103, #104, #106, #111. Partially addresses #96.

A batch of signal-to-noise fixes reported together. Individually small; together
they are the difference between a dashboard someone reads and one they learn to
ignore.

#101 — security heuristics can be muted
  A warning you disagree with and cannot dismiss discredits every other finding
  beside it. Cleartext API is the reported case: on a fleet reachable only across
  WireGuard, TLS adds nothing to the threat model, and the check is permanent noise.
  Missing input-chain firewall is the same on a switch behind a router — the message
  even says "confirm that is the case", while offering no way to record that you did.

  Checks can now be muted per device or fleet-wide. Muted findings are still listed,
  greyed and labelled, and excluded from the score — not hidden. Hiding them would
  make the posture unauditable, which is a worse failure than noise.

#103 — the app's own requests are hidden from Recent activity
  Audit entries for writes made through the UI are noise from an operations point of
  view. Off by default now, with a toggle that names how many are hidden, remembered
  per browser since it is a viewing preference rather than fleet configuration.

#104 — quick actions moved into the header strip
  Matching the Wireless page. "Things to handle" takes the full width it was already
  overflowing.

#106 — map lookups can be turned off
  Geocoding hits nominatim.openstreetmap.org and the map pulls OSM tiles. On an
  isolated network those hang; everywhere else they disclose where your devices are
  to a third party, which the report understates. A maps_enabled setting stops both
  requests; stored coordinates are still shown as plain text so nothing is lost.

#111 — the CPU sparkline says something
  It was drawn in one colour and auto-scaled to each device's own peak, so an idle
  device and a saturated one produced an identical picture. Now plotted against a
  fixed 0-100 scale, coloured by the band its peak falls into — a flat line low in
  the box is an idle device and looks like one.

#96 — groundwork
  TX-retry figures come from CCQ, which only the legacy `wireless` driver reports, so
  the panel is permanently empty on a RouterOS 7 fleet. The wireless package each
  device runs is now recorded, and a capabilities endpoint answers the fleet-wide
  question. The panel is dropped and the density chart takes the full width when
  nothing can supply the data — and shown when nothing has been polled yet, rather
  than hiding a panel that may populate a minute later.

Verified against the fleet: muting cleartext API moved a device's score from 87 to
90 with the finding still listed and marked.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2GT-Rich
2026-08-22 14:18:41 -07:00
co-authored by Claude Opus 5
parent 4cc8c84ea6
commit dcd89fe86b
14 changed files with 327 additions and 70 deletions
+23
View File
@@ -174,6 +174,20 @@ export interface SecurityCheck {
title: string;
detail: string;
serviceId?: string;
/** Operator judged this inapplicable — still shown, but excluded from the score. */
suppressed?: boolean;
suppressed_scope?: 'device' | 'fleet';
suppressed_reason?: string | null;
}
export interface SecuritySuppression {
id: number;
check_id: string;
device_id: number | null;
device_name: string | null;
reason: string | null;
created_by: string | null;
created_at: string;
}
/**
@@ -340,6 +354,12 @@ export const devicesApi = {
setServiceDisabled: (id: number, serviceId: string, disabled: boolean, confirmLockout = false) =>
api.put<Record<string, string>[]>(`/devices/${id}/services/${encodeURIComponent(serviceId)}`,
{ disabled, ...(confirmLockout ? { confirm_lockout: true } : {}) }),
listSecuritySuppressions: () =>
api.get<SecuritySuppression[]>('/devices/security/suppressions'),
suppressSecurityCheck: (check_id: string, device_id: number | null, reason?: string) =>
api.post('/devices/security/suppressions', { check_id, device_id, reason }),
unsuppressSecurityCheck: (id: number) =>
api.delete(`/devices/security/suppressions/${id}`),
getSecurityPosture: (id: number) =>
api.get<{ score: number; checks: SecurityCheck[] }>(`/devices/${id}/security-posture`),
// Config Health — cached findings load instantly; the scan re-reads the device.
@@ -1035,6 +1055,9 @@ export const wirelessApi = {
// RF Health (fleet-wide, or scoped to one AP via deviceId)
getChannelUsage: (deviceId?: number) =>
api.get<import('../types').RfChannelUsage>('/wireless/rf/channels', { params: { deviceId } }),
getRfCapabilities: () =>
api.get<{ packages: Record<string, number>; legacyRadios: number; txRetriesAvailable: boolean }>(
'/wireless/rf/capabilities'),
getClientSignals: (deviceId?: number) =>
api.get<import('../types').RfSignalRow[]>('/wireless/rf/signals', { params: { deviceId } }),
getTxQuality: (deviceId?: number, range = '6h') =>