mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-04 02:31:27 +00:00
feat(ui): team/LoggingSettings to shadcn
- antd Divider/Tooltip + @ant-design InfoCircleOutlined + @tremor Button/Card/TextInput + @heroicons Plus/Trash/Cog/Ban \u2192 shadcn Button/Card/Input/Tooltip + lucide Ban/Info/Plus/Settings/Trash2. - antd Select preserved for mode='multiple' + optionLabelProp='label' + custom Option renders. - Card decoration='top' decorationColor='blue' \u2192 border-t-2 border-t-blue-500 (categorical accent, file added to .eslintrc). - Sensitive/Number chips + success/failure/blue dots keep categorical amber/emerald/red/blue palette. Gates: TS \u2713 | Build \u2713. Co-authored-by: yuneng-jiang <yuneng-berri@users.noreply.github.com>
This commit is contained in:
parent
8bea28b5d6
commit
459890c986
2 changed files with 183 additions and 105 deletions
|
|
@ -72,6 +72,7 @@
|
|||
"src/components/policies/policy_templates.tsx",
|
||||
"src/components/mcp_tools/mcp_server_view.tsx",
|
||||
"src/components/claude_code_plugins/plugin_info.tsx",
|
||||
"src/components/team/LoggingSettings.tsx",
|
||||
"src/components/permissions/VectorStorePermissions.tsx",
|
||||
"src/components/permissions/AgentPermissions.tsx",
|
||||
"src/components/permissions/MCPServerPermissions.tsx",
|
||||
|
|
|
|||
|
|
@ -1,11 +1,22 @@
|
|||
/* eslint-disable @next/next/no-img-element */
|
||||
/* eslint-disable react/no-unescaped-entities */
|
||||
import React from "react";
|
||||
import { Select, Tooltip, Divider } from "antd";
|
||||
import { InfoCircleOutlined } from "@ant-design/icons";
|
||||
import { Button, Card, TextInput } from "@tremor/react";
|
||||
import { PlusIcon, TrashIcon, CogIcon, BanIcon } from "@heroicons/react/outline";
|
||||
import { callbackInfo, callback_map, mapDisplayToInternalNames } from "../callback_info_helpers";
|
||||
import { Select } from "antd";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipProvider,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import { Ban, Info, Plus, Settings, Trash2 } from "lucide-react";
|
||||
import {
|
||||
callbackInfo,
|
||||
callback_map,
|
||||
mapDisplayToInternalNames,
|
||||
} from "../callback_info_helpers";
|
||||
import NumericalInput from "../shared/numerical_input";
|
||||
|
||||
const { Option } = Select;
|
||||
|
|
@ -61,7 +72,12 @@ const LoggingSettings: React.FC<LoggingSettingsProps> = ({
|
|||
handleChange(newValue);
|
||||
};
|
||||
|
||||
const updateLoggingConfig = (index: number, field: keyof LoggingConfig, newValue: any) => {
|
||||
const updateLoggingConfig = (
|
||||
index: number,
|
||||
field: keyof LoggingConfig,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
newValue: any,
|
||||
) => {
|
||||
const updatedConfigs = [...value];
|
||||
if (field === "callback_name") {
|
||||
// Convert display name to callback value and reset callback_vars when callback changes
|
||||
|
|
@ -105,47 +121,66 @@ const LoggingSettings: React.FC<LoggingSettingsProps> = ({
|
|||
if (Object.keys(dynamicParams).length === 0) return null;
|
||||
|
||||
return (
|
||||
<div className="mt-6 pt-4 border-t border-gray-100">
|
||||
<div className="mt-6 pt-4 border-t border-border">
|
||||
<div className="flex items-center space-x-2 mb-4">
|
||||
<div className="w-3 h-3 bg-blue-100 rounded-full flex items-center justify-center">
|
||||
<div className="w-3 h-3 bg-blue-100 dark:bg-blue-950/30 rounded-full flex items-center justify-center">
|
||||
<div className="w-1.5 h-1.5 bg-blue-500 rounded-full"></div>
|
||||
</div>
|
||||
<span className="text-sm font-medium text-gray-700">Integration Parameters</span>
|
||||
<span className="text-sm font-medium text-foreground">
|
||||
Integration Parameters
|
||||
</span>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 gap-4">
|
||||
{Object.entries(dynamicParams).map(([paramName, paramType]) => (
|
||||
<div key={paramName} className="space-y-2">
|
||||
<label className="text-sm font-medium text-gray-700 capitalize flex items-center space-x-1">
|
||||
<label className="text-sm font-medium text-foreground capitalize flex items-center space-x-1">
|
||||
<span>{paramName.replace(/_/g, " ")}</span>
|
||||
<Tooltip title={`Environment variable reference recommended: os.environ/${paramName.toUpperCase()}`}>
|
||||
<InfoCircleOutlined className="text-gray-400 cursor-help text-xs" />
|
||||
</Tooltip>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Info className="h-3 w-3 text-muted-foreground cursor-help" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
Environment variable reference recommended:
|
||||
os.environ/{paramName.toUpperCase()}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
{paramType === "password" && (
|
||||
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 text-yellow-800">
|
||||
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-amber-100 text-amber-800 dark:bg-amber-950/30 dark:text-amber-300">
|
||||
Sensitive
|
||||
</span>
|
||||
)}
|
||||
{paramType === "number" && (
|
||||
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 text-yellow-800">
|
||||
<span className="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-amber-100 text-amber-800 dark:bg-amber-950/30 dark:text-amber-300">
|
||||
Number
|
||||
</span>
|
||||
)}
|
||||
</label>
|
||||
{paramType === "number" && <span className="text-xs text-gray-500">Value must be between 0 and 1</span>}
|
||||
{paramType === "number" && (
|
||||
<span className="text-xs text-muted-foreground">
|
||||
Value must be between 0 and 1
|
||||
</span>
|
||||
)}
|
||||
{paramType === "number" ? (
|
||||
<NumericalInput
|
||||
step={0.01}
|
||||
width={400}
|
||||
placeholder={`os.environ/${paramName.toUpperCase()}`}
|
||||
value={config.callback_vars[paramName] || ""}
|
||||
onChange={(e: any) => updateCallbackVar(configIndex, paramName, e.target.value)}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
onChange={(e: any) =>
|
||||
updateCallbackVar(configIndex, paramName, e.target.value)
|
||||
}
|
||||
/>
|
||||
) : (
|
||||
<TextInput
|
||||
<Input
|
||||
type={paramType === "password" ? "password" : "text"}
|
||||
placeholder={`os.environ/${paramName.toUpperCase()}`}
|
||||
value={config.callback_vars[paramName] || ""}
|
||||
onChange={(e) => updateCallbackVar(configIndex, paramName, e.target.value)}
|
||||
onChange={(e) =>
|
||||
updateCallbackVar(configIndex, paramName, e.target.value)
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
|
|
@ -160,15 +195,27 @@ const LoggingSettings: React.FC<LoggingSettingsProps> = ({
|
|||
{/* Disabled Callbacks Section */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<BanIcon className="w-5 h-5 text-red-500" />
|
||||
<span className="text-base font-semibold text-gray-800">Disabled Callbacks</span>
|
||||
<Tooltip title="Select callbacks to disable for this key. Disabled callbacks will not receive any logging data.">
|
||||
<InfoCircleOutlined className="text-gray-400 cursor-help" />
|
||||
</Tooltip>
|
||||
<Ban className="w-5 h-5 text-destructive" />
|
||||
<span className="text-base font-semibold text-foreground">
|
||||
Disabled Callbacks
|
||||
</span>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Info className="h-4 w-4 text-muted-foreground cursor-help" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent className="max-w-xs">
|
||||
Select callbacks to disable for this key. Disabled callbacks
|
||||
will not receive any logging data.
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-gray-700">Disabled Callbacks</label>
|
||||
<label className="text-sm font-medium text-foreground">
|
||||
Disabled Callbacks
|
||||
</label>
|
||||
<Select
|
||||
mode="multiple"
|
||||
placeholder="Select callbacks to disable"
|
||||
|
|
@ -179,62 +226,72 @@ const LoggingSettings: React.FC<LoggingSettingsProps> = ({
|
|||
>
|
||||
{allCallbacks.map((callbackName) => {
|
||||
const logo = callbackInfo[callbackName]?.logo;
|
||||
const description = callbackInfo[callbackName]?.description;
|
||||
return (
|
||||
<Option key={callbackName} value={callbackName} label={callbackName}>
|
||||
<Tooltip title={description} placement="right">
|
||||
<div className="flex items-center space-x-2">
|
||||
{logo && (
|
||||
<img
|
||||
src={logo}
|
||||
alt={callbackName}
|
||||
className="w-4 h-4 object-contain"
|
||||
onError={(e) => {
|
||||
// Create a div with callback initial as fallback
|
||||
const target = e.target as HTMLImageElement;
|
||||
const parent = target.parentElement;
|
||||
if (parent) {
|
||||
const fallbackDiv = document.createElement("div");
|
||||
fallbackDiv.className =
|
||||
"w-4 h-4 rounded-full bg-gray-200 flex items-center justify-center text-xs";
|
||||
fallbackDiv.textContent = callbackName.charAt(0);
|
||||
parent.replaceChild(fallbackDiv, target);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<span>{callbackName}</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Option
|
||||
key={callbackName}
|
||||
value={callbackName}
|
||||
label={callbackName}
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
{logo && (
|
||||
<img
|
||||
src={logo}
|
||||
alt={callbackName}
|
||||
className="w-4 h-4 object-contain"
|
||||
onError={(e) => {
|
||||
const target = e.target as HTMLImageElement;
|
||||
const parent = target.parentElement;
|
||||
if (parent) {
|
||||
const fallbackDiv = document.createElement("div");
|
||||
fallbackDiv.className =
|
||||
"w-4 h-4 rounded-full bg-muted flex items-center justify-center text-xs";
|
||||
fallbackDiv.textContent = callbackName.charAt(0);
|
||||
parent.replaceChild(fallbackDiv, target);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<span>{callbackName}</span>
|
||||
</div>
|
||||
</Option>
|
||||
);
|
||||
})}
|
||||
</Select>
|
||||
<div className="text-xs text-gray-500">
|
||||
Select callbacks that should be disabled for this key. These callbacks will not receive any logging data.
|
||||
<div className="text-xs text-muted-foreground">
|
||||
Select callbacks that should be disabled for this key. These
|
||||
callbacks will not receive any logging data.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Divider />
|
||||
<hr className="my-4 border-border" />
|
||||
|
||||
{/* Logging Integrations Section */}
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex items-center space-x-2">
|
||||
<CogIcon className="w-5 h-5 text-blue-500" />
|
||||
<span className="text-base font-semibold text-gray-800">Logging Integrations</span>
|
||||
<Tooltip title="Configure callback logging integrations for this team.">
|
||||
<InfoCircleOutlined className="text-gray-400 cursor-help" />
|
||||
</Tooltip>
|
||||
<Settings className="w-5 h-5 text-blue-500" />
|
||||
<span className="text-base font-semibold text-foreground">
|
||||
Logging Integrations
|
||||
</span>
|
||||
<TooltipProvider>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Info className="h-4 w-4 text-muted-foreground cursor-help" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
Configure callback logging integrations for this team.
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={addLoggingConfig}
|
||||
icon={PlusIcon}
|
||||
size="sm"
|
||||
className="hover:border-blue-400 hover:text-blue-500"
|
||||
type="button"
|
||||
>
|
||||
<Plus className="h-4 w-4" />
|
||||
Add Integration
|
||||
</Button>
|
||||
</div>
|
||||
|
|
@ -249,67 +306,79 @@ const LoggingSettings: React.FC<LoggingSettingsProps> = ({
|
|||
return (
|
||||
<Card
|
||||
key={index}
|
||||
className="border border-gray-200 shadow-sm hover:shadow-md transition-shadow duration-200"
|
||||
decoration="top"
|
||||
decorationColor="blue"
|
||||
className="border border-border shadow-sm hover:shadow-md transition-shadow duration-200 p-4 border-t-2 border-t-blue-500"
|
||||
>
|
||||
<div className="flex justify-between items-start mb-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
{logoUrl && <img src={logoUrl} alt={callbackDisplayName} className="w-5 h-5 object-contain" />}
|
||||
<span className="text-sm font-medium">{callbackDisplayName || "New Integration"} Configuration</span>
|
||||
{logoUrl && (
|
||||
<img
|
||||
src={logoUrl}
|
||||
alt={callbackDisplayName}
|
||||
className="w-5 h-5 object-contain"
|
||||
/>
|
||||
)}
|
||||
<span className="text-sm font-medium">
|
||||
{callbackDisplayName || "New Integration"} Configuration
|
||||
</span>
|
||||
</div>
|
||||
<Button
|
||||
variant="light"
|
||||
variant="ghost"
|
||||
onClick={() => removeLoggingConfig(index)}
|
||||
icon={TrashIcon}
|
||||
size="xs"
|
||||
color="red"
|
||||
className="hover:bg-red-50"
|
||||
size="sm"
|
||||
className="text-destructive hover:text-destructive hover:bg-destructive/10"
|
||||
type="button"
|
||||
>
|
||||
<Trash2 className="h-3.5 w-3.5" />
|
||||
Remove
|
||||
</Button>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-gray-700">Integration Type</label>
|
||||
<label className="text-sm font-medium text-foreground">
|
||||
Integration Type
|
||||
</label>
|
||||
<Select
|
||||
value={callbackDisplayName}
|
||||
placeholder="Select integration"
|
||||
onChange={(value) => updateLoggingConfig(index, "callback_name", value)}
|
||||
onChange={(value) =>
|
||||
updateLoggingConfig(index, "callback_name", value)
|
||||
}
|
||||
className="w-full"
|
||||
optionLabelProp="label"
|
||||
>
|
||||
{supportedCallbacks.map((callbackName) => {
|
||||
const logo = callbackInfo[callbackName]?.logo;
|
||||
const description = callbackInfo[callbackName]?.description;
|
||||
return (
|
||||
<Option key={callbackName} value={callbackName} label={callbackName}>
|
||||
<Tooltip title={description} placement="right">
|
||||
<div className="flex items-center space-x-2">
|
||||
{logo && (
|
||||
<img
|
||||
src={logo}
|
||||
alt={callbackName}
|
||||
className="w-4 h-4 object-contain"
|
||||
onError={(e) => {
|
||||
// Create a div with callback initial as fallback
|
||||
const target = e.target as HTMLImageElement;
|
||||
const parent = target.parentElement;
|
||||
if (parent) {
|
||||
const fallbackDiv = document.createElement("div");
|
||||
fallbackDiv.className =
|
||||
"w-4 h-4 rounded-full bg-gray-200 flex items-center justify-center text-xs";
|
||||
fallbackDiv.textContent = callbackName.charAt(0);
|
||||
parent.replaceChild(fallbackDiv, target);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<span>{callbackName}</span>
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Option
|
||||
key={callbackName}
|
||||
value={callbackName}
|
||||
label={callbackName}
|
||||
>
|
||||
<div className="flex items-center space-x-2">
|
||||
{logo && (
|
||||
<img
|
||||
src={logo}
|
||||
alt={callbackName}
|
||||
className="w-4 h-4 object-contain"
|
||||
onError={(e) => {
|
||||
const target =
|
||||
e.target as HTMLImageElement;
|
||||
const parent = target.parentElement;
|
||||
if (parent) {
|
||||
const fallbackDiv =
|
||||
document.createElement("div");
|
||||
fallbackDiv.className =
|
||||
"w-4 h-4 rounded-full bg-muted flex items-center justify-center text-xs";
|
||||
fallbackDiv.textContent =
|
||||
callbackName.charAt(0);
|
||||
parent.replaceChild(fallbackDiv, target);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<span>{callbackName}</span>
|
||||
</div>
|
||||
</Option>
|
||||
);
|
||||
})}
|
||||
|
|
@ -317,15 +386,19 @@ const LoggingSettings: React.FC<LoggingSettingsProps> = ({
|
|||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-gray-700">Event Type</label>
|
||||
<label className="text-sm font-medium text-foreground">
|
||||
Event Type
|
||||
</label>
|
||||
<Select
|
||||
value={config.callback_type}
|
||||
onChange={(value) => updateLoggingConfig(index, "callback_type", value)}
|
||||
onChange={(value) =>
|
||||
updateLoggingConfig(index, "callback_type", value)
|
||||
}
|
||||
className="w-full"
|
||||
>
|
||||
<Option value="success">
|
||||
<div className="flex items-center space-x-2">
|
||||
<div className="w-2 h-2 bg-green-500 rounded-full"></div>
|
||||
<div className="w-2 h-2 bg-emerald-500 rounded-full"></div>
|
||||
<span>Success Only</span>
|
||||
</div>
|
||||
</Option>
|
||||
|
|
@ -353,10 +426,14 @@ const LoggingSettings: React.FC<LoggingSettingsProps> = ({
|
|||
</div>
|
||||
|
||||
{value.length === 0 && (
|
||||
<div className="text-center py-12 text-gray-500 border-2 border-dashed border-gray-200 rounded-lg bg-gray-50/50">
|
||||
<CogIcon className="w-12 h-12 text-gray-300 mb-3 mx-auto" />
|
||||
<div className="text-base font-medium mb-1">No logging integrations configured</div>
|
||||
<div className="text-sm text-gray-400">Click "Add Integration" to configure logging for this team</div>
|
||||
<div className="text-center py-12 text-muted-foreground border-2 border-dashed border-border rounded-lg bg-muted/50">
|
||||
<Settings className="w-12 h-12 text-muted-foreground/60 mb-3 mx-auto" />
|
||||
<div className="text-base font-medium mb-1">
|
||||
No logging integrations configured
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground/80">
|
||||
Click "Add Integration" to configure logging for this team
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue