Skip to content

refactor(ui): migrate cost-tracking and users to shadcn - #34605

Closed
yuneng-berri wants to merge 2 commits into
litellm_internal_stagingfrom
litellm_/eager-bose-ff3493
Closed

refactor(ui): migrate cost-tracking and users to shadcn#34605
yuneng-berri wants to merge 2 commits into
litellm_internal_stagingfrom
litellm_/eager-bose-ff3493

Conversation

@yuneng-berri

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • Cost Tracking and Internal Users still render antd and Tremor
  • Mixing three UI kits on one page keeps theming inconsistent
  • Those imports block the shadcn migration from finishing

How it solves it:

  • Swaps antd and Tremor for installed shadcn primitives
  • Markup only; no data flow or props contract changes
  • Route tests stay untouched and green, proving behaviour held

Relevant issues

Linear ticket

Pre-Submission checklist

Please complete all items before asking a LiteLLM maintainer to review your PR

  • I have added meaningful tests
  • My PR passes all CI/CD checks (e.g., lint, format, unit tests)
  • My PR's scope is as isolated as possible; it only solves 1 specific problem
  • I have received a Greptile Confidence Score of at least 4/5 before requesting a maintainer review (Greptile reviews automatically once the PR is opened; only comment @greptileai to re-request a review after pushing changes)

Delays in PR merge?

If you're seeing a delay in your PR being merged, ping the LiteLLM Team on Slack (#pr-review).

Screenshots / Proof of Fix

Verified against fdafcf28b2 on a local proxy with the dashboard dev server. Screenshots to follow on this PR.

What each step should show. On Internal Users the tab strip moves from the Tremor underline style to the shadcn pill style and the table below is unchanged. In Default User Settings the teams editor, the User Role select and the Models multi-select are the parts a page screenshot never reaches, so they need driving by hand. On Cost Tracking, HowItWorks sits behind a collapsed accordion and a tab, and MultiExportDropdown only mounts once the pricing calculator has results, so neither is visible on a plain page load.

  1. Start the proxy and the dashboard dev server, then open http://localhost:3000/users/
  2. Confirm the Users tab still lists users, then click Default User Settings
  3. Click Edit Settings, then Add Team; type a team id, set User Role to Admin, and pick an entry in Models. Each should update in place
  4. Click Cancel, then reopen the tab and confirm the saved values render read-only
  5. Open http://localhost:3000/cost-tracking/, expand Provider Discounts, and select the Test It tab
  6. In Discount Calculator, enter 0.0171938125 and 0.0009049375; expect a 5.00% discount applied
  7. Expand Pricing Calculator, choose any model, and click Export; expect PDF and CSV entries
  8. Narrow the window to about 600px tall on both routes and confirm the page scrolls rather than clipping

Type

🧹 Refactoring

Changes

cost-tracking, two route-exclusive components. how_it_works.tsx drops Tremor Text and TextInput for plain elements with token classes and ui/input. multi_export_dropdown.tsx drops the Tremor Button and the three @ant-design/icons for ui/button and lucide Download, FileText and FileSpreadsheet. The hand-rolled menu and its outside-click handling are deliberately left alone; they are plain markup, not antd or Tremor, and porting them to ui/dropdown-menu would change behaviour the existing test pins.

users, two route-exclusive components. view_users.tsx moves the Tremor tab group to ui/tabs, the two antd buttons to ui/button and the antd skeletons to ui/skeleton. DefaultUserSettings.tsx is the larger one: Tremor Card, Title, Text, Divider and TextInput and antd Button, Typography, Spin, Switch, Select and InputNumber become ui/card, ui/button, ui/input, ui/select, ui/switch, ui/separator, ui/badge, ui/combobox and ui/ui-loading-spinner, with the antd icons replaced by lucide Plus and Trash2.

caching was analysed as part of the same batch and needs no changes, so no caching file is touched here. cache_dashboard.tsx was already migrated in #34468; its one remaining Tremor reference is the DateRangePickerValue type, which exists solely to satisfy the props contract of shared/usage_date_picker.tsx, a component the analyzer classifies as reached by 2 pages and therefore out of scope. cache_settings/cacheSettingsFields.ts and coordination_redis_settings/coordinationRedisFields.ts each carry a single import type { FormItemProps } from "antd", used only to type validation rules that feed antd forms, and forms stay deferred. Caching is included in the verification below as a route that must stay pixel-identical, which it did.

Skipped elsewhere, quoting the analyzer's buckets. Under TABLE, "hand off to /migrate-simple-table": pricing_calculator/index.tsx, pricing_calculator/multi_cost_results.tsx, provider_discount_table.tsx, provider_margin_table.tsx, common_components/simple_table.tsx and users/_components/BulkEditUsers.tsx. Under DEFERRED, "contains an antd Form -- do not touch until forms unblock": add_margin_form.tsx, add_provider_form.tsx, cost_tracking_settings.tsx, check_openapi_schema.tsx, edit_user.tsx, user_edit_view.tsx, view_users/user_info_view.tsx, CreateUserButton.tsx, and the four caching form files. Under SHARED, "reached by another page -- REFUSE to edit", the notable ones for this batch are budget_duration_dropdown.tsx at 11 pages, team_dropdown.tsx at 13, DeleteResourceModal.tsx at 23 and shared/numerical_input.tsx at 14. BudgetDurationDropdown is why the Default User Settings panel still renders antd markup after this PR; it belongs to the shared-component track.

Two commits, deliberately ordered. The first adds characterisation tests for the DefaultUserSettings boolean switch and teams editor, asserted through the save payload so they are library-agnostic, and proves them green against the antd component. The second migrates the components and touches no test file. All 342 tests across the three routes pass afterwards without a single test edit, which is what makes them evidence rather than decoration.

