Skip to content

[Refactor] UI - Virtual Keys: migrate regenerate key modal to AntD#25406

Merged
yuneng-berri merged 15 commits intomainfrom
litellm_regen_key_modal_antd
Apr 12, 2026
Merged

[Refactor] UI - Virtual Keys: migrate regenerate key modal to AntD#25406
yuneng-berri merged 15 commits intomainfrom
litellm_regen_key_modal_antd

Conversation

@yuneng-berri
Copy link
Copy Markdown
Collaborator

@yuneng-berri yuneng-berri commented Apr 9, 2026

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

  • New file RegenerateKeyModal.tsx (renamed from regenerate_key_modal.tsx) with all Tremor components replaced by AntD.
  • Form view uses Row/Col to put Max Budget, TPM Limit, and RPM Limit on one row and Expire Key with Grace Period on another.
  • Success view: warning banner → Key Alias label/value → Virtual Key label + gray-boxed monospace key (full modal width).
  • Copy action lives in the modal footer as a primary button with icon; clicking swaps it to Copied with 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.
  • Regenerate button gained a SyncOutlined icon.
  • Imports updated in key_info_view.tsx.

Testing

  • New unit test suite RegenerateKeyModal.test.tsx covers form fields, cancel/close behavior, the successful regeneration flow, success view contents (including the Virtual Key label), the Copy button copied-state swap, and the onKeyUpdate callback (21 tests, all passing).
  • Updated the Playwright spec keys.spec.ts to scope the Regenerate and Copy Key assertions to the modal (the SyncOutlined icon's aria-label gets concatenated into the accessible name, and the Regenerate Key button on KeyInfoHeader is still in the DOM behind the modal backdrop). Verified the scenario runs locally.
  • npx vitest run src/components/organisms/RegenerateKeyModal.test.tsx passes.

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 Key label above a full-width gray container with the key in monospace → Copy Key primary button in the footer, which swaps to Copied after click.

image

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.
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 9, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
litellm Ready Ready Preview, Comment Apr 12, 2026 3:51am

Request Review

@codspeed-hq
Copy link
Copy Markdown
Contributor

codspeed-hq bot commented Apr 9, 2026

Merging this PR will not alter performance

✅ 16 untouched benchmarks


Comparing litellm_regen_key_modal_antd (1857be4) with main (fdd7500)

Open in CodSpeed

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 9, 2026

Greptile Summary

This 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 maskClosable={false}. It also corrects a spread-order bug in the old code where ...response at the end would silently override the user's just-submitted form values with stale server-echoed values.

  • Stale "New expiry" preview on re-open (P2): handleClose doesn't reset regenerateFormData, so the useEffect that drives newExpiryTime never re-fires on the next modal open, leaving a stale preview visible until the user types again. One-line fix: add setRegenerateFormData(null) to handleClose.

Confidence Score: 5/5

Safe 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.

Important Files Changed

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()
Loading

Reviews (14): Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." | Re-trigger Greptile

Comment thread ui/litellm-dashboard/src/components/organisms/RegenerateKeyModal.test.tsx Outdated
…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]);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
@yuneng-berri yuneng-berri temporarily deployed to integration-postgres April 12, 2026 01:01 — with GitHub Actions Inactive
@yuneng-berri yuneng-berri temporarily deployed to integration-postgres April 12, 2026 01:01 — with GitHub Actions Inactive
@yuneng-berri yuneng-berri temporarily deployed to integration-postgres April 12, 2026 01:01 — with GitHub Actions Inactive
@yuneng-berri yuneng-berri temporarily deployed to integration-postgres April 12, 2026 03:45 — with GitHub Actions Inactive
@yuneng-berri yuneng-berri temporarily deployed to integration-postgres April 12, 2026 03:45 — with GitHub Actions Inactive
@yuneng-berri yuneng-berri temporarily deployed to integration-postgres April 12, 2026 03:45 — with GitHub Actions Inactive
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.

3 participants