refactor(ui/team): extract GuardrailSettingsView and reuse across team views

Pulls the Global / Team-specific subsection rendering out of
TeamInfo.tsx into a shared GuardrailSettingsView component with
card and inline variants, used on both the team Overview tab
(inside the existing Tremor Card) and the Team Settings tab read
view. The Global subsection header now carries a GlobalOutlined
icon, and since the icon is load-bearing the edit-form chip
coloring is simplified to a single blue instead of green/blue.
This commit is contained in:
Ryan Crabbe
2026-04-13 11:38:23 -07:00
parent 1dcccfc1d1
commit 84d7816bc9
2 changed files with 115 additions and 85 deletions
@@ -0,0 +1,97 @@
import React from "react";
import { Badge, Text } from "@tremor/react";
import { GlobalOutlined } from "@ant-design/icons";
interface GuardrailSettingsViewProps {
globalGuardrailNames: Set<string>;
teamGuardrails?: string[];
optedOutGlobalGuardrails?: string[];
killSwitchOn?: boolean;
variant?: "card" | "inline";
className?: string;
}
export function GuardrailSettingsView({
globalGuardrailNames,
teamGuardrails = [],
optedOutGlobalGuardrails = [],
killSwitchOn = false,
variant = "card",
className = "",
}: GuardrailSettingsViewProps) {
const optedOutSet = new Set(optedOutGlobalGuardrails);
const globalsRunning = Array.from(globalGuardrailNames).filter(
(n) => !optedOutSet.has(n),
);
const nonGlobalOptIns = teamGuardrails.filter(
(n) => !globalGuardrailNames.has(n),
);
const isEmpty =
!killSwitchOn && globalsRunning.length === 0 && nonGlobalOptIns.length === 0;
const content = isEmpty ? (
<Text className="text-gray-500">No guardrails configured</Text>
) : (
<div className="flex flex-col gap-4">
<div>
<Text className="text-sm font-medium text-gray-700 mb-2">
<GlobalOutlined style={{ marginInlineEnd: 4 }} aria-label="Global guardrail" />
Global
</Text>
{killSwitchOn ? (
<Badge color="yellow">Bypassed for this team</Badge>
) : globalsRunning.length > 0 ? (
<div className="flex flex-wrap gap-2">
{globalsRunning.map((name) => (
<Badge key={name} color="blue">
{name}
</Badge>
))}
</div>
) : (
<Text className="text-gray-500 text-sm">None configured</Text>
)}
</div>
<div>
<Text className="text-sm font-medium text-gray-700 mb-2">Team-specific</Text>
{nonGlobalOptIns.length > 0 ? (
<div className="flex flex-wrap gap-2">
{nonGlobalOptIns.map((name) => (
<Badge key={name} color="blue">
{name}
</Badge>
))}
</div>
) : (
<Text className="text-gray-500 text-sm">None configured</Text>
)}
</div>
</div>
);
if (variant === "card") {
return (
<div className={`bg-white border border-gray-200 rounded-lg p-6 ${className}`}>
<div className="flex items-center gap-2 mb-6">
<div>
<span className="block font-semibold text-gray-900">Guardrails Settings</span>
<span className="block text-xs text-gray-500">
Global and team-specific guardrails applied to this team
</span>
</div>
</div>
{content}
</div>
);
}
return (
<div className={`${className}`}>
<span className="block font-medium text-gray-900 mb-3">Guardrails Settings</span>
{content}
</div>
);
}
export default GuardrailSettingsView;
@@ -31,6 +31,7 @@ import DeleteResourceModal from "../common_components/DeleteResourceModal";
import DurationSelect from "../common_components/DurationSelect";
import PassThroughRoutesSelector from "../common_components/PassThroughRoutesSelector";
import { unfurlWildcardModelsInList } from "../key_team_helpers/fetch_available_models_team_key";
import GuardrailSettingsView from "../GuardrailSettingsView";
import LoggingSettingsView from "../logging_settings_view";
import MCPServerSelector from "../mcp_server_management/MCPServerSelector";
import MCPToolPermissions from "../mcp_server_management/MCPToolPermissions";
@@ -613,9 +614,6 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
const nonGlobalOptIns: string[] = (info.metadata?.guardrails || []).filter(
(n: string) => !globalGuardrailNames.has(n),
);
const globalsRunning: string[] = Array.from(globalGuardrailNames).filter(
(n) => !optedOutGlobals.has(n),
);
const effectiveGuardrails: string[] = initialKillSwitchOn
? nonGlobalOptIns
: [
@@ -632,7 +630,7 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
const isGlobal = globalGuardrailNames.has(value);
return (
<Tag
color={isGlobal ? "green" : "blue"}
color="blue"
closable={closable}
onClose={onClose}
onMouseDown={preventTagMouseDown}
@@ -772,45 +770,13 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
/>
<Card>
<Text className="font-semibold text-gray-900 mb-3">Guardrails</Text>
{!initialKillSwitchOn &&
globalsRunning.length === 0 &&
nonGlobalOptIns.length === 0 ? (
<Text className="text-gray-500">No guardrails configured</Text>
) : (
<div className="flex flex-col gap-3">
<div>
<Text className="text-sm font-medium text-gray-700 mb-2">Global</Text>
{initialKillSwitchOn ? (
<Badge color="yellow">Bypassed for this team</Badge>
) : globalsRunning.length > 0 ? (
<div className="flex flex-wrap gap-2">
{globalsRunning.map((name) => (
<Badge key={name} color="blue">
{name}
</Badge>
))}
</div>
) : (
<Text className="text-gray-500 text-sm">None running</Text>
)}
</div>
<div>
<Text className="text-sm font-medium text-gray-700 mb-2">Team-specific</Text>
{nonGlobalOptIns.length > 0 ? (
<div className="flex flex-wrap gap-2">
{nonGlobalOptIns.map((name) => (
<Badge key={name} color="blue">
{name}
</Badge>
))}
</div>
) : (
<Text className="text-gray-500 text-sm">None configured</Text>
)}
</div>
</div>
)}
<GuardrailSettingsView
globalGuardrailNames={globalGuardrailNames}
teamGuardrails={info.metadata?.guardrails || []}
optedOutGlobalGuardrails={info.metadata?.opted_out_global_guardrails || []}
killSwitchOn={initialKillSwitchOn}
variant="inline"
/>
</Card>
<Card>
@@ -1484,48 +1450,6 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
<Badge color={info.blocked ? "red" : "green"}>{info.blocked ? "Blocked" : "Active"}</Badge>
</div>
<div>
<Text className="font-medium">Guardrails</Text>
{!initialKillSwitchOn &&
globalsRunning.length === 0 &&
nonGlobalOptIns.length === 0 ? (
<Text className="text-gray-500">No guardrails configured</Text>
) : (
<div className="flex flex-col gap-3 mt-1">
<div>
<Text className="text-sm text-gray-700 mb-1">Global</Text>
{initialKillSwitchOn ? (
<Badge color="yellow">Bypassed for this team</Badge>
) : globalsRunning.length > 0 ? (
<div className="flex flex-wrap gap-2">
{globalsRunning.map((name) => (
<Badge key={name} color="blue">
{name}
</Badge>
))}
</div>
) : (
<Text className="text-gray-500 text-sm">None running</Text>
)}
</div>
<div>
<Text className="text-sm text-gray-700 mb-1">Team-specific</Text>
{nonGlobalOptIns.length > 0 ? (
<div className="flex flex-wrap gap-2">
{nonGlobalOptIns.map((name) => (
<Badge key={name} color="blue">
{name}
</Badge>
))}
</div>
) : (
<Text className="text-gray-500 text-sm">None configured</Text>
)}
</div>
</div>
)}
</div>
<ObjectPermissionsView
objectPermission={info.object_permission}
variant="inline"
@@ -1533,6 +1457,15 @@ const TeamInfoView: React.FC<TeamInfoProps> = ({
accessToken={accessToken}
/>
<GuardrailSettingsView
globalGuardrailNames={globalGuardrailNames}
teamGuardrails={info.metadata?.guardrails || []}
optedOutGlobalGuardrails={info.metadata?.opted_out_global_guardrails || []}
killSwitchOn={initialKillSwitchOn}
variant="inline"
className="pt-4 border-t border-gray-200"
/>
<LoggingSettingsView
loggingConfigs={info.metadata?.logging || []}
disabledCallbacks={[]}