Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 0 additions & 66 deletions ui/litellm-dashboard/eslint-suppressions.json
Original file line number Diff line number Diff line change
Expand Up @@ -838,15 +838,7 @@
"count": 1
}
},
"src/app/(dashboard)/mcp-servers/_components/MCPLogoSelector.tsx": {
"no-restricted-imports": {
"count": 1
}
},
"src/app/(dashboard)/mcp-servers/_components/MCPNetworkSettings.tsx": {
"no-restricted-imports": {
"count": 1
},
"react-hooks/immutability": {
"count": 2
}
Expand All @@ -861,11 +853,6 @@
"count": 1
}
},
"src/app/(dashboard)/mcp-servers/_components/MCPServerCard.tsx": {
"no-restricted-imports": {
"count": 2
}
},
"src/app/(dashboard)/mcp-servers/_components/MCPSubmissionsTab.tsx": {
"react-hooks/set-state-in-effect": {
"count": 1
Expand Down Expand Up @@ -904,9 +891,6 @@
}
},
"src/app/(dashboard)/mcp-servers/_components/OpenAPIQuickPicker.tsx": {
"no-restricted-imports": {
"count": 1
},
"react-hooks/set-state-in-effect": {
"count": 1
}
Expand Down Expand Up @@ -947,11 +931,6 @@
"count": 1
}
},
"src/app/(dashboard)/mcp-servers/_components/TruePassthroughWarning.tsx": {
"no-restricted-imports": {
"count": 1
}
},
"src/app/(dashboard)/mcp-servers/_components/UserEnvVarsModal.tsx": {
"no-nested-ternary": {
"count": 2
Expand Down Expand Up @@ -999,9 +978,6 @@
},
"no-nested-ternary": {
"count": 3
},
"no-restricted-imports": {
"count": 2
}
},
"src/app/(dashboard)/mcp-servers/_components/mcp_discovery.tsx": {
Expand All @@ -1011,27 +987,18 @@
"local/no-complex-jsx-arrow": {
"count": 1
},
"no-restricted-imports": {
"count": 1
},
"react-hooks/set-state-in-effect": {
"count": 2
}
},
"src/app/(dashboard)/mcp-servers/_components/mcp_server_cost_config.tsx": {
"local/filename-pascal-case": {
"count": 1
},
"no-restricted-imports": {
"count": 2
}
},
"src/app/(dashboard)/mcp-servers/_components/mcp_server_cost_display.tsx": {
"local/filename-pascal-case": {
"count": 1
},
"no-restricted-imports": {
"count": 1
}
},
"src/app/(dashboard)/mcp-servers/_components/mcp_server_edit.tsx": {
Expand All @@ -1057,9 +1024,6 @@
"src/app/(dashboard)/mcp-servers/_components/mcp_server_view.tsx": {
"local/filename-pascal-case": {
"count": 1
},
"no-restricted-imports": {
"count": 2
}
},
"src/app/(dashboard)/mcp-servers/_components/mcp_servers.tsx": {
Expand All @@ -1072,19 +1036,13 @@
"no-nested-ternary": {
"count": 1
},
"no-restricted-imports": {
"count": 2
},
"react-hooks/set-state-in-effect": {
"count": 2
}
},
"src/app/(dashboard)/mcp-servers/_components/mcp_tool_configuration.tsx": {
"local/filename-pascal-case": {
"count": 1
},
"no-restricted-imports": {
"count": 2
}
},
"src/app/(dashboard)/mcp-servers/_components/mcp_tools.tsx": {
Expand All @@ -1097,9 +1055,6 @@
"no-nested-ternary": {
"count": 1
},
"no-restricted-imports": {
"count": 2
},
"react-hooks/set-state-in-effect": {
"count": 2
}
Expand Down Expand Up @@ -1864,9 +1819,6 @@
"local/filename-pascal-case": {
"count": 1
},
"no-restricted-imports": {
"count": 1
},
"react-hooks/set-state-in-effect": {
"count": 1
}
Expand Down Expand Up @@ -2435,11 +2387,6 @@
"count": 1
}
},
"src/components/Settings/AdminSettings/MCPSemanticFilterSettings/MCPSemanticFilterTestPanel.tsx": {
"no-restricted-imports": {
"count": 1
}
},
"src/components/Settings/AdminSettings/PluginSettings/PluginSettings.tsx": {
"no-restricted-imports": {
"count": 1
Expand Down Expand Up @@ -2605,20 +2552,7 @@
}
},
"src/components/ToolDetail.tsx": {
"no-restricted-imports": {
"count": 1
},
"unused-imports/no-unused-imports": {
"count": 2
}
},
"src/components/ToolPolicies/PolicySelect.tsx": {
"no-restricted-imports": {
"count": 1
}
},
"src/components/ToolPolicies/ToolPoliciesTableColumns.tsx": {
"no-restricted-imports": {
"count": 1
}
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import React from "react";
import { Input, Tooltip } from "antd";
import { InfoCircleOutlined, LinkOutlined } from "@ant-design/icons";
import { Info, Link as LinkIcon } from "lucide-react";
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
import { InputGroup, InputGroupAddon, InputGroupInput } from "@/components/ui/input-group";
import { cn } from "@/lib/cva.config";
import { Logo } from "@/components/molecules/logo/Logo";
import githubLogo from "../../../../../public/assets/logos/github.svg";
import slackLogo from "../../../../../public/assets/logos/slack.svg";
Expand Down Expand Up @@ -61,72 +63,83 @@
};

return (
<div>
<div className="flex items-center gap-2 mb-2">
<span className="text-sm font-medium text-gray-700">Logo</span>
<Tooltip title="Select a well-known logo or paste a URL to any image. The logo is shown on the admin and chat pages.">
<InfoCircleOutlined className="text-blue-400 hover:text-blue-600 cursor-help" />
</Tooltip>
</div>
<TooltipProvider>
<div>
<div className="mb-2 flex items-center gap-2">
<span className="text-sm font-medium">Logo</span>
<Tooltip>
<TooltipTrigger
render={<Info className="size-4 cursor-help text-muted-foreground" aria-label="About the logo" />}
/>
<TooltipContent>
Select a well-known logo or paste a URL to any image. The logo is shown on the admin and chat pages.
</TooltipContent>
</Tooltip>
</div>

{/* Preview */}
{value && (
<div className="flex items-center gap-3 mb-3 p-3 bg-gray-50 rounded-lg border border-gray-200">
<Logo
src={selectedWellKnown?.src ?? value}
label="Selected"
className="w-10 h-10 object-contain rounded-sm"
/>
<div className="flex-1 min-w-0">
<div className="text-xs text-gray-500 truncate">{value}</div>
{/* Preview */}
{value && (
<div className="mb-3 flex items-center gap-3 rounded-lg border border-border bg-muted p-3">
<Logo
src={selectedWellKnown?.src ?? value}
label="Selected"
className="h-10 w-10 rounded-sm object-contain"
/>
<div className="min-w-0 flex-1">
<div className="truncate text-xs text-muted-foreground">{value}</div>
</div>
<button
type="button"
onClick={() => onChange?.(undefined)}
className="cursor-pointer border-none bg-transparent text-xs text-muted-foreground hover:text-destructive"
>
</button>
</div>
<button
type="button"
onClick={() => onChange?.(undefined)}
className="text-xs text-gray-400 hover:text-red-500 cursor-pointer bg-transparent border-none"
>
</button>
)}

{/* Well-known logo grid */}
<div className="mb-3 grid grid-cols-10 gap-1.5">
{WELL_KNOWN_LOGOS.map((logo) => {
const isSelected = value === logo.url;
return (
<Tooltip key={logo.name}>
<TooltipTrigger
render={
<button
type="button"
onClick={() => handleSelect(logo.url)}
className={cn(
"flex size-10 cursor-pointer items-center justify-center rounded-lg border p-2 transition-all",
isSelected ? "border-primary bg-accent shadow-xs" : "border-border hover:bg-accent",
)}
>
<img src={logo.src} alt={logo.name} className="h-5 w-5 object-contain" />

Check warning on line 117 in ui/litellm-dashboard/src/app/(dashboard)/mcp-servers/_components/MCPLogoSelector.tsx

View workflow job for this annotation

GitHub Actions / frontend-lint

Using `<img>` could result in slower LCP and higher bandwidth. Consider using `<Image />` from `next/image` or a custom image loader to automatically optimize images. This may incur additional usage or cost from your provider. See: https://nextjs.org/docs/messages/no-img-element
</button>
}
/>
<TooltipContent>{logo.name}</TooltipContent>
</Tooltip>
);
})}
</div>
)}

{/* Well-known logo grid */}
<div className="grid grid-cols-10 gap-1.5 mb-3">
{WELL_KNOWN_LOGOS.map((logo) => {
const isSelected = value === logo.url;
return (
<Tooltip key={logo.name} title={logo.name}>
<button
type="button"
onClick={() => handleSelect(logo.url)}
className={`flex items-center justify-center p-2 rounded-lg border transition-all cursor-pointer
${
isSelected
? "border-blue-500 bg-blue-50 shadow-xs"
: "border-gray-200 hover:border-blue-300 hover:bg-gray-50"
}`}
style={{ width: 40, height: 40 }}
>
<img src={logo.src} alt={logo.name} className="w-5 h-5 object-contain" />
</button>
</Tooltip>
);
})}
{/* Custom URL input */}
<InputGroup>
<InputGroupAddon>
<LinkIcon className="size-4 text-muted-foreground" />
</InputGroupAddon>
<InputGroupInput
placeholder="Or paste a custom logo URL..."
value={value && !selectedWellKnown ? value : ""}
onChange={(e) => {
const v = e.target.value.trim();
onChange?.(v || undefined);
}}
/>
</InputGroup>
</div>

{/* Custom URL input */}
<Input
prefix={<LinkOutlined className="text-gray-400" />}
placeholder="Or paste a custom logo URL..."
value={value && !selectedWellKnown ? value : ""}
onChange={(e) => {
const v = e.target.value.trim();
onChange?.(v || undefined);
}}
className="rounded-lg"
size="small"
/>
</div>
</TooltipProvider>
);
};

Expand Down
Loading
Loading