Skip to content

refactor(ui): migrate the last antd components off antd onto shadcn - #37569

Merged
ryan-crabbe-berri merged 2 commits into
litellm_internal_stagingfrom
litellm_antd_shadcn_final
Aug 20, 2026
Merged

refactor(ui): migrate the last antd components off antd onto shadcn#37569
ryan-crabbe-berri merged 2 commits into
litellm_internal_stagingfrom
litellm_antd_shadcn_final

Conversation

@ryan-crabbe-berri

@ryan-crabbe-berri ryan-crabbe-berri commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

TLDR

Problem this solves:

  • 18 dashboard components were still rendering antd
  • Two UI kits on one page, inconsistent controls
  • antd dropdowns sit on a separate stacking layer
  • Every .ant-* selector in the e2e suite was dead

How it solves it:

  • Ports the last 18 components onto shadcn primitives
  • Zero from "antd" imports left in src
  • Prunes 63 stale no-restricted-imports suppressions
  • Rewrites 47 e2e selectors onto roles and labels
  • Fixes a router settings race the speedup exposed

User Flow

Before: an admin creating a key sees the modal in two visual languages, and a router setting they just typed can be dropped from the request

  1. They open https://litellm-domain/ui/?page=api-keys and click Create New Key
  2. The dialog mixes antd and shadcn controls: the Models picker, Key Type picker and Router Settings inputs do not match the surrounding fields, and their dropdown popups render on a separate stacking layer
  3. They expand Router Settings, change a value, and click Create within about a tenth of a second
  4. The key is created without the router settings they just entered, silently

After: the same modal renders as one consistent kit, and the value they typed is always the value that is sent

  1. They open https://litellm-domain/ui/?page=api-keys and click Create New Key
  2. Every control in the dialog is the same shadcn kit as the rest of the page, and the dropdowns open in the same layer as everything else
  3. They expand Router Settings, change a value, and click Create immediately
  4. The key is created with exactly the router settings shown on screen

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
  • The handful of test files covering my change pass locally
  • My PR passes all required CI/CD checks (e.g., lint, schema.d.ts sync check, etc.)
  • 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

Screenshots / Proof of Fix

Shared setup: proxy on http://localhost:4000 with STORE_MODEL_IN_DB=True, dashboard dev server on http://localhost:3000, signed in as a proxy admin

Before (c2b3c4b)

Create Key dialog renders one consistent kit

  1. Open http://localhost:4000/ui/?page=api-keys and click Create New Key
  2. Screenshot the dialog, then expand Router Settings and screenshot again

Router settings survive an immediate submit

  1. In the same dialog, fill Key Name, pick a team and a model
  2. Expand Router Settings, set Number of Retries to 7, and click Create Key without pausing
  3. Open the new key's info page and screenshot the router settings it stored

Teams page header and tabs

  1. Open http://localhost:4000/ui/?page=teams and screenshot the header, the Create Team button, and the tab row
  2. Click Available Teams, then Default Team Settings, screenshotting each

Add Model, Prompts, Policies, Agents, Plugin Settings, Bulk Edit Users

  1. Screenshot http://localhost:4000/ui/?page=new_model, ?page=prompts, ?page=policies, ?page=agents, ?page=admin-panel and the Bulk Edit drawer on ?page=users

After (3dc475c)

Create Key dialog renders one consistent kit

  1. Same steps as Before
  2. Every control is shadcn and the popups open in the page's own layer

Router settings survive an immediate submit

  1. Same steps as Before
  2. The stored router settings show Number of Retries 7

Teams page header and tabs

  1. Same steps as Before
  2. The header, Create Team button, divider and tab underline match the Before layout

Add Model, Prompts, Policies, Agents, Plugin Settings, Bulk Edit Users

  1. Same steps as Before

Type

🧹 Refactoring

Caveats

  • Switches lose antd's inline Yes/No labels
  • The "New" tag is a primary badge, not purple
  • PageHeader's tabs render-prop form now has no callers
  • The antd dependency stays installed, removed separately
  • One Add Model e2e test fails on a pre-existing crash

QA runbook

