"use client"; import React, { useEffect, useState, useCallback, useRef } from "react"; import { ColumnDef, Row } from "@tanstack/react-table"; import { DataTable } from "./view_logs/table"; import { Select, SelectItem } from "@tremor/react" import { Button } from "@tremor/react" import KeyInfoView from "./key_info_view"; import { Tooltip } from "antd"; import { Team, KeyResponse } from "./key_team_helpers/key_list"; import FilterComponent from "./common_components/filter"; import { FilterOption } from "./common_components/filter"; import { keyListCall, Organization, userListCall } from "./networking"; import { createTeamSearchFunction } from "./key_team_helpers/team_search_fn"; import { createOrgSearchFunction } from "./key_team_helpers/organization_search_fn"; import { useFilterLogic } from "./key_team_helpers/filter_logic"; import { Setter } from "@/types"; import { updateExistingKeys } from "@/utils/dataUtils"; import { debounce } from "lodash"; import { defaultPageSize } from "./constants"; import { fetchAllTeams } from "./key_team_helpers/filter_helpers"; import { fetchAllOrganizations } from "./key_team_helpers/filter_helpers"; import { flexRender, getCoreRowModel, getSortedRowModel, SortingState, useReactTable, } from "@tanstack/react-table"; import { Table, TableHead, TableHeaderCell, TableBody, TableRow, TableCell, } from "@tremor/react"; import { SwitchVerticalIcon, ChevronUpIcon, ChevronDownIcon } from "@heroicons/react/outline"; interface AllKeysTableProps { keys: KeyResponse[]; setKeys: Setter; isLoading?: boolean; pagination: { currentPage: number; totalPages: number; totalCount: number; }; onPageChange: (page: number) => void; pageSize?: number; teams: Team[] | null; selectedTeam: Team | null; setSelectedTeam: (team: Team | null) => void; selectedKeyAlias: string | null; setSelectedKeyAlias: Setter; accessToken: string | null; userID: string | null; userRole: string | null; organizations: Organization[] | null; setCurrentOrg: React.Dispatch>; refresh?: () => void; onSortChange?: (sortBy: string, sortOrder: 'asc' | 'desc') => void; currentSort?: { sortBy: string; sortOrder: 'asc' | 'desc'; }; } // Define columns similar to our logs table interface UserResponse { user_id: string; user_email: string; user_role: string; } const TeamFilter = ({ teams, selectedTeam, setSelectedTeam }: { teams: Team[] | null; selectedTeam: Team | null; setSelectedTeam: (team: Team | null) => void; }) => { const handleTeamChange = (value: string) => { const team = teams?.find(t => t.team_id === value); setSelectedTeam(team || null); }; return (
Where Team is
); }; /** * AllKeysTable – a new table for keys that mimics the table styling used in view_logs. * The team selector and filtering have been removed so that all keys are shown. */ export function AllKeysTable({ keys, setKeys, isLoading = false, pagination, onPageChange, pageSize = 50, teams, selectedTeam, setSelectedTeam, selectedKeyAlias, setSelectedKeyAlias, accessToken, userID, userRole, organizations, setCurrentOrg, refresh, onSortChange, currentSort, }: AllKeysTableProps) { const [selectedKeyId, setSelectedKeyId] = useState(null); const [userList, setUserList] = useState([]); const [sorting, setSorting] = React.useState(() => { if (currentSort) { return [{ id: currentSort.sortBy, desc: currentSort.sortOrder === 'desc' }]; } return [{ id: "created_at", desc: true }]; }); // Use the filter logic hook const { filters, filteredKeys, allKeyAliases, allTeams, allOrganizations, handleFilterChange, handleFilterReset } = useFilterLogic({ keys, teams, organizations, accessToken, }); useEffect(() => { if (accessToken) { const user_IDs = keys.map(key => key.user_id).filter(id => id !== null); const fetchUserList = async () => { const userListData = await userListCall(accessToken, user_IDs, 1, 100); setUserList(userListData.users); }; fetchUserList(); } }, [accessToken, keys]); // Add a useEffect to call refresh when a key is created useEffect(() => { if (refresh) { const handleStorageChange = () => { refresh(); }; // Listen for storage events that might indicate a key was created window.addEventListener('storage', handleStorageChange); return () => { window.removeEventListener('storage', handleStorageChange); }; } }, [refresh]); const columns: ColumnDef[] = [ { id: "expander", header: () => null, cell: ({ row }) => row.getCanExpand() ? ( ) : null, }, { id: "token", accessorKey: "token", header: "Key ID", cell: (info) => (
), }, { id: "key_alias", accessorKey: "key_alias", header: "Key Alias", cell: (info) => { const value = info.getValue() as string; return {value ? (value.length > 20 ? `${value.slice(0, 20)}...` : value) : "-"} } }, { id: "key_name", accessorKey: "key_name", header: "Secret Key", cell: (info) => {info.getValue() as string}, }, { id: "team_alias", accessorKey: "team_id", header: "Team Alias", cell: ({ row, getValue }) => { const teamId = getValue() as string; const team = teams?.find(t => t.team_id === teamId); return team?.team_alias || "Unknown"; }, }, { id: "team_id", accessorKey: "team_id", header: "Team ID", cell: (info) => {info.getValue() ? `${(info.getValue() as string).slice(0, 7)}...` : "-"} }, { id: "organization_id", accessorKey: "organization_id", header: "Organization ID", cell: (info) => info.getValue() ? info.renderValue() : "-", }, { id: "user_email", accessorKey: "user_id", header: "User Email", cell: (info) => { const userId = info.getValue() as string; const user = userList.find(u => u.user_id === userId); return user?.user_email ? user.user_email : "-"; }, }, { id: "user_id", accessorKey: "user_id", header: "User ID", cell: (info) => { const userId = info.getValue() as string; return userId ? ( {userId.slice(0, 7)}... ) : "-"; }, }, { id: "created_at", accessorKey: "created_at", header: "Created At", cell: (info) => { const value = info.getValue(); return value ? new Date(value as string).toLocaleDateString() : "-"; }, }, { id: "created_by", accessorKey: "created_by", header: "Created By", cell: (info) => { const value = info.getValue(); return value ? value : "Unknown"; }, }, { id: "expires", accessorKey: "expires", header: "Expires", cell: (info) => { const value = info.getValue(); return value ? new Date(value as string).toLocaleDateString() : "Never"; }, }, { id: "spend", accessorKey: "spend", header: "Spend (USD)", cell: (info) => Number(info.getValue()).toFixed(4), }, { id: "max_budget", accessorKey: "max_budget", header: "Budget (USD)", cell: (info) => info.getValue() !== null && info.getValue() !== undefined ? info.getValue() : "Unlimited", }, { id: "budget_reset_at", accessorKey: "budget_reset_at", header: "Budget Reset", cell: (info) => { const value = info.getValue(); return value ? new Date(value as string).toLocaleString() : "Never"; }, }, { id: "models", accessorKey: "models", header: "Models", cell: (info) => { const models = info.getValue() as string[]; return (
{models && models.length > 0 ? ( models.map((model, index) => ( {model} )) ) : ( "-" )}
); }, }, { id: "rate_limits", header: "Rate Limits", cell: ({ row }) => { const key = row.original; return (
TPM: {key.tpm_limit !== null ? key.tpm_limit : "Unlimited"}
RPM: {key.rpm_limit !== null ? key.rpm_limit : "Unlimited"}
); }, }, ]; const filterOptions: FilterOption[] = [ { name: 'Team ID', label: 'Team ID', isSearchable: true, searchFn: async (searchText: string) => { if (!allTeams || allTeams.length === 0) return []; const filteredTeams = allTeams.filter(team => team.team_id.toLowerCase().includes(searchText.toLowerCase()) || (team.team_alias && team.team_alias.toLowerCase().includes(searchText.toLowerCase())) ); return filteredTeams.map(team => ({ label: `${team.team_alias || team.team_id} (${team.team_id})`, value: team.team_id })); } }, { name: 'Organization ID', label: 'Organization ID', isSearchable: true, searchFn: async (searchText: string) => { if (!allOrganizations || allOrganizations.length === 0) return []; const filteredOrgs = allOrganizations.filter(org => org.organization_id?.toLowerCase().includes(searchText.toLowerCase()) ?? false ); return filteredOrgs .filter(org => org.organization_id !== null && org.organization_id !== undefined) .map(org => ({ label: `${org.organization_id || 'Unknown'} (${org.organization_id})`, value: org.organization_id as string })); } }, { name: "Key Alias", label: "Key Alias", isSearchable: true, searchFn: async (searchText) => { const filteredKeyAliases = allKeyAliases.filter(key => { return key.toLowerCase().includes(searchText.toLowerCase()) }); return filteredKeyAliases.map((key) => { return { label: key, value: key } }); } }, { name: 'User ID', label: 'User ID', isSearchable: false, }, { name: 'Key Hash', label: 'Key Hash', isSearchable: false, } ]; console.log(`keys: ${JSON.stringify(keys)}`) const table = useReactTable({ data: filteredKeys, columns: columns.filter(col => col.id !== 'expander'), state: { sorting, }, onSortingChange: (updaterOrValue) => { const newSorting = typeof updaterOrValue === 'function' ? updaterOrValue(sorting) : updaterOrValue; console.log(`newSorting: ${JSON.stringify(newSorting)}`) setSorting(newSorting); if (newSorting && newSorting.length > 0) { const sortState = newSorting[0]; const sortBy = sortState.id; const sortOrder = sortState.desc ? 'desc' : 'asc'; console.log(`sortBy: ${sortBy}, sortOrder: ${sortOrder}`) handleFilterChange({ ...filters, 'Sort By': sortBy, 'Sort Order': sortOrder }); onSortChange?.(sortBy, sortOrder); } }, getCoreRowModel: getCoreRowModel(), getSortedRowModel: getSortedRowModel(), enableSorting: true, manualSorting: false, }); // Update local sorting state when currentSort prop changes React.useEffect(() => { if (currentSort) { setSorting([{ id: currentSort.sortBy, desc: currentSort.sortOrder === 'desc' }]); } }, [currentSort]); return (
{selectedKeyId ? ( setSelectedKeyId(null)} keyData={filteredKeys.find(k => k.token === selectedKeyId)} onKeyDataUpdate={(updatedKeyData) => { setKeys(keys => keys.map(key => { if (key.token === updatedKeyData.token) { return updateExistingKeys(key, updatedKeyData) } return key })) }} onDelete={() => { setKeys(keys => keys.filter(key => key.token !== selectedKeyId)) }} accessToken={accessToken} userID={userID} userRole={userRole} teams={allTeams} /> ) : (
Showing {isLoading ? "..." : `${(pagination.currentPage - 1) * pageSize + 1} - ${Math.min(pagination.currentPage * pageSize, pagination.totalCount)}`} of {isLoading ? "..." : pagination.totalCount} results
Page {isLoading ? "..." : pagination.currentPage} of {isLoading ? "..." : pagination.totalPages}
{table.getHeaderGroups().map((headerGroup) => ( {headerGroup.headers.map((header) => (
{header.isPlaceholder ? null : ( flexRender( header.column.columnDef.header, header.getContext() ) )}
{header.id !== 'actions' && (
{header.column.getIsSorted() ? ( { asc: , desc: }[header.column.getIsSorted() as string] ) : ( )}
)}
))}
))}
{isLoading ? (

🚅 Loading keys...

) : filteredKeys.length > 0 ? ( table.getRowModel().rows.map((row) => ( {row.getVisibleCells().map((cell) => ( {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} )) ) : (

No keys found

)}
)}
); }