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:
Cursor Agent 2026-04-23 17:17:07 +00:00
parent 8bea28b5d6
commit 459890c986
No known key found for this signature in database
2 changed files with 183 additions and 105 deletions

View file

@ -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",

View file

@ -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>