diff --git a/ui/litellm-dashboard/.eslintrc.json b/ui/litellm-dashboard/.eslintrc.json index 7012eddb850..2a742806f48 100644 --- a/ui/litellm-dashboard/.eslintrc.json +++ b/ui/litellm-dashboard/.eslintrc.json @@ -55,6 +55,7 @@ "src/components/view_users/columns.tsx", "src/components/Projects/ProjectsPage.tsx", "src/components/pass_through_settings.tsx", + "src/components/policies/attachment_table.tsx", "src/components/permissions/VectorStorePermissions.tsx", "src/components/permissions/AgentPermissions.tsx", "src/components/permissions/MCPServerPermissions.tsx", diff --git a/ui/litellm-dashboard/src/components/policies/attachment_table.tsx b/ui/litellm-dashboard/src/components/policies/attachment_table.tsx index d9de8378a8a..eead89c0e9c 100644 --- a/ui/litellm-dashboard/src/components/policies/attachment_table.tsx +++ b/ui/litellm-dashboard/src/components/policies/attachment_table.tsx @@ -1,7 +1,27 @@ import React, { useState } from "react"; -import { Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow, Icon, Badge } from "@tremor/react"; -import { TrashIcon, SwitchVerticalIcon, ChevronUpIcon, ChevronDownIcon } from "@heroicons/react/outline"; -import { Tooltip, Tag } from "antd"; +// eslint-disable-next-line litellm-ui/no-banned-ui-imports +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeaderCell, + TableRow, +} from "@tremor/react"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { + ArrowUpDown, + ChevronDown, + ChevronUp, + Trash2, +} from "lucide-react"; import { ColumnDef, flexRender, @@ -21,6 +41,37 @@ interface AttachmentTableProps { accessToken: string | null; } +const listChip = ( + items: string[], + classes: string, + maxVisible: number = 2, +): React.ReactNode => { + if (items.length === 0) return -; + return ( +
+ {items.slice(0, maxVisible).map((t, i) => ( + + {t} + + ))} + {items.length > maxVisible && ( + + + + + +{items.length - maxVisible} + + + + {items.slice(maxVisible).join(", ")} + + + + )} +
+ ); +}; + const AttachmentTable: React.FC = ({ attachments, isLoading, @@ -28,9 +79,10 @@ const AttachmentTable: React.FC = ({ isAdmin, accessToken, }) => { - const [sorting, setSorting] = useState([{ id: "created_at", desc: true }]); + const [sorting, setSorting] = useState([ + { id: "created_at", desc: true }, + ]); - // Format date helper function const formatDate = (dateString?: string) => { if (!dateString) return "-"; const date = new Date(dateString); @@ -41,25 +93,30 @@ const AttachmentTable: React.FC = ({ { header: "Attachment ID", accessorKey: "attachment_id", - cell: (info: any) => ( - - - {info.getValue() ? `${String(info.getValue()).slice(0, 7)}...` : ""} - - - ), + cell: (info) => { + const v = String(info.getValue() || ""); + return ( + + + + + {v ? `${v.slice(0, 7)}...` : ""} + + + {v} + + + ); + }, }, { header: "Policy", accessorKey: "policy_name", - cell: ({ row }) => { - const attachment = row.original; - return ( - - {attachment.policy_name} - - ); - }, + cell: ({ row }) => ( + + {row.original.policy_name} + + ), }, { header: "Scope", @@ -68,7 +125,7 @@ const AttachmentTable: React.FC = ({ const attachment = row.original; if (attachment.scope === "*") { return ( - + Global (*) ); @@ -76,109 +133,45 @@ const AttachmentTable: React.FC = ({ return attachment.scope ? ( {attachment.scope} ) : ( - - + - ); }, }, { header: "Teams", accessorKey: "teams", - cell: ({ row }) => { - const attachment = row.original; - const teams = attachment.teams || []; - if (teams.length === 0) { - return -; - } - return ( -
- {teams.slice(0, 2).map((t, i) => ( - - {t} - - ))} - {teams.length > 2 && ( - - +{teams.length - 2} - - )} -
- ); - }, + cell: ({ row }) => + listChip( + row.original.teams || [], + "bg-cyan-100 text-cyan-700 dark:bg-cyan-950 dark:text-cyan-300", + ), }, { header: "Keys", accessorKey: "keys", - cell: ({ row }) => { - const attachment = row.original; - const keys = attachment.keys || []; - if (keys.length === 0) { - return -; - } - return ( -
- {keys.slice(0, 2).map((k, i) => ( - - {k} - - ))} - {keys.length > 2 && ( - - +{keys.length - 2} - - )} -
- ); - }, + cell: ({ row }) => + listChip( + row.original.keys || [], + "bg-purple-100 text-purple-700 dark:bg-purple-950 dark:text-purple-300", + ), }, { header: "Models", accessorKey: "models", - cell: ({ row }) => { - const attachment = row.original; - const models = attachment.models || []; - if (models.length === 0) { - return -; - } - return ( -
- {models.slice(0, 2).map((m, i) => ( - - {m} - - ))} - {models.length > 2 && ( - - +{models.length - 2} - - )} -
- ); - }, + cell: ({ row }) => + listChip( + row.original.models || [], + "bg-emerald-100 text-emerald-700 dark:bg-emerald-950 dark:text-emerald-300", + ), }, { header: "Tags", accessorKey: "tags", - cell: ({ row }) => { - const attachment = row.original; - const tags = attachment.tags || []; - if (tags.length === 0) { - return -; - } - return ( -
- {tags.slice(0, 2).map((t, i) => ( - - {t} - - ))} - {tags.length > 2 && ( - - +{tags.length - 2} - - )} -
- ); - }, + cell: ({ row }) => + listChip( + row.original.tags || [], + "bg-orange-100 text-orange-700 dark:bg-orange-950 dark:text-orange-300", + ), }, { header: "Created At", @@ -186,9 +179,16 @@ const AttachmentTable: React.FC = ({ cell: ({ row }) => { const attachment = row.original; return ( - - {formatDate(attachment.created_at)} - + + + + + {formatDate(attachment.created_at)} + + + {attachment.created_at} + + ); }, }, @@ -198,17 +198,25 @@ const AttachmentTable: React.FC = ({ cell: ({ row }) => { const attachment = row.original; return ( -
+
{isAdmin && ( - - onDeleteClick(attachment.attachment_id)} - className="cursor-pointer hover:text-red-500" - /> - + + + + + + Delete attachment + + )}
); @@ -219,9 +227,7 @@ const AttachmentTable: React.FC = ({ const table = useReactTable({ data: attachments, columns, - state: { - sorting, - }, + state: { sorting }, onSortingChange: setSorting, getCoreRowModel: getCoreRowModel(), getSortedRowModel: getSortedRowModel(), @@ -239,23 +245,34 @@ const AttachmentTable: React.FC = ({
- {header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())} + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )}
{header.id !== "actions" && (
{header.column.getIsSorted() ? ( { - asc: , - desc: , + asc: ( + + ), + desc: ( + + ), }[header.column.getIsSorted() as string] ) : ( - + )}
)} @@ -268,8 +285,11 @@ const AttachmentTable: React.FC = ({ {isLoading ? ( - -
+ +

Loading...

@@ -282,19 +302,25 @@ const AttachmentTable: React.FC = ({ key={cell.id} className={`py-0.5 max-h-8 overflow-hidden text-ellipsis whitespace-nowrap ${ cell.column.id === "actions" - ? "sticky right-0 bg-white shadow-[-4px_0_8px_-6px_rgba(0,0,0,0.1)]" + ? "sticky right-0 bg-background shadow-[-4px_0_8px_-6px_rgba(0,0,0,0.1)]" : "" }`} > - {flexRender(cell.column.columnDef.cell, cell.getContext())} + {flexRender( + cell.column.columnDef.cell, + cell.getContext(), + )} ))} )) ) : ( - -
+ +

No attachments found