feat(ui): theme-aware logo, dashboard bar graph, server-side client sorting (v0.14.1-beta)

UI/UX refinements on top of v0.14.0-beta, all validated on the test server:

- Logo: new theme-aware compass iconmark (light/dark SVGs swapped via the
  dark class), sized to 45px; favicon updated to match. Wordmark casing
  corrected to "MikroTik Manager" (font size unchanged).
- Navigation icons enlarged 15 -> 17px.
- Dashboard: "Connected clients" line chart converted to a bar chart with a
  category axis + light downsampling so bars stay readably thick across ranges.
- Clients: sorting is now server-side across the whole dataset (was sorting
  only the current page, so Type/others appeared to do nothing on a uniform
  page); table uses a fixed column layout + keepPreviousData so columns no
  longer jump or blank out when sorting/paging. Backend clients route gains
  whitelisted sort/dir params (safe numeric IP sort, deterministic tiebreaker).
- Connections tab: surfaces the live /ip/firewall/connection/tracking state
  (on/auto/off pill) and clearer empty-state copy explaining auto-mode.

Lint/typecheck/build/jest green in both workspaces; npm audit --audit-level=high
clean.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2GT-Rich
2026-06-18 13:37:11 -07:00
co-authored by Claude Opus 4.8
parent ee9c9ff9f7
commit d79eb13119
18 changed files with 402 additions and 93 deletions
+2 -2
View File
@@ -897,9 +897,9 @@ router.delete('/:id/address-lists/:entryId', requireWrite, async (req, res) => {
router.get('/:id/connections', async (req, res) => {
const limit = Math.min(parseInt(String(req.query.limit || '500'), 10) || 500, 2000);
await withCollector(req.params.id, res, async (c) => {
const rows = await c.getConnections();
const [rows, tracking] = await Promise.all([c.getConnections(), c.getConnectionTracking()]);
// Connection tables can be huge; cap and surface the total so the UI can note truncation.
return { total: rows.length, connections: rows.slice(0, limit) };
return { total: rows.length, connections: rows.slice(0, limit), tracking };
});
});