Improvements on the Regenerate Key Flow (#12788)

* improve regeneration ux

* toLocaleString

* regenerate key twice in a row

* fix delete key
This commit is contained in:
tanjiro
2025-07-22 23:47:26 +09:00
committed by GitHub
parent 03baf23ad1
commit 03e420f91a
6 changed files with 334 additions and 135 deletions
@@ -41,9 +41,9 @@ import { getModelDisplayName } from "./key_team_helpers/fetch_available_models_t
import { formatNumberWithCommas } from "@/utils/dataUtils";
interface AllKeysTableProps {
keys: KeyResponse[];
setKeys: Setter<KeyResponse[]>;
isLoading?: boolean;
keys: KeyResponse[]
setKeys: (keys: KeyResponse[] | ((prev: KeyResponse[]) => KeyResponse[])) => void
isLoading?: boolean
pagination: {
currentPage: number;
totalPages: number;
@@ -64,10 +64,11 @@ interface AllKeysTableProps {
refresh?: () => void;
onSortChange?: (sortBy: string, sortOrder: 'asc' | 'desc') => void;
currentSort?: {
sortBy: string;
sortOrder: 'asc' | 'desc';
};
premiumUser: boolean;
sortBy: string
sortOrder: "asc" | "desc"
}
premiumUser: boolean
setAccessToken?: (token: string) => void
}
// Define columns similar to our logs table
@@ -143,6 +144,7 @@ export function AllKeysTable({
onSortChange,
currentSort,
premiumUser,
setAccessToken,
}: AllKeysTableProps) {
const [selectedKeyId, setSelectedKeyId] = useState<string | null>(null);
const [userList, setUserList] = useState<UserResponse[]>([]);
@@ -625,6 +627,7 @@ export function AllKeysTable({
userRole={userRole}
teams={allTeams}
premiumUser={premiumUser}
setAccessToken={setAccessToken}
/>
) : (
<div className="border-b py-4 flex-1 overflow-hidden">
@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useEffect, useState } from "react";
import {
Card,
Text,
@@ -30,26 +30,62 @@ import { CopyIcon, CheckIcon } from "lucide-react";
import { callback_map, mapInternalToDisplayNames, mapDisplayToInternalNames } from "./callback_info_helpers";
interface KeyInfoViewProps {
keyId: string;
onClose: () => void;
keyData: KeyResponse | undefined;
onKeyDataUpdate?: (data: Partial<KeyResponse>) => void;
onDelete?: () => void;
accessToken: string | null;
userID: string | null;
userRole: string | null;
teams: any[] | null;
premiumUser: boolean;
keyId: string
onClose: () => void
keyData: KeyResponse | undefined
onKeyDataUpdate?: (data: Partial<KeyResponse>) => void
onDelete?: () => void
accessToken: string | null
userID: string | null
userRole: string | null
teams: any[] | null
premiumUser: boolean
setAccessToken?: (token: string) => void
}
export default function KeyInfoView({ keyId, onClose, keyData, accessToken, userID, userRole, teams, onKeyDataUpdate, onDelete, premiumUser }: KeyInfoViewProps) {
const [isEditing, setIsEditing] = useState(false);
const [form] = Form.useForm();
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [isRegenerateModalOpen, setIsRegenerateModalOpen] = useState(false);
const [copiedStates, setCopiedStates] = useState<Record<string, boolean>>({});
export default function KeyInfoView({
keyId,
onClose,
keyData,
accessToken,
userID,
userRole,
teams,
onKeyDataUpdate,
onDelete,
premiumUser,
setAccessToken,
}: KeyInfoViewProps) {
const [isEditing, setIsEditing] = useState(false)
const [form] = Form.useForm()
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false)
const [isRegenerateModalOpen, setIsRegenerateModalOpen] = useState(false)
const [copiedStates, setCopiedStates] = useState<Record<string, boolean>>({})
if (!keyData) {
// Add local state to maintain key data and track regeneration
const [currentKeyData, setCurrentKeyData] = useState<KeyResponse | undefined>(keyData)
const [lastRegeneratedAt, setLastRegeneratedAt] = useState<Date | null>(null)
const [isRecentlyRegenerated, setIsRecentlyRegenerated] = useState(false)
// Update local state when keyData prop changes (but don't reset to undefined)
useEffect(() => {
if (keyData) {
setCurrentKeyData(keyData)
}
}, [keyData])
// Reset recent regeneration indicator after 5 seconds
useEffect(() => {
if (isRecentlyRegenerated) {
const timer = setTimeout(() => {
setIsRecentlyRegenerated(false)
}, 5000)
return () => clearTimeout(timer)
}
}, [isRecentlyRegenerated])
// Use currentKeyData instead of keyData throughout the component
if (!currentKeyData) {
return (
<div className="p-4">
<Button
@@ -75,7 +111,7 @@ export default function KeyInfoView({ keyId, onClose, keyData, accessToken, user
// Handle object_permission updates
if (formValues.vector_stores !== undefined) {
formValues.object_permission = {
...keyData.object_permission,
...currentKeyData.object_permission,
vector_stores: formValues.vector_stores || []
};
// Remove vector_stores from the top level as it should be in object_permission
@@ -85,7 +121,7 @@ export default function KeyInfoView({ keyId, onClose, keyData, accessToken, user
if (formValues.mcp_servers_and_groups !== undefined) {
const { servers, accessGroups } = formValues.mcp_servers_and_groups || { servers: [], accessGroups: [] };
formValues.object_permission = {
...keyData.object_permission,
...currentKeyData.object_permission,
mcp_servers: servers || [],
mcp_access_groups: accessGroups || []
};
@@ -145,7 +181,11 @@ export default function KeyInfoView({ keyId, onClose, keyData, accessToken, user
formValues.budget_duration = durationMap[formValues.budget_duration];
}
const newKeyValues = await keyUpdateCall(accessToken, formValues);
const newKeyValues = await keyUpdateCall(accessToken, formValues)
// Update local state
setCurrentKeyData((prevData) => (prevData ? { ...prevData, ...newKeyValues } : undefined))
if (onKeyDataUpdate) {
onKeyDataUpdate(newKeyValues)
}
@@ -160,9 +200,9 @@ export default function KeyInfoView({ keyId, onClose, keyData, accessToken, user
const handleDelete = async () => {
try {
if (!accessToken) return;
await keyDeleteCall(accessToken as string, keyData.token);
message.success("Key deleted successfully");
if (!accessToken) return
await keyDeleteCall(accessToken as string, currentKeyData.token || currentKeyData.token_id)
message.success("Key deleted successfully")
if (onDelete) {
onDelete()
}
@@ -178,10 +218,51 @@ export default function KeyInfoView({ keyId, onClose, keyData, accessToken, user
if (success) {
setCopiedStates((prev) => ({ ...prev, [key]: true }));
setTimeout(() => {
setCopiedStates((prev) => ({ ...prev, [key]: false }));
}, 2000);
setCopiedStates((prev) => ({ ...prev, [key]: false }))
}, 2000)
}
};
}
const handleRegenerateKeyUpdate = (updatedKeyData: Partial<KeyResponse>) => {
// Update local state immediately with ALL the new data
setCurrentKeyData((prevData) => {
if (!prevData) return undefined
const newData = {
...prevData,
...updatedKeyData, // This should include the new token (key-id)
// Update the created_at to show when it was regenerated
created_at: new Date().toLocaleString(),
}
return newData
})
// Track regeneration timestamp
setLastRegeneratedAt(new Date())
setIsRecentlyRegenerated(true)
if (onKeyDataUpdate) {
onKeyDataUpdate({
...updatedKeyData,
created_at: new Date().toLocaleString(),
})
}
}
// Update the formatTimestamp function to use the desired date format
const formatTimestamp = (timestamp: string | Date) => {
const date = new Date(timestamp)
const dateStr = date.toLocaleDateString("en-US", {
year: "numeric",
month: "short",
day: "numeric",
})
const timeStr = date.toLocaleTimeString("en-US", {
hour: "numeric",
minute: "2-digit",
hour12: true,
})
return `${dateStr} at ${timeStr}`
}
return (
<div className="w-full h-screen p-4">
@@ -195,22 +276,46 @@ export default function KeyInfoView({ keyId, onClose, keyData, accessToken, user
>
Back to Keys
</Button>
<Title>{keyData.key_alias || "API Key"}</Title>
<div className="flex items-center cursor-pointer"
>
<Text className="text-gray-500 font-mono">{keyData.token}</Text>
<Title>{currentKeyData.key_alias || "API Key"}</Title>
<div className="flex items-center cursor-pointer mb-2 space-y-6">
<div>
<Text className="text-xs text-gray-400 uppercase tracking-wide mt-2">Key ID</Text>
<Text className="text-gray-500 font-mono text-sm">{currentKeyData.token_id || currentKeyData.token}</Text>
</div>
<AntdButton
type="text"
size="small"
icon={copiedStates["key-id"] ? <CheckIcon size={12} /> : <CopyIcon size={12} />}
onClick={() => copyToClipboard(keyData.token, "key-id")}
className={`left-2 z-10 transition-all duration-200 ${
copiedStates["key-id"]
? 'text-green-600 bg-green-50 border-green-200'
: 'text-gray-500 hover:text-gray-700 hover:bg-gray-100'
onClick={() => copyToClipboard(currentKeyData.token_id || currentKeyData.token, "key-id")}
className={`ml-2 transition-all duration-200${
copiedStates["key-id"]
? "text-green-600 bg-green-50 border-green-200"
: "text-gray-500 hover:text-gray-700 hover:bg-gray-100"
}`}
/>
</div>
{/* Add timestamp and regeneration indicator */}
<div className="flex items-center gap-2 flex-wrap">
<Text className="text-sm text-gray-500">
{currentKeyData.updated_at && currentKeyData.updated_at !== currentKeyData.created_at
? `Updated: ${formatTimestamp(currentKeyData.updated_at)}`
: `Created: ${formatTimestamp(currentKeyData.created_at)}`}
</Text>
{isRecentlyRegenerated && (
<Badge color="green" size="xs" className="animate-pulse">
Recently Regenerated
</Badge>
)}
{lastRegeneratedAt && (
<Badge color="blue" size="xs">
Regenerated
</Badge>
)}
</div>
</div>
{userRole && rolesWithWriteAccess.includes(userRole) && (
<div className="flex gap-2">
@@ -241,11 +346,13 @@ export default function KeyInfoView({ keyId, onClose, keyData, accessToken, user
{/* Add RegenerateKeyModal */}
<RegenerateKeyModal
selectedToken={keyData}
selectedToken={currentKeyData}
visible={isRegenerateModalOpen}
onClose={() => setIsRegenerateModalOpen(false)}
accessToken={accessToken}
premiumUser={premiumUser}
setAccessToken={setAccessToken}
onKeyUpdate={handleRegenerateKeyUpdate}
/>
{/* Delete Confirmation Modal */}
@@ -303,24 +410,29 @@ export default function KeyInfoView({ keyId, onClose, keyData, accessToken, user
<Card>
<Text>Spend</Text>
<div className="mt-2">
<Title>${formatNumberWithCommas(keyData.spend, 4)}</Title>
<Text>of {keyData.max_budget !== null ? `$${formatNumberWithCommas(keyData.max_budget)}` : "Unlimited"}</Text>
<Title>${formatNumberWithCommas(currentKeyData.spend, 4)}</Title>
<Text>
of{" "}
{currentKeyData.max_budget !== null
? `$${formatNumberWithCommas(currentKeyData.max_budget)}`
: "Unlimited"}
</Text>
</div>
</Card>
<Card>
<Text>Rate Limits</Text>
<div className="mt-2">
<Text>TPM: {keyData.tpm_limit !== null ? keyData.tpm_limit : "Unlimited"}</Text>
<Text>RPM: {keyData.rpm_limit !== null ? keyData.rpm_limit : "Unlimited"}</Text>
<Text>TPM: {currentKeyData.tpm_limit !== null ? currentKeyData.tpm_limit : "Unlimited"}</Text>
<Text>RPM: {currentKeyData.rpm_limit !== null ? currentKeyData.rpm_limit : "Unlimited"}</Text>
</div>
</Card>
<Card>
<Text>Models</Text>
<div className="mt-2 flex flex-wrap gap-2">
{keyData.models && keyData.models.length > 0 ? (
keyData.models.map((model, index) => (
{currentKeyData.models && currentKeyData.models.length > 0 ? (
currentKeyData.models.map((model, index) => (
<Badge key={index} color="red">
{model}
</Badge>
@@ -333,17 +445,19 @@ export default function KeyInfoView({ keyId, onClose, keyData, accessToken, user
<Card>
<ObjectPermissionsView
objectPermission={keyData.object_permission}
objectPermission={currentKeyData.object_permission}
variant="inline"
accessToken={accessToken}
/>
</Card>
<LoggingSettingsView
loggingConfigs={extractLoggingSettings(keyData.metadata)}
disabledCallbacks={Array.isArray(keyData.metadata?.litellm_disabled_callbacks)
? mapInternalToDisplayNames(keyData.metadata.litellm_disabled_callbacks)
: []}
loggingConfigs={extractLoggingSettings(currentKeyData.metadata)}
disabledCallbacks={
Array.isArray(currentKeyData.metadata?.litellm_disabled_callbacks)
? mapInternalToDisplayNames(currentKeyData.metadata.litellm_disabled_callbacks)
: []
}
variant="card"
/>
</Grid>
@@ -363,7 +477,7 @@ export default function KeyInfoView({ keyId, onClose, keyData, accessToken, user
{isEditing ? (
<KeyEditView
keyData={keyData}
keyData={currentKeyData}
onCancel={() => setIsEditing(false)}
onSubmit={handleKeyUpdate}
teams={teams}
@@ -375,49 +489,61 @@ export default function KeyInfoView({ keyId, onClose, keyData, accessToken, user
<div className="space-y-4">
<div>
<Text className="font-medium">Key ID</Text>
<Text className="font-mono">{keyData.token}</Text>
<Text className="font-mono">{currentKeyData.token_id || currentKeyData.token}</Text>
</div>
<div>
<Text className="font-medium">Key Alias</Text>
<Text>{keyData.key_alias || "Not Set"}</Text>
<Text>{currentKeyData.key_alias || "Not Set"}</Text>
</div>
<div>
<Text className="font-medium">Secret Key</Text>
<Text className="font-mono">{keyData.key_name}</Text>
<Text className="font-mono">{currentKeyData.key_name}</Text>
</div>
<div>
<Text className="font-medium">Team ID</Text>
<Text>{keyData.team_id || "Not Set"}</Text>
<Text>{currentKeyData.team_id || "Not Set"}</Text>
</div>
<div>
<Text className="font-medium">Organization</Text>
<Text>{keyData.organization_id || "Not Set"}</Text>
<Text>{currentKeyData.organization_id || "Not Set"}</Text>
</div>
<div>
<Text className="font-medium">Created</Text>
<Text>{new Date(keyData.created_at).toLocaleString()}</Text>
<Text>{formatTimestamp(currentKeyData.created_at)}</Text>
</div>
{lastRegeneratedAt && (
<div>
<Text className="font-medium">Last Regenerated</Text>
<div className="flex items-center gap-2">
<Text>{formatTimestamp(lastRegeneratedAt)}</Text>
<Badge color="green" size="xs">
Recent
</Badge>
</div>
</div>
)}
<div>
<Text className="font-medium">Expires</Text>
<Text>{keyData.expires ? new Date(keyData.expires).toLocaleString() : "Never"}</Text>
<Text>{currentKeyData.expires ? formatTimestamp(currentKeyData.expires) : "Never"}</Text>
</div>
<div>
<Text className="font-medium">Spend</Text>
<Text>${formatNumberWithCommas(keyData.spend, 4)} USD</Text>
<Text>${formatNumberWithCommas(currentKeyData.spend, 4)} USD</Text>
</div>
<div>
<Text className="font-medium">Budget</Text>
<Text>
{keyData.max_budget !== null
? `$${formatNumberWithCommas(keyData.max_budget, 2)}`
{currentKeyData.max_budget !== null
? `$${formatNumberWithCommas(currentKeyData.max_budget, 2)}`
: "Unlimited"}
</Text>
</div>
@@ -425,12 +551,9 @@ export default function KeyInfoView({ keyId, onClose, keyData, accessToken, user
<div>
<Text className="font-medium">Models</Text>
<div className="flex flex-wrap gap-2 mt-1">
{keyData.models && keyData.models.length > 0 ? (
keyData.models.map((model, index) => (
<span
key={index}
className="px-2 py-1 bg-blue-100 rounded text-xs"
>
{currentKeyData.models && currentKeyData.models.length > 0 ? (
currentKeyData.models.map((model, index) => (
<span key={index} className="px-2 py-1 bg-blue-100 rounded text-xs">
{model}
</span>
))
@@ -442,34 +565,49 @@ export default function KeyInfoView({ keyId, onClose, keyData, accessToken, user
<div>
<Text className="font-medium">Rate Limits</Text>
<Text>TPM: {keyData.tpm_limit !== null ? keyData.tpm_limit : "Unlimited"}</Text>
<Text>RPM: {keyData.rpm_limit !== null ? keyData.rpm_limit : "Unlimited"}</Text>
<Text>Max Parallel Requests: {keyData.max_parallel_requests !== null ? keyData.max_parallel_requests : "Unlimited"}</Text>
<Text>Model TPM Limits: {keyData.metadata?.model_tpm_limit ? JSON.stringify(keyData.metadata.model_tpm_limit) : "Unlimited"}</Text>
<Text>Model RPM Limits: {keyData.metadata?.model_rpm_limit ? JSON.stringify(keyData.metadata.model_rpm_limit) : "Unlimited"}</Text>
<Text>TPM: {currentKeyData.tpm_limit !== null ? currentKeyData.tpm_limit : "Unlimited"}</Text>
<Text>RPM: {currentKeyData.rpm_limit !== null ? currentKeyData.rpm_limit : "Unlimited"}</Text>
<Text>
Max Parallel Requests:{" "}
{currentKeyData.max_parallel_requests !== null
? currentKeyData.max_parallel_requests
: "Unlimited"}
</Text>
<Text>
Model TPM Limits:{" "}
{currentKeyData.metadata?.model_tpm_limit
? JSON.stringify(currentKeyData.metadata.model_tpm_limit)
: "Unlimited"}
</Text>
<Text>
Model RPM Limits:{" "}
{currentKeyData.metadata?.model_rpm_limit
? JSON.stringify(currentKeyData.metadata.model_rpm_limit)
: "Unlimited"}
</Text>
</div>
<div>
<Text className="font-medium">Metadata</Text>
<pre className="bg-gray-100 p-2 rounded text-xs overflow-auto mt-1">
{formatMetadataForDisplay(keyData.metadata)}
{formatMetadataForDisplay(currentKeyData.metadata)}
</pre>
</div>
<ObjectPermissionsView
objectPermission={keyData.object_permission}
objectPermission={currentKeyData.object_permission}
variant="inline"
className="pt-4 border-t border-gray-200"
accessToken={accessToken}
/>
<LoggingSettingsView
loggingConfigs={extractLoggingSettings(
keyData.metadata,
)}
disabledCallbacks={Array.isArray(keyData.metadata?.litellm_disabled_callbacks)
? mapInternalToDisplayNames(keyData.metadata.litellm_disabled_callbacks)
: []}
loggingConfigs={extractLoggingSettings(currentKeyData.metadata)}
disabledCallbacks={
Array.isArray(currentKeyData.metadata?.litellm_disabled_callbacks)
? mapInternalToDisplayNames(currentKeyData.metadata.litellm_disabled_callbacks)
: []
}
variant="inline"
className="pt-4 border-t border-gray-200"
/>
@@ -18,6 +18,7 @@ export interface Team {
export interface KeyResponse {
token: string;
token_id: string;
key_name: string;
key_alias: string;
spend: number;
@@ -7,11 +7,13 @@ import { KeyResponse } from "./key_team_helpers/key_list";
import { CopyToClipboard } from "react-copy-to-clipboard";
interface RegenerateKeyModalProps {
selectedToken: KeyResponse | null;
visible: boolean;
onClose: () => void;
accessToken: string | null;
premiumUser: boolean;
selectedToken: KeyResponse | null
visible: boolean
onClose: () => void
accessToken: string | null
premiumUser: boolean
setAccessToken?: (token: string) => void
onKeyUpdate?: (updatedKeyData: Partial<KeyResponse>) => void
}
export function RegenerateKeyModal({
@@ -20,37 +22,53 @@ export function RegenerateKeyModal({
onClose,
accessToken,
premiumUser,
setAccessToken,
onKeyUpdate,
}: RegenerateKeyModalProps) {
const [form] = Form.useForm();
const [regeneratedKey, setRegeneratedKey] = useState<string | null>(null);
const [regenerateFormData, setRegenerateFormData] = useState<any>(null);
const [newExpiryTime, setNewExpiryTime] = useState<string | null>(null);
const [isRegenerating, setIsRegenerating] = useState(false);
const [form] = Form.useForm()
const [regeneratedKey, setRegeneratedKey] = useState<string | null>(null)
const [regenerateFormData, setRegenerateFormData] = useState<any>(null)
const [newExpiryTime, setNewExpiryTime] = useState<string | null>(null)
const [isRegenerating, setIsRegenerating] = useState(false)
// Track whether this is the user's own authentication key
const [isOwnKey, setIsOwnKey] = useState<boolean>(false)
// Keep track of the current valid access token locally
const [currentAccessToken, setCurrentAccessToken] = useState<string | null>(null)
useEffect(() => {
if (visible && selectedToken) {
if (visible && selectedToken && accessToken) {
form.setFieldsValue({
key_alias: selectedToken.key_alias,
max_budget: selectedToken.max_budget,
tpm_limit: selectedToken.tpm_limit,
rpm_limit: selectedToken.rpm_limit,
duration: selectedToken.duration || "",
});
})
// Initialize the current access token
setCurrentAccessToken(accessToken)
// Check if this is the user's own authentication key by comparing the key values
const isUserOwnKey = selectedToken.key_name === accessToken
setIsOwnKey(isUserOwnKey)
}
}, [visible, selectedToken, form]);
}, [visible, selectedToken, form, accessToken])
useEffect(() => {
if (!visible) {
// Reset states when modal is closed
setRegeneratedKey(null);
setIsRegenerating(false);
form.resetFields();
setRegeneratedKey(null)
setIsRegenerating(false)
setIsOwnKey(false)
setCurrentAccessToken(null)
form.resetFields()
}
}, [visible, form]);
}, [visible, form])
useEffect(() => {
const calculateNewExpiryTime = (duration: string | undefined) => {
if (!duration) return null;
const calculateNewExpiryTime = (duration: string | undefined): string | null => {
if (!duration) return null
try {
const now = new Date();
@@ -72,6 +90,7 @@ export function RegenerateKeyModal({
}
};
useEffect(() => {
if (regenerateFormData?.duration) {
setNewExpiryTime(calculateNewExpiryTime(regenerateFormData.duration));
} else {
@@ -80,14 +99,48 @@ export function RegenerateKeyModal({
}, [regenerateFormData?.duration]);
const handleRegenerateKey = async () => {
if (!selectedToken || !accessToken) return;
if (!selectedToken || !currentAccessToken) return
setIsRegenerating(true);
try {
const formValues = await form.validateFields();
const response = await regenerateKeyCall(accessToken, selectedToken.token, formValues);
setRegeneratedKey(response.key);
message.success("API Key regenerated successfully");
const formValues = await form.validateFields()
// Use the current access token for the API call
const response = await regenerateKeyCall(currentAccessToken, selectedToken.token || selectedToken.token_id, formValues)
setRegeneratedKey(response.key)
message.success("API Key regenerated successfully")
console.log("Full regenerate response:", response) // Debug log to see what's returned
// Create updated key data with ALL new values from the response
const updatedKeyData: Partial<KeyResponse> = {
// Use the new token/key ID from the response (this is what was missing!)
token: response.token || response.key_id || selectedToken.token, // Try different possible field names
key_name: response.key, // This is the new secret key string
max_budget: formValues.max_budget,
tpm_limit: formValues.tpm_limit,
rpm_limit: formValues.rpm_limit,
expires: formValues.duration ? calculateNewExpiryTime(formValues.duration) : selectedToken.expires,
// Include any other fields that might be returned by the API
...response, // Spread the entire response to capture all updated fields
}
console.log("Updated key data with new token:", updatedKeyData) // Debug log
// If user regenerated their own auth key, update both local and global access tokens
if (isOwnKey) {
setCurrentAccessToken(response.key) // Update local token immediately
if (setAccessToken) {
setAccessToken(response.key) // Update global token
}
}
// Update the parent component with new key data
if (onKeyUpdate) {
onKeyUpdate(updatedKeyData)
}
setIsRegenerating(false)
} catch (error) {
console.error("Error regenerating key:", error);
message.error("Failed to regenerate API Key");
@@ -96,11 +149,13 @@ export function RegenerateKeyModal({
};
const handleClose = () => {
setRegeneratedKey(null);
setIsRegenerating(false);
form.resetFields();
onClose();
};
setRegeneratedKey(null)
setIsRegenerating(false)
setIsOwnKey(false)
setCurrentAccessToken(null)
form.resetFields()
onClose()
}
return (
<Modal
@@ -381,6 +381,7 @@ const UserDashboard: React.FC<UserDashboardProps> = ({
setCurrentOrg={setCurrentOrg}
organizations={organizations}
createClicked={createClicked}
setAccessToken={setAccessToken}
/>
</Col>
</Grid>
@@ -90,21 +90,22 @@ interface ModelLimitModalProps {
// Define the props type
interface ViewKeyTableProps {
userID: string;
userRole: string | null;
accessToken: string;
selectedTeam: any | null;
userID: string | null
userRole: string | null
accessToken: string | null
selectedTeam: Team | null
setSelectedTeam: React.Dispatch<React.SetStateAction<any | null>>;
data: any[] | null;
data: KeyResponse[] | null
setData: React.Dispatch<React.SetStateAction<any[] | null>>;
teams: Team[] | null;
premiumUser: boolean;
currentOrg: Organization | null;
organizations: Organization[] | null;
setCurrentOrg: React.Dispatch<React.SetStateAction<Organization | null>>;
selectedKeyAlias: string | null;
setSelectedKeyAlias: Setter<string | null>;
createClicked: boolean;
selectedKeyAlias: string | null
setSelectedKeyAlias: Setter<string | null>
createClicked: boolean
setAccessToken?: (token: string) => void
}
interface ItemData {
@@ -155,7 +156,8 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
setCurrentOrg,
selectedKeyAlias,
setSelectedKeyAlias,
createClicked
createClicked,
setAccessToken,
}) => {
const [isButtonClicked, setIsButtonClicked] = useState(false);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
@@ -178,10 +180,10 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
// Pass filters into the hook so the API call includes these query parameters.
const { keys, isLoading, error, pagination, refresh, setKeys } = useKeyList({
selectedTeam,
selectedTeam: selectedTeam || undefined,
currentOrg,
selectedKeyAlias,
accessToken,
accessToken: accessToken || "",
createClicked,
});
@@ -292,7 +294,8 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
}
try {
await keyDeleteCall(accessToken, keyToDelete);
if (!accessToken) return
await keyDeleteCall(accessToken, keyToDelete)
// Successfully completed the deletion. Update the state to trigger a rerender.
const filteredData = data.filter((item) => item.token !== keyToDelete);
setData(filteredData);
@@ -345,13 +348,10 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
}
try {
const formValues = await regenerateForm.validateFields();
const response = await regenerateKeyCall(
accessToken,
selectedToken.token,
formValues
);
setRegeneratedKey(response.key);
const formValues = await regenerateForm.validateFields()
if (!accessToken) return;
const response = await regenerateKeyCall(accessToken, selectedToken.token || selectedToken.token_id, formValues)
setRegeneratedKey(response.key)
// Update the data state with the new key_name
if (data) {
@@ -375,7 +375,7 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
return (
<div>
<AllKeysTable
<AllKeysTable
keys={keys}
setKeys={setKeys}
isLoading={isLoading}
@@ -394,6 +394,7 @@ const ViewKeyTable: React.FC<ViewKeyTableProps> = ({
selectedKeyAlias={selectedKeyAlias}
setSelectedKeyAlias={setSelectedKeyAlias}
premiumUser={premiumUser}
setAccessToken={setAccessToken}
/>
{isDeleteModalOpen && (