Skip to content

feat(dashboard): add dark mode, pagination in recent requests, and management key persistence - #2

Merged
dohwi merged 8 commits into
dohwi:mainfrom
ngnsusinn:feat/dashboard-dark-mode-pagination
Nov 4, 2026
Merged

dohwi merged 8 commits into
dohwi:mainfrom
ngnsusinn:feat/dashboard-dark-mode-pagination

Conversation

@ngnsusinn

Copy link
Copy Markdown
Contributor

Summary

This PR adds three requested features to the Usage Audit dashboard:

  1. Dark mode: Toggle button (sun/moon) on both the filter toolbar and the authentication gate, automatic system color scheme detection via prefers-color-scheme, persisted in localStorage (cpa_plugin_theme), with a full slate dark palette for KPI cards, tables, charts, and modals.
  2. Pagination in Recent Requests: Standard pagination navigation (< Prev, page numbers 1, 2, ..., Next >) displaying 20 items per page with instant client-side switching across loaded pages and automatic cursor-based fetching for subsequent pages.
  3. Management Key Persistence: Added an auto-save / "Remember management key" option (enabled by default) saving the accepted management key to localStorage (cpa_usage_audit_mgmt_key), eliminating the need to re-enter the key on every tab/session reload. Rejection (401/403) continues to clear both storage areas.

Changes

  • internal/plugin/style.css: Added :root[data-theme='dark'] tokens, dark mode component overrides, theme toggle button styles, and .ui-pagination bar layout.
  • internal/plugin/dashboard.html: Added theme toggle buttons, auto-save checkbox in auth gate, and pagination controls in #loadMoreBar.
  • internal/plugin/app.js: Added theme switching and persistence logic, paging state & page slicing in paintRecords(), auto-save management key handling, and updated i18n catalogs (ko / en).
  • internal/plugin/i18n_test.go: Updated storage contract test to account for the persisted theme and management key storage accessors.
  • ui/build.mjs: Replaced hardcoded table backgrounds with var(--bg-card) and var(--surface-hover) for clean theme compatibility.

Verification

  • go test -v ./internal/plugin/... passed completely (all tests passing including storage contract and i18n completions).
  • go test -buildvcs=false ./... passed across all packages.
  • go vet ./... passed with zero warnings.
  • npm run build in ui/ succeeds and keeps self-contained dashboard bundle in sync.

@dohwi

dohwi commented Nov 4, 2026 •

Copy link
Copy Markdown
Owner

Thanks for the PR! Dark mode and pagination look good. I pushed one follow-up commit (30155cf) to this branch (maintainer edits) that changes two things:

1. Management key: reuse the host's key read-only instead of persisting our own copy

  • Reverted the localStorage copy of the key and the "remember" checkbox. SECURITY.md promises the key is never written to localStorage, and the new auth.desc text said "securely stored", which is inaccurate for plaintext localStorage.
  • To keep the "no re-entry" goal, the page now reads the key the host Management Center already stores for the same origin (localStorage['cli-proxy-auth'], incl. enc::v1::/enc::v2::), as described in the Management API docs and done by other store plugins. It is read-only, skipped when rememberPassword is off or the saved apiBase is another origin, and a rejected host key is not retried during that page load (host IP-ban counter).
  • Order: tab sessionStorage -> host key -> input gate. SECURITY.md, auth.desc and the storage-contract test were updated to match (localStorage: language/theme read+write, cli-proxy-auth read-only).

2. Pagination: state.targetRecordsPage is now reset when a page load fails, so a later append cannot jump to a stale page.

go vet, go test ./... and npm run build pass. The host-key path is verified against synthetic plain/v1/v2 payloads, not yet against a live Management Center.

Optional follow-ups (not done): inline <head> theme script to avoid a light flash, and removing the now-unused #loadMoreBtn/loadMoreRecords().

…ting it

- Drop the localStorage copy of the management key and the remember checkbox.
- Read the host Management Center key (cli-proxy-auth, enc::v1/v2) read-only,
  skipping it when rememberPassword is off or apiBase is another origin;
  a rejected host key is not retried during the page load.
- Reset targetRecordsPage when a page load fails.
- Update storage contract test and SECURITY.md accordingly.
@ngnsusinn
ngnsusinn force-pushed the feat/dashboard-dark-mode-pagination branch from 48862ed to 30155cf Compare November 4, 2026 03:29
- Theme toggle (system -> light -> dark) next to the language button, on the
  toolbar and the auth gate. Default 'system' follows the host Management
  Center (parent data-theme, cli-proxy-theme, then the OS); the choice is the
  only thing stored (cpa_plugin_theme). The host entry is read, never written.
- Keep the original light design; dark uses the same cool-slate identity with
  lower saturation. Hard-coded light surfaces moved to tokens so dark can
  override them without changing any light value.
- Storage contract test and SECURITY.md updated accordingly.
@ngnsusinn

