feat(security): reflow the node Security page for mobile (#1372)

* feat(security): reflow the node Security page for mobile

Below the md breakpoint the Security page now reads as a phone surface
instead of a squeezed desktop, with no change to the desktop layout.

- Masthead stat cluster moves into a full-width 3-cell strip
  (critical / high / last scan) below the tab strip, since the masthead
  hides its inline cluster on a phone.
- The eight-section tab strip becomes a horizontally scrollable mono row
  with an edge mask-fade and a cyan underline on the active tab; every
  section stays reachable by scroll.
- The six totals render as a 3x2 hairline-divided grid instead of the
  640px-wide rail that forced a horizontal scroll.
- The Images tab becomes a filterable, scrollable list (severity dot,
  truncated ref, freshness, critical/high count tags) with a chip row,
  in place of the desktop table.
- A freshness footer band states scan recency and scanner version.

All mobile treatment is gated by useIsMobile() or max-md: utilities, so
the desktop view is byte-identical. Charts are reused full-width.

* feat(security): make the mobile Security page a bespoke masthead-led screen

On a phone the Security page now drops the global top bar and leads with
its masthead (the notifications + more-menu cluster moves into the
masthead's right slot), matching Home and Fleet so the mobile shell is
continuous across pages. The view is reclassified bespoke and rendered
through the masthead-led path; the desktop layout is unchanged.

The mobile "more" menu now lists every destination instead of omitting
the bottom-tab views, so the same menu opens the same set on every
screen rather than changing contents from page to page.
This commit is contained in:
Anso
2026-06-14 21:28:13 -04:00
committed by GitHub
parent a5109e7916
commit 0066887cee
12 changed files with 712 additions and 96 deletions
@@ -13,8 +13,8 @@ describe('mobile treatments', () => {
}
});
it('treats the Security view as responsive (reflowed, not bespoke or desktop-only)', () => {
expect(MOBILE_TREATMENTS.security).toBe('responsive');
it('treats the Security view as a bespoke masthead-led phone screen', () => {
expect(MOBILE_TREATMENTS.security).toBe('bespoke');
});
it('keeps BESPOKE_MOBILE_VIEWS in lockstep with the bespoke treatments', () => {
@@ -27,10 +27,12 @@ describe('mobile treatments', () => {
it('pins the set of bespoke phone screens (update deliberately when adding one)', () => {
// A change here means a top-level view gained or lost a bespoke phone screen.
// Updating this list should go hand in hand with adding the screen under
// components/mobile/ and wiring its case in EditorLayout's renderMobileBespoke.
// Updating this list should go hand in hand with wiring the screen's case in
// EditorLayout's renderMobileBespoke (a dedicated component under
// components/mobile/, or a masthead-led mobile branch of the desktop view as
// Security does).
expect([...BESPOKE_MOBILE_VIEWS].sort()).toEqual(
['dashboard', 'fleet', 'scheduled-ops', 'settings'],
['dashboard', 'fleet', 'scheduled-ops', 'security', 'settings'],
);
});
});
@@ -22,7 +22,7 @@ export const MOBILE_TREATMENTS: Record<ActiveView, MobileTreatment> = {
settings: 'bespoke',
editor: 'detail',
resources: 'responsive',
security: 'responsive',
security: 'bespoke',
templates: 'responsive',
'global-observability': 'responsive',
'auto-updates': 'responsive',