Commit 56abc6c
authored
fix(usage): correct chart clipping, expand truncated rows, add source mix (#7199)
* fix(usage): correct chart clipping, expand truncated rows, add source 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.
* fix(usage): repair the chart family's shared geometry and the audit export
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.
* fix(usage): hide the workspace audit link where audit logs are disabled
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.
* fix(audit-logs): keep an unresolved workspace scope from widening the 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.
* fix(usage): correct the grid floor, tooltip height estimate, and Other 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.
* fix(audit-logs): present nothing when the workspace scope cannot be answered
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.
* fix(audit-logs): make the placeholder scope-aware, and separate a failed 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.
* improvement(emcn): drop the number-stepper reset for plain text numeric 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.
* chore(audit-logs): scope the refresh refetch, drop a test that could 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.
* fix(audit-logs): stop Refresh issuing the read the scope gate exists 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.1 parent d8ebcc0 commit 56abc6c
40 files changed
Lines changed: 2189 additions & 474 deletions
File tree
- apps/sim
- app
- api/audit-logs
- export
- workspace/[workspaceId]/settings
- [section]
- components
- billing/components/usage-limit-field
- manage-credits-modal
- usage/events
- components/charts
- ee
- audit-logs
- components
- hooks
- organization-usage
- components
- hooks
- session-policy/components
- hooks/queries
- utils
- lib
- api/contracts
- billing/application/organization-usage
- packages/emcn/src
- components
- chip-input
- chip-modal
- icons
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
164 | 164 | | |
165 | 165 | | |
166 | 166 | | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
167 | 192 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
66 | 66 | | |
67 | 67 | | |
68 | 68 | | |
69 | | - | |
70 | | - | |
| 69 | + | |
71 | 70 | | |
72 | 71 | | |
73 | 72 | | |
| |||
77 | 76 | | |
78 | 77 | | |
79 | 78 | | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
80 | 91 | | |
81 | 92 | | |
82 | 93 | | |
83 | 94 | | |
84 | 95 | | |
85 | 96 | | |
86 | | - | |
87 | | - | |
88 | | - | |
89 | | - | |
90 | | - | |
91 | | - | |
92 | | - | |
93 | | - | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
94 | 107 | | |
95 | 108 | | |
96 | 109 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
27 | 27 | | |
28 | 28 | | |
29 | 29 | | |
| 30 | + | |
30 | 31 | | |
31 | 32 | | |
32 | 33 | | |
| |||
Lines changed: 5 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
176 | 176 | | |
177 | 177 | | |
178 | 178 | | |
179 | | - | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
180 | 184 | | |
181 | 185 | | |
182 | 186 | | |
| |||
Lines changed: 6 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
121 | 121 | | |
122 | 122 | | |
123 | 123 | | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
124 | 130 | | |
125 | | - | |
126 | 131 | | |
127 | | - | |
128 | 132 | | |
129 | 133 | | |
130 | 134 | | |
| |||
135 | 139 | | |
136 | 140 | | |
137 | 141 | | |
138 | | - | |
139 | 142 | | |
140 | 143 | | |
141 | 144 | | |
| |||
Lines changed: 9 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
109 | 109 | | |
110 | 110 | | |
111 | 111 | | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
112 | 120 | | |
113 | 121 | | |
114 | | - | |
| 122 | + | |
115 | 123 | | |
116 | 124 | | |
117 | 125 | | |
| |||
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
19 | | - | |
| 19 | + | |
20 | 20 | | |
21 | 21 | | |
22 | 22 | | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| 11 | + | |
11 | 12 | | |
12 | 13 | | |
13 | | - | |
14 | 14 | | |
15 | 15 | | |
16 | 16 | | |
17 | 17 | | |
| 18 | + | |
18 | 19 | | |
19 | 20 | | |
20 | 21 | | |
21 | 22 | | |
22 | 23 | | |
23 | 24 | | |
| 25 | + | |
24 | 26 | | |
25 | 27 | | |
26 | 28 | | |
| |||
47 | 49 | | |
48 | 50 | | |
49 | 51 | | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
50 | 63 | | |
51 | 64 | | |
52 | 65 | | |
| |||
71 | 84 | | |
72 | 85 | | |
73 | 86 | | |
74 | | - | |
75 | | - | |
76 | | - | |
77 | 87 | | |
78 | 88 | | |
79 | | - | |
80 | 89 | | |
81 | 90 | | |
82 | | - | |
83 | | - | |
| 91 | + | |
84 | 92 | | |
85 | 93 | | |
86 | 94 | | |
| |||
100 | 108 | | |
101 | 109 | | |
102 | 110 | | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
103 | 115 | | |
104 | 116 | | |
105 | 117 | | |
106 | 118 | | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
107 | 129 | | |
108 | 130 | | |
109 | 131 | | |
| |||
127 | 149 | | |
128 | 150 | | |
129 | 151 | | |
130 | | - | |
131 | | - | |
132 | | - | |
133 | | - | |
134 | | - | |
135 | | - | |
136 | | - | |
137 | | - | |
138 | | - | |
139 | | - | |
140 | 152 | | |
141 | 153 | | |
142 | 154 | | |
143 | 155 | | |
144 | | - | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
145 | 160 | | |
146 | 161 | | |
147 | 162 | | |
| |||
156 | 171 | | |
157 | 172 | | |
158 | 173 | | |
159 | | - | |
| 174 | + | |
160 | 175 | | |
161 | 176 | | |
162 | 177 | | |
| |||
185 | 200 | | |
186 | 201 | | |
187 | 202 | | |
188 | | - | |
189 | | - | |
| 203 | + | |
| 204 | + | |
190 | 205 | | |
191 | 206 | | |
192 | 207 | | |
| |||
202 | 217 | | |
203 | 218 | | |
204 | 219 | | |
205 | | - | |
206 | | - | |
207 | | - | |
208 | | - | |
209 | | - | |
210 | | - | |
211 | | - | |
212 | | - | |
213 | | - | |
214 | | - | |
215 | | - | |
216 | | - | |
217 | | - | |
| 220 | + | |
218 | 221 | | |
| 222 | + | |
219 | 223 | | |
220 | 224 | | |
221 | 225 | | |
| |||
229 | 233 | | |
230 | 234 | | |
231 | 235 | | |
232 | | - | |
| 236 | + | |
233 | 237 | | |
234 | 238 | | |
235 | 239 | | |
| |||
240 | 244 | | |
241 | 245 | | |
242 | 246 | | |
243 | | - | |
| 247 | + | |
244 | 248 | | |
245 | 249 | | |
246 | 250 | | |
| |||
313 | 317 | | |
314 | 318 | | |
315 | 319 | | |
316 | | - | |
| 320 | + | |
317 | 321 | | |
318 | 322 | | |
319 | 323 | | |
320 | 324 | | |
321 | 325 | | |
322 | 326 | | |
323 | 327 | | |
324 | | - | |
| 328 | + | |
325 | 329 | | |
326 | 330 | | |
327 | | - | |
| 331 | + | |
328 | 332 | | |
329 | 333 | | |
330 | 334 | | |
| |||
338 | 342 | | |
339 | 343 | | |
340 | 344 | | |
341 | | - | |
| 345 | + | |
342 | 346 | | |
343 | 347 | | |
344 | 348 | | |
| |||
347 | 351 | | |
348 | 352 | | |
349 | 353 | | |
350 | | - | |
| 354 | + | |
| 355 | + | |
351 | 356 | | |
352 | 357 | | |
353 | 358 | | |
354 | 359 | | |
355 | 360 | | |
356 | 361 | | |
| 362 | + | |
| 363 | + | |
357 | 364 | | |
358 | 365 | | |
359 | | - | |
360 | | - | |
361 | | - | |
362 | | - | |
363 | | - | |
| 366 | + | |
364 | 367 | | |
365 | 368 | | |
366 | 369 | | |
| |||
0 commit comments