Copy link
Copy Markdown
Contributor Author

Thank you so much @dohwi for the thorough review, guidance, and the follow-up commits (30155cf and 4f0e5fb)!

I sincerely apologize for overlooking the security contract in SECURITY.md and loosening the storage-contract test in the initial PR. Reusing the host Management Center's key read-only and adding the 3-way system/light/dark theme toggle is a much cleaner and safer approach.

All tests (go test ./..., go vet ./..., and npm run build) pass cleanly. The PR is ready whenever you would like to merge. Thanks again for maintainer edits and patience!

dohwi added 5 commits November 4, 2026 12:43
…verflowing

- Prev/Next buttons: aria-label now follows the language (data-i18n-aria).
- With the theme toggle added next to the language button the mobile footer
  reserved 124px of padding and squeezed the filter text to zero width at
  <=359px; stack the text above the buttons there and shift the reset button
  at <=767px so it no longer overlaps the new toggle.
- 'System' mapped the host's white theme to a pure-white page while the
  explicit Light mode is the original off-white, so switching System -> Light
  changed the background even though both are light. This page has no separate
  white look: host white now resolves to light.
- Colour transitions on individual controls (tabs, buttons, selects, ...) ran
  on their own 0.15s clocks while cards switched instantly. Freeze them during
  the swap and crossfade the whole page once via the View Transitions API
  (falls back to an instant, transition-free swap; honours reduced motion).
The View Transitions crossfade made the page, cards and buttons switch
instantly while selects and toggles (which carry their own 0.15s colour
transitions) faded afterwards, so they changed at different times; the control
under the pointer also lost :hover during the transition and tweened late.
Drop the view transition and, for the duration of the swap, force one uniform
colour transition on every element so everything fades together.
The selected tab's tint (#e8eef5) was only ~1.05:1 against the tab group's
background, so the selected state was hard to see in light mode. Draw it as a
white pill with a soft shadow and hairline ring (same idea as the selected
period button); in dark mode add a hairline ring to the tinted pill.
Controls carry their own 0.15s colour transitions while cards and the page do
not, so a theme change looked staggered. Switch every transition off for the
swap and restore it once the new colours are painted; hover/focus transitions
are unaffected.
@dohwi

dohwi commented Nov 4, 2026

Copy link
Copy Markdown
Owner

Merging this with a few follow-up commits on top of the original change. Summary of what ended up in the PR:

Original (thanks @ngnsusinn)

  • Dark mode, pagination in "Recent requests" (20 per page, cursor-based fetching for later pages).

Follow-ups pushed to this branch

  • Management key: dropped the localStorage copy of the key and the "remember" checkbox (it contradicted SECURITY.md and was on by default). The page now reads the key the host Management Center already stores for the same origin (cli-proxy-auth, incl. enc::v1::/enc::v2::), read-only, only when the host's "remember password" is on and its apiBase matches this origin. A rejected host key is not retried (host IP-ban counter). The key itself stays in the tab's sessionStorage only.
  • Theme: system / light / dark toggle next to the language button (toolbar and auth gate). "System" follows the host (parent data-theme, cli-proxy-theme, then the OS; the host's white counts as light); only the chosen mode is stored. Light keeps the original design; dark uses the same cool-slate palette with lower saturation. The switch is instant (no per-component colour animation).
  • Selected breakdown tab is now clearly visible (white pill + soft shadow in light, ring in dark).
  • Pagination: reset targetRecordsPage when a page load fails; localized prev/next aria-label.
  • Responsive: the footer fits the extra toggle at <=767px, stacks at <=359px.
  • SECURITY.md and the storage-contract test updated to match.

Verified with make verify and the browser scripts (test-usage-dashboard-{auth,language,layout,loading}.cjs), plus a deployment to a CLIProxyAPI instance. Not verified against a live Management Center iframe theme change.

@dohwi
dohwi merged commit 40ce069 into dohwi:main Nov 4, 2026
@dohwi

dohwi commented Nov 4, 2026

Copy link
Copy Markdown
Owner

@ngnsusinn Thanks again for the contribution! 🎉

This PR is now fully merged into main (merge commit 40ce069, your original commit and authorship are kept in the history), and it shipped as v0.4.1.

You can update from the CLIProxyAPI plugin store now: usage-audit v0.4.1 is the latest release, so the store's update button will pick it up. No registry change was needed.

What's in v0.4.1 (your dark mode and pagination, plus the follow-ups on top):

  • Dark mode + a system / light / dark toggle next to the language button ("system" follows the Management Center theme)
  • Recent requests pagination (20 per page)
  • The dashboard reuses the management key the Management Center already stores (read-only), instead of keeping its own copy
  • Selected-tab visibility, theme switch, and narrow-screen layout fixes

Please don't push more commits to feat/dashboard-dark-mode-pagination; it's merged, and the branch can be deleted. For further changes, a new PR against main is welcome.

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.

2 participants