hermes - ✅(Solved) Fix [Feature]: Add interactive column sorting to dashboard analytics tables [1 pull requests, 1 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
NousResearch/hermes-agent#17381Fetched 2026-04-30 06:48:02
View on GitHub
Comments
0
Participants
1
Timeline
6
Reactions
0
Participants
Timeline (top)
labeled ×3renamed ×2cross-referenced ×1

Fix Action

Fixed

PR fix notes

PR #17394: feat(dashboard): add interactive column sorting to analytics tables

Description (problem / solution / changelog)

What does this PR do?

Adds interactive column sorting to the three analytics tables (Daily Breakdown, Per-Model Breakdown, Top Skills). Previously the tables had fixed, hardcoded ordering. Now any column header can be clicked to sort by that column, with a second click toggling asc ↔ desc.

Related Issue

Fixes https://github.com/NousResearch/hermes-agent/issues/17381

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

  • web/src/pages/AnalyticsPage.tsx — added useTableSort<T> generic hook and SortHeader component; applied sorting to DailyTable, ModelTable, and SkillTable

Note on the Per-Model default sort: The previous hardcoded sort ordered by input_tokens + output_tokens combined. This PR defaults to input_tokens descending instead — a real column rather than a derived value. In practice the ranking is identical since input tokens dominate output by 3–10×, and using a real field keeps the sort hook generic without needing computed fields.

How to Test

  1. Run cd web && npm run dev and open the Analytics page
  2. Click any column header in the Daily Breakdown, Per-Model Breakdown, or Top Skills table — the table should sort by that column descending
  3. Click the same header again — order should flip to ascending; the arrow icon updates accordingly
  4. Click a different header — sort resets to descending on the new column; the inactive column headers show a dim ↕ indicator

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: macOS 15

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

Screenshots / Logs

<!-- Add a screenshot of the sorted table if you have one -->
  1. default column for sorting shown (descending icons)
  2. sorting functionality for columns (default neutral icons)
<img width="2560" height="1473" alt="image" src="https://github.com/user-attachments/assets/bc549814-a97e-4e45-9eee-756b89d3abc5" />

Changed files

  • web/src/pages/AnalyticsPage.tsx (modified, +121/-60)

Code Example

function useTableSort<T extends Record<string, unknown>>(
  data: T[],
  defaultKey: keyof T,
  defaultDir: 'asc' | 'desc' = 'desc'
) {
  const [sortKey, setSortKey] = useState<keyof T>(defaultKey);
  const [sortDir, setSortDir] = useState<'asc' | 'desc'>(defaultDir);

  const sorted = useMemo(() => {
    return [...data].sort((a, b) => {
      const aVal = a[sortKey];
      const bVal = b[sortKey];
      if (aVal === bVal) return 0;
      const cmp = aVal > bVal ? 1 : -1;
      return sortDir === 'asc' ? cmp : -cmp;
    });
  }, [data, sortKey, sortDir]);

  const toggle = (key: keyof T) => {
    if (key === sortKey) {
      setSortDir(d => (d === 'asc' ? 'desc' : 'asc'));
    } else {
      setSortKey(key);
      setSortDir('desc');
    }
  };

  return { sorted, sortKey, sortDir, toggle };
}

---

function SortHeader({ label, col, sortKey, sortDir, toggle }: SortHeaderProps) {
  const active = col === sortKey;
  return (
    <th onClick={() => toggle(col)} style={{ cursor: 'pointer', userSelect: 'none' }}>
      {label} {active ? (sortDir === 'asc' ? '▲' : '▼') : ''}
    </th>
  );
}

---
RAW_BUFFERClick to expand / collapse

Problem or Use Case

The Analytics page in the Hermes dashboard displays usage data across three tables (Daily Usage, Model Usage, Skill Usage) but none of the tables support user-controlled sorting. Sort orders are either hardcoded or absent, making it difficult to browse and compare data — e.g. finding the most-used model, the skill with the most edits, or a specific date range.

Column headers are not interactive. Sort orders are fixed:

TableCurrent Sort Behavior
DailyTableHardcoded reverse chronological
ModelTableHardcoded by total tokens (not shown to user)
SkillTableUnsorted — raw API data order

Users have no way to reorder data by any column.

Proposed Solution

Clicking a column header should sort the table by that column in ascending order. Clicking again should toggle to descending. The active sort column and direction should be visually indicated (e.g. ▲ / ▼ icons in the header).

Possible Implementation

Add a reusable useTableSort hook and apply it to all three tables with clickable, styled column headers.

Hook:

function useTableSort<T extends Record<string, unknown>>(
  data: T[],
  defaultKey: keyof T,
  defaultDir: 'asc' | 'desc' = 'desc'
) {
  const [sortKey, setSortKey] = useState<keyof T>(defaultKey);
  const [sortDir, setSortDir] = useState<'asc' | 'desc'>(defaultDir);

  const sorted = useMemo(() => {
    return [...data].sort((a, b) => {
      const aVal = a[sortKey];
      const bVal = b[sortKey];
      if (aVal === bVal) return 0;
      const cmp = aVal > bVal ? 1 : -1;
      return sortDir === 'asc' ? cmp : -cmp;
    });
  }, [data, sortKey, sortDir]);

  const toggle = (key: keyof T) => {
    if (key === sortKey) {
      setSortDir(d => (d === 'asc' ? 'desc' : 'asc'));
    } else {
      setSortKey(key);
      setSortDir('desc');
    }
  };

  return { sorted, sortKey, sortDir, toggle };
}

Column header helper:

function SortHeader({ label, col, sortKey, sortDir, toggle }: SortHeaderProps) {
  const active = col === sortKey;
  return (
    <th onClick={() => toggle(col)} style={{ cursor: 'pointer', userSelect: 'none' }}>
      {label} {active ? (sortDir === 'asc' ? '▲' : '▼') : ''}
    </th>
  );
}

Apply to all three tables. Complexity is medium but fully isolated to AnalyticsPage.tsx with no backend changes required.

Alternatives Considered

No response

Feature Type

Developer experience (tests, docs, CI)

Scope

None

Contribution

  • I'd like to implement this myself and submit a PR

Debug Report (optional)

extent analysis

TL;DR

Implementing the proposed useTableSort hook and applying it to the three tables in the Analytics page will enable user-controlled sorting.

Guidance

  • Apply the useTableSort hook to each of the three tables (DailyTable, ModelTable, SkillTable) to enable sorting by column.
  • Use the SortHeader component to create clickable column headers that toggle the sort order.
  • Verify that the sorting works as expected by testing different columns and sort directions.
  • Ensure the active sort column and direction are visually indicated using icons (▲ / ▼) in the header.

Example

// Example usage of useTableSort hook in a table component
function MyTable() {
  const { sorted, sortKey, sortDir, toggle } = useTableSort(data, 'defaultKey');
  return (
    <table>
      <thead>
        <tr>
          <SortHeader label="Column 1" col="column1" sortKey={sortKey} sortDir={sortDir} toggle={toggle} />
          <SortHeader label="Column 2" col="column2" sortKey={sortKey} sortDir={sortDir} toggle={toggle} />
        </tr>
      </thead>
      <tbody>
        {sorted.map(row => (
          <tr key={row.id}>
            <td>{row.column1}</td>
            <td>{row.column2}</td>
          </tr>
        ))}
      </tbody>
    </table>
  );
}

Notes

The proposed solution does not require any backend changes, and the complexity is isolated to the AnalyticsPage.tsx file.

Recommendation

Apply the workaround by implementing the useTableSort hook and SortHeader component in the Analytics page, as it provides a clear and isolated solution to enable user-controlled sorting.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

hermes - ✅(Solved) Fix [Feature]: Add interactive column sorting to dashboard analytics tables [1 pull requests, 1 participants]