eslint-suppressions.json loses the four no-restricted-imports entries these files no longer need, via eslint . --prune-suppressions.

Final Attestation

  • The tests check the right things, including the edge cases, and regressions in the respective real-world customer use-cases are not possible after this PR

…dcn migration

Covers the boolean switch and the teams editor through the save payload, so the
behaviour is pinned by assertions that pass against the current antd/Tremor
component and are library-agnostic.
Replaces antd and Tremor markup in the four route-exclusive components with
installed shadcn primitives from @/components/ui plus token utilities. Markup
only; no data flow, request or props contract changes.

The route tests were not touched by this commit and stay green, which is what
makes them evidence: they were written against the antd/Tremor markup.
@greptile-apps

greptile-apps Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

Migrates the Cost Tracking and Internal Users interfaces from Ant Design and Tremor components to local shadcn/Base UI primitives.

  • Replaces cost-calculation inputs, export controls, icons, and styling.
  • Migrates default-user-settings cards, inputs, selectors, switches, loading states, and team editing controls.
  • Replaces the users-page tabs, buttons, and skeletons.
  • Adds characterization tests for boolean and team-setting save payloads.
  • Removes obsolete restricted-import suppressions.

Confidence Score: 5/5

The PR appears safe to merge, with no concrete changed-code regression identified.

The migrated controls use supported local component APIs, preserve the existing settings and export flows, and the added tests exercise the newly migrated boolean and team-editing interactions.

Important Files Changed

Filename Overview
ui/litellm-dashboard/src/app/(dashboard)/users/_components/DefaultUserSettings.tsx Migrates settings controls to local UI primitives while retaining the existing data-fetching and save-payload flow.
ui/litellm-dashboard/src/app/(dashboard)/users/_components/DefaultUserSettings.test.tsx Adds focused coverage for boolean toggling and adding or removing teams without weakening existing assertions.
ui/litellm-dashboard/src/app/(dashboard)/users/_components/view_users.tsx Replaces Tremor tabs and Ant Design controls with compatible local primitives without changing the existing tab lifecycle behavior.
ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/how_it_works.tsx Replaces Tremor typography and inputs with semantic markup and standard controlled inputs while preserving calculation behavior.
ui/litellm-dashboard/src/app/(dashboard)/cost-tracking/_components/pricing_calculator/multi_export_dropdown.tsx Migrates the export button and icons while preserving the existing menu and export handlers.
ui/litellm-dashboard/eslint-suppressions.json Removes restricted-import suppressions made obsolete by the component migration.

Reviews (1): Last reviewed commit: "refactor(ui): migrate cost-tracking and ..." | Re-trigger Greptile

@devin-ai-integration

Copy link
Copy Markdown
Contributor

Ran the full runbook against a local proxy (:4000 with Postgres) and dashboard dev server (:3000) as admin, plus the scoped frontend unit tests. Everything passes; no visual regressions, broken interactions, or console errors attributable to the migration

Default User Settings edit flow (the large DefaultUserSettings.tsx migration)

Add Team, controlled Team ID / Max Budget Inputs, the per-team role Select, and the Models Combobox all update in place. The adversarial check for the updateTeam rewrite from mutable array assignment to .map() passes: adding and filling Team 2 leaves Team 1 completely intact

Team 2 added without clobbering Team 1

Save returns to the shadcn Badge/Card read-only render, and the values survive a full page reload, which proves the migrated controls actually fed the PATCH payload rather than only local state

Values persisted after reload

Editing Team 1's id to should-not-persist then hitting Cancel restores qa-team-alpha

Discount Calculator (how_it_works.tsx)

Entering 0.0171938125 and 0.0009049375 in the two migrated shadcn Inputs produces exactly the expected numbers, so the Tremor onValueChange -> onChange rewiring is correct

Discount calculator results

Export dropdown (multi_export_dropdown.tsx)

The dropdown mounts only once results exist and shows exactly the two lucide-iconed entries. Both produce real files: a 496 B CSV and a 96 KB PDF report

Export dropdown with PDF and CSV

PDF report content

Users tabs, short viewport, /caching regression, unit tests

Tab strip is a two-pill shadcn TabsList with the table below unchanged (Showing 1-1 of 1), and Select Users still flips to Cancel Selection with a disabled Bulk Edit (0 selected)

Users tab with shadcn pills and unchanged table

At a 1200x600 window both routes scroll to their last section instead of clipping

Cost tracking scrolled at 600px

/caching renders its tabs, metric cards and charts with no error boundary

Scoped unit tests: npx vitest run "src/app/(dashboard)/cost-tracking" "src/app/(dashboard)/users" "src/app/(dashboard)/caching" gives 28 passed (28) files and 342 passed (342) tests. Note vitest needs Node 22; Node 20 fails with ERR_REQUIRE_ESM

Console output

No React errors or warnings from any of the four migrated files; no controlled/uncontrolled input, Select value, or key warnings. The Next.js dev overlay does show 2 issues on /users/, but both are antd warnings from src/components/bulk_create_users_button.tsx, which this PR does not touch

Dev overlay attributing the warning to bulk_create_users_button.tsx

One unrelated nit spotted while testing, worth a separate cleanup: a team saved with no budget renders Max Budget: $- after reload because the backend echoes max_budget_in_team: null and the read-only branch only guards against undefined; before saving the same block reads No limit. That condition is unchanged by this PR

The only red CI check is osv-scan, which is a dependency scan unrelated to this diff

Tested by this Devin session

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant