From 32392518f64a3299b3ee751f0fbd443e7801ebce Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 23 Apr 2026 10:29:50 +0000 Subject: [PATCH] feat(ui): migrate small top-level components to shadcn - query_param_input: antd Button/Space + @ant-design/icons \u2192 shadcn Button/Input + lucide MinusCircle/Plus, dashed-outline add button. - object_permissions_view: @tremor Text \u2192 plain

with semantic tokens. - route_preview: antd Card/Typography + @ant-design/icons ChevronRight/ InfoCircle \u2192 shadcn Card + lucide ChevronRight/Info. - skill_hub_table_columns: @tremor Badge/Text + antd Tooltip + @ant-design/ icons CopyOutlined/LinkOutlined \u2192 shadcn Badge/Tooltip + lucide Copy/Link. Added to overrides for the categorical blue/emerald badge palette. - claude_code_plugins: @tremor Button + antd Modal \u2192 shadcn Button + AlertDialog (destructive variant for the Delete action). Gates: TS \u2713 | Build \u2713. Co-authored-by: yuneng-jiang --- ui/litellm-dashboard/.eslintrc.json | 3 +- .../src/components/claude_code_plugins.tsx | 81 ++++++---- .../components/object_permissions_view.tsx | 13 +- .../src/components/query_param_input.tsx | 46 ++++-- .../src/components/route_preview.tsx | 140 ++++++++++-------- .../components/skill_hub_table_columns.tsx | 68 ++++++--- 6 files changed, 223 insertions(+), 128 deletions(-) diff --git a/ui/litellm-dashboard/.eslintrc.json b/ui/litellm-dashboard/.eslintrc.json index 82948f0e360..52f6d6d108b 100644 --- a/ui/litellm-dashboard/.eslintrc.json +++ b/ui/litellm-dashboard/.eslintrc.json @@ -43,7 +43,8 @@ "src/components/mcp_tools/ByokCredentialModal.tsx", "src/components/shared/usage_date_picker.tsx", "src/components/shared/advanced_date_picker.tsx", - "src/components/activity_metrics.tsx" + "src/components/activity_metrics.tsx", + "src/components/skill_hub_table_columns.tsx" ], "rules": { "litellm-ui/no-banned-ui-imports": "off", diff --git a/ui/litellm-dashboard/src/components/claude_code_plugins.tsx b/ui/litellm-dashboard/src/components/claude_code_plugins.tsx index 67d5f6e0ad5..55191f1c625 100644 --- a/ui/litellm-dashboard/src/components/claude_code_plugins.tsx +++ b/ui/litellm-dashboard/src/components/claude_code_plugins.tsx @@ -1,6 +1,15 @@ import React, { useState, useEffect } from "react"; -import { Button } from "@tremor/react"; -import { Modal } from "antd"; +import { Button } from "@/components/ui/button"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from "@/components/ui/alert-dialog"; import { getClaudeCodePluginsList, deleteClaudeCodePlugin, @@ -40,7 +49,7 @@ const ClaudeCodePluginsPanel: React.FC = ({ try { const response: ListPluginsResponse = await getClaudeCodePluginsList( accessToken, - false + false, ); setPluginsList(response.plugins); } catch (error) { @@ -52,6 +61,7 @@ const ClaudeCodePluginsPanel: React.FC = ({ useEffect(() => { fetchPlugins(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [accessToken]); const handleDeleteClick = (pluginName: string, displayName: string) => { @@ -64,7 +74,9 @@ const ClaudeCodePluginsPanel: React.FC = ({ setIsDeleting(true); try { await deleteClaudeCodePlugin(accessToken, pluginToDelete.name); - NotificationsManager.success(`Skill "${pluginToDelete.displayName}" deleted successfully`); + NotificationsManager.success( + `Skill "${pluginToDelete.displayName}" deleted successfully`, + ); fetchPlugins(); } catch (error) { console.error("Error deleting skill:", error); @@ -89,13 +101,19 @@ const ClaudeCodePluginsPanel: React.FC = ({ <>

Skills

-

- Register Claude Code skills. Published skills appear in the Skill Hub for all users and - are served via{" "} - /claude-code/marketplace.json. +

+ Register Claude Code skills. Published skills appear in the Skill + Hub for all users and are served via{" "} + + /claude-code/marketplace.json + + .

-
@@ -122,23 +140,34 @@ const ClaudeCodePluginsPanel: React.FC = ({ onSuccess={fetchPlugins} /> - {pluginToDelete && ( - setPluginToDelete(null)} - confirmLoading={isDeleting} - okText="Delete" - okButtonProps={{ danger: true }} - > -

- Are you sure you want to delete skill:{" "} - {pluginToDelete.displayName}? -

-

This action cannot be undone.

-
- )} + (!o ? setPluginToDelete(null) : undefined)} + > + + + Delete Skill + + Are you sure you want to delete skill:{" "} + {pluginToDelete?.displayName}? This action cannot + be undone. + + + + Cancel + { + e.preventDefault(); + handleDeleteConfirm(); + }} + className="bg-destructive text-destructive-foreground hover:bg-destructive/90" + > + {isDeleting ? "Deleting…" : "Delete"} + + + +
); }; diff --git a/ui/litellm-dashboard/src/components/object_permissions_view.tsx b/ui/litellm-dashboard/src/components/object_permissions_view.tsx index 685467e1d3e..95d4cce6091 100644 --- a/ui/litellm-dashboard/src/components/object_permissions_view.tsx +++ b/ui/litellm-dashboard/src/components/object_permissions_view.tsx @@ -1,5 +1,4 @@ import React from "react"; -import { Text } from "@tremor/react"; import VectorStorePermissions from "./permissions/VectorStorePermissions"; import MCPServerPermissions from "./permissions/MCPServerPermissions"; import AgentPermissions from "./permissions/AgentPermissions"; @@ -56,11 +55,15 @@ export function ObjectPermissionsView({ if (variant === "card") { return ( -
+
- Object Permissions - Access control for Vector Stores and MCP Servers +

Object Permissions

+

+ Access control for Vector Stores and MCP Servers +

{content} @@ -70,7 +73,7 @@ export function ObjectPermissionsView({ return (
- Object Permissions +

Object Permissions

{content}
); diff --git a/ui/litellm-dashboard/src/components/query_param_input.tsx b/ui/litellm-dashboard/src/components/query_param_input.tsx index 4a98ab48bca..8482564c400 100644 --- a/ui/litellm-dashboard/src/components/query_param_input.tsx +++ b/ui/litellm-dashboard/src/components/query_param_input.tsx @@ -1,15 +1,20 @@ import React, { useState } from "react"; -import { Button, Space } from "antd"; -import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons"; -import { TextInput } from "@tremor/react"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { MinusCircle, Plus } from "lucide-react"; interface QueryParamInputProps { value?: Record; onChange?: (value: Record) => void; } -const QueryParamInput: React.FC = ({ value = {}, onChange }) => { - const [pairs, setPairs] = useState<[string, string][]>(Object.entries(value)); +const QueryParamInput: React.FC = ({ + value = {}, + onChange, +}) => { + const [pairs, setPairs] = useState<[string, string][]>( + Object.entries(value), + ); const handleAdd = () => { setPairs([...pairs, ["", ""]]); @@ -29,29 +34,40 @@ const QueryParamInput: React.FC = ({ value = {}, onChange }; return ( -
+
{pairs.map(([key, val], index) => ( - - + handleChange(index, e.target.value, val)} /> - handleChange(index, key, e.target.value)} /> -
- handleRemove(index)} style={{ cursor: "pointer" }} /> -
-
+ +
))} -
); }; -export default QueryParamInput; \ No newline at end of file +export default QueryParamInput; diff --git a/ui/litellm-dashboard/src/components/route_preview.tsx b/ui/litellm-dashboard/src/components/route_preview.tsx index 2c616ad8670..6933f3df9e4 100644 --- a/ui/litellm-dashboard/src/components/route_preview.tsx +++ b/ui/litellm-dashboard/src/components/route_preview.tsx @@ -1,8 +1,6 @@ import React from "react"; -import { Card, Typography } from "antd"; -import { RightOutlined, InfoCircleOutlined } from "@ant-design/icons"; - -const { Title, Text } = Typography; +import { Card } from "@/components/ui/card"; +import { ChevronRight, Info } from "lucide-react"; import { getProxyBaseUrl } from "./networking"; interface RoutePreviewProps { @@ -11,94 +9,114 @@ interface RoutePreviewProps { includeSubpath: boolean; } -const RoutePreview: React.FC = ({ pathValue, targetValue, includeSubpath }) => { +const RoutePreview: React.FC = ({ + pathValue, + targetValue, + includeSubpath, +}) => { const proxyBaseUrl = getProxyBaseUrl(); - const getLiteLLMProxyUrl = () => { - return pathValue ? `${proxyBaseUrl}${pathValue}` : ""; - }; + const getLiteLLMProxyUrl = () => + pathValue ? `${proxyBaseUrl}${pathValue}` : ""; - // Only show if both path and target are provided if (!pathValue || !targetValue) { return null; } return ( - Route Preview - How your requests will be routed +
+ Route Preview +
+

+ How your requests will be routed +

- {/* Basic routing */}
-
Basic routing:
+
+ Basic routing: +
- {/* Your endpoint */} -
-
Your endpoint
- {getLiteLLMProxyUrl()} +
+
+ Your endpoint +
+ + {getLiteLLMProxyUrl()} +
- {/* Arrow */} -
- +
+
- {/* Forwards to */} -
-
Forwards to
- {targetValue} +
+
+ Forwards to +
+ + {targetValue} +
{includeSubpath && ( - <> - {/* With subpaths */} -
-
With subpaths:
-
- {/* Your endpoint + subpath */} -
-
Your endpoint + subpath
- - {pathValue && `${proxyBaseUrl}${pathValue}`} - /v1/text-to-image/base/model - -
- - {/* Arrow */} -
- -
- - {/* Forwards to with subpath */} -
-
Forwards to
- - {targetValue} - /v1/text-to-image/base/model - +
+
+ With subpaths: +
+
+
+
+ Your endpoint + subpath
+ + {pathValue && `${proxyBaseUrl}${pathValue}`} + + /v1/text-to-image/base/model + +
- {/* Note */} -
- Any path after {pathValue} will be appended to the target URL +
+ +
+ +
+
+ Forwards to +
+ + {targetValue} + + /v1/text-to-image/base/model + +
- + +
+ Any path after {pathValue} will be appended to the target URL +
+
)} {!includeSubpath && ( -
+
- -
- Not seeing the routing you wanted? Try enabling - Include Subpaths - - above - this allows subroutes like{" "} - /api/v1/models to be - forwarded automatically. + +
+ + Not seeing the routing you wanted? + {" "} + Try enabling - Include Subpaths - above - this allows subroutes + like{" "} + + /api/v1/models + {" "} + to be forwarded automatically.
diff --git a/ui/litellm-dashboard/src/components/skill_hub_table_columns.tsx b/ui/litellm-dashboard/src/components/skill_hub_table_columns.tsx index 8ec9d79956e..2146e6edd81 100644 --- a/ui/litellm-dashboard/src/components/skill_hub_table_columns.tsx +++ b/ui/litellm-dashboard/src/components/skill_hub_table_columns.tsx @@ -1,12 +1,18 @@ import { ColumnDef } from "@tanstack/react-table"; -import { Badge, Text } from "@tremor/react"; -import { Tooltip } from "antd"; -import { CopyOutlined, LinkOutlined } from "@ant-design/icons"; +import { Badge } from "@/components/ui/badge"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { Copy, Link as LinkIcon } from "lucide-react"; import { Plugin } from "./claude_code_plugins/types"; export const skillHubColumns = ( showModal: (skill: Plugin) => void, copyToClipboard: (text: string) => void, + // eslint-disable-next-line @typescript-eslint/no-unused-vars publicPage: boolean = false, ): ColumnDef[] => [ { @@ -21,22 +27,31 @@ export const skillHubColumns = (
- - copyToClipboard(skill.name)} - className="cursor-pointer text-gray-500 hover:text-blue-500 text-xs" - /> - + + + + + + Copy skill name + +
{skill.description && ( - +

{skill.description} - +

)}
); @@ -47,7 +62,9 @@ export const skillHubColumns = ( accessorKey: "description", enableSorting: false, cell: ({ row }) => ( - {row.original.description || "-"} +

+ {row.original.description || "-"} +

), }, { @@ -56,8 +73,12 @@ export const skillHubColumns = ( enableSorting: true, cell: ({ row }) => { const cat = row.original.category; - if (!cat) return -; - return {cat}; + if (!cat) return -; + return ( + + {cat} + + ); }, }, { @@ -65,7 +86,7 @@ export const skillHubColumns = ( accessorKey: "domain", enableSorting: true, cell: ({ row }) => ( - {row.original.domain || "-"} + {row.original.domain || "-"} ), }, { @@ -86,17 +107,18 @@ export const skillHubColumns = ( url = src.url; label = src.url.replace(/^https?:\/\//, ""); } - if (!url) return -; + if (!url) + return -; return ( {label} - + ); }, @@ -106,7 +128,13 @@ export const skillHubColumns = ( accessorKey: "enabled", enableSorting: true, cell: ({ row }) => ( - + {row.original.enabled ? "Public" : "Draft"} ),