mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-04 02:31:27 +00:00
feat(ui): another small batch \u2014 router settings, survey, team/model tabs
- LatencyBasedConfiguration / ReliabilityRetriesSection: antd Input \u2192 shadcn Input. Semantic tokens for labels/muted text. - survey/ClaudeCodeModal: antd Button with inline color style \u2192 shadcn Button with purple-600 bg class. File added to overrides for the intentional purple feedback-form accent. - PriceDataManagementTab / TeamsHeaderTabs: still rely on tremor TabPanel/TabGroup inside a cross-cutting caller, so tremor imports preserved with inline eslint-disable. Migrated all @heroicons RefreshIcon \u2192 lucide RefreshCw. - Navbar/WorkerDropdown: antd Select + @ant-design CloudServer \u2192 shadcn Select. Gates: TS \u2713 | Build \u2713. Co-authored-by: yuneng-jiang <yuneng-berri@users.noreply.github.com>
This commit is contained in:
parent
30f6ef0d95
commit
948c80da15
7 changed files with 101 additions and 64 deletions
|
|
@ -54,6 +54,7 @@
|
|||
"src/components/permissions/VectorStorePermissions.tsx",
|
||||
"src/components/permissions/AgentPermissions.tsx",
|
||||
"src/components/permissions/MCPServerPermissions.tsx",
|
||||
"src/components/survey/ClaudeCodeModal.tsx",
|
||||
"src/components/policies/impact_preview_alert.tsx",
|
||||
"src/components/policies/impact_popover.tsx",
|
||||
"src/components/DeletedKeysPage/DeletedKeysPage.tsx",
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { TabPanel, Text, Title } from "@tremor/react";
|
||||
// eslint-disable-next-line litellm-ui/no-banned-ui-imports
|
||||
import { TabPanel } from "@tremor/react";
|
||||
import PriceDataReload from "@/components/price_data_reload";
|
||||
import React from "react";
|
||||
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
|
||||
|
|
@ -12,10 +13,12 @@ const PriceDataManagementTab = () => {
|
|||
<TabPanel>
|
||||
<div className="p-6">
|
||||
<div className="mb-6">
|
||||
<Title>Price Data Management</Title>
|
||||
<Text className="text-tremor-content">
|
||||
<h2 className="text-lg font-semibold text-foreground">
|
||||
Price Data Management
|
||||
</h2>
|
||||
<p className="text-muted-foreground">
|
||||
Manage model pricing data and configure automatic reload schedules
|
||||
</Text>
|
||||
</p>
|
||||
</div>
|
||||
<PriceDataReload
|
||||
accessToken={accessToken}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
// eslint-disable-next-line litellm-ui/no-banned-ui-imports
|
||||
import { Icon, Tab, TabGroup, TabList, TabPanels, Text } from "@tremor/react";
|
||||
import { isAdminRole } from "@/utils/roles";
|
||||
import { RefreshIcon } from "@heroicons/react/outline";
|
||||
import { RefreshCw } from "lucide-react";
|
||||
import React from "react";
|
||||
|
||||
type TeamsHeaderTabsProps = {
|
||||
|
|
@ -10,7 +11,12 @@ type TeamsHeaderTabsProps = {
|
|||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
const TeamsHeaderTabs = ({ lastRefreshed, onRefresh, userRole, children }: TeamsHeaderTabsProps) => {
|
||||
const TeamsHeaderTabs = ({
|
||||
lastRefreshed,
|
||||
onRefresh,
|
||||
userRole,
|
||||
children,
|
||||
}: TeamsHeaderTabsProps) => {
|
||||
return (
|
||||
<TabGroup className="gap-2 h-[75vh] w-full">
|
||||
<TabList className="flex justify-between mt-2 w-full items-center">
|
||||
|
|
@ -22,7 +28,7 @@ const TeamsHeaderTabs = ({ lastRefreshed, onRefresh, userRole, children }: Teams
|
|||
<div className="flex items-center space-x-2">
|
||||
{lastRefreshed && <Text>Last Refreshed: {lastRefreshed}</Text>}
|
||||
<Icon
|
||||
icon={RefreshIcon} // Modify as necessary for correct icon name
|
||||
icon={RefreshCw}
|
||||
variant="shadow"
|
||||
size="xs"
|
||||
className="self-center"
|
||||
|
|
|
|||
|
|
@ -1,8 +1,13 @@
|
|||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { Select } from "antd";
|
||||
import { CloudServerOutlined } from "@ant-design/icons";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { useWorker } from "@/hooks/useWorker";
|
||||
|
||||
interface WorkerDropdownProps {
|
||||
|
|
@ -16,22 +21,24 @@ const WorkerDropdown: React.FC<WorkerDropdownProps> = ({ onWorkerSwitch }) => {
|
|||
|
||||
return (
|
||||
<Select
|
||||
showSearch
|
||||
filterOption={(input, option) =>
|
||||
(option?.label as string ?? "").toLowerCase().includes(input.toLowerCase())
|
||||
}
|
||||
value={selectedWorker.worker_id}
|
||||
style={{ minWidth: 180 }}
|
||||
suffixIcon={<CloudServerOutlined />}
|
||||
options={workers.map((w) => ({
|
||||
label: w.name,
|
||||
value: w.worker_id,
|
||||
disabled: w.worker_id === selectedWorker.worker_id,
|
||||
}))}
|
||||
onChange={(newWorkerId) => {
|
||||
onWorkerSwitch(newWorkerId);
|
||||
}}
|
||||
/>
|
||||
onValueChange={onWorkerSwitch}
|
||||
>
|
||||
<SelectTrigger className="min-w-[180px]">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{workers.map((w) => (
|
||||
<SelectItem
|
||||
key={w.worker_id}
|
||||
value={w.worker_id}
|
||||
disabled={w.worker_id === selectedWorker.worker_id}
|
||||
>
|
||||
{w.name}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import { Input } from "antd";
|
||||
import { Input } from "@/components/ui/input";
|
||||
|
||||
interface routingStrategyArgs {
|
||||
ttl?: number;
|
||||
|
|
@ -12,6 +12,7 @@ const defaultLowestLatencyArgs: routingStrategyArgs = {
|
|||
};
|
||||
|
||||
interface LatencyBasedConfigurationProps {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
routingStrategyArgs: { [key: string]: any };
|
||||
}
|
||||
|
||||
|
|
@ -28,23 +29,33 @@ const LatencyBasedConfiguration: React.FC<LatencyBasedConfigurationProps> = ({
|
|||
<>
|
||||
<div className="space-y-6">
|
||||
<div className="max-w-3xl">
|
||||
<h3 className="text-sm font-medium text-gray-900">Latency-Based Configuration</h3>
|
||||
<p className="text-xs text-gray-500 mt-1">Fine-tune latency-based routing behavior</p>
|
||||
<h3 className="text-sm font-medium text-foreground">
|
||||
Latency-Based Configuration
|
||||
</h3>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Fine-tune latency-based routing behavior
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="grid grid-cols-1 gap-6 lg:grid-cols-2 xl:grid-cols-3">
|
||||
{Object.entries(routingStrategyArgs || defaultLowestLatencyArgs).map(([param, value]) => (
|
||||
{Object.entries(
|
||||
routingStrategyArgs || defaultLowestLatencyArgs,
|
||||
).map(([param, value]) => (
|
||||
<div key={param} className="space-y-2">
|
||||
<label className="block">
|
||||
<span className="text-xs font-medium text-gray-700 uppercase tracking-wide">
|
||||
<span className="text-xs font-medium text-foreground uppercase tracking-wide">
|
||||
{param.replace(/_/g, " ")}
|
||||
</span>
|
||||
<p className="text-xs text-gray-500 mt-0.5 mb-2">
|
||||
<p className="text-xs text-muted-foreground mt-0.5 mb-2">
|
||||
{paramExplanation[param] || ""}
|
||||
</p>
|
||||
<Input
|
||||
name={param}
|
||||
defaultValue={typeof value === "object" ? JSON.stringify(value, null, 2) : value?.toString()}
|
||||
defaultValue={
|
||||
typeof value === "object"
|
||||
? JSON.stringify(value, null, 2)
|
||||
: value?.toString()
|
||||
}
|
||||
className="font-mono text-sm w-full"
|
||||
/>
|
||||
</label>
|
||||
|
|
@ -52,11 +63,10 @@ const LatencyBasedConfiguration: React.FC<LatencyBasedConfigurationProps> = ({
|
|||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-gray-200" />
|
||||
|
||||
<div className="border-t border-border" />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LatencyBasedConfiguration;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
import React from "react";
|
||||
import { Input } from "antd";
|
||||
import { Input } from "@/components/ui/input";
|
||||
|
||||
interface ReliabilityRetriesSectionProps {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
routerSettings: { [key: string]: any };
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
routerFieldsMetadata: { [key: string]: any };
|
||||
}
|
||||
|
||||
|
|
@ -13,14 +15,18 @@ const ReliabilityRetriesSection: React.FC<ReliabilityRetriesSectionProps> = ({
|
|||
return (
|
||||
<div className="space-y-6">
|
||||
<div className="max-w-3xl">
|
||||
<h3 className="text-sm font-medium text-gray-900">Reliability & Retries</h3>
|
||||
<p className="text-xs text-gray-500 mt-1">Configure retry logic and failure handling</p>
|
||||
<h3 className="text-sm font-medium text-foreground">
|
||||
Reliability & Retries
|
||||
</h3>
|
||||
<p className="text-xs text-muted-foreground mt-1">
|
||||
Configure retry logic and failure handling
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-6 lg:grid-cols-2 xl:grid-cols-3">
|
||||
{Object.entries(routerSettings)
|
||||
.filter(
|
||||
([param, value]) =>
|
||||
([param]) =>
|
||||
param != "fallbacks" &&
|
||||
param != "context_window_fallbacks" &&
|
||||
param != "routing_strategy_args" &&
|
||||
|
|
@ -30,10 +36,10 @@ const ReliabilityRetriesSection: React.FC<ReliabilityRetriesSectionProps> = ({
|
|||
.map(([param, value]) => (
|
||||
<div key={param} className="space-y-2">
|
||||
<label className="block">
|
||||
<span className="text-xs font-medium text-gray-700 uppercase tracking-wide">
|
||||
<span className="text-xs font-medium text-foreground uppercase tracking-wide">
|
||||
{routerFieldsMetadata[param]?.ui_field_name || param}
|
||||
</span>
|
||||
<p className="text-xs text-gray-500 mt-0.5 mb-2">
|
||||
<p className="text-xs text-muted-foreground mt-0.5 mb-2">
|
||||
{routerFieldsMetadata[param]?.field_description || ""}
|
||||
</p>
|
||||
<Input
|
||||
|
|
@ -57,4 +63,3 @@ const ReliabilityRetriesSection: React.FC<ReliabilityRetriesSectionProps> = ({
|
|||
};
|
||||
|
||||
export default ReliabilityRetriesSection;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import React from "react";
|
||||
import { X, Code, ExternalLink } from "lucide-react";
|
||||
import { Button } from "antd";
|
||||
import { Button } from "@/components/ui/button";
|
||||
|
||||
interface ClaudeCodeModalProps {
|
||||
isOpen: boolean;
|
||||
|
|
@ -10,7 +10,11 @@ interface ClaudeCodeModalProps {
|
|||
|
||||
const GOOGLE_FORM_URL = "https://forms.gle/LZeJQ3XytBakckYa9";
|
||||
|
||||
export function ClaudeCodeModal({ isOpen, onClose, onComplete }: ClaudeCodeModalProps) {
|
||||
export function ClaudeCodeModal({
|
||||
isOpen,
|
||||
onClose,
|
||||
onComplete,
|
||||
}: ClaudeCodeModalProps) {
|
||||
if (!isOpen) return null;
|
||||
|
||||
const handleOpenForm = () => {
|
||||
|
|
@ -20,45 +24,47 @@ export function ClaudeCodeModal({ isOpen, onClose, onComplete }: ClaudeCodeModal
|
|||
|
||||
return (
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 sm:p-6">
|
||||
{/* Backdrop */}
|
||||
<div className="fixed inset-0 bg-black/40 backdrop-blur-sm" onClick={onClose} />
|
||||
<div
|
||||
className="fixed inset-0 bg-black/40 backdrop-blur-sm"
|
||||
onClick={onClose}
|
||||
/>
|
||||
|
||||
{/* Modal */}
|
||||
<div className="relative w-full max-w-md bg-white rounded-xl shadow-2xl overflow-hidden transform transition-all duration-300 ease-out">
|
||||
{/* Header */}
|
||||
<div className="px-6 py-4 border-b border-gray-100 flex items-center justify-between bg-gray-50/50">
|
||||
<div className="flex items-center gap-2 text-purple-600">
|
||||
<div className="relative w-full max-w-md bg-background rounded-xl shadow-2xl overflow-hidden transform transition-all duration-300 ease-out">
|
||||
<div className="px-6 py-4 border-b border-border flex items-center justify-between bg-muted/50">
|
||||
<div className="flex items-center gap-2 text-purple-600 dark:text-purple-400">
|
||||
<Code className="h-5 w-5" />
|
||||
<span className="font-semibold text-sm tracking-wide uppercase">Claude Code Feedback</span>
|
||||
<span className="font-semibold text-sm tracking-wide uppercase">
|
||||
Claude Code Feedback
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-gray-400 hover:text-gray-600 transition-colors p-1 rounded-full hover:bg-gray-100"
|
||||
className="text-muted-foreground hover:text-foreground transition-colors p-1 rounded-full hover:bg-accent"
|
||||
aria-label="Close"
|
||||
>
|
||||
<X className="h-5 w-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="p-8">
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-4">
|
||||
<h2 className="text-2xl font-bold text-foreground mb-4">
|
||||
Help us improve your experience
|
||||
</h2>
|
||||
<p className="text-gray-600 mb-6">
|
||||
We'd love to hear about your experience using LiteLLM with Claude Code. Your feedback helps us improve the product for everyone.
|
||||
<p className="text-muted-foreground mb-6">
|
||||
We'd love to hear about your experience using LiteLLM with
|
||||
Claude Code. Your feedback helps us improve the product for
|
||||
everyone.
|
||||
</p>
|
||||
<p className="text-sm text-gray-500 mb-6">
|
||||
<p className="text-sm text-muted-foreground mb-6">
|
||||
This brief survey takes about 2-3 minutes to complete.
|
||||
</p>
|
||||
|
||||
<Button
|
||||
type="primary"
|
||||
size="large"
|
||||
block
|
||||
size="lg"
|
||||
className="w-full bg-purple-600 hover:bg-purple-700 text-white"
|
||||
onClick={handleOpenForm}
|
||||
icon={<ExternalLink className="h-4 w-4" />}
|
||||
style={{ backgroundColor: '#7c3aed', borderColor: '#7c3aed' }}
|
||||
>
|
||||
<ExternalLink className="h-4 w-4" />
|
||||
Open Feedback Form
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -66,4 +72,3 @@ export function ClaudeCodeModal({ isOpen, onClose, onComplete }: ClaudeCodeModal
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue