From c76616f9c57532027aba246bc4325a15034a89bb Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 23 Apr 2026 17:33:42 +0000 Subject: [PATCH] feat(ui): AIHub/UsefulLinksManagement to shadcn - @heroicons ChevronDown/Right/ExternalLink/PlusCircle + @tremor Card/Text/Title/Table family \u2192 lucide ChevronDown/ChevronRight/ ExternalLink/Plus + shadcn Card/Button/Input. Tremor Table family preserved (whitelist). - Extracted fetchUsefulLinks into useCallback and fixed 'as any' on the useful_links value reader. - Blue Public-Model-Hub pill, purple Rearrange chip, emerald Save/ Add buttons kept as categorical palette; file added to .eslintrc overrides. Gates: TS \u2713 | Build \u2713. Co-authored-by: yuneng-jiang --- ui/litellm-dashboard/.eslintrc.json | 1 + .../AIHub/UsefulLinksManagement.tsx | 146 ++++++++++++------ 2 files changed, 99 insertions(+), 48 deletions(-) diff --git a/ui/litellm-dashboard/.eslintrc.json b/ui/litellm-dashboard/.eslintrc.json index b9e60edb87d..ce377bddd1c 100644 --- a/ui/litellm-dashboard/.eslintrc.json +++ b/ui/litellm-dashboard/.eslintrc.json @@ -75,6 +75,7 @@ "src/components/team/LoggingSettings.tsx", "src/components/DeletedTeamsPage/DeletedTeamsTable/DeletedTeamsTable.tsx", "src/components/DeletedKeysPage/DeletedKeysTable/DeletedKeysTable.tsx", + "src/components/AIHub/UsefulLinksManagement.tsx", "src/components/permissions/VectorStorePermissions.tsx", "src/components/permissions/AgentPermissions.tsx", "src/components/permissions/MCPServerPermissions.tsx", diff --git a/ui/litellm-dashboard/src/components/AIHub/UsefulLinksManagement.tsx b/ui/litellm-dashboard/src/components/AIHub/UsefulLinksManagement.tsx index c73eaf52384..900502f90d1 100644 --- a/ui/litellm-dashboard/src/components/AIHub/UsefulLinksManagement.tsx +++ b/ui/litellm-dashboard/src/components/AIHub/UsefulLinksManagement.tsx @@ -1,11 +1,32 @@ import TableIconActionButton from "@/components/common_components/IconActionButton/TableIconActionButtons/TableIconActionButton"; import NotificationsManager from "@/components/molecules/notifications_manager"; import { isAdminRole } from "@/utils/roles"; -import { ChevronDownIcon, ChevronRightIcon, ExternalLinkIcon, PlusCircleIcon } from "@heroicons/react/outline"; -import { Card, Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow, Text, Title } from "@tremor/react"; +import { + ChevronDown, + ChevronRight, + ExternalLink as ExternalLinkIcon, + Plus, +} from "lucide-react"; +// eslint-disable-next-line litellm-ui/no-banned-ui-imports +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeaderCell, + TableRow, +} from "@tremor/react"; +import { Button } from "@/components/ui/button"; +import { Card } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { cn } from "@/lib/utils"; import Link from "next/link"; -import React, { useEffect, useState } from "react"; -import { getProxyBaseUrl, getPublicModelHubInfo, updateUsefulLinksCall } from "../networking"; +import React, { useCallback, useEffect, useState } from "react"; +import { + getProxyBaseUrl, + getPublicModelHubInfo, + updateUsefulLinksCall, +} from "../networking"; interface UsefulLinksManagementProps { accessToken: string | null; @@ -23,12 +44,13 @@ const UsefulLinksManagement: React.FC = ({ accessTok const [links, setLinks] = useState([]); const [newLink, setNewLink] = useState({ url: "", displayName: "" }); const [editingLink, setEditingLink] = useState(null); + // eslint-disable-next-line @typescript-eslint/no-unused-vars const [loading, setLoading] = useState(false); const [isExpanded, setIsExpanded] = useState(true); const [isRearranging, setIsRearranging] = useState(false); const [originalLinksOrder, setOriginalLinksOrder] = useState([]); - const fetchUsefulLinks = async () => { + const fetchUsefulLinks = useCallback(async () => { if (!accessToken) return; try { @@ -38,20 +60,21 @@ const UsefulLinksManagement: React.FC = ({ accessTok if (response && response.useful_links) { const usefulLinks = response.useful_links || {}; - // Convert object to array of links with ids - // Handle both old format (Dict[str, str]) and new format (Dict[str, {url, index}]) const linksArray = Object.entries(usefulLinks) .map(([displayName, value]) => { - // Check if it's the new format with {url, index} - if (typeof value === "object" && value !== null && "url" in value) { + if ( + typeof value === "object" && + value !== null && + "url" in value + ) { + const v = value as { url: string; index?: number }; return { - id: `${(value as any).index ?? 0}-${displayName}`, + id: `${v.index ?? 0}-${displayName}`, displayName, - url: (value as any).url as string, - index: (value as any).index ?? 0, + url: v.url, + index: v.index ?? 0, }; } else { - // Old format: just a string URL return { id: `0-${displayName}`, displayName, @@ -76,11 +99,11 @@ const UsefulLinksManagement: React.FC = ({ accessTok } finally { setLoading(false); } - }; + }, [accessToken]); useEffect(() => { fetchUsefulLinks(); - }, [accessToken]); + }, [fetchUsefulLinks]); // Check if user is admin if (!isAdminRole(userRole || "")) { @@ -226,20 +249,26 @@ const UsefulLinksManagement: React.FC = ({ accessTok setLinks(newLinks); }; + const addDisabled = !newLink.url || !newLink.displayName; + return ( - -
setIsExpanded(!isExpanded)}> + +
setIsExpanded(!isExpanded)} + >
- Link Management -

