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 ? (
-
-
+
+
@@ -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(),
+ )}
))}
))
) : (
-
-