mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-23 10:26:09 +00:00
[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:
co-authored by
Cursor Agent
Ishaan Jaff
parent
a33d3e035d
commit
dd183a7fcb
Generated
-15
@@ -12984,21 +12984,6 @@
|
|||||||
"type": "github",
|
"type": "github",
|
||||||
"url": "https://github.com/sponsors/wooorm"
|
"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",
|
accessorKey: "server_id",
|
||||||
header: "Server ID",
|
header: "Server ID",
|
||||||
|
enableSorting: true,
|
||||||
cell: ({ row }) => (
|
cell: ({ row }) => (
|
||||||
<button
|
<button
|
||||||
onClick={() => onView(row.original.server_id)}
|
onClick={() => onView(row.original.server_id)}
|
||||||
@@ -27,10 +28,12 @@ export const mcpServerColumns = (
|
|||||||
{
|
{
|
||||||
accessorKey: "server_name",
|
accessorKey: "server_name",
|
||||||
header: "Name",
|
header: "Name",
|
||||||
|
enableSorting: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
accessorKey: "alias",
|
accessorKey: "alias",
|
||||||
header: "Alias",
|
header: "Alias",
|
||||||
|
enableSorting: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: "url",
|
id: "url",
|
||||||
@@ -47,6 +50,7 @@ export const mcpServerColumns = (
|
|||||||
{
|
{
|
||||||
accessorKey: "transport",
|
accessorKey: "transport",
|
||||||
header: "Transport",
|
header: "Transport",
|
||||||
|
enableSorting: true,
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const transport = row.original.transport || "http";
|
const transport = row.original.transport || "http";
|
||||||
const specPath = row.original.spec_path;
|
const specPath = row.original.spec_path;
|
||||||
@@ -58,6 +62,7 @@ export const mcpServerColumns = (
|
|||||||
{
|
{
|
||||||
accessorKey: "auth_type",
|
accessorKey: "auth_type",
|
||||||
header: "Auth Type",
|
header: "Auth Type",
|
||||||
|
enableSorting: true,
|
||||||
cell: ({ getValue }) => <span>{(getValue() as string) || "none"}</span>,
|
cell: ({ getValue }) => <span>{(getValue() as string) || "none"}</span>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -166,6 +171,7 @@ export const mcpServerColumns = (
|
|||||||
{
|
{
|
||||||
header: "Created At",
|
header: "Created At",
|
||||||
accessorKey: "created_at",
|
accessorKey: "created_at",
|
||||||
|
enableSorting: true,
|
||||||
sortingFn: "datetime",
|
sortingFn: "datetime",
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const server = row.original;
|
const server = row.original;
|
||||||
@@ -177,6 +183,7 @@ export const mcpServerColumns = (
|
|||||||
{
|
{
|
||||||
header: "Updated At",
|
header: "Updated At",
|
||||||
accessorKey: "updated_at",
|
accessorKey: "updated_at",
|
||||||
|
enableSorting: true,
|
||||||
sortingFn: "datetime",
|
sortingFn: "datetime",
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const server = row.original;
|
const server = row.original;
|
||||||
|
|||||||
@@ -410,6 +410,7 @@ const MCPServers: React.FC<MCPServerProps> = ({ accessToken, userRole, userID })
|
|||||||
isLoading={isLoadingServers}
|
isLoading={isLoadingServers}
|
||||||
noDataMessage="No MCP servers configured"
|
noDataMessage="No MCP servers configured"
|
||||||
loadingMessage="🚅 Loading MCP servers..."
|
loadingMessage="🚅 Loading MCP servers..."
|
||||||
|
enableSorting={true}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { Fragment } from "react";
|
import { Fragment, useState } from "react";
|
||||||
import { ColumnDef, flexRender, getCoreRowModel, getExpandedRowModel, Row, useReactTable } from "@tanstack/react-table";
|
import { ColumnDef, flexRender, getCoreRowModel, getExpandedRowModel, Row, useReactTable, getSortedRowModel, SortingState } from "@tanstack/react-table";
|
||||||
|
|
||||||
import { Table, TableHead, TableHeaderCell, TableBody, TableRow, TableCell } from "@tremor/react";
|
import { Table, TableHead, TableHeaderCell, TableBody, TableRow, TableCell } from "@tremor/react";
|
||||||
|
|
||||||
@@ -15,6 +15,8 @@ interface DataTableProps<TData, TValue> {
|
|||||||
isLoading?: boolean;
|
isLoading?: boolean;
|
||||||
loadingMessage?: string;
|
loadingMessage?: string;
|
||||||
noDataMessage?: 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>({
|
export function DataTable<TData, TValue>({
|
||||||
@@ -27,18 +29,28 @@ export function DataTable<TData, TValue>({
|
|||||||
isLoading = false,
|
isLoading = false,
|
||||||
loadingMessage = "🚅 Loading logs...",
|
loadingMessage = "🚅 Loading logs...",
|
||||||
noDataMessage = "No logs found",
|
noDataMessage = "No logs found",
|
||||||
|
enableSorting = false,
|
||||||
}: DataTableProps<TData, TValue>) {
|
}: DataTableProps<TData, TValue>) {
|
||||||
const supportsExpansion = !!(renderSubComponent || renderChildRows) && !!getRowCanExpand;
|
const supportsExpansion = !!(renderSubComponent || renderChildRows) && !!getRowCanExpand;
|
||||||
|
const [sorting, setSorting] = useState<SortingState>([]);
|
||||||
|
|
||||||
const table = useReactTable<TData>({
|
const table = useReactTable<TData>({
|
||||||
data,
|
data,
|
||||||
columns,
|
columns,
|
||||||
|
...(enableSorting && {
|
||||||
|
state: {
|
||||||
|
sorting,
|
||||||
|
},
|
||||||
|
onSortingChange: setSorting,
|
||||||
|
enableSortingRemoval: false,
|
||||||
|
}),
|
||||||
...(supportsExpansion && { getRowCanExpand }),
|
...(supportsExpansion && { getRowCanExpand }),
|
||||||
getRowId: (row: TData, index: number) => {
|
getRowId: (row: TData, index: number) => {
|
||||||
const _row: any = row as any;
|
const _row: any = row as any;
|
||||||
return _row?.request_id ?? String(index);
|
return _row?.request_id ?? String(index);
|
||||||
},
|
},
|
||||||
getCoreRowModel: getCoreRowModel(),
|
getCoreRowModel: getCoreRowModel(),
|
||||||
|
...(enableSorting && { getSortedRowModel: getSortedRowModel() }),
|
||||||
...(supportsExpansion && { getExpandedRowModel: getExpandedRowModel() }),
|
...(supportsExpansion && { getExpandedRowModel: getExpandedRowModel() }),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -49,9 +61,25 @@ export function DataTable<TData, TValue>({
|
|||||||
{table.getHeaderGroups().map((headerGroup) => (
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
<TableRow key={headerGroup.id}>
|
<TableRow key={headerGroup.id}>
|
||||||
{headerGroup.headers.map((header) => {
|
{headerGroup.headers.map((header) => {
|
||||||
|
const canSort = enableSorting && header.column.getCanSort();
|
||||||
|
const isSorted = header.column.getIsSorted();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<TableHeaderCell key={header.id} className="py-1 h-8">
|
<TableHeaderCell
|
||||||
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
|
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>
|
</TableHeaderCell>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
|
|||||||
Reference in New Issue
Block a user