Skip to content

[WIP]: feat(usage-dashboard): usage dashboard prototype: show every state on fixture data - #8192

Draft
talissoncosta wants to merge 33 commits into
mainfrom
spike/usage-billing-transparency
Draft

[WIP]: feat(usage-dashboard): usage dashboard prototype: show every state on fixture data#8192
talissoncosta wants to merge 33 commits into
mainfrom
spike/usage-billing-transparency

Conversation

@talissoncosta

@talissoncosta talissoncosta commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Thanks for submitting a PR! Please check the boxes below:

  • 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

Contributes to #8184

Prototype, not for merge. It replaces the usage page on this branch so the v0.2 designs can be reviewed before we commit to building them. The production work is tracked under #8183.

The page runs on fixtures, so every designed state can be shown without depending on what an organisation has actually used:

  • healthy
  • approaching the limit
  • over limit, grace period covering this month
  • over limit, grace period used and charged
  • free plan, inside the 7 day countdown
  • free plan, restricted

A "Live data" option uses the real usage endpoints.

On the page: billing period with reset date and grace chip, "% of plan consumed" meter with notification markers, tiles that change per state, cumulative usage against the plan limit with a shaded overage zone and a dashed projection, request-type breakdown, and the notifications screen.

Reset date, grace state and cost are faked here. The API work that would make them real is in #8183.

Also included: the fix for #6099, current billing period ignoring terms longer than a year. One line and a test, no dependency on the prototype, and it can be split out to merge on its own.

How did you test this code?

Type checking and linting only. The page has not been rendered yet, I had no local API running to load it against.

To try it: check out the branch, run the frontend against an API, and open Organisation Settings, then Usage. The state switch at the top moves between the six fixtures, and the tabs move between the usage and notifications screens. Only "Live data" needs the API to return anything.

talissoncosta and others added 11 commits July 31, 2026 12:13
Reframes OrganisationUsagePage with a "usage vs plan limit" summary:
% of plan consumed, a meter, and a cumulative-vs-limit chart, all wired
to the existing usage-data + max_api_calls (no backend change).

Everything the API cannot feed yet is marked TODO(BE) and listed in the
component so the diff doubles as the backend ask: reset date / billing
period boundaries, the current-billing-period date-range bug, projection,
grace-period status, and cost in currency.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Expands the prototype to the full v0.2 layout for the global view:
billing-period strip, hero meter with Notify markers, stat tiles,
cumulative usage-vs-limit chart (Area + plan-limit ReferenceLine + today
dot), and the request-type breakdown. Global view now renders this in
place of the old totals + bar chart; By SDK view is unchanged.

Still wired only to existing data; reset date, projection and cost remain
TODO(BE).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Swaps the hardcoded hex (accent/danger/success/warning + panel/border/
text/track) for the --color-* semantic tokens via common/theme/tokens, so
the prototype flips correctly under .dark.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`_get_start_date_and_stop_date_for_subscribed_organisation` derived the
current period start from relativedelta(...).months only, dropping the
years component, so terms that started >12 months ago (annual plans)
resolved to the wrong year. Add years*12, mirroring the existing
PREVIOUS_BILLING_PERIOD branch, and cover the annual case with a test.

Note: api env (uv) not available locally; needs CI to run.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The reframed design drops the left "By Endpoint / By SDK" sidebar (SDK
becomes a breakdown-dimension option instead), so remove the sidebar and
the global/user-agents toggle from the page.

Keep the Period/Project/Environment filters. Per the agreed behaviour, the
plan limit is org-level, so the meter + cumulative chart use an org-wide
query (period only) while Project/Environment filter a second query that
feeds only the request-type breakdown.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Environment is a drill-down within a project and too granular for
org-level usage; project is the meaningful pivot. Remove the environment
select from UsageChartFilters and the page's filtered query.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Match the design: Usage title on the left, Period + Project selects on the
right of the page header (dropping the separate labelled filter row). Give
the surface a max-width and centre it instead of the left-aligned, narrow
app-container layout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Recharts auto-scaled the y-axis to usage, so with tiny usage against a
large limit the plan-limit ceiling fell off-screen and the "usage vs
limit" comparison was lost. Fix the domain to [0, max(limit, usage)] so
the ceiling is always visible (and over-limit usage still shows above it).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The end marker's label was centred on the far-right point and half of it
overflowed the panel; right-align it so it stays within bounds.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds a scenario switch backed by fixtures so the over-limit, restricted and
grace states can be shown on demand, fills in the tiles that were printing
"needs BE", and adds the notifications screen.

