[Feat] UI - Allow sorting MCPs by created_at, Display name date (#22825)
* Add column sorting to MCP servers table - Added sorting state management to DataTable component - Enabled getSortedRowModel for tanstack/react-table - Made column headers clickable with sort indicators (↑↓⇅) - Added enableSorting: true to sortable columns in mcp_server_columns - Columns now support ascending/descending sort by clicking headers - Updated package-lock.json and tsconfig.json from build process Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com> * Make table sorting opt-in to avoid conflicts with existing consumers Address Greptile feedback (score 2/5): - Added enableSorting prop to DataTable (defaults to false) - Only enable sorting features when explicitly requested - Pass enableSorting=true from MCP servers component - This prevents unintended sorting on other DataTable consumers: * view_logs (has server-side sorting) * pass_through_settings * UsagePage - Sorting UI (indicators, click handlers) only shown when enabled Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Ishaan Jaff <ishaan-jaff@users.noreply.github.com>
This commit is contained in:
parent
a33d3e035d
commit
dd183a7fcb
15
ui/litellm-dashboard/package-lock.json
generated
15
ui/litellm-dashboard/package-lock.json
generated
@ -12984,21 +12984,6 @@
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wooorm"
|
||||
}
|
||||
},
|
||||
"node_modules/@next/swc-win32-ia32-msvc": {
|
||||
"version": "14.2.33",
|
||||
"resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.33.tgz",
|
||||
"integrity": "sha512-pc9LpGNKhJ0dXQhZ5QMmYxtARwwmWLpeocFmVG5Z0DzWq5Uf0izcI8tLc+qOpqxO1PWqZ5A7J1blrUIKrIFc7Q==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 10"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ export const mcpServerColumns = (
|
||||
{
|
||||
accessorKey: "server_id",
|
||||
header: "Server ID",
|
||||
enableSorting: true,
|
||||
cell: ({ row }) => (
|
||||
<button
|
||||
onClick={() => onView(row.original.server_id)}
|
||||
@ -27,10 +28,12 @@ export const mcpServerColumns = (
|
||||
{
|
||||
accessorKey: "server_name",
|
||||
header: "Name",
|
||||
enableSorting: true,
|
||||
},
|
||||
{
|
||||
accessorKey: "alias",
|
||||
header: "Alias",
|
||||
enableSorting: true,
|
||||
},
|
||||
{
|
||||
id: "url",
|
||||
@ -47,6 +50,7 @@ export const mcpServerColumns = (
|
||||
{
|
||||
accessorKey: "transport",
|
||||
header: "Transport",
|
||||
enableSorting: true,
|
||||
cell: ({ row }) => {
|
||||
const transport = row.original.transport || "http";
|
||||
const specPath = row.original.spec_path;
|
||||
@ -58,6 +62,7 @@ export const mcpServerColumns = (
|
||||
{
|
||||
accessorKey: "auth_type",
|
||||
header: "Auth Type",
|
||||
enableSorting: true,
|
||||
cell: ({ getValue }) => <span>{(getValue() as string) || "none"}</span>,
|
||||
},
|
||||
{
|
||||
@ -166,6 +171,7 @@ export const mcpServerColumns = (
|
||||
{
|
||||
header: "Created At",
|
||||
accessorKey: "created_at",
|
||||
enableSorting: true,
|
||||
sortingFn: "datetime",
|
||||
cell: ({ row }) => {
|
||||
const server = row.original;
|
||||
@ -177,6 +183,7 @@ export const mcpServerColumns = (
|
||||
{
|
||||
header: "Updated At",
|
||||
accessorKey: "updated_at",
|
||||
enableSorting: true,
|
||||
sortingFn: "datetime",
|
||||
cell: ({ row }) => {
|
||||
const server = row.original;
|
||||
|
||||
@ -410,6 +410,7 @@ const MCPServers: React.FC<MCPServerProps> = ({ accessToken, userRole, userID })
|
||||
isLoading={isLoadingServers}
|
||||
noDataMessage="No MCP servers configured"
|
||||
loadingMessage="🚅 Loading MCP servers..."
|
||||
enableSorting={true}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { Fragment } from "react";
|
||||
import { ColumnDef, flexRender, getCoreRowModel, getExpandedRowModel, Row, useReactTable } from "@tanstack/react-table";
|
||||
import { Fragment, useState } from "react";
|
||||
import { ColumnDef, flexRender, getCoreRowModel, getExpandedRowModel, Row, useReactTable, getSortedRowModel, SortingState } from "@tanstack/react-table";
|
||||
|
||||
import { Table, TableHead, TableHeaderCell, TableBody, TableRow, TableCell } from "@tremor/react";
|
||||
|
||||
@ -15,6 +15,8 @@ interface DataTableProps<TData, TValue> {
|
||||
isLoading?: boolean;
|
||||
loadingMessage?: string;
|
||||
noDataMessage?: string;
|
||||
/** Enable client-side column sorting (defaults to false to avoid conflicts with server-side sorting) */
|
||||
enableSorting?: boolean;
|
||||
}
|
||||
|
||||
export function DataTable<TData, TValue>({
|
||||
@ -27,18 +29,28 @@ export function DataTable<TData, TValue>({
|
||||
isLoading = false,
|
||||
loadingMessage = "🚅 Loading logs...",
|
||||
noDataMessage = "No logs found",
|
||||
enableSorting = false,
|
||||
}: DataTableProps<TData, TValue>) {
|
||||
const supportsExpansion = !!(renderSubComponent || renderChildRows) && !!getRowCanExpand;
|
||||
const [sorting, setSorting] = useState<SortingState>([]);
|
||||
|
||||
const table = useReactTable<TData>({
|
||||
data,
|
||||
columns,
|
||||
...(enableSorting && {
|
||||
state: {
|
||||
sorting,
|
||||
},
|
||||
onSortingChange: setSorting,
|
||||
enableSortingRemoval: false,
|
||||
}),
|
||||
...(supportsExpansion && { getRowCanExpand }),
|
||||
getRowId: (row: TData, index: number) => {
|
||||
const _row: any = row as any;
|
||||
return _row?.request_id ?? String(index);
|
||||
},
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
...(enableSorting && { getSortedRowModel: getSortedRowModel() }),
|
||||
...(supportsExpansion && { getExpandedRowModel: getExpandedRowModel() }),
|
||||
});
|
||||
|
||||
@ -49,9 +61,25 @@ export function DataTable<TData, TValue>({
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => {
|
||||
const canSort = enableSorting && header.column.getCanSort();
|
||||
const isSorted = header.column.getIsSorted();
|
||||
|
||||
return (
|
||||
<TableHeaderCell key={header.id} className="py-1 h-8">
|
||||
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
|
||||
<TableHeaderCell
|
||||
key={header.id}
|
||||
className={`py-1 h-8 ${canSort ? 'cursor-pointer select-none hover:bg-gray-50' : ''}`}
|
||||
onClick={canSort ? header.column.getToggleSortingHandler() : undefined}
|
||||
>
|
||||
{header.isPlaceholder ? null : (
|
||||
<div className="flex items-center gap-1">
|
||||
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||
{canSort && (
|
||||
<span className="text-gray-400">
|
||||
{isSorted === 'asc' ? '↑' : isSorted === 'desc' ? '↓' : '⇅'}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</TableHeaderCell>
|
||||
);
|
||||
})}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user