refactor(ui): migrate access-groups, vector-stores, organizations to shadcn - #34466
Conversation
…ts from antd markup Prepares the shadcn migration of these three routes by removing every assertion that depends on the current component library, so the same tests can gate the migration without being edited. FiltersButton and its OrganizationFilters consumer both asserted on the ".ant-badge" wrapper class; they now assert the active-filter indicator element itself, and FiltersButton additionally asserts that it is absent when there are no active filters. TestVectorStoreTab drove the antd Select with fireEvent.mouseDown and picked options by node; it now clicks through the combobox role and the option text, which works against any listbox implementation. The vector-stores index test relied on Tremor mounting every TabPanel at once, so it read the Manage tab's table without ever opening that tab. It now clicks the tab first, which is what a user does and what any tabs implementation supports. VectorStoreTester had no test at all, so this adds a characterisation suite covering the empty state, the blank-query guard, the search call and its rendered result, result expansion, Enter versus Shift+Enter, the failure path and clearing history. All of these pass against the current antd and Tremor components
…shadcn Moves the nine files these three routes exclusively own off antd and Tremor onto the shadcn primitives in src/components/ui. Scope came from the migration analyzer's import closure, so nothing reached by a second route is touched and every file carrying an antd Form is left alone until #34195 lands. access-groups gets the page header, search box and the whole detail view; vector-stores gets the tab shell, the store picker and the tester panel; organizations gets the organization detail view and the three filter controls. Two changes are behavioural rather than cosmetic. The vector-stores tab strip moves from Tremor, which mounts every panel at once, to Base UI, which mounts only the active panel; that is the correct behaviour and the reworked test now opens the tab it asserts on. The antd Select on the Test Vector Store tab becomes a combobox rather than a plain select so its showSearch type-ahead survives. organization_view keeps one antd import, the ColumnsType used to build the extra columns it hands to the shared MemberTable; that is dictated by the shared component's API and goes away when MemberTable migrates. eslint-suppressions.json ratchets down accordingly: eight files lose their no-restricted-imports entry and organization_view drops from three to one. Every test passes unedited across the migration, and the visual gate reports the three migrated routes changed with the other 32 pixel-identical
…itellm_/sleepy-pascal-0e7ee6
Greptile SummaryMigrates the access-groups, vector-stores, and organizations interfaces from antd and Tremor to shared shadcn primitives
Confidence Score: 5/5The PR appears safe to merge with no blocking failures remaining from the reviewed follow-up threads No blocking failures remain
|
| Filename | Overview |
|---|---|
| ui/litellm-dashboard/src/components/organization/organization_view.tsx | Migrates the organization detail tabs and content to shadcn while preserving settings form state across tab switches |
| ui/litellm-dashboard/src/hooks/useVisitedTabs.ts | Adds visited-tab tracking used to retain stateful panels after their first activation |
| ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/TestVectorStoreTab.tsx | Replaces the vector-store selector with a searchable shadcn combobox |
| ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/VectorStoreTester.tsx | Migrates the vector-store testing interface to shared shadcn controls |
| ui/litellm-dashboard/src/app/(dashboard)/vector-stores/_components/index.tsx | Migrates the vector-store page tabs and actions while preserving visited stateful panels |
Reviews (2): Last reviewed commit: "fix(ui): keep tab panel state across tab..." | Re-trigger Greptile
| const [selectedVectorStoreId, setSelectedVectorStoreId] = useState<string | undefined>( | ||
| vectorStores.length > 0 ? vectorStores[0].vector_store_id : undefined, | ||
| ); | ||
| const [selectedVectorStore, setSelectedVectorStore] = useState<VectorStore | null>(vectorStores[0] ?? null); |
There was a problem hiding this comment.
Async store selection stays empty
When the Test tab mounts before the initial vector-store request completes, this state initializes to null and does not follow the populated vectorStores prop, leaving the combobox unselected and VectorStoreTester hidden until the user manually selects a store.
Knowledge Base Used: Admin dashboard (ui/litellm-dashboard)
| <div | ||
| className="flex justify-between items-center p-3 cursor-pointer hover:bg-gray-100 transition-colors" | ||
| className="flex cursor-pointer items-center justify-between p-3 transition-colors hover:bg-muted" | ||
| onClick={() => toggleResultExpansion(index, resultIndex)} | ||
| > | ||
| <div className="flex items-center"> |
There was a problem hiding this comment.
Result disclosure lacks keyboard access
The result header is a non-focusable div whose expansion behavior is available only through onClick; keyboard users cannot open the result content, and assistive technology receives no button or expanded-state semantics.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Greptile caught a real regression in the shadcn migration: starting to edit organization settings and switching to another tab silently discarded the unsaved input. antd Tabs and Tremor TabGroup mount a panel lazily and then keep it mounted, so a half-filled form or a search history survives leaving the tab and coming back. Base UI unmounts inactive panels instead. Its keepMounted escape hatch is not equivalent either: it mounts every panel eagerly, which renders work the user may never ask for and, on the organization view, put the organization name on screen twice. useVisitedTabs reproduces the original semantics by tracking which tabs have been opened and keeping only those mounted. It is applied to the two tab strips whose panels wrap stateful children: organization Settings, and the vector-stores Create and Test tabs, where an in-progress upload or a search history was equally exposed. The access-group detail tabs render lists derived from props, so they stay lazy. The added regression test fails without the fix and passes with it, and it also passes against the pre-migration antd component, so it pins parity rather than the new markup.
QA — all runbook scenarios pass ✅Ran the proxy on Results: S1 list/search/clear ✅ · S2 detail/copy/sub-tabs ✅ (clipboard verified) · S3 View All(7)/Show Less ✅ · S4 vs tabs/refresh/add modal ✅ · S5 combobox/query/expand/clear ✅ · S6 org search/dot/reset ✅ · S7 org tabs/copy/edit ✅ · tab-state preservation (org Settings + vs Create) ✅ Full walkthrough recordingVector Store combobox type-to-filter + real query (Scenario 5)The new shadcn combobox filters as you type, and a real query returns a live result: Note: some pre-existing antd deprecation console warnings remain ( |
TLDR
Problem this solves:
How it solves it:
Relevant issues
Linear ticket
Pre-Submission checklist
Please complete all items before asking a LiteLLM maintainer to review your PR
@greptileaito 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
This is markup-only, so the proof is that each page still behaves identically while rendering shadcn primitives. Steps to check by hand against a local proxy on
localhost:4000with the dashboard dev server onlocalhost:3000:Before and after screenshots of all three routes are attached below.
Type
🧹 Refactoring
✅ Test
Changes
Scope came from the migration analyzer, which walks the real import closure from each route's
page.tsxand buckets every antd or Tremor file it reaches. Only files a single route exclusively owns are touched here.Migrated, grouped by route:
AccessGroupsPage.tsx,AccessGroupsDetailsPage.tsx_components/index.tsx,TestVectorStoreTab.tsx,VectorStoreTester.tsxorganization_view.tsx,Filters/FilterInput.tsx,Filters/FiltersButton.tsx,Filters/ResetFiltersButton.tsxDeliberately not touched. Files the analyzer bucketed SHARED are reached by more than one route, so editing one from a page PR would silently change up to 34 other pages; that includes
DeleteResourceModal.tsx(23 pages),message_manager.tsxandnotifications_manager.tsx(53 pages each),MemberTable.tsx,ObjectPermissionsView,ModelSelect,MCPServerSelector,VectorStoreSelector,numerical_input.tsx,budget_duration_dropdown.tsx,DefaultProxyAdminTag.tsxand theIconActionButtonpair. Files bucketed DEFERRED contain an antdForm, which stays blocked until #34195 lands; that covers the access group create and edit modals,CreateVectorStore.tsx,VectorStoreForm.tsx,S3VectorsConfig.tsx,vector_store_info.tsx,OrganizationsPanel.tsx,user_search_modal.tsxandEditMembership.tsx.Because shared components stay on antd, these routes still pull antd in through their children; that is expected under this packaging and clears as the shared components migrate.
organization_view.tsxkeeps one antd import, theColumnsTypetype used to build the extra Spend and Created At columns it hands to the sharedMemberTable. That is a type-only import dictated by the shared component's API, and it goes away whenMemberTablemigrates.eslint-suppressions.jsonratchets down accordingly: eight files drop theirno-restricted-importssuppression entirely andorganization_view.tsxgoes from three to one.Two behavioural notes. The antd
Selecton the Test Vector Store tab becomes a shadcn combobox rather than a plain select, so itsshowSearchtype-ahead is preserved.Tab panel mounting needed care. antd
Tabsand TremorTabGroupmount a panel lazily and then keep it mounted, so a half-filled form or a search history survives switching tabs and coming back. Base UI unmounts inactive panels, and itskeepMountedescape hatch mounts every panel eagerly, which is not the same thing.useVisitedTabsreproduces the original semantics (mount on first visit, keep thereafter) and is applied to the two tab strips whose panels wrap stateful children: organization Settings, and the vector-stores Create and Test tabs. The access-group detail tabs render derived lists with no state, so they stay lazy. A regression test covers the organization settings case, which is the one a user would actually lose typing to.Final Attestation