[WIP]: feat(usage-dashboard): usage dashboard prototype: show every state on fixture data - #8192
[WIP]: feat(usage-dashboard): usage dashboard prototype: show every state on fixture data#8192talissoncosta wants to merge 20 commits into
Conversation
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>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
📝 WalkthroughWalkthroughThe change fixes annual billing-period date calculation and adds a regression test. It extends Estimated code review effort: 5 (Critical) | ~120 minutes ✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
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. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #8192 +/- ##
=======================================
Coverage 98.71% 98.71%
=======================================
Files 1531 1531
Lines 61234 61272 +38
=======================================
+ Hits 60446 60484 +38
Misses 788 788 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
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>
Adds the inline note under the meter, which is the only line on the page that draws a conclusion rather than stating a number: on track, over by how much, what it costs, what happens next. Wording follows the state, so the restricted and grace-countdown screens explain themselves. Adds the breakdown dimension menu (request type, project, environment, SDK) with obviously invented project and environment names, so nobody mistakes the demo for their own data. Live data only has request type, and says so. Labels the dashed projection on the chart, which was an unexplained line. Two fixes the screenshots turned up: - the period selector read "Last 90 days" while the strip underneath said "Billing period 18 Jul to 17 Aug". It follows the fixture now - rolling windows claimed a reset date. They are "Usage window" now, with no reset, since a rolling window never resets Also adds the "See docs" link the notifications design has. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The y-axis top was the projected value itself, so its label had nowhere to render and got cut off by the edge of the chart. The axis now carries 8% headroom above the highest value, and the label sits above the endpoint rather than inside the corner. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
You could add a notification but never get rid of one. Each row now has a remove control next to its toggle, labelled for screen readers. Rows above 100% claimed "You have reached your plan limit", which is wrong once you are past it. They now read "You are over your plan limit", and only the 100% row claims to be at it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The settings screen kept its own copy of the notifications, so removing 75% left the "Notify 75%" marker sitting on the meter, and edits vanished on tab switch. State lives on the page now and both screens read the same list. Changing scenario drops the edits, so each fixture still shows its own notifications. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Docker builds report
|
There was a problem hiding this comment.
Actionable comments posted: 15
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: c0ad5152-842a-4e5b-882f-774d8fbcbb0b
📒 Files selected for processing (23)
api/app_analytics/analytics_db_service.pyapi/tests/unit/app_analytics/test_analytics_db_service.pyfrontend/documentation/components/StatItem.stories.tsxfrontend/web/components/StatItem.scssfrontend/web/components/StatItem.tsxfrontend/web/components/organisation-settings/usage/UsageBillingPrototype/GraceChip.tsxfrontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageBadge.scssfrontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageBadge.tsxfrontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageBanner.tsxfrontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageBillingPrototype.scssfrontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageBillingPrototype.tsxfrontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageBillingPrototypePage.tsxfrontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageChart.tsxfrontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageNote.tsxfrontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageNotifications.tsxfrontend/web/components/organisation-settings/usage/UsageBillingPrototype/fixtures.tsfrontend/web/components/organisation-settings/usage/UsageBillingPrototype/format.tsfrontend/web/components/organisation-settings/usage/UsageBillingPrototype/index.tsfrontend/web/components/organisation-settings/usage/UsageBillingPrototype/types.tsfrontend/web/components/organisation-settings/usage/UsageBillingPrototype/usePrototypeUsage.tsfrontend/web/components/pages/OrganisationUsagePage.tsxfrontend/web/components/pages/admin-dashboard/components/InstanceMetricsCards.tsxfrontend/web/components/pages/organisation-settings/tabs/BillingTab.tsx
| const periodStart = moment().subtract(daysElapsed - 1, 'days') | ||
| const resetsAt = periodStart.clone().add(periodDays, 'days') | ||
|
|
||
| return { | ||
| breakdowns: buildBreakdowns(total), | ||
| channels: { email: true, inApp: true }, | ||
| grace, | ||
| graceDaysLeft, | ||
| limit, | ||
| notifications: [ | ||
| { enabled: true, percent: 75 }, | ||
| { enabled: true, percent: 100 }, | ||
| ], | ||
| overageCost, | ||
| period: { | ||
| daysRemaining: periodDays - daysElapsed, | ||
| isBillingPeriod, | ||
| label: | ||
| periodLabel ?? | ||
| `${periodStart.format('D MMM')} to ${resetsAt.format('D MMM YYYY')}`, | ||
| // Rolling windows never reset, so they get no reset date. | ||
| resetsAt: isBillingPeriod ? resetsAt.format('D MMM YYYY') : '', |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Align daysRemaining with resetsAt.
periodStart is daysElapsed - 1 days ago, so it counts today as an elapsed day. resetsAt is therefore periodDays - daysElapsed + 1 days in the future, but daysRemaining is periodDays - daysElapsed. For the healthy fixture the panel states 13 days remaining while the reset date is 14 days away. Design reviewers see both values on the same screen.
🐛 Proposed fix to make both values agree
return {
breakdowns: buildBreakdowns(total),
channels: { email: true, inApp: true },
grace,
graceDaysLeft,
limit,
notifications: [
{ enabled: true, percent: 75 },
{ enabled: true, percent: 100 },
],
overageCost,
period: {
- daysRemaining: periodDays - daysElapsed,
+ daysRemaining: periodDays - daysElapsed + 1,
isBillingPeriod,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const periodStart = moment().subtract(daysElapsed - 1, 'days') | |
| const resetsAt = periodStart.clone().add(periodDays, 'days') | |
| return { | |
| breakdowns: buildBreakdowns(total), | |
| channels: { email: true, inApp: true }, | |
| grace, | |
| graceDaysLeft, | |
| limit, | |
| notifications: [ | |
| { enabled: true, percent: 75 }, | |
| { enabled: true, percent: 100 }, | |
| ], | |
| overageCost, | |
| period: { | |
| daysRemaining: periodDays - daysElapsed, | |
| isBillingPeriod, | |
| label: | |
| periodLabel ?? | |
| `${periodStart.format('D MMM')} to ${resetsAt.format('D MMM YYYY')}`, | |
| // Rolling windows never reset, so they get no reset date. | |
| resetsAt: isBillingPeriod ? resetsAt.format('D MMM YYYY') : '', | |
| const periodStart = moment().subtract(daysElapsed - 1, 'days') | |
| const resetsAt = periodStart.clone().add(periodDays, 'days') | |
| return { | |
| breakdowns: buildBreakdowns(total), | |
| channels: { email: true, inApp: true }, | |
| grace, | |
| graceDaysLeft, | |
| limit, | |
| notifications: [ | |
| { enabled: true, percent: 75 }, | |
| { enabled: true, percent: 100 }, | |
| ], | |
| overageCost, | |
| period: { | |
| daysRemaining: periodDays - daysElapsed + 1, | |
| isBillingPeriod, | |
| label: | |
| periodLabel ?? | |
| `${periodStart.format('D MMM')} to ${resetsAt.format('D MMM YYYY')}`, | |
| // Rolling windows never reset, so they get no reset date. | |
| resetsAt: isBillingPeriod ? resetsAt.format('D MMM YYYY') : '', |
| export const currency = (amount: number): string => | ||
| `$${amount.toLocaleString()}` |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Format currency with Intl.NumberFormat.
toLocaleString() applies the browser locale grouping and decimal separators, but the $ prefix is hard-coded. In a non-US locale the result mixes conventions, for example $1.234,5. Fractional cents also render with a variable number of digits.
♻️ Proposed fix
-export const currency = (amount: number): string =>
- `$${amount.toLocaleString()}`
+const CURRENCY_FORMAT = new Intl.NumberFormat('en-US', {
+ currency: 'USD',
+ maximumFractionDigits: 0,
+ style: 'currency',
+})
+
+export const currency = (amount: number): string => CURRENCY_FORMAT.format(amount)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| export const currency = (amount: number): string => | |
| `$${amount.toLocaleString()}` | |
| const CURRENCY_FORMAT = new Intl.NumberFormat('en-US', { | |
| currency: 'USD', | |
| maximumFractionDigits: 0, | |
| style: 'currency', | |
| }) | |
| export const currency = (amount: number): string => CURRENCY_FORMAT.format(amount) |
| const label = | ||
| grace === 'countdown' && daysLeft ? `${daysLeft} days left` : LABEL[grace] |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Day counts read "1 days" in three messages. All three sites interpolate a day count into a hard-coded plural noun. One shared helper, for example days(count), fixes every site.
frontend/web/components/organisation-settings/usage/UsageBillingPrototype/GraceChip.tsx#L41-L42: pluralise the countdown label built fromdaysLeft.frontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageBanner.tsx#L29-L31: pluralise "Flag serving pauses in ... days" built fromview.graceDaysLeft.frontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageNote.tsx#L45-L54: pluralise "Flag serving pauses in ... days" built fromview.graceDaysLeft.
📍 Affects 3 files
frontend/web/components/organisation-settings/usage/UsageBillingPrototype/GraceChip.tsx#L41-L42(this comment)frontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageBanner.tsx#L29-L31frontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageNote.tsx#L45-L54
| import { FC, ReactNode } from 'react' | ||
| import './UsageBadge.scss' |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Components under frontend/web/components/ must each live in their own folder. All six new components sit flat inside UsageBillingPrototype/, with stylesheets beside them and no barrel files. The shared root cause is one layout decision for the whole prototype folder.
frontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageBadge.tsx#L1-L2: createUsageBadge/UsageBadge.tsx, moveUsageBadge.scssinto that folder, and addUsageBadge/index.ts.frontend/web/components/organisation-settings/usage/UsageBillingPrototype/GraceChip.tsx#L1-L4: createGraceChip/GraceChip.tsxwithGraceChip/index.ts, and importUsageBadgeby folder path.frontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageBanner.tsx#L1-L4: createUsageBanner/UsageBanner.tsxwithUsageBanner/index.ts.frontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageNote.tsx#L1-L9: createUsageNote/UsageNote.tsxwithUsageNote/index.ts.frontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageChart.tsx#L1-L21: createUsageChart/UsageChart.tsxwithUsageChart/index.ts.frontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageBillingPrototype.tsx#L1-L12: keepUsageBillingPrototype.tsxandUsageBillingPrototype.scssin theUsageBillingPrototype/folder, and import each sibling by its new folder path.
As per coding guidelines: "Each new component must live in its own folder with a barrel index.ts, a ComponentName/ComponentName.tsx file, co-located ComponentName.scss, any subcomponents, and imports should use the component folder path rather than the inner file."
📍 Affects 6 files
frontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageBadge.tsx#L1-L2(this comment)frontend/web/components/organisation-settings/usage/UsageBillingPrototype/GraceChip.tsx#L1-L4frontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageBanner.tsx#L1-L4frontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageNote.tsx#L1-L9frontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageChart.tsx#L1-L21frontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageBillingPrototype.tsx#L1-L12
Source: Coding guidelines
| &__pct { | ||
| font-size: 40px; | ||
| font-weight: 700; | ||
| line-height: 1; | ||
| } |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
Merge the duplicated selector blocks.
Three classes are declared twice in this file: &__pct at lines 124-128 and 169-181, &__marker-label at lines 183-191 and 206-214, and &__bar-fill at lines 193-195 and 305-308. The __bar-fill declarations sit 110 lines apart, so the base styles and the tone modifiers are easy to change independently by mistake. Merge each pair into one block.
Also applies to: 169-195, 305-308
| const toggleRow = (percent: number) => | ||
| onChange( | ||
| rows.map((row) => | ||
| row.percent === percent ? { ...row, enabled: !row.enabled } : row, | ||
| ), | ||
| ) | ||
|
|
||
| const removeRow = (percent: number) => | ||
| onChange(rows.filter((row) => row.percent !== percent)) | ||
|
|
||
| const addRow = () => { | ||
| const highest = [...rows].map((row) => row.percent).sort((a, b) => b - a)[0] | ||
| onChange( | ||
| rows.concat({ | ||
| enabled: true, | ||
| percent: Math.min((highest ?? 50) + 25, 500), | ||
| }), | ||
| ) | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Prevent duplicate percent values in addRow.
percent is the row identity for the React key, toggleRow, and removeRow. addRow clamps the new value to 500, so once a 500 row exists, every further click appends a second row at 500. React then warns about duplicate keys, and one toggle or one remove acts on both rows. Stop adding a row when the clamp cannot produce a new value.
🐛 Proposed fix
const addRow = () => {
- const highest = [...rows].map((row) => row.percent).sort((a, b) => b - a)[0]
- onChange(
- rows.concat({
- enabled: true,
- percent: Math.min((highest ?? 50) + 25, 500),
- }),
- )
+ const highest = rows.reduce((acc, row) => Math.max(acc, row.percent), 50)
+ const percent = Math.min(highest + 25, 500)
+ if (rows.some((row) => row.percent === percent)) return
+ onChange(rows.concat({ enabled: true, percent }))
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const toggleRow = (percent: number) => | |
| onChange( | |
| rows.map((row) => | |
| row.percent === percent ? { ...row, enabled: !row.enabled } : row, | |
| ), | |
| ) | |
| const removeRow = (percent: number) => | |
| onChange(rows.filter((row) => row.percent !== percent)) | |
| const addRow = () => { | |
| const highest = [...rows].map((row) => row.percent).sort((a, b) => b - a)[0] | |
| onChange( | |
| rows.concat({ | |
| enabled: true, | |
| percent: Math.min((highest ?? 50) + 25, 500), | |
| }), | |
| ) | |
| } | |
| const toggleRow = (percent: number) => | |
| onChange( | |
| rows.map((row) => | |
| row.percent === percent ? { ...row, enabled: !row.enabled } : row, | |
| ), | |
| ) | |
| const removeRow = (percent: number) => | |
| onChange(rows.filter((row) => row.percent !== percent)) | |
| const addRow = () => { | |
| const highest = rows.reduce((acc, row) => Math.max(acc, row.percent), 50) | |
| const percent = Math.min(highest + 25, 500) | |
| if (rows.some((row) => row.percent === percent)) return | |
| onChange(rows.concat({ enabled: true, percent })) | |
| } |
| <Switch | ||
| checked={row.enabled} | ||
| onChange={() => toggleRow(row.percent)} | ||
| /> |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== candidate files =="
fd -a 'UsageNotifications\.tsx|Switch\.(tsx|ts|js|jsx)$' frontend || true
echo
echo "== target file outline =="
ast-grep outline frontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageNotifications.tsx || true
echo
echo "== target relevant lines =="
cat -n frontend/web/components/organisation-settings/usage/UsageBillingPrototype/UsageNotifications.tsx | sed -n '1,170p'
echo
echo "== Switch definitions/usages =="
rg -n "export .*Switch|const Switch|function Switch|Switch\s+|aria-label" frontend/web/components frontend/common frontend/project -g '*.{ts,tsx,js,jsx}' | head -200Repository: Flagsmith/flagsmith
Length of output: 18475
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Switch component =="
cat -n frontend/web/components/Switch.tsx | sed -n '1,90p'
echo
echo "== nearby Switch accessible usages =="
cat -n frontend/web/components/CompareEnvironments/CompareFeatureRow.tsx | sed -n '48,72p'
cat -n frontend/web/components/modals/AuditLogWebhooks.tsx | sed -n '108,118p'
echo
echo "== repo package dependency mentions =="
rg -n '"react-rc-switch|"rc-switch|"Switch"' frontend -g 'package.json' || trueRepository: Flagsmith/flagsmith
Length of output: 3745
🌐 Web query:
rc-switch 1.9.0 Props aria-label source
💡 Result:
The rc-switch library, specifically version 1.9.0, does not explicitly list an aria-label prop in its official documentation or API properties [1][2][3]. The documented properties for rc-switch (as of version 1.9.0 and similar historical versions) include prefixCls, className, checked, defaultChecked, onChange, tabIndex, onClick, autoFocus, disabled, and loadingIcon [1][2][3]. While modern versions of the component (now under the @rc-component/switch package) have evolved and include various improvements, if you are working specifically with the legacy 1.9.0 version, you may need to apply an aria-label directly to the component if it is accepted as an arbitrary HTML attribute, or handle accessibility labels through an external wrapper or label element if the component does not natively pass through unrecognized attributes to the underlying DOM node [4]. If you require native aria-label support for a switch component, you might consider migrating to a newer version or an alternative like React Aria, which explicitly supports aria-label [5][6].
Citations:
- 1: https://www.npmjs.com/package/rc-switch
- 2: https://registry.npmjs.org/rc-switch
- 3: https://github.com/moklick/switch
- 4: https://github.com/react-component/switch/
- 5: https://react-aria.adobe.com/Switch/useSwitch.html
- 6: https://react-aria.adobe.com/Switch
🌐 Web query:
github react-component switch src rc-switch 1.9.0 render input id className aria-label
💡 Result:
In the rc-switch library (versions 1.9.0 and consistent throughout the library's history), the component is designed as a high-level UI primitive rather than a standard HTML <input> wrapper that exposes all native input attributes directly. Regarding the specific properties requested for rc-switch (version 1.9.0 and similar): 1. className: This property is supported and functions as intended, applying additional class names to the root node of the component [1][2][3][4]. 2. id and aria-label: These properties are not explicitly listed in the standard rc-switch API documentation for version 1.9.0 or the current versions [1][5][2][3]. Unlike standard HTML input elements, rc-switch renders as a semantic element (typically a <button> with role="switch") [5], and it does not provide a direct pass-through mechanism for all arbitrary id or aria-* attributes to an underlying input element. If you require full control over id, aria-label, or other specific input attributes for accessibility or form integration, you may need to: - Use a wrapper element to attach these attributes, although this will not apply them to the internal component logic. - Consider using an alternative library designed for full ARIA/input support if these attributes are critical for your implementation (e.g., react-aria or Headless UI which are built to expose such attributes explicitly [6][7][8]). For the specific version 1.9.0, you can verify the supported API by checking the README.md or package.json in the corresponding GitHub tag/branch, as the property list is constrained to the documented props (such as prefixCls, checked, disabled, etc.) [1][2][3].
Citations:
- 1: https://github.com/react-component/switch/blob/master/README.md
- 2: https://registry.npmjs.org/rc-switch
- 3: https://www.npmjs.com/package/rc-switch
- 4: https://github.com/wangzianan/switch
- 5: https://github.com/react-component/switch
- 6: https://github.com/mui/base-ui/blob/6583c18f/packages/react/src/switch/root/SwitchRoot.tsx
- 7: https://github.com/tailwindlabs/headlessui/blob/main/packages/@headlessui-react/src/components/switch/switch.tsx
- 8: https://react-aria.adobe.com/Switch
🌐 Web query:
github react-component switch v1.9.0 index.html children aria-label
💡 Result:
The rc-switch library (now maintained under @rc-component/switch) is an accessible React switch component commonly used within the Ant Design ecosystem [1][2]. Regarding your specific interests: Accessibility and Attributes The current version of the library explicitly supports accessibility by rendering a native button with role="switch" and aria-checked attributes [1][2]. While older versions (such as 1.9.x) primarily focused on basic functionality [3], modern versions have improved accessibility features [2]. If you require an aria-label for screen reader support—especially when the switch has no visible text—you can pass it as a standard HTML attribute to the Switch component, as it accepts standard HTML attributes [1][2][4]. Children The component provides specific slots for content via the checkedChildren and unCheckedChildren props [1][2]. These allow you to render custom React nodes (such as text or icons) that appear when the switch is in the checked or unchecked state, respectively [1][2]. Version 1.9.0 Context Version 1.9.0 was released around January 2019 [3]. It predates the recent migration to the @rc-component namespace and the introduction of more advanced semantic slots (like classNames and styles objects introduced in later major versions) [1][3][2]. For projects relying on this legacy version, you would typically use the checkedChildren and unCheckedChildren props to display content, and accessibility would generally be handled by the DOM elements rendered by the library at that time [5][3]. It is highly recommended to migrate to the latest version of @rc-component/switch to benefit from continued maintenance, bug fixes, and updated accessibility standards [1][2].
Citations:
- 1: https://github.com/react-component/switch
- 2: https://github.com/react-component/switch/
- 3: https://registry.npmjs.org/rc-switch
- 4: https://github.com/components-kit/packages/blob/967c36c385bcd7201dd6d8c803cd4603f63f933c/libs/react/src/components/switch/README.md
- 5: https://github.com/react-component/switch/blob/master/README.md
🌐 Web query:
react-component/switch 1.9.0 source class Switch render htmlAttributesSpread
💡 Result:
The term htmlAttributesSpread is not a recognized function, method, or property within the source code or API of rc-switch (react-component/switch) version 1.9.0 [1][2][3]. In the context of React development, "html attributes spread" typically refers to the pattern of using the object spread operator (e.g., {...props}) to pass arbitrary HTML attributes to a component's underlying DOM element. In version 1.9.0 of rc-switch, the component was designed to handle specific, documented properties such as prefixCls, className, checked, defaultChecked, onChange, onClick, tabIndex, autoFocus, disabled, and loadingIcon [4][5][2][3]. There is no evidence that version 1.9.0 implemented a specific internal method named htmlAttributesSpread to handle the passing of remaining attributes [1][3]. If you are encountering this term in a codebase, it is likely either a custom helper function implemented locally by your team to manage prop spreading, or a misidentification of the standard React JSX spread syntax used to pass extraneous props down to the root DOM node.
Citations:
- 1: https://shoulder.dev/npm/rc-switch/1.9.0
- 2: https://registry.npmjs.org/rc-switch
- 3: https://git.talenttic.com/galads/icehrm/src/commit/2ff6a1ba8d8b131671f5158d7bd34a96a2af3ce1/web/node_modules/rc-switch/HISTORY.md
- 4: https://github.com/react-component/switch/blob/master/README.md
- 5: https://www.npmjs.com/package/rc-switch
🌐 Web query:
"https://cdn.jsdelivr.net/npm/rc-switch@1.9.0/lib/index.js"
💡 Result:
The URL you provided, https://cdn.jsdelivr.net/npm/rc-switch@1.9.0/lib/index.js, points to a specific version (1.9.0) of a JavaScript library named rc-switch [1]. There are two distinct projects that commonly use the name "rc-switch": 1. React Component Switch: This is a widely used React UI library for a switch component [1][2][3]. It is part of the react-component ecosystem and is often used as a dependency in larger frameworks like Ant Design [3]. The version 1.9.0 of this library was published around 2019 [1]. Note that more recent versions of this package have transitioned to the scope @rc-component/switch [3]. 2. Arduino RC-Switch: This is a completely different library for Arduino and other microcontrollers (like the ESP8266 or Raspberry Pi) designed to operate 433/315MHz remote radio-controlled devices, such as power outlets [4][5][6]. Given that your link uses the jsDelivr CDN to serve a JavaScript file from the npm registry, it refers to the React Component library [1]. You should be aware that version 1.9.0 is significantly outdated [1]. If you are building a modern React application, it is recommended to use the current version of the package, which is now maintained under the @rc-component/switch namespace [3].
Citations:
- 1: https://registry.npmjs.org/rc-switch
- 2: https://react-component.github.io/switch/
- 3: https://github.com/react-component/switch
- 4: https://docs.arduino.cc/libraries/rc-switch/
- 5: https://github.com/sui77/rc-switch
- 6: https://sming.readthedocs.io/en/latest/_inc/Sming/Libraries/RCSwitch/index.html
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== fetch rc-switch 1.9.0 CDN source =="
curl -fsSL https://unpkg.com/rc-switch@1.9.0/lib/index.js >/tmp/rc-switch-1.9.0.js
wc -l /tmp/rc-switch-1.9.0.js
rg -n "props|className|id|aria|children|switch" /tmp/rc-switch-1.9.0.js | head -80
echo
echo "== inspect rendering section =="
sed -n '80,180p' /tmp/rc-switch-1.9.0.jsRepository: Flagsmith/flagsmith
Length of output: 222
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== try registry tarball =="
curl -fsSL https://registry.npmjs.org/rc-switch/1.9.0:tarball -o /tmp/rc-switch-1.9.0.tgz || true
if [ -f /tmp/rc-switch-1.9.0.tgz ]; then
tar -tzf /tmp/rc-switch-1.9.0.tgz | sed -n '1,80p'
mkdir -p /tmp/rs-tmp
tar -xzf /tmp/rc-switch-1.9.0.tgz -C /tmp/rs-tmp
rg -n "props|className|id|aria|children|switch|spreadProps|objectSpread" /tmp/rs-tmp/package -g '*.js' | head -120
echo
echo "== inspect rendered button/label sections =="
rg -n "className=|return |a label|children" /tmp/rs-tmp/package/lib/index.js -A6 -B4
sed -n '1,220p' /tmp/rs-tmp/package/lib/*.js | sed -n '1,180p'
fi
echo
echo "== direct CDN text =="
curl -fsSLi https://unpkg.com/rc-switch@1.9.0/lib/index.js | sed -n '1,40p'Repository: Flagsmith/flagsmith
Length of output: 1171
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== fetch rc-switch 1.9.0 actual files =="
curl -fsSL https://unpkg.com/rc-switch@1.9.0/lib/Switch.js >/tmp/Switch.js
wc -l /tmp/Switch.js
sed -n '1,260p' /tmp/Switch.js
echo
echo "== compare with local component wrapper =="
cat -n frontend/web/components/Switch.tsx | sed -n '1,80p'Repository: Flagsmith/flagsmith
Length of output: 11457
Give each Switch an accessible name.
The switches at lines 98, 126, and 135 are rendered without their own label or aria-label, so screen readers announce unnamed switches. Add explicit labels and keep them descriptive, for example Notify me at ${row.percent}% of plan consumed, in-app, and email. [accessibility]
| flex: 1; | ||
| min-width: 180px; |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Prevent the dashboard metric row from overflowing.
InstanceMetricsCards renders five StatItem elements in a non-wrapping row. These minimum widths require at least 996px, including its gaps. At narrower content widths, the row overflows instead of reflowing. Add wrapping in that consumer or make this minimum width layout-specific.
| import Icon, { IconName } from './icons/Icon' | ||
| import Tooltip from './Tooltip' |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Replace the relative component imports.
The frontend import rule does not permit relative imports in this file. Import Icon and Tooltip through components/icons/Icon and components/Tooltip.
As per coding guidelines: frontend/**/*.{js,jsx,ts,tsx} must use common/, components/, or project/ import paths and must not use relative imports.
Source: Coding guidelines
| icon?: IconName | ||
| /** 'sm' for text values like emails, which overflow at the default size. */ | ||
| size?: 'default' | 'sm' |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Extract the inline union types into named types.
Define named types for these unions, then use the names at each declaration.
frontend/web/components/StatItem.tsx#L20-L22: defineStatItemSizefor'default' | 'sm'.frontend/web/components/pages/organisation-settings/tabs/BillingTab.tsx#L45-L69: define a named type forLimitItem | undefined.
As per coding guidelines: extract inline union types into named types.
📍 Affects 2 files
frontend/web/components/StatItem.tsx#L20-L22(this comment)frontend/web/components/pages/organisation-settings/tabs/BillingTab.tsx#L45-L69
Source: Coding guidelines
✅ private-cloud · depot-ubuntu-latest-arm-16 — run #18970 (attempt 1)Playwright Test Results (private-cloud - depot-ubuntu-latest-arm-16)Details
🗂️ Previous results✅ private-cloud · depot-ubuntu-latest-16 — run #18970 (attempt 1)Playwright Test Results (private-cloud - depot-ubuntu-latest-16)Details
Skipped testsfirefox › tests/onboarding-tests.pw.ts › Onboarding › New user connects via the single-page onboarding flow @oss ✅ oss · depot-ubuntu-latest-arm-16 — run #18970 (attempt 1)Playwright Test Results (oss - depot-ubuntu-latest-arm-16)Details
✅ oss · depot-ubuntu-latest-16 — run #18970 (attempt 1)Playwright Test Results (oss - depot-ubuntu-latest-16)Details
|
Visual Regression19 screenshots compared. See report for details. |
Thanks for submitting a PR! Please check the boxes below:
docs/if required so people know about the feature.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:
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.