Loading state for Keys table

This commit is contained in:
yuneng-jiang
2025-12-30 10:35:25 -08:00
parent c7734cb8bd
commit 42e437a418
3 changed files with 45 additions and 62 deletions
@@ -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>