Restores the existing usage page and puts the prototype behind the
usage_billing_prototype flag, so nothing changes until the flag is on.

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

vercel Bot commented Jul 31, 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 11, 2026 12:50am
flagsmith-frontend-staging Ready Ready Preview Aug 11, 2026 12:50am
1 Skipped Deployment
Project Deployment Actions Updated (UTC)
docs Ignored Ignored Preview Aug 11, 2026 12:50am

Request Review

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: fc4babe1-3a81-42d7-943a-c007c8b51d4e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The change fixes annual billing-period date calculation and adds a regression test. It extends StatItem with optional subtext, badges, icons, and size variants, then updates related views and stories. It adds a typed usage billing prototype with fixtures, live data mapping, charts, banners, notes, notifications, billing controls, and status components. The organisation usage page now renders the prototype page.

Estimated code review effort: 5 (Critical) | ~120 minutes

✨ 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.

@codecov

codecov Bot commented Jul 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.73%. Comparing base (10a3bb7) to head (66acbdc).
⚠️ Report is 31 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8192      +/-   ##
==========================================
+ Coverage   98.71%   98.73%   +0.02%     
==========================================
  Files        1531     1556      +25     
  Lines       61234    61903     +669     
==========================================
+ Hits        60446    61120     +674     
+ Misses        788      783       -5     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

The branch is never merged, so gating it behind a flag only means creating
one in Flagsmith before anyone can look at it. Check the branch out and the
prototype is there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Swaps the hand-rolled pieces for what already exists: StatItem for the tiles,
Chip for the grace status, BareButton for the state and screen switches, and
Format.shortenNumber instead of a local copy. Adds progressbar semantics to
the meter and group labels to the switches.

The tile badges go with this: StatItem has no badge slot, so the state now
reads from the meter colour, the banner and the chart. Whether StatItem should
grow one is a question for #8185.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
StatItem and Chip pulled the page away from the design: the tiles lost their
badge and sub-label and grew icon blocks, and the grace chip read as a flat
pill. Both are back to the designed layout.

Badges now follow the dot-and-label pattern from experiments/StatusBadge,
which looks like where the app is heading. That component is typed to
experiment statuses, so this is a local copy with a tone prop rather than a
change to shared code. Generalising it is a question for #8185.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
StatItem was a label, a value and an icon block, so anything richer got
hand-rolled next to it: the admin metrics cards render their sub-line in a
<small> outside the component, and the usage prototype had its own tiles.

It now carries its own card: label and optional badge on one line, the value,
then an optional sub-line. The icon is optional, since dense rows of figures
read better without one.

Call sites: the admin metrics cards drop their external <small>, Billing loses
the panel that was wrapping the limit cards, and the usage prototype uses
StatItem instead of its own tiles. Stories cover the new slots.

Lives on the prototype branch by choice, so it needs porting when this becomes
real work.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
One model for everyone: every org has a period, so the reset date, days
remaining and the projection work the same way regardless of plan, and the
selector offers billing periods to everybody.

This is the target state, not what we enforce today. Free limits are still
enforced on a rolling 30 days, so the backend change has to land with this or
before it, otherwise the page shows a period nobody is measured against.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
talissoncosta and others added 2 commits August 10, 2026 11:58
55c386c moved everyone onto billing periods as the target state, on the
assumption the backend change would land with it. It is not landing yet:
Matt is still costing it, and free orgs have no Chargebee subscription at
all, so there is no billing term to read.

Until then the page has to describe what is actually enforced. Free plans
are measured over a trailing 30 days (api/organisations/task_helpers.py),
which has no start, no reset and no end to project to.

So for a rolling window:

- The period carries no reset date, no days remaining, and the selector
  offers the rolling options rather than billing periods.
- The chart is daily volume rather than cumulative. Cumulative against a
  ceiling only reads correctly when the total resets; on a trailing window
  it can fall as old days drop off the back. Daily values come from the
  same cumulative series, so no second shape is needed from the API.
- No projection, which is the part that misleads hardest.
- The projection tile becomes "If you reach 100%", since free plans are
  restricted rather than charged.

Paid is untouched.

Cancelled subscriptions take the same rolling branch in the backend and
have no fixture here yet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Standardising the tiles: a trailing window has no end to run to, so the
slot is left empty rather than filled with a substitute. Free plans get a
shorter row.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every free fixture was over its limit, so the two states most free orgs are
actually in could not be viewed, and those are the ones where the row is
shortest. Adds free-healthy and free-approaching.

The sub-line was doing six different jobs across the row: a denominator, a
period, a date, a method, a consequence and a remedy. It now states the
scope or boundary the value applies to, and nothing else. Method is what
the Estimate badge is for, and advice belongs in the banner.

- Projected: "by 17 Aug 2026" rather than "at the current run rate"
- Overage: "for this billing period" rather than "charged at the end of it"
- Restricted: "until usage drops or you upgrade". The banner already said
  service returns when usage falls back under the limit; the tile claimed
  only upgrading would do it.

Rolling windows say allowance, not plan limit, matching the headline and
the strip, and "last 30 days" rather than "this window".

Drops resumesAt from free-restricted-now. It read "the period resets",
which a trailing window never does.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The banner said "flag serving pauses in N days". Nothing in the API can
produce that number. The 7 day lag runs from the qualifying usage
notification (organisations/tasks.py:303), and that date is not exposed,
so the countdown was invented.

Without a date the banner now says serving may be paused if usage stays
over, which is what we can actually stand behind. free-countdown-dated
keeps the version with a number, so the gap between what we can say today
and what we want to say stays visible in the picker.

Adds the state we never modelled: over the limit with grace already
spent. That org has no countdown at all and can be cut off on the next
task run, which is the most urgent message on the page, and we were
jumping straight from countdown to restricted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The comment claimed the API only breaks usage down by request type. It
does not. Every row of events_list carries labels.user_agent, so grouping
by SDK needs no second request and no backend work.

Project and environment are genuinely different: usage-data takes them as
filters (project_id, environment_id) rather than as a grouping, so they
need one request per key or a group_by parameter. That is an optimisation
rather than a blocker, and the comment now says so.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
usage-data takes project_id and environment_id as filters, so the data was
always reachable, just one key at a time. The panel claimed the API could
not break usage down this way, which was wrong.

Both dimensions now fetch per key and sum. Only the visible dimension is
fetched, so the dimension moved up to the page next to the other query
state.

Environments belong to projects, so with no project selected this would
fan out to every project times every environment. The panel asks for a
project instead of doing that.

A group_by on usage-data would turn each of these into one request. That
is an optimisation now rather than the thing blocking the feature.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The page showed 75 and 100. The backend notifies on 75, 90, 100, 120, 200,
300, 400 and 500 (api/organisations/constants.py). Two of eight.

That matters more now notifications are scoped to a page that states what
we do rather than one that configures it. A read-only page listing two
thresholds would be wrong on its only job.

The meter draws the ones at or below 100, since above the limit it is
already full and they would stack at the far end. The notifications list
carries all eight.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The prototype had the same plan-name inference as the shipped page, so
fixing one and not the other left the branch that is meant to show the
target behaviour doing the wrong thing.

Also drops the 90 day default on a rolling window. The prototype says
"last 30 days" everywhere while asking the API for 90, so the picture and
the copy were describing different windows.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
organisations/{id}/api-usage-notification/ already returns percent_usage
and notified_at for the current period, and nothing in the frontend was
calling it. We had written off "days over limit" and the grace countdown
as needing new API work; they did not.

The earliest crossing at or above 100% is when the organisation went over,
so days over limit comes from it and the free grace window runs 7 days
from the same date.

Still missing, and still #8256: whether grace has already been spent. So
an organisation over the limit gets the countdown rather than the "grace
already used" state, which is the one that matters most.

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

api Issue related to the REST API front-end Issue related to the React Front End Dashboard

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants