fix(usage): correct chart clipping, expand truncated rows, add source mix - #7199
Conversation
… mix Two rendering defects on the organization usage panel, plus the follow-on cleanup they surfaced. The y-axis maximum was clipped at the container's left edge. The chart family used a fixed 26px left gutter, which leaves 18px of drawable width once the label gap is taken out — four narrow glyphs — so every tick from `7.3k` up lost its first character. Both charts now derive the gutter from the labels they are about to draw, through one shared `resolveChartPadding` so a bar and a line chart still line up when stacked. Hovering near the foot of the plot raised a vertical scrollbar over the chart. The scroll container sets `overflow-x`, which promotes `overflow-y` to `auto`, and the tooltip's vertical clamp was a fixed inset that ignored the box's real height. The clamp now measures the tooltip, and the container pins `overflow-y`. Also on the panel: - The axis rules were invisible. `hsl(var(--border))` is unparseable — the token is a hex — so the presentation attribute was dropped and SVG's initial `stroke: none` applied. - GLM rendered without a mark: the settings provider-icon map held 11 of the registry's 24 providers. Completed, with a test that fails when the two drift. The server's parallel label map was the same 11-entry duplicate and now reads the registry directly. - `Other (N more)` opens the tail in place, raising the row limit to the API's ceiling. Fixed the drill-down name lookup this exposed, which was pinned to the top ten and so refused to open for any row below it. - A radar chart of the source mix sits beside the Sources list. The rows rank the sources; they cannot show whether spend is concentrated or spread. - "Open logs" pointed at `/workspace/<id>/logs` for a workspace picked from an organization-wide list. Organization admin is not workspace membership, so for any workspace the admin had not joined it was a one-way trip to an access denial. It now opens the organization's audit feed scoped to that workspace, which required threading the workspace filter the query builder already supported through the internal contract, route, hook, and URL state. - BYOK is withheld from the tab strip until the ledger carries BYOK rows. - The chip number field suppresses the native stepper, which painted browser chrome inside a flat chip surface. The component owns it, not its callers. - `ChartColumn` was a uniform 0.86 downscale of its source art, so it rendered ~2px small beside every other icon in the settings nav.
…xport Follow-up review of the previous commit, across the whole diff. The CSV export ignored the workspace filter. The contract accepted it, the on-screen feed applied it, and the export route dropped it on the floor — every field of `AuditLogFilterParams` is optional, so omitting one still type-checks. An admin exporting a workspace-scoped feed downloaded the whole organization, under a truncation warning that blamed the date range. The route now forwards the parsed query whole and refuses an out-of-organization id the way the list route does, with tests for both. The line chart's derived hover index was not clamped. The previous commit replaced stored state with a derivation, but reproduced the clamp only for the bar chart: `padding.left` follows the axis labels and `chartWidth` follows the container, so a sidebar collapse mid-hover pushed the ratio past 1 and indexed off the end — the dot, the rule and the tooltip all vanished until the cursor moved. Per-chart gutters de-aligned the logs dashboard, where three line charts sit in one row. Deriving each from its own labels put their plot origins at 26, 27 and 32 where they had shared one. The gutter now rounds up to a step, which collapses differences that small and leaves several pixels of slack instead of the sub-pixel margin `Math.ceil` alone gave. The radar chart, reviewed against its siblings: - Its hover targets were triangles, whose far edge is the chord. Along its own spoke a triangle reaches only `reach·cos(π/n)` — at three axes, 50px against a 74px radius — so the largest value's vertex, the one a reader aims at, sat outside every target. They are arc sectors now. - The tooltip was positioned against the scroll container rather than the plot, so below the width floor it stayed nailed while the web slid under it. It now sits beside the hovered vertex through the family's own placer, instead of covering the densest part of the gradient. - Captions below the centreline rode ~3px off the ring rather than the gap they were given, and captions beside the web were misaligned from their own vertex. - Web opacities, stroke width, vertex radius, the per-theme fill relationship and the `screen` blend now match the bar and line charts rather than freelancing. - Its rings read the shared grid fractions instead of dividing into even steps that agreed with the siblings only by coincidence. Also: one `expanded` flag drove both lists in the workspace drill-down, so opening either tail silently opened the other's; the Overview and tab lists rendered an inert `Other` row while the same row two clicks away was a button; the expand chevron knocked the value column out of alignment; row hover had regressed to the chip surface where every other settings row uses the active one; and the radar and the list beside it printed two different `Other (N more)` counts under identical wording. The Overview's two readings of the source data now share one section rather than drawing two half-width rules on one line. Rendered-geometry tests cover the clipping and caption bugs against the real SVG — a unit test of the helpers could not have caught either, since both came from a callsite combining correct helpers wrongly.
Usage monitoring and Audit logs share their hosted and enterprise gates, so reaching the usage panel proves both — but their self-hosted overrides are separate flags. An install with usage monitoring on and audit logs off was handed a drill-down action pointing at a section it had switched off. The window is deliberately not carried across the link: the audit feed speaks in rolling ranges and this panel in billing periods, so there is no honest mapping for the current-period preset.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
Greptile SummaryThis PR expands the usage and audit-log interfaces while correcting chart geometry, export filtering, provider metadata, navigation, and numeric-input behavior.
Confidence Score: 5/5The PR appears safe to merge. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| apps/sim/ee/audit-logs/components/audit-logs.tsx | Adds explicit workspace-scope states and gates audit-row presentation and export when the requested scope cannot be resolved. |
| apps/sim/ee/audit-logs/hooks/audit-logs.ts | Makes audit query identity scope-aware so placeholder pages are retained only across filter changes within one organization/workspace scope. |
| apps/sim/app/api/audit-logs/export/route.ts | Validates workspace membership and forwards the complete parsed audit filter set into CSV generation. |
| apps/sim/components/charts/chart-geometry.ts | Adds adaptive, quantized y-axis padding derived from rendered label estimates. |
| apps/sim/components/charts/chart-tooltip.tsx | Adds height-aware tooltip placement and aligns tooltip styling with current theme tokens. |
| apps/sim/ee/organization-usage/components/usage-source-mix.tsx | Introduces the source-mix visualization used alongside ranked usage sources. |
| apps/sim/lib/billing/application/organization-usage/get-organization-usage-breakdown.ts | Extends organization usage aggregation to support the new source and provider presentation. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
U[Usage dashboard] --> C[Consumer breakdown]
U --> S[Source ranking]
S --> R[Source-mix radar]
U --> E[Usage events]
U --> A[Workspace-scoped audit feed]
A --> F[Validated workspace filter]
F --> L[Audit-log query]
F --> X[CSV export]
Reviews (7): Last reviewed commit: "fix(audit-logs): stop Refresh issuing th..." | Re-trigger Greptile
… feed A workspace id in the URL that no longer resolves — deleted since, or never one of ours — dropped the filter, so a request for one workspace's history was answered with the whole organization's, under a URL that still claimed to be scoped. The CSV export followed the same filter and would have carried the same widening. Every other deep-linked id in the app degrades to the unfiltered view, which is right where the fallback shows less than was asked for. An audit feed is the one place where widening is the dangerous direction, so it now stays closed and says so, with the filter chip still rendered so the scope can be cleared.
There was a problem hiding this comment.
All reported issues were addressed
You’re at about 95% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
|
@cubic review |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
…r row slot Three findings from review. A grid track minimum is a hard floor, so `minmax(320px, 1fr)` made the source section wider than its column on a narrow viewport and overflowed instead of collapsing. `min(320px, 100%)` caps the floor at the width actually available. The tooltip height estimate used font sizes where it needed line boxes. The type scale pairs no line-height with a size, so a line occupies the ambient 1.5 — a 10px date line is 15px, an 11px row is 16.5 — and the estimate came in ~1.5px under the real box. Since it is what the clamp measures against and the chart clips its overflow, an underestimate cuts the bottom off the box rather than moving it up. Now derived from the line boxes, every part rounded up, with a test that fails if any of the three constants drops below the rendered height. The `Other` row drew its disclosure chevron bare at 14px while the rows above reserved the 16px arrow or the 30px menu slot, pulling its figure out of the column. It now centres in the same slot the rest of the list reserves.
There was a problem hiding this comment.
All reported issues were addressed
You’re at about 97% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
…nswered Disabling the query was not enough. An unresolved scope drops the filter, so its query key equals the unscoped feed's, and a disabled query still serves whatever is cached under its key — an admin reading the organization-wide feed who then followed a stale scoped link kept those rows on screen, with Export still armed against them because that gate reads the same list. The rule now has a name and a seam: `presentableAuditEntries` returns nothing unless the feed can answer the scope the URL asks for, and the export action states that condition where it is read rather than inheriting it through an empty list.
|
@cubic review |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 39 files
You’re at about 97% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
…led lookup Two more from review, both on the same surface. The placeholder I added held previous pages across a workspace change, so following a scoped link from the organization-wide feed painted the organization's rows under a workspace-scoped URL until the scoped page arrived — with Export armed against them. Gating presentation on `isPlaceholderData` would have fixed it by throwing away the reason the placeholder exists, blanking the feed on every keystroke again. The scope now leads the query key instead, ahead of the filters, so "hold across a filter change, never across a scope change" is a prefix comparison — the same shape the breakdown query already uses, and it retires the hand-maintained key index. A failed workspace lookup was reported as a workspace that is not part of the organization. That is a wrong answer rather than a cautious one, and it offered nothing to do about it. The two states are now distinct, the error one says so, and Refresh retries the lookup alongside the feed so the control on screen can actually clear the state it is showing.
|
@cubic review |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 39 files
You’re at about 98% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
…ic fields
The stepper was suppressed with a vendor-pseudo-element class string inside
`ChipInput`. Removing browser chrome with custom CSS is the wrong end of the
problem: the fields never wanted a stepper in the first place.
They are text fields with a numeric input mode now — the choice the retry
settings field already documents ("the native spinner is all that buys, and it
does not fit the field chrome"). No CSS, the numeric keypad is unchanged, and
`ChipModalField` gained an `inputMode` prop so a modal field can say the same
thing without asking for the stepper.
This also fixes a real defect in the credit-limit field. A number input reports
`''` for anything the browser considers invalid, so a typo arrived
indistinguishable from a cleared field and saved as "no limit"; as text it reaches
the `Number.isInteger` check and is refused. The usage-limit field's `min`
attribute went the same way — the minimum is enforced on commit, where it can
explain itself, rather than silently by the browser.
|
@cubic review |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
…not fail `refetch` ignores `enabled`, so refreshing the unscoped feed fired a workspace lookup it has no use for and could fail a refresh that otherwise succeeded. It now runs only when a scope asked for it. The hook test claiming to cover an unresolved workspace scope passed `enabled: false` with no workspace at all, so it asserted TanStack's disabled handling and would have passed with the scope protection removed. The rule it named is derived in the component and is covered there by `presentableAuditEntries`; a test that cannot fail for its stated reason is worse than none.
|
@cubic review |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 40 files
You’re at about 99% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.
You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
…to prevent `refetch` ignores `enabled`, so pressing Refresh while the workspace scope was unresolved or its lookup had failed fired the audit query anyway — and its filter carries no workspace in that state, so the request was the organization-wide read the gate exists to prevent. The result was never presented, but it was still asked for. Refresh now repeats the gate: the feed is refetched only while the scope is answerable, and the lookup — the thing that has to succeed for a closed feed to reopen — is retried whenever a scope asked for it.
|
@cubic review |
@waleedlatif1 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
All reported issues were addressed across 40 files
You’re at about 99% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.
You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.
Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.
Re-trigger cubic
Summary
overflow-xpromotesoverflow-ytoauto, and the tooltip's clamp ignored its own height.hsl(var(--border))is unparseable because the token is a hex, so SVG fell back tostroke: none.Other (N more)now expands in place. Fixed the drill-down name lookup this exposed, which was pinned to the top ten and refused to open for any row below it.ChipInputowns it, so no caller re-derives the vendor rules.ChartColumnwas a uniform 0.86 downscale of its source art, rendering ~2px small beside every other settings nav icon.Type of Change
Testing
Tested manually. Added rendered-geometry tests that assert against the real SVG across widths 280–1024 and 3–7 axes — both clipping bugs were invisible to a unit test of the helpers, since each came from a callsite combining correct helpers wrongly. Every new test verified to fail when its fix is reverted.
bun run lint:check26/26,bun run check:audits37/37,check:migrationsn/a (no migrations), type-check clean, 6659 app tests + 142 emcn tests passing.Checklist