Skip to content

feat(usage): break usage down by request type or SDK - #8343

Open
talissoncosta wants to merge 33 commits into
mainfrom
feat/usage-breakdown-8186
Open

feat(usage): break usage down by request type or SDK#8343
talissoncosta wants to merge 33 commits into
mainfrom
feat/usage-breakdown-8186

Conversation

@talissoncosta

@talissoncosta talissoncosta commented Aug 21, 2026

Copy link
Copy Markdown
Contributor
  • I have read the Contributing Guide.
  • I have added information to docs/ if required so people know about the feature.
  • I have filled in the "Changes" section below.
  • I have filled in the "How did you test this code" section below.

Changes

Closes #8186.

A section under the chart showing where the usage came from, with a menu for request type or SDK. Ranked rows, totals only, no plan limit, since a single request type has no allowance of its own. Both dimensions come from the response the charts already fetch, so it costs no extra requests.

Project and environment are split out into #8362, blocked on #8361. Each costs one request per key against an endpoint throttled at 5/min per user, so a multi-project org would get 429s and a partial ranking. Nothing is lost today: the current page has a project filter, not a project ranking.

Two changes reach outside the feature:

  • shared/ValueBar is extracted from UsageBar, same track-and-fill markup. ProjectUsageTab and EnvironmentOverrideUsage render it unchanged
  • the plan bar reports role="meter" instead of role="progressbar". ARIA reserves progressbar for a task advancing. Meter has patchier screen reader support, so it is a deliberate trade

How did you test this code?

8 unit tests over the derivations.

With usage_dashboard on:

  • request type: four rows, biggest first, shares summing to 100%
  • SDK: one row per user agent, unattributed under "Unknown"
  • change the period or project filter: rows follow, no stale numbers
  • no usage in the period: empty state, not zero rows
  • Project Settings > Usage and environment override bars unchanged, they share ValueBar

@vercel

vercel Bot commented Aug 21, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
flagsmith-frontend-preview Ready Ready Preview Aug 25, 2026 7:36pm
flagsmith-frontend-staging Ready Ready Preview Aug 25, 2026 7:36pm
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Ignored Ignored Preview Aug 25, 2026 7:36pm

Request Review

@coderabbitai

coderabbitai Bot commented Aug 21, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d137d3db-a854-4e7c-a6be-203057129b40

📥 Commits

Reviewing files that changed from the base of the PR and between 4f84749 and 77aeaff.

📒 Files selected for processing (1)
  • frontend/web/components/pages/usage/components/UsageBreakdown/components/Row/Row.scss

Included review availability: Your plan provides up to 8 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

Adds request-type and SDK usage aggregation with ranked rows and empty-state handling. Adds the UsageBreakdown selector, list, row, hook, styles, tests, and Storybook stories. Integrates the breakdown into UsageDashboardPage and UsageDashboard. Adds the reusable ValueBar component and updates UsageBar to use it.

Estimated code review effort: 3 (Moderate) | ~30 minutes

Merge Risk: 🟠 High · up to 77aea

This PR adds usage breakdowns, but the current implementation can fail to render and can display stale or misleading totals—or remain stuck loading—when scope requests fail or change. These correctness and availability risks should be fixed before merging.

✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the feature New feature or request label Aug 21, 2026
@talissoncosta
talissoncosta force-pushed the feat/usage-breakdown-8186 branch from 6a7bc06 to dbf1eed Compare August 21, 2026 16:36
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Aug 21, 2026
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Aug 21, 2026
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Aug 21, 2026
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Aug 21, 2026
@talissoncosta talissoncosta changed the title feat(usage): Break usage down by request type, project, environment or SDK feat(usage): break usage down by request type, project, environment or SDK Aug 21, 2026
@github-actions github-actions Bot added feature New feature or request and removed feature New feature or request labels Aug 21, 2026
@talissoncosta
talissoncosta force-pushed the feat/usage-breakdown-8186 branch from 80bb450 to 35abec8 Compare August 21, 2026 22:23
talissoncosta and others added 29 commits August 25, 2026 14:46
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two defects in the per-scope breakdown:

- totals were keyed by name, so two projects sharing one would overwrite
  each other. They key on the scope now
- totals were never cleared, so changing the period or project left the
  previous numbers on screen while the new requests were in flight, with
  nothing to mark them stale. They reset when the query identity changes

The section also waits for every scope rather than filling in, because a
partial list ranks wrongly and ranking is the point.

Extracts shared/BarTrack from UsageBar and UsageBreakdownRow, which had
near-identical markup and each hand-wrote the same transition and
reduced-motion guard. Rows carry the colour each request type has always
had on the usage page.

Also: the barrel exports the container, so callers stop reaching past it;
UsageBreakdownList replaces a function-in-render; skipToken replaces a cast;
and the row's flex, spacing and border move to utilities.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
BarTrack led with the noun and named the groove. Everything else here is
<Noun>Bar: ButterBar, CalloutBar, DistributionBar, UsageBar.

Not ProgressBar: half its callers compare values against the largest row
rather than tracking a task, which is why the progressbar semantics are
optional rather than built in.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
ARIA reserves progressbar for a task advancing and meter for a value inside
a known range. Usage against a plan allowance is the second, so a screen
reader was told a task was underway.

Changes what ProjectUsageTab and EnvironmentOverrideUsage announce as well,
since they share the bar. No selectors depend on the old role. Note that
meter has patchier assistive-tech support than progressbar, which is the
trade being taken deliberately.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
They were the legend for a stacked chart that is no longer there. Without it
the colour distinguished nothing the adjacent label and the bar length did not
already, and only request-type rows had one, so the same section looked
colour-coded on one dimension and plain on the next.

