[Refactor] UI - Virtual Keys: migrate regenerate key modal to AntD#25406
[Refactor] UI - Virtual Keys: migrate regenerate key modal to AntD#25406yuneng-berri merged 15 commits intomainfrom
Conversation
Replace Tremor components in the regenerate key modal with Ant Design equivalents and move the component to a new PascalCase file. The form layout now uses Row/Col to place Max Budget, TPM Limit, and RPM Limit on one row and Expire Key with Grace Period on another, reducing the vertical footprint. The success view shows an Alert banner, the key alias as secondary context, and the regenerated key in a monospace block with an inline primary Copy button. Also adds unit tests for the new component and updates the existing Playwright spec to match the new banner and button text.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR completes the Tremor→AntD migration for the Regenerate Key modal, renames the file to PascalCase, improves the layout (Row/Col grouping), and overhauls the success view with a copy-to-footer pattern and
Confidence Score: 5/5Safe to merge; the only open finding is a P2 stale-preview cosmetic issue that doesn't affect API calls or data integrity. Both prior thread concerns (setIsOwnKey crash and require() in ESM mock) are resolved in the new code. The spread-order regression from the old component is fixed. The remaining issue — regenerateFormData not reset on close — only causes a stale "New expiry" label on modal re-open and has no impact on actual key regeneration behavior. RegenerateKeyModal.tsx handleClose — add setRegenerateFormData(null) to prevent stale expiry preview.
|
| Filename | Overview |
|---|---|
| ui/litellm-dashboard/src/components/organisms/RegenerateKeyModal.tsx | New AntD modal replaces Tremor/mixed version; fixes the spread-order bug in updatedKeyData and adds maskClosable/loading/copy UX improvements — one minor state-reset omission in handleClose leaves a stale "New expiry" preview on modal re-open. |
| ui/litellm-dashboard/src/components/organisms/RegenerateKeyModal.test.tsx | Comprehensive 21-test suite covering form fields, cancel/close, success view, copy-state swap, onKeyUpdate callback, duration preview, and edge cases (null token, bogus duration, echoed API values). |
| ui/litellm-dashboard/src/components/organisms/regenerate_key_modal.tsx | Old Tremor-mixed file deleted; replaced by PascalCase RegenerateKeyModal.tsx. |
| ui/litellm-dashboard/src/components/templates/key_info_view.tsx | Single import path update from snake_case to PascalCase to match renamed file. |
| ui/litellm-dashboard/src/components/templates/KeyInfoView.handleKeyUpdate.test.tsx | Mock path updated to RegenerateKeyModal to match renamed file; no logic changes. |
| ui/litellm-dashboard/e2e_tests/tests/proxy-admin/keys.spec.ts | E2E spec scoped to the visible AntD modal to avoid icon aria-label ambiguity; timeout extended to 20s for the Copy button; assertions updated to regex selectors matching new component text. |
Sequence Diagram
sequenceDiagram
participant U as User
participant M as RegenerateKeyModal
participant API as regenerateKeyCall
U->>M: Open modal (visible=true)
M->>M: useEffect → form.setFieldsValue(selectedToken)
U->>M: Edit fields (duration, budget, etc.)
M->>M: onValuesChange → setRegenerateFormData(duration)
M->>M: useEffect → setNewExpiryTime(calculated)
U->>M: Click Regenerate
M->>M: form.validateFields()
M->>API: regenerateKeyCall(accessToken, token, formValues)
API-->>M: { key, token, ... }
M->>M: setRegeneratedKey(response.key)
M->>M: onKeyUpdate({ ...response, formValues win })
M->>U: Show success view (key + Copy footer button)
U->>M: Click Copy Key
M->>M: setCopied(true) → button swaps to Copied
U->>M: Click Close
M->>M: handleClose() → reset states + form.resetFields()
M->>M: onClose()
Reviews (14): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile
…antd
Use Flex, Typography.Paragraph (with copyable), and Typography.Text
instead of raw divs + code block + CopyToClipboard wrapper. Drops the
direct react-copy-to-clipboard dependency in this component in favor
of antd's native copyable support.
Also fixes two test issues surfaced when running the e2e locally:
- RegenerateKeyModal.test.tsx no longer mocks react-copy-to-clipboard
(the component no longer imports it), removing the CJS require()
inside an ESM mock factory flagged by Greptile.
- keys.spec.ts scopes the Regenerate and Copy lookups to the modal.
The Regenerate button has an icon whose aria-label ("sync") is
concatenated into the button's accessible name, so an exact-match
lookup on "Regenerate" failed; and the new Paragraph copyable
renders a generic "Copy" button that collided with the other
copyable fields on the key info view.
- Label the key block with a small "Virtual Key" caption so the gray box is clearly the key container. - Move the Copy Key action to the modal footer as a primary button with icon; inline copy icon next to the key is removed. - Swap the button to "Copied" with a check icon on success instead of firing a notification — less noisy and keeps feedback in place. - Disable clicking outside the modal to close (maskClosable=false) so users must explicitly dismiss via Close or X. - Enlarge the key text and let its container span the full modal width. - Tests updated accordingly, including a new test for the copied-state swap and the "Virtual Key" label.
The regenerate endpoint returns a GenerateKeyResponse that inherits max_budget/tpm_limit/rpm_limit from KeyRequestBase, so the API echoes the existing values back. The previous updatedKeyData layout spread ...response *after* the explicit formValues assignments, which meant the user's just-submitted edits were silently overwritten by the API echo before being propagated to the parent via onKeyUpdate. Reorder so the response spread comes first and the formValues-derived fields override it, and add a regression test that mocks a response with stale limits to lock the behavior in. Also drop the two leftover debug console.log statements.
calculateNewExpiryTime only handled s/h/d, but the grace-period validation and backend accept m, w, and mo as well. Entering any of those in the Expire Key field caused the function to return null, which then propagated as expires: null in the onKeyUpdate payload — the parent UI would then render the expiry as "Never" even though the backend had correctly applied the new expiry. Extend the suffix check to cover s/m/h/d/w/mo, matching "mo" before "m" so "1mo" isn't misread as minutes. Also nullish-coalesce the call site so an unparseable duration falls back to the previous expiry instead of null. Add parametric tests for each supported suffix plus a regression test for the null fallback.
Remove LITELLM_LICENSE from the run step's environment block — YAML
environment maps may pass the literal string "${LITELLM_LICENSE}" instead
of interpolating the project env var, overriding it with a value that
fails license validation. The project-level env var is inherited
automatically by the proxy process.
On case-sensitive Linux CI, the old regenerate_key_modal.tsx from main can coexist with the new RegenerateKeyModal.tsx after merge. The old modal renders "Copy Virtual Key" while the new one renders "Copy Key". Use /Copy.*Key/ to match both.
| setCopied(false); | ||
| form.resetFields(); | ||
| } | ||
| }, [visible, form]); |
There was a problem hiding this comment.
isnt this useeffect doing the same thing as handleClose?
| const [copied, setCopied] = useState(false); | ||
|
|
||
| // Keep track of the current valid access token locally | ||
| const [currentAccessToken, setCurrentAccessToken] = useState<string | null>(null); |
There was a problem hiding this comment.
i think this doesnt ever change right does it need to be state?
- Remove redundant useEffect cleanup that duplicated handleClose logic - Remove unnecessary currentAccessToken state, use accessToken from hook directly
Relevant issues
Summary
The regenerate key modal used a mix of Tremor and Ant Design components. This PR migrates it fully to AntD, moves it to a new PascalCase file, and tightens the layout so the fields no longer feel sparse.
Changes
RegenerateKeyModal.tsx(renamed fromregenerate_key_modal.tsx) with all Tremor components replaced by AntD.Row/Colto put Max Budget, TPM Limit, and RPM Limit on one row and Expire Key with Grace Period on another.Key Aliaslabel/value →Virtual Keylabel + gray-boxed monospace key (full modal width).Copiedwith a check icon instead of firing a notification.maskClosable={false}so clicking outside the modal doesn't dismiss it — users must click Close or the X.SyncOutlinedicon.key_info_view.tsx.Testing
RegenerateKeyModal.test.tsxcovers form fields, cancel/close behavior, the successful regeneration flow, success view contents (including theVirtual Keylabel), the Copy button copied-state swap, and theonKeyUpdatecallback (21 tests, all passing).keys.spec.tsto scope the Regenerate and Copy Key assertions to the modal (theSyncOutlinedicon'saria-labelgets concatenated into the accessible name, and theRegenerate Keybutton onKeyInfoHeaderis still in the DOM behind the modal backdrop). Verified the scenario runs locally.npx vitest run src/components/organisms/RegenerateKeyModal.test.tsxpasses.Type
🧹 Refactoring
✅ Test
Screenshots
Form view
Max Budget / TPM / RPM share a row, and Expire Key / Grace Period share another.
Success view
Warning banner → key alias (subtle) →
Virtual Keylabel above a full-width gray container with the key in monospace →Copy Keyprimary button in the footer, which swaps toCopiedafter click.