From 4c8dc48b913a3857fc3a51db6c12c52dc448ae34 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 23 Apr 2026 10:37:26 +0000 Subject: [PATCH] feat(ui): migrate common_components small selects + LabeledField to shadcn - DurationSelect: antd Select \u2192 shadcn Select (Daily/Weekly/Monthly). - LabeledField: antd Typography.Text copyable indicator \u2192 custom hook using navigator.clipboard + lucide Copy/Check icon with Tooltip feedback. DefaultProxyAdminTag branch preserved. - OrganizationDropdown / ProjectDropdown: antd Select with allowClear + filterOption \u2192 shadcn Select with an explicit '__all__' sentinel value mapped back to an empty string on the callback side (primitives don't accept undefined like antd's allowClear). Loading state uses the disabled=true fallback since Radix Select has no spinner slot. Gates: TS \u2713 | Build \u2713. Co-authored-by: yuneng-jiang --- .../common_components/DurationSelect.tsx | 28 ++++-- .../common_components/LabeledField.tsx | 87 +++++++++++++++---- .../OrganizationDropdown.tsx | 63 ++++++++------ .../common_components/ProjectDropdown.tsx | 58 ++++++------- 4 files changed, 157 insertions(+), 79 deletions(-) diff --git a/ui/litellm-dashboard/src/components/common_components/DurationSelect.tsx b/ui/litellm-dashboard/src/components/common_components/DurationSelect.tsx index a84e8aeb110..b7d518c4507 100644 --- a/ui/litellm-dashboard/src/components/common_components/DurationSelect.tsx +++ b/ui/litellm-dashboard/src/components/common_components/DurationSelect.tsx @@ -1,4 +1,11 @@ -import { Select } from "antd"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { cn } from "@/lib/utils"; interface DurationSelectProps { className?: string; @@ -6,12 +13,21 @@ interface DurationSelectProps { onChange?: (value: string) => void; } -export default function DurationSelect({ className, value, onChange }: DurationSelectProps) { +export default function DurationSelect({ + className, + value, + onChange, +}: DurationSelectProps) { return ( - onChange?.(v)}> + + + + + Daily + Weekly + Monthly + ); } diff --git a/ui/litellm-dashboard/src/components/common_components/LabeledField.tsx b/ui/litellm-dashboard/src/components/common_components/LabeledField.tsx index 75a8236a2fa..f20501aa8a6 100644 --- a/ui/litellm-dashboard/src/components/common_components/LabeledField.tsx +++ b/ui/litellm-dashboard/src/components/common_components/LabeledField.tsx @@ -1,9 +1,14 @@ -import React from "react"; -import { Typography, Space } from "antd"; +import React, { useState } from "react"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip"; +import { Check, Copy } from "lucide-react"; +import { cn } from "@/lib/utils"; import DefaultProxyAdminTag from "./DefaultProxyAdminTag"; -const { Text } = Typography; - interface LabeledFieldProps { label: string; value: string; @@ -13,6 +18,55 @@ interface LabeledFieldProps { defaultUserIdCheck?: boolean; } +const CopyableValue: React.FC<{ + value: string; + label: string; + truncate?: boolean; +}> = ({ value, label, truncate }) => { + const [copied, setCopied] = useState(false); + const handleCopy = async () => { + try { + await navigator.clipboard.writeText(value); + setCopied(true); + setTimeout(() => setCopied(false), 1500); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (_err) { + /* noop */ + } + }; + return ( +
+ + {value} + + + + + + + {copied ? "Copied!" : `Copy ${label}`} + + +
+ ); +}; + export default function LabeledField({ label, value, @@ -28,24 +82,25 @@ export default function LabeledField({ const valueEl = isDefaultUser ? ( + ) : isCopyable ? ( + ) : ( - {displayValue} - + ); + return (
- - {icon} - - {label} - - +
+ {icon && {icon}} + {label} +
{valueEl}
); diff --git a/ui/litellm-dashboard/src/components/common_components/OrganizationDropdown.tsx b/ui/litellm-dashboard/src/components/common_components/OrganizationDropdown.tsx index db8e75302fe..2a53313bc2c 100644 --- a/ui/litellm-dashboard/src/components/common_components/OrganizationDropdown.tsx +++ b/ui/litellm-dashboard/src/components/common_components/OrganizationDropdown.tsx @@ -1,8 +1,13 @@ import React from "react"; -import { Select, Typography } from "antd"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; import { Organization } from "../networking"; - -const { Text } = Typography; +import { cn } from "@/lib/utils"; interface OrganizationDropdownProps { organizations?: Organization[] | null; @@ -11,8 +16,11 @@ interface OrganizationDropdownProps { disabled?: boolean; loading?: boolean; style?: React.CSSProperties; + className?: string; } +const ALL = "__all__"; + const OrganizationDropdown: React.FC = ({ organizations, value, @@ -20,35 +28,34 @@ const OrganizationDropdown: React.FC = ({ disabled, loading, style, + className, }) => { return ( ); }; diff --git a/ui/litellm-dashboard/src/components/common_components/ProjectDropdown.tsx b/ui/litellm-dashboard/src/components/common_components/ProjectDropdown.tsx index f91cbd4a14b..7b8ced85602 100644 --- a/ui/litellm-dashboard/src/components/common_components/ProjectDropdown.tsx +++ b/ui/litellm-dashboard/src/components/common_components/ProjectDropdown.tsx @@ -1,6 +1,11 @@ import React from "react"; -import { Select, Spin } from "antd"; -import { LoadingOutlined } from "@ant-design/icons"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; import { ProjectResponse } from "@/app/(dashboard)/hooks/projects/useProjects"; interface ProjectDropdownProps { @@ -13,6 +18,8 @@ interface ProjectDropdownProps { teamId?: string | null; } +const ALL = "__all__"; + const ProjectDropdown: React.FC = ({ projects, value, @@ -27,34 +34,27 @@ const ProjectDropdown: React.FC = ({ return ( ); };