A categorical palette is still available in CHART_COLOURS if a chart ever
needs one; it does not have to live on a breakdown row to be reachable.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three of them, so they move into components/ and drop the prefix the path
already supplies: Row, List, ScopeTotal.

The pair also matches UsageDashboard now. The bare name is the container, so
the folder, the file and the default export agree, and callers stop reaching
past the barrel for it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Switching to project or environment showed "No usage recorded" before the
key list had even arrived: with no scopes yet there was nothing to wait on,
so the section read as empty rather than loading.

The select also had no accessible name, same as the page filters.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Row explained its bar-versus-share split in the docblock and again on each
prop. List repeated the view's needsProject comment verbatim. ValueBar made
the meter-versus-comparison point twice. ScopeTotal and useScopedBreakdown
were each a paragraph longer than the thing they explain.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
It is a section of the new dashboard, so it sits in pages/usage/components
alongside UsageMeter and UsageOverTime rather than in the folder the old page
is being deleted from.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A scoped request that failed never reported, so the section counted it as
still in flight and waited on it forever. One failed project left the whole
breakdown spinning. Failure now answers with null: the scope drops out of the
ranking rather than holding up the rest.

Scope lists also read currentData rather than data, since RTK keeps the
previous result while new arguments are in flight, so switching project
briefly listed the old project's environments. The organisation is part of the
totals identity for the same reason.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The section's queries, dimension state and row derivation were in the
component. They are now in useUsageBreakdown, leaving 42 lines that wire the
hook to the view.

The split existed to separate stateful logic from rendering, which is what a
hook does without needing a second component to hold it. ScopeTotal still
renders, because project and environment need one request per key and a hook
cannot mount a query per item of a list that changes length.

Also drops a guard that could not fire: with no project selected the
environments query is skipped, so scopes is already empty.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
UsageBreakdown is now the component: five props in, no queries. The wiring
moved up to UsageDashboardPage, which is allowed to fetch, so nothing needs a
suffix to distinguish it from the part that renders.

The dashboard takes the breakdown through a slot, the same way it already
takes filters and for the same reason: what goes in there fetches per key. It
also sheds organisationId, billingPeriod and projectId, which it only ever
forwarded.

Both story sets still work. UsageBreakdown renders from props, and the
dashboard stories omit the slot.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Restores the palette the four types have always had on the usage page, using
the chart tokens rather than the status ones: these are categorical, and
Identities is not "success".

Uses the ColorSwatch component rather than a hand-rolled span, which also
makes the swatch aria-hidden, since it is decorative and the label beside it
already names the row.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The page docblock still referred to "the view", which no longer exists.
ValueBar's className comment described the prop rather than explaining it,
totalOf's repeated its own name, and the hook's docblock took two sentences
to say one thing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Eight lines explaining a role attribute that is visible two lines below it.
The reasoning is in the commit that made the change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Seven lines became four. Dropped "it draws nothing", which the return says,
and the group_by aside, which is a wish rather than a warning. Kept why it is
one request per key and why it cannot be a loop of hooks, since the obvious
refactor of both reintroduces a bug.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Same sweep as the base branch. The record is in
frontend/USAGE_DASHBOARD_DECISIONS.md.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The usage endpoint types environment_id as an IntegerField, so it wanted the
environment's numeric id and we were sending its api_key. Nothing matched,
every environment came back with no usage, and the rows were filtered out, so
the section read "No usage recorded" for every project.

Also from a pass over the review patterns on #8320:

- ScopeTotal reads currentData rather than data. RTK keeps the previous
  result while new arguments are in flight, so a period change reported the
  old period's totals as current, right after they had been cleared
- long row labels ellipsise instead of wrapping, with the full name on hover

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
… value

A scope only reported once it produced a total, so anything that finished
without one never answered and the section waited on it forever. It now
reports as soon as fetching stops, with null when there is nothing to show.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Row, List and ScopeTotal were flat files in components/. Each now has a
folder and a barrel, per the component structure rule. Row in particular
had its own stylesheet sitting beside it.

Names the tri-state a scope reports while it is doing so: undefined is
still in flight, null is answered with nothing, a number is answered.
That was written out three times. UsageScope moves to utils alongside it
so the components take their types from one place rather than from each
other.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both cost one usage request per key, and the endpoint is throttled at
five a minute per user via InfluxQueryThrottle. An organisation with a
handful of projects gets 429s on first use, and a throttled scope
reported null, which the list turned into zero, so the breakdown showed
zeros rather than an error.

Ranking is the point of a breakdown, and a partial list ranks wrongly: a
throttled scope could be the largest. Better to ship the two dimensions
that are always right than four that mislead.

Request type and SDK derive from the payload the page already holds, so
they cost nothing extra. The other two come back on a group_by.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The label was a rigid 280px, so with the value and share columns and the
gaps the row needed about 440px before anything could give. It now
shrinks and truncates instead, which is what the ellipsis on the name was
always for.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
REQUEST_TYPES and RequestTypeKey were exported but only ever used inside
this module.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Row and List are internal to the breakdown, so re-exporting their props
types from their barrels put names into the world that nothing can
sensibly import.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Each row rounded its own share, so three equal rows read 33% and totalled
99%. The shares are worked out across the whole list now, with the spare
points going to the largest remainders.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A row worth a fraction of a percent of the largest rounded to a zero
width bar, so a real contributor looked like nothing at all. Anything
above zero now draws at least one percent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The section changed scope with the page filters and never said so, and
the control that changes it sits at the top of the page rather than
beside the numbers it affects. The header now names the project and the
period.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request front-end Issue related to the React Front End Dashboard

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Break usage down by request type or SDK

3 participants