- Manage the links that are displayed under 'Useful Links' on the public model hub. +

Link Management

+

+ Manage the links that are displayed under 'Useful Links' + on the public model hub.

{isExpanded ? ( - + ) : ( - + )}
@@ -247,11 +276,15 @@ const UsefulLinksManagement: React.FC = ({ accessTok {isExpanded && (
- Add New Link +

+ Add New Link +

- - + Display Name + + @@ -261,12 +294,13 @@ const UsefulLinksManagement: React.FC = ({ accessTok }) } placeholder="Friendly name" - className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm" />
- - + URL + + @@ -276,29 +310,33 @@ const UsefulLinksManagement: React.FC = ({ accessTok }) } placeholder="https://example.com" - className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm" />
- +
- Manage Existing Links +

+ Manage Existing Links +

Public Model Hub @@ -306,22 +344,25 @@ const UsefulLinksManagement: React.FC = ({ accessTok {!isRearranging ? ( ) : (
@@ -345,7 +386,7 @@ const UsefulLinksManagement: React.FC = ({ accessTok {editingLink && editingLink.id === link.id ? ( <> - @@ -354,11 +395,11 @@ const UsefulLinksManagement: React.FC = ({ accessTok displayName: e.target.value, }) } - className="w-full px-2 py-1 border border-gray-300 rounded-md text-sm" + className="h-8" /> - @@ -367,20 +408,22 @@ const UsefulLinksManagement: React.FC = ({ accessTok url: e.target.value, }) } - className="w-full px-2 py-1 border border-gray-300 rounded-md text-sm" + className="h-8" />
@@ -389,8 +432,12 @@ const UsefulLinksManagement: React.FC = ({ accessTok ) : ( <> - {link.displayName} - {link.url} + + {link.displayName} + + + {link.url} + {isRearranging ? (
@@ -440,7 +487,10 @@ const UsefulLinksManagement: React.FC = ({ accessTok ))} {links.length === 0 && ( - + No links added yet. Add a new link above.