Selector-only rewrites, so the bullets group by spec rather than repeating the same three sentences for each of 34 tests. Every changed line swaps an antd class lookup for a role, label or test id; no assertion changed

  • tests/e2e/ui/tests/proxy-admin/keys.spec.ts, "Create a key in a team", "Regenerate key", "Create a key with All Proxy Models (no team)", "Create a key with a specific proxy model (no team)" - the key creation and regeneration flows still drive the real dialogs
    • Opens Create New Key, picks the team from the combobox, then opens the models field by its accessible name "Select models" rather than by antd's overflow container
    • Picks "All Team Models", "All Proxy Models" or a named model as role=option scoped to the page, because the popup portals to the body and is not a descendant of the dialog
    • Scopes the regenerate step to role=dialog named "Regenerate Virtual Key", which is what kept the stale Regenerate button behind the modal from being clicked
    • Reads the generated key out of the dialog named "Save your Key" and calls /chat/completions with it, proving the key the UI displayed is a working key
    • Drops an option.evaluate(el => el.click()) workaround that existed only because antd rendered options off-viewport during its open animation
    • Sanity check: this test makes sense to add and is not hand-wavey or potentially flaky
  • tests/e2e/ui/tests/proxy-admin/teams.spec.ts, "Create a team", "Invite a user to a team", "Edit team member for team proxy admin does not belong to", "Team in org - edit team member" - the team and membership dialogs are addressed by name
    • Each dialog is now role=dialog with its own title, so the four sites that all read .ant-modal:visible can no longer match each other
    • Fills the team name by its test id, because the shared form field derives its control id from useId() and #team_alias cannot exist any more
    • Sanity check: this test makes sense to add and is not hand-wavey or potentially flaky
  • tests/e2e/ui/tests/team-admin/teamAdmin.spec.ts, "Team admin can add a member to their team", "Team admin can create a team key with All Team Models" - same two swaps as above, on the team-admin seat
    • Scopes the add-member step to the dialog named "Add Team Member"
    • Picks "All Team Models" through the combobox, then asserts on the captured /key/generate body, which is the part that actually proves team scoping
    • Sanity check: this test makes sense to add and is not hand-wavey or potentially flaky
  • tests/e2e/ui/tests/modelsPage/addModel.spec.ts, six tests covering the provider dropdown, model picker, Team-BYOK toggle and connection test - the provider picker interaction model changed, not just its selector
    • Rewrites selectProvider to click the combobox open before typing, because a Base UI combobox ignores fill() while closed and highlights nothing, so the old fill-then-Enter selected nothing
    • Matches the provider option on its visible text, since its accessible name also carries the logo's alt text
    • Corrects one provider label that never matched what /public/providers/fields returns
    • Reaches the Team-BYOK switch by its accessible name and the connection-test dialog by its title
    • Sanity check: this test makes sense to add and is not hand-wavey or potentially flaky
  • tests/e2e/ui/tests/settings/routerSettings.spec.ts, "Add a fallback and verify it appears in the table" - the fallback pickers are searchable comboboxes now
    • Opens each field by its accessible name, types to filter, then clicks the option, because nothing is pre-highlighted and Enter would select nothing
    • Drops a trailing Escape that used to dismiss the antd popup and would now close the whole dialog
    • Still asserts through the dialog's own state, the primary model's tab appearing and the chain reading "(1/10 used)", rather than through the popup
    • Sanity check: this test makes sense to add and is not hand-wavey or potentially flaky
  • tests/e2e/ui/tests/mcp/mcpServers.spec.ts and tests/e2e/ui/helpers/mcp.ts, "Add a custom MCP server via the discovery → custom form" - the shared helper that every MCP spec builds on
    • Keeps a hasText filter on the form dialog, because the discovery dialog is still open behind it and both are role=dialog
    • Fills the name and URL by label and picks transport and auth by combobox name, with the auth lookup exact so it cannot also match "Authentication Value"
    • Sanity check: this test makes sense to add and is not hand-wavey or potentially flaky
  • tests/e2e/ui/tests/modelsPage/credentials.spec.ts, "changing only the api base does not overwrite the stored api key with its masked value" - scopes to the dialog named "Edit Credential"
    • Only the modal locator changed; the masked-key assertion is untouched
    • Sanity check: this test makes sense to add and is not hand-wavey or potentially flaky
  • tests/e2e/ui/tests/usage/usagePage.spec.ts, "Top Virtual Keys lists a key that served traffic, toggles views, and opens key info" - widens the top-N control
    • Clicks "50" through the radio group's label, because the radio input itself is screen-reader-only and fails Playwright's hit-target check
    • Needs TZ=UTC locally, since the browser asks for the local date while the proxy aggregates under UTC
    • Sanity check: this test makes sense to add and is not hand-wavey or potentially flaky
  • tests/e2e/ui/tests/users/searchUsers.spec.ts and tests/e2e/ui/tests/users/viewInternalUsers.spec.ts - loading-skeleton waits
    • Waits on [data-slot="skeleton"] instead of .ant-skeleton
    • Both files are skipped at the describe level and were not executed, so treat this pair as converted but unverified
    • Sanity check: this test makes sense to add and is not hand-wavey or potentially flaky

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

@greptile-apps

greptile-apps Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR completes the dashboard migration from Ant Design controls to shared shadcn primitives and adds synchronous router-settings capture during key creation

  • Replaces remaining Ant Design dialogs, selects, tables, tabs, switches, upload controls, typography, and loading indicators
  • Updates focused UI tests and removes obsolete restricted-import suppressions
  • Captures current router-setting inputs directly before constructing a create-key request

Confidence Score: 5/5

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

The migrated controls preserve their form values, submission paths, reset behavior, and role gating, while the key workflow now captures displayed router settings before building its request

Important Files Changed

