Skip to content

feat(web): console-style redesign on shadcn-svelte components - #136

Open
flamboh wants to merge 9 commits into
feat/redesignfrom
feat/console-ui
Open

flamboh wants to merge 9 commits into
feat/redesignfrom
feat/console-ui

Conversation

@flamboh

@flamboh flamboh commented Oct 9, 2026 •

Copy link
Copy Markdown
Owner

Note

🤖 GPT-6.1-Sol on behalf of Oliver

Why

Oliver asked for a more modern dashboard built from familiar shared components, and picked option A ("Console", in the Vercel/Geist style) from three mockups. This PR stacks on #135. It restyles the app on shadcn-svelte (bits-ui) primitives and removes the hand-rolled rail controls. Chart rendering is unchanged apart from theme tokens.

What changed

  • Shell. The top bar now reads Logo / dataset switcher (a Popover with a searchable Command list), then a ⌘K command menu and the theme toggle on the right. Below it is a row of underline tabs: Dashboard, Files, Alerts. On the home page the tabs are Datasets and Files.

  • One filter toolbar instead of the left rail. The toolbar holds:

    • Date range: a Popover with a Range Calendar, presets, and typed YYYY-MM-DD fields. The presets are Last day, Last 7 days, Last 30 days and All available.
    • Sources: a Command checklist with search and select all/clear.
    • Interval and Direction: Selects.
    • MAAD: a Popover with measure and address-family segmented toggles.
    • Reset.

    Each control shows its current value in muted text and wraps at narrow widths.

  • KPI row. Four cards show totals for the selected window: Flows (with TCP share), Packets (per flow), Bytes (per packet), and Complete buckets (with partial/unknown counts). They're computed from the /api/netflow/stats window the traffic card already uses. No new query and no "vs prior" deltas. Unique-IP counts aren't summed.

  • Chart cards. Each header has the drag handle and title on the left. The right side holds segmented ToggleGroups, a Metrics/Ranges checklist popover, and a ⋯ DropdownMenu (Move up/down, Taller/Shorter). These replace the old title-bar buttons and keep drag reorder and native resize. Cards stay in the packed single column.

  • Visual system. Neutral Geist-like greys, 1px borders, an 8px radius, a small card shadow and tabular numbers. Blue #2563eb is used only for selection, focus and data. Light and dark themes both got full token sets, and the chart grid/axis tokens were retuned for the new surfaces.

  • Other pages use the same shell:

    • Home: a datasets table.
    • Files: a form with a NativeSelect that keeps ?dataset in sync with the top bar.
    • File detail: a header, inline MAAD toggles, and per-source summary tables with badges.
    • Alerts: segmented filters, a Live/Idle badge, Table and Empty states.
  • Removed: AnalysisLayout, FilterSheet, ControlPopover, SegmentedControl, CardLayoutControls, DragGrip, PrimaryFilters, RouterFilter, DateRangeFilter, MetricSelector, Direction/MAAD filters and DatasetTabs. Filter and metric semantics still live in the existing modules (schemas.ts, chart-utils.ts, the types.ts options).

Logic and accessibility review

GPT-6.1-Sol reviewed the full diff against feat/redesign and hardened the following paths:

  • Reversed traffic windows are rejected before the window cache can normalize them. Cached filter changes hide the previous request's totals until the selected request settles.
  • The KPI sums additive API fields and counts the API's coverage states. Complete buckets with zero-valued data still count as complete. Unknown coverage is labeled "unknown", rather than "missing".
  • Card menus use bits-ui's normal Tab and Shift+Tab handling. Their visible order matches their DOM order at narrow widths. The persistent card slot still restores focus after moving a card, including an unmounted placeholder that activates during the move.
  • Lazy source and dataset lists focus their search only while focus remains on the opening popover. They preserve focus if the user has already interacted with another control. The dataset popover has the accessible name "Choose dataset" and both lists announce loading.
  • The unused app-wide Tooltip provider was removed. Dashboard initial JS is now 201.6 KiB gzip, compared with 202.0 KiB before review and 182.7 KiB on the base branch. The remaining bits-ui cost was left in place to preserve the requested components.

The shared page-load stats request and date-preset decisions are retained. Immediate KPIs need that request, and activating the Traffic card reuses it. Selected-end-date presets support historical captures. "All available" keeps the existing configured dataset-start-to-today window; dataset summaries currently provide no last-available date.

Regression coverage in console-accessibility.spec.ts and console-window.spec.ts checks menu Tab order, slow list loading, dialog naming and cancellation, focus after placeholder activation, reversed windows and cached-window totals. Keyboard command-menu focus was also checked at 390px and 1280px using the fixture dataset. No deployment was changed during the review.

Design screenshots (Playwright fixture data, viewport captures)

Each sheet shows Before (#135) next to the console design before the logic review, in light and dark. The review moves the narrow card menu after its controls so visual order matches keyboard order.

Dashboard at 1280px, before and after, light and dark
Dashboard at 390px, before and after, light and dark
Files at 1280px, before and after, light and dark
Files at 390px, before and after, light and dark
File detail at 1280px, before and after, light and dark
File detail at 390px, before and after, light and dark

Human review guide

Setup: bun install, then bun run dev:web against any dataset. Alternatively, bun run test:e2e seeds the Playwright fixture (playwright and playwright-external-maad). The perf stage on :8090 runs the initial console design on uoregon-v5 (read-only). The review commits have not been redeployed there.

Flows to try

  1. Dashboard toolbar:
    • Pick a preset, then a calendar range, then type an invalid date such as 2025-02-31 (the field is flagged and the URL is unchanged).
    • Use the Sources search with Enter to toggle, then Clear and Select all.
    • Change the Interval, Direction and MAAD options. Reload and use back/forward; the URL state should survive.
    • Reset.
  2. KPI row: change the window or sources, and compare the numbers with the traffic chart tooltip totals.
  3. ⋯ menu on several cards: move up/down and taller/shorter by keyboard. Focus should return to the menu trigger after a move or resize, and the order should persist across reloads. Tab and Shift+Tab should leave the menu normally, in the same order as the visible controls. Drag reorder and the native resize corner should still work.
  4. Dataset switcher and ⌘K: switch datasets from the dashboard and from alerts (you stay on the same tab), jump to Files, and toggle the theme.
  5. Files → open 202503010200 → file detail: change the MAAD toggles and use Next File.
  6. Alerts: change tail, horizon and sort, use show more, and check the copy-command empty state.

Edge cases

  • Reversed dates show the alert inside the toolbar.
  • With no sources selected or reversed dates, the KPIs read "Unavailable". Reversed windows do not issue a stats request.
  • A deferred (not yet loaded) card still has a working ⋯ menu, and moving a card mounts it.
  • Coarse pointers get 44px toolbar and menu targets.
  • At 320px the header fits; the dataset name truncates.

Decisions to review

  • The KPI row requests the stats window up front, even when the traffic card is deferred or ordered later. It's still exactly one request, shared with the card (netflow-stats-data.svelte.ts, asserted in dashboard-lazy-charts).
  • Window presets end on the selected end date ("Last 7 days" = end−6 … end), because the data is date-granular and may be historical. "All available" spans dataset start … today.
  • Interval and Direction are Selects rather than segmented controls. The MAAD options are in a popover on the dashboard but inline on file detail.
  • Card titles now match the visible chart titles ("Unique IP Counts", "Unique Protocol Counts", "Spectrum").
  • The command palette, the dataset/source command lists and the range calendar load on first open to keep route JS down.

Verification

Automated (all passing)

  • bun run format, bun run lint (eslint, infra and clippy) and bun run typecheck (svelte-check: 0 errors, 0 warnings).
  • CI run 37908423730 passed Web Checks, Rust Pipeline Checks and Playwright E2E on 3962184.
  • bun run test:web: 246 tests. New: tests/lib/console-ui.test.ts covers date presets and validation, window totals and shell routing. The stats route test also covers exclusive end bounds, complete zero-valued buckets, partial observations and unknown coverage.
  • bun run test:e2e run serially (--workers=1) against the production build: 110 passed.
    • New console-ui.spec.ts covers:
      • KPIs matched to the /api/netflow/stats response;
      • presets, calendar and typed fields;
      • Sources search;
      • the ⋯ menu move and resize with focus retained;
      • packed panels;
      • placeholder menus;
      • the dataset switcher and ⌘K;
      • toolbar wrapping;
      • coarse-pointer targets;
      • the file layout.
    • The existing specs were updated to the new locators through tests/e2e/toolbar-helpers.ts. redesign.spec.ts (which tested the old rail and sheet) was replaced.
  • bun run perf:bundle for feat(web): redesign analysis as an Ops console #135 and this branch, measured with measureBundles (gzip):
Route initial JS (gzip) #135 This PR Δ
/ 97.7 KiB 107.6 KiB +10.1%
/datasets/[dataset] 182.7 KiB 201.6 KiB +10.4%
/datasets/[dataset]/alerts 102.2 KiB 113.5 KiB +11.1%
/netflow/files 98.9 KiB 108.4 KiB +9.7%
/netflow/files/[slug] 159.2 KiB 174.4 KiB +9.6%
All emitted JS 204.0 KiB 255.6 KiB +25.3%
CSS 12.6 KiB 15.7 KiB +24.4%

The cost is bits-ui:

  • Popover, Select, DropdownMenu and ToggleGroup, plus floating-ui positioning, account for the eager component cost. The unused Tooltip provider has been removed.
  • Command, RangeCalendar and @internationalized/date are split into chunks that load on first open.

The first cut was +21.5 KiB on / and +45 KiB on the dashboard. Lazy-loading the palette, the lists and the calendar brought that down to about +10 KiB and +19 KiB. The CSS growth comes from component utility classes.

Manual checks done

  • Fixture screenshots at 1280 and 390, light and dark, on the dashboard, Files and file detail (above).
  • Real-data QA on the redeployed perf stage (:8090, uoregon-v5 read-only): KPIs, all eight cards, both themes and 390px. These screenshots are private and not included. The test stage container was verified unchanged.

Remaining manual checks

  • Screen-reader pass on the Range Calendar and Command lists, and Safari/Firefox checks; only Chromium was tested.
  • Bundle increase sign-off.

Design and implementation by Claude Opus 5.5 in Claude Code. Review fixes by GPT-6.1-Sol through the Codex harness in T3 Code.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant