feat(ui/): improve ux around adding new content filter categories

easy to skip adding a category, so make it a 1-click thing
This commit is contained in:
Krrish Dholakia 2026-02-14 13:24:10 -08:00
parent 472546289a
commit 1da6656f20
3 changed files with 83 additions and 6 deletions

View file

@ -109,6 +109,7 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
const [selectedPatterns, setSelectedPatterns] = useState<any[]>([]);
const [blockedWords, setBlockedWords] = useState<any[]>([]);
const [selectedContentCategories, setSelectedContentCategories] = useState<any[]>([]);
const [pendingCategorySelection, setPendingCategorySelection] = useState<string>("");
const [toolPermissionConfig, setToolPermissionConfig] = useState<ToolPermissionConfig>({
rules: [],
default_action: "deny",
@ -169,6 +170,12 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
setGlobalSeverityThreshold(2);
setCategorySpecificThresholds({});
// Reset Content Filter selections
setSelectedPatterns([]);
setBlockedWords([]);
setSelectedContentCategories([]);
setPendingCategorySelection("");
setToolPermissionConfig({
rules: [],
default_action: "deny",
@ -247,6 +254,39 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
setCurrentStep(currentStep - 1);
};
const handleAddAndContinue = () => {
if (!pendingCategorySelection || !guardrailSettings) return;
const contentFilterSettings = guardrailSettings.content_filter_settings;
if (!contentFilterSettings) return;
const category = contentFilterSettings.content_categories?.find((c) => c.name === pendingCategorySelection);
if (!category) return;
// Check if already added
if (selectedContentCategories.some((c) => c.category === pendingCategorySelection)) {
setPendingCategorySelection("");
setCurrentStep(currentStep + 1);
return;
}
// Add the category
setSelectedContentCategories([
...selectedContentCategories,
{
id: `category-${Date.now()}`,
category: category.name,
display_name: category.display_name,
action: category.default_action as "BLOCK" | "MASK",
severity_threshold: "medium",
},
]);
// Clear pending selection and advance to next step
setPendingCategorySelection("");
setCurrentStep(currentStep + 1);
};
const resetForm = () => {
form.resetFields();
setSelectedProvider(null);
@ -258,6 +298,7 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
setSelectedPatterns([]);
setBlockedWords([]);
setSelectedContentCategories([]);
setPendingCategorySelection("");
setToolPermissionConfig({
rules: [],
default_action: "deny",
@ -667,6 +708,8 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
selectedContentCategories.map((c) => (c.id === id ? { ...c, [field]: value } : c))
);
}}
pendingCategorySelection={pendingCategorySelection}
onPendingCategorySelectionChange={setPendingCategorySelection}
accessToken={accessToken}
showStep={step}
/>
@ -729,6 +772,8 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
const renderStepButtons = () => {
const totalSteps = shouldRenderContentFilterConfigSettings(selectedProvider) ? 4 : 2;
const isLastStep = currentStep === totalSteps - 1;
const isCategoriesStep = shouldRenderContentFilterConfigSettings(selectedProvider) && currentStep === 1;
const hasPendingCategory = pendingCategorySelection !== "";
return (
<div className="flex justify-end space-x-2 mt-4">
@ -737,11 +782,30 @@ const AddGuardrailForm: React.FC<AddGuardrailFormProps> = ({ visible, onClose, a
Previous
</Button>
)}
{!isLastStep && <Button type="primary" onClick={nextStep}>Next</Button>}
{isLastStep && (
<Button type="primary" onClick={handleSubmit} loading={loading}>
Create Guardrail
</Button>
{isCategoriesStep ? (
<>
<Button onClick={nextStep}>
Skip
</Button>
<Button
type="primary"
onClick={handleAddAndContinue}
disabled={!hasPendingCategory}
>
Add & Continue →
</Button>
</>
) : (
<>
{!isLastStep && (
<Button type="primary" onClick={nextStep}>Next</Button>
)}
{isLastStep && (
<Button type="primary" onClick={handleSubmit} loading={loading}>
Create Guardrail
</Button>
)}
</>
)}
<Button onClick={handleClose}>
Cancel

View file

@ -28,6 +28,8 @@ interface ContentCategoryConfigurationProps {
onCategoryRemove: (id: string) => void;
onCategoryUpdate: (id: string, field: string, value: any) => void;
accessToken?: string | null;
pendingSelection?: string;
onPendingSelectionChange?: (value: string) => void;
}
const ContentCategoryConfiguration: React.FC<ContentCategoryConfigurationProps> = ({
@ -37,8 +39,13 @@ const ContentCategoryConfiguration: React.FC<ContentCategoryConfigurationProps>
onCategoryRemove,
onCategoryUpdate,
accessToken,
pendingSelection,
onPendingSelectionChange,
}) => {
const [selectedCategoryName, setSelectedCategoryName] = React.useState<string>("");
// Use controlled state if parent provides it, otherwise use local state
const [localSelectedCategoryName, setLocalSelectedCategoryName] = React.useState<string>("");
const selectedCategoryName = pendingSelection !== undefined ? pendingSelection : localSelectedCategoryName;
const setSelectedCategoryName = onPendingSelectionChange || setLocalSelectedCategoryName;
const [categoryYaml, setCategoryYaml] = React.useState<{ [key: string]: string }>({});
const [categoryFileTypes, setCategoryFileTypes] = React.useState<{ [key: string]: string }>({});
const [loadingYaml, setLoadingYaml] = React.useState<{ [key: string]: boolean }>({});

View file

@ -69,6 +69,8 @@ interface ContentFilterConfigurationProps {
onContentCategoryAdd?: (category: SelectedContentCategory) => void;
onContentCategoryRemove?: (id: string) => void;
onContentCategoryUpdate?: (id: string, field: string, value: any) => void;
pendingCategorySelection?: string;
onPendingCategorySelectionChange?: (value: string) => void;
}
const ContentFilterConfiguration: React.FC<ContentFilterConfigurationProps> = ({
@ -90,6 +92,8 @@ const ContentFilterConfiguration: React.FC<ContentFilterConfigurationProps> = ({
onContentCategoryAdd,
onContentCategoryRemove,
onContentCategoryUpdate,
pendingCategorySelection,
onPendingCategorySelectionChange,
}) => {
const [patternModalVisible, setPatternModalVisible] = useState(false);
const [keywordModalVisible, setKeywordModalVisible] = useState(false);
@ -278,6 +282,8 @@ const ContentFilterConfiguration: React.FC<ContentFilterConfigurationProps> = ({
onCategoryRemove={onContentCategoryRemove}
onCategoryUpdate={onContentCategoryUpdate}
accessToken={accessToken}
pendingSelection={pendingCategorySelection}
onPendingSelectionChange={onPendingCategorySelectionChange}
/>
)}