mirror of
https://github.com/tiennm99/litellm.git
synced 2026-07-29 16:22:08 +00:00
Loading state for Keys table
This commit is contained in:
@@ -24,7 +24,7 @@ import {
|
||||
TableRow,
|
||||
Text,
|
||||
} from "@tremor/react";
|
||||
import { Tooltip } from "antd";
|
||||
import { Skeleton, Tooltip } from "antd";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { getModelDisplayName } from "../key_team_helpers/fetch_available_models_team_key";
|
||||
import { useFilterLogic } from "../key_team_helpers/filter_logic";
|
||||
@@ -520,6 +520,10 @@ export function VirtualKeysTable({ teams, organizations, onSortChange, currentSo
|
||||
}
|
||||
}, [currentSort]);
|
||||
|
||||
const { pageIndex, pageSize } = table.getState().pagination;
|
||||
const start = pageIndex * pageSize + 1;
|
||||
const end = Math.min((pageIndex + 1) * pageSize, totalCount);
|
||||
const rangeLabel = `${start} - ${end}`;
|
||||
return (
|
||||
<div className="w-full h-full overflow-hidden">
|
||||
{selectedKey ? (
|
||||
@@ -541,38 +545,46 @@ export function VirtualKeysTable({ teams, organizations, onSortChange, currentSo
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-between w-full mb-4">
|
||||
<span className="inline-flex text-sm text-gray-700">
|
||||
Showing{" "}
|
||||
{isLoading
|
||||
? "..."
|
||||
: `${table.getState().pagination.pageIndex * table.getState().pagination.pageSize + 1} - ${Math.min(
|
||||
(table.getState().pagination.pageIndex + 1) * table.getState().pagination.pageSize,
|
||||
totalCount,
|
||||
)}`}{" "}
|
||||
of {isLoading ? "..." : totalCount} results
|
||||
</span>
|
||||
{isLoading ? (
|
||||
<Skeleton.Input active style={{ width: 200, height: 20 }} />
|
||||
) : (
|
||||
<span className="inline-flex text-sm text-gray-700">
|
||||
Showing {rangeLabel} of {totalCount} results
|
||||
</span>
|
||||
)}
|
||||
|
||||
<div className="inline-flex items-center gap-2">
|
||||
<span className="text-sm text-gray-700">
|
||||
Page {isLoading ? "..." : table.getState().pagination.pageIndex + 1} of{" "}
|
||||
{isLoading ? "..." : table.getPageCount()}
|
||||
</span>
|
||||
{isLoading ? (
|
||||
<Skeleton.Input active size="small" style={{ width: 50, height: 20 }} />
|
||||
) : (
|
||||
<span className="text-sm text-gray-700">
|
||||
Page {pageIndex + 1} of {table.getPageCount()}
|
||||
</span>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={isLoading || !table.getCanPreviousPage()}
|
||||
className="px-3 py-1 text-sm border rounded-md hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
Previous
|
||||
</button>
|
||||
{isLoading ? (
|
||||
<Skeleton.Button active size="small" style={{ width: 84, height: 30 }} />
|
||||
) : (
|
||||
<button
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={isLoading || !table.getCanPreviousPage()}
|
||||
className="px-3 py-1 text-sm border rounded-md hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
Previous
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={isLoading || !table.getCanNextPage()}
|
||||
className="px-3 py-1 text-sm border rounded-md hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
{isLoading ? (
|
||||
<Skeleton.Button active size="small" style={{ width: 58, height: 30 }} />
|
||||
) : (
|
||||
<button
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={isLoading || !table.getCanNextPage()}
|
||||
className="px-3 py-1 text-sm border rounded-md hover:bg-gray-50 disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
Next
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-[75vh] overflow-auto">
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
"use client";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
import { formatNumberWithCommas } from "@/utils/dataUtils";
|
||||
import { InfoCircleOutlined } from "@ant-design/icons";
|
||||
import { Accordion, AccordionBody, AccordionHeader, Button, Col, Grid, Text, TextInput, Title } from "@tremor/react";
|
||||
@@ -39,14 +40,10 @@ import VectorStoreSelector from "../vector_store_management/VectorStoreSelector"
|
||||
const { Option } = Select;
|
||||
|
||||
interface CreateKeyProps {
|
||||
userID: string;
|
||||
team: Team | null;
|
||||
userRole: string | null;
|
||||
accessToken: string;
|
||||
data: any[] | null;
|
||||
teams: Team[] | null;
|
||||
addKey: (data: any) => void;
|
||||
premiumUser?: boolean;
|
||||
}
|
||||
|
||||
interface User {
|
||||
@@ -137,16 +134,8 @@ export const fetchUserModels = async (
|
||||
* Please contribute to the new refactor.
|
||||
* ─────────────────────────────────────────────────────────────────────────
|
||||
*/
|
||||
const CreateKey: React.FC<CreateKeyProps> = ({
|
||||
userID,
|
||||
team,
|
||||
teams,
|
||||
userRole,
|
||||
accessToken,
|
||||
data,
|
||||
addKey,
|
||||
premiumUser = false,
|
||||
}) => {
|
||||
const CreateKey: React.FC<CreateKeyProps> = ({ team, teams, data, addKey }) => {
|
||||
const { accessToken, userId: userID, userRole, premiumUser } = useAuthorized();
|
||||
const [form] = Form.useForm();
|
||||
const [isModalVisible, setIsModalVisible] = useState(false);
|
||||
const [apiKey, setApiKey] = useState(null);
|
||||
@@ -170,7 +159,6 @@ const CreateKey: React.FC<CreateKeyProps> = ({
|
||||
const [modelAliases, setModelAliases] = useState<{ [key: string]: string }>({});
|
||||
const [autoRotationEnabled, setAutoRotationEnabled] = useState<boolean>(false);
|
||||
const [rotationInterval, setRotationInterval] = useState<string>("30d");
|
||||
|
||||
const handleOk = () => {
|
||||
setIsModalVisible(false);
|
||||
form.resetFields();
|
||||
@@ -490,14 +478,7 @@ const CreateKey: React.FC<CreateKeyProps> = ({
|
||||
+ Create New Key
|
||||
</Button>
|
||||
)}
|
||||
<Modal
|
||||
// title="Create Key"
|
||||
visible={isModalVisible}
|
||||
width={1000}
|
||||
footer={null}
|
||||
onOk={handleOk}
|
||||
onCancel={handleCancel}
|
||||
>
|
||||
<Modal open={isModalVisible} width={1000} footer={null} onOk={handleOk} onCancel={handleCancel}>
|
||||
<Form form={form} onFinish={handleCreate} labelCol={{ span: 8 }} wrapperCol={{ span: 16 }} labelAlign="left">
|
||||
{/* Section 1: Key Ownership */}
|
||||
<div className="mb-8">
|
||||
|
||||
@@ -92,13 +92,7 @@ const UserDashboard: React.FC<UserDashboardProps> = ({
|
||||
const [teamSpend, setTeamSpend] = useState<number | null>(null);
|
||||
const [userModels, setUserModels] = useState<string[]>([]);
|
||||
const [proxySettings, setProxySettings] = useState<ProxySettings | null>(null);
|
||||
const defaultTeam: TeamInterface = {
|
||||
models: [],
|
||||
team_alias: "Default Team",
|
||||
team_id: null,
|
||||
};
|
||||
const [selectedTeam, setSelectedTeam] = useState<any | null>(null);
|
||||
const [selectedKeyAlias, setSelectedKeyAlias] = useState<string | null>(null);
|
||||
// check if window is not undefined
|
||||
if (typeof window !== "undefined") {
|
||||
window.addEventListener("beforeunload", function () {
|
||||
@@ -352,14 +346,10 @@ const UserDashboard: React.FC<UserDashboardProps> = ({
|
||||
<Col numColSpan={1} className="flex flex-col gap-2">
|
||||
<CreateKey
|
||||
key={selectedTeam ? selectedTeam.team_id : null}
|
||||
userID={userID}
|
||||
team={selectedTeam as Team | null}
|
||||
teams={teams as Team[]}
|
||||
userRole={userRole}
|
||||
accessToken={accessToken}
|
||||
data={keys}
|
||||
addKey={addKey}
|
||||
premiumUser={premiumUser}
|
||||
/>
|
||||
<VirtualKeysTable teams={teams} organizations={organizations} />
|
||||
</Col>
|
||||
|
||||
Reference in New Issue
Block a user