feat(analytics): sticky range + sortable per-model table columns - #463
Closed
jasnoorgill wants to merge 2 commits into
Closed
feat(analytics): sticky range + sortable per-model table columns#463jasnoorgill wants to merge 2 commits into
jasnoorgill wants to merge 2 commits into
Conversation
Remembers the last-selected 24h/7d/30d range across reloads and new sessions so the user lands back on the window they were inspecting. Same localStorage shape as the theme toggle (key: 'theme') and the locale picker (key: 'freellmapi.locale'). Storage key: 'freellmapi.analytics.range'. Invalid or missing values fall back to the 7d default; storage errors (quota, private mode) are swallowed so the page keeps working.
3-state cycle per column: unsorted (API order) → asc → desc → unsorted. Clicking a different column resets to asc on the new column. Last selection persists in localStorage so reloads land on the user's preferred sort (key: 'freellmapi.analytics.byModelSort'). Sortable columns: model, provider, requests, success%, latency, inTokens, outTokens, saved ($). The 'pinned' column stays static because it renders '—' for zero-pinned rows and the unsortable 0/>0 distinction would confuse the indicator. Null values sort last in both directions (Excel/Sheets convention). Same persistence pattern as the range selector (tashfeenahmed#462) and the existing 'theme' / 'freellmapi.locale' keys. Right-aligned columns flip the sort-indicator with flex-row-reverse so the label stays closest to the data. aria-sort attributes set on the buttons for screen readers.
Owner
|
Thanks, the sticky range and sortable columns work looks good and CI was green. Main has since shipped the redesigned Analytics page (#467) plus a new Recent Calls table (#474), so this now conflicts on AnalyticsPage.tsx. Could you rebase onto latest main and re-apply the sticky range and sort on the new table layout? Happy to merge once it's green again. |
Contributor
Author
|
Rebased, filed #490
Thanks
Jasnoor
…On Tue, Jul 7, 2026 at 2:10 AM Tashfeen ***@***.***> wrote:
*tashfeenahmed* left a comment (tashfeenahmed/freellmapi#463)
<#463 (comment)>
Thanks, the sticky range and sortable columns work looks good and CI was
green. Main has since shipped the redesigned Analytics page (#467
<#467>) plus a new Recent
Calls table (#474 <#474>),
so this now conflicts on AnalyticsPage.tsx. Could you rebase onto latest
main and re-apply the sticky range and sort on the new table layout? Happy
to merge once it's green again.
—
Reply to this email directly, view it on GitHub
<#463?email_source=notifications&email_token=ABJ5OOWG6FSMCJSLSTEWIGT5DQFCRA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTIOBZG42DCMZWGIY2M4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2KYZTPN52GK4S7MNWGSY3L#issuecomment-4897413621>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/ABJ5OOQDK35CHVXY53A5VEL5DQFCRAVCNFSNUABGKJSXA33TNF2G64TZHMYTEMJXGA2DAMJYGU5US43TOVSTWNBYGA4DSMJVGI4DRILWAI>
.
Triage notifications, keep track of coding agent tasks and review pull
requests on the go with GitHub Mobile for iOS
<https://github.com/notifications/mobile/ios/ABJ5OOW5P7AOY3T2FBRXZ5T5DQFCRA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTIOBZG42DCMZWGIY2M4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2KUZTPN52GK4S7NFXXG>
and Android
<https://github.com/notifications/mobile/android/ABJ5OOXE67WROI4FUS6JHWD5DQFCRA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTIOBZG42DCMZWGIY2M4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2K4ZTPN52GK4S7MFXGI4TPNFSA>.
Download it today!
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two small but related client-side improvements to the analytics page,
shipped together because they share infrastructure and would otherwise
conflict on the same lines of
AnalyticsPage.tsx:in
localStorageso reloads and new sessions land on the user'slast choice.
breakdown" table (except
pinned, which renders—) is nowclickable to sort. 3-state cycle:
unsorted (API order) → asc → desc → unsorted. Last selection persists inlocalStoragetoo.Both use the same persistence shape as the existing
themetoggleand
freellmapi.localepreference. SameuseState+useEffectpattern, same try/catch on every storage access.
Why combined
Originally shipped as PR #462 (sticky range only). Adding the sort
work on top of #462's branch required a rebase that would have
re-written #462's commits — at that point the cleanest move was a
single PR covering both. The two features are thematically adjacent
(analytics-page UX) and small individually, so combining avoids
two PRs reviewing the same file.
Changes
client/src/pages/AnalyticsPage.tsxuseMemoto imports,STORAGE_KEY/loadStoredRange/sort types,SortableHeaderhelper component,sortstate + persistence effect,onHeaderClickreducer,sortedByModelmemoized sort, replaced 8 of 9 column headers with<SortableHeader>(pinned stays static), addedlucide-reactArrowUp/ArrowDown/ChevronsUpDown imports.1 file, +188 / -11 across 2 commits.
Sort behavior
key removed.
flex-row-reversesothe label stays closest to the data.
aria-sortattribute is set on each header button for screen readers.pinnedis intentionally not sortable: it renders—for zero-pinnedrows, and the 0/>0 distinction is meaningless to sort on.
localStorage keys
freellmapi.analytics.range'24h'|'7d'|'30d'freellmapi.analytics.byModelSort{"column": "...", "direction": "asc"|"desc"}or absentBoth keys are validated against an allowlist on read so a corrupted
entry falls back to the default rather than crashing the page. Both
writes are wrapped in try/catch for SSR / private-mode safety.
Test plan
npx tsc -binclient/— clean, 0 errors.npx tsc -binserver/— 9 errors inserver/src/routes/keys.tsfrom upstream's
multertyping bug (PR I added Agnes AI, but it doesn’t appear in the model list. #438, pre-existing onorigin/mainat340f414). Not introduced by this PR —same errors reproduce on a clean
git checkout origin/main.npx vitest runinserver/— 524/524 passing, 0 regressions.integration/all-my-prs @ d08383b.Service running on PID 864746, client bundle has both
freellmapi.analytics.rangeandfreellmapi.analytics.byModelSortstorage keys + the sort logic.
Manual smoke:
/analytics, click 30d. Hard-reload — opens on 30d.shows ↑, header text darkens.
removed from localStorage, the page loads in API order.
Compatibility
No breaking changes. Defaults are unchanged (7d range, API-natural
row order). The persistence is additive. New
localStoragekeysare namespaced under
freellmapi.*per the existing convention.Supersedes
Closes #462 — the sticky-range half of this work is included here.
References operator sessions 2026-07-04. No upstream issue number
yet — happy to file one if requested.