Filename Overview
ui/litellm-dashboard/src/components/organisms/create_key_button.tsx Migrates key-creation controls and reads current router settings synchronously before payload construction; no actionable regression was established
ui/litellm-dashboard/src/components/add_model/AddModelForm.tsx Replaces Ant Design layout and selectors while preserving provider, credential, mode, and mounted-form value handling
ui/litellm-dashboard/src/app/(dashboard)/agents/_components/add_agent_form.tsx Replaces agent type selection, badges, and progress steps with shared primitives without changing the creation workflow
ui/litellm-dashboard/src/app/(dashboard)/users/_components/BulkEditUsers.tsx Migrates bulk-user table, checkbox, team selector, and budget input while retaining the existing update behavior
ui/litellm-dashboard/src/components/CreateUserButton.tsx Replaces the standalone invitation modal with Dialog and retains form reset and submission behavior
ui/litellm-dashboard/src/components/Settings/AdminSettings/PluginSettings/PluginSettings.tsx Migrates plugin settings presentation to shared cards and tables while preserving credential-edit semantics
ui/litellm-dashboard/src/app/(dashboard)/prompts/_components/add_prompt_form.tsx Replaces Ant Design Upload with a validated native file picker and preserves prompt conversion and creation
ui/litellm-dashboard/src/components/Teams.tsx Migrates team navigation from Ant Design tabs to shared tabs while retaining role-gated tab contents and actions

Reviews (1): Last reviewed commit: "refactor(ui): migrate the last antd comp..." | Re-trigger Greptile

@ryan-crabbe-berri
ryan-crabbe-berri enabled auto-merge (squash) August 20, 2026 02:20
@ryan-crabbe-berri
ryan-crabbe-berri force-pushed the litellm_antd_shadcn_final branch from 776069b to a6dff2b Compare August 20, 2026 02:54
Converts the remaining dashboard components that still imported antd: admin panel, agents, MCP toolsets, policies, prompts, bulk user edit, create user, plugin settings, teams, add model, auto router, cloudzero export, BYOK credentials, credential modal, onboarding link, create key and routing groups.

Primitives map onto the house shadcn set: Typography onto semantic tags, Select onto ui/select, SearchSelect or MultiSelect, Input onto ui/input, Tooltip onto SimpleTooltip, Card, Table, Tabs, Switch, Checkbox, Radio, Tag onto Badge, Divider onto Separator, Spin onto UiLoadingSpinner, Modal onto Dialog, message onto toast, and Space, Row, Col, Flex and Layout onto flex containers.
The dashboard no longer renders antd, so every `.ant-*` locator in the
Playwright suite matched nothing. Most were already dead before this
branch, broken by the earlier shadcn ports; the models multi-select in
the key specs and the provider picker in addModel are the ones this
branch broke.

Queries now go through roles, labels and test ids. Select popups portal
to the body, so option lookups are page-scoped rather than scoped to the
dialog that opened them. Two locators fall back to `data-slot` because
the elements have no distinguishing accessible name.
@ryan-crabbe-berri
ryan-crabbe-berri force-pushed the litellm_antd_shadcn_final branch from a6dff2b to 3dc475c Compare August 20, 2026 02:58
@ryan-crabbe-berri
ryan-crabbe-berri merged commit 0b37454 into litellm_internal_staging Aug 20, 2026
66 of 67 checks passed
@ryan-crabbe-berri
ryan-crabbe-berri deleted the litellm_antd_shadcn_final branch August 20, 2026 03:01
felixboelter added a commit to aihpi/tool-litellm that referenced this pull request Aug 20, 2026
Upstream finished migrating the dashboard off antd (BerriAI#37569, BerriAI#37574), so
AntdGlobalProvider.tsx no longer exists and antd is gone from package.json.
The ConfigProvider patch was dead, and its missing file failed the build.
Deleted it: the brand colour already goes through shadcn's --primary.

They also extracted the login SSO notice into a <SsoEnabledNotice /> component,
so the legal-links patch now matches one short line instead of a multi-line
antd Alert. Less to churn against.

Separately, branding/layout.tsx had gone stale without anyone noticing. It was
an unguarded whole-file copy taken before e368eea, so every build silently
dropped upstream's NoRedisWarningBanner from both app shells, and it had also
stripped their comments. Rebuilt it as upstream's current file plus only our
four additions, so the delta is now just the two Legal imports and the column
shell that makes the footer a real row.

To stop that recurring, layout.tsx joins the guarded manifest. The guard was
never Authentik-specific, so manifest.txt, baseline.sha256 and rebaseline.sh
move up to aihpi/ and the manifest names copies relative to that. Both copies
now fail the build when upstream moves them and get 3-way merged by the nightly
re-sync step before the push.

Verified: apply.sh applies clean, is idempotent, and exits 1 naming layout.tsx
when its upstream version is altered. npm ci and npm run build on Node 24
compile. In the built bundle --primary:#dd6108 is present, the legal links
reach both the login page and the dashboard shell, "AI Model Hub",
"KI-Servicezentrum" and "Login with Authentik" are all there, and
AUTO_REDIRECT_UI_LOGIN_TO_SSO and "Access your LiteLLM Admin UI" are gone.
The patched layout keeps NoRedisWarningBanner in all three places.
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.

2 participants