From 9fe16d4e29321a4b77a565028746afa04ad44827 Mon Sep 17 00:00:00 2001 From: Ishaan Jaffer Date: Fri, 23 Jan 2026 11:28:16 -0800 Subject: [PATCH] UI fixes --- ui/litellm-dashboard/src/app/page.tsx | 3 + .../policies/add_attachment_form.tsx | 78 ++-- .../components/policies/add_policy_form.tsx | 94 ++-- .../components/policies/attachment_table.tsx | 348 ++++++++++----- .../src/components/policies/index.tsx | 340 +++++++-------- .../src/components/policies/policy_info.tsx | 98 ++--- .../src/components/policies/policy_table.tsx | 405 ++++++++++++------ 7 files changed, 829 insertions(+), 537 deletions(-) diff --git a/ui/litellm-dashboard/src/app/page.tsx b/ui/litellm-dashboard/src/app/page.tsx index 8ca25eb9e4c..8ac1f756f96 100644 --- a/ui/litellm-dashboard/src/app/page.tsx +++ b/ui/litellm-dashboard/src/app/page.tsx @@ -14,6 +14,7 @@ import LoadingScreen from "@/components/common_components/LoadingScreen"; import { CostTrackingSettings } from "@/components/CostTrackingSettings"; import GeneralSettings from "@/components/general_settings"; import GuardrailsPanel from "@/components/guardrails"; +import PoliciesPanel from "@/components/policies"; import { Team } from "@/components/key_team_helpers/key_list"; import { MCPServers } from "@/components/mcp_tools"; import ModelHubTable from "@/components/AIHub/ModelHubTable"; @@ -472,6 +473,8 @@ export default function CreateKeyPage() { ) : page == "guardrails" ? ( + ) : page == "policies" ? ( + ) : page == "agents" ? ( ) : page == "prompts" ? ( diff --git a/ui/litellm-dashboard/src/components/policies/add_attachment_form.tsx b/ui/litellm-dashboard/src/components/policies/add_attachment_form.tsx index 37428c03d77..7b302333943 100644 --- a/ui/litellm-dashboard/src/components/policies/add_attachment_form.tsx +++ b/ui/litellm-dashboard/src/components/policies/add_attachment_form.tsx @@ -1,15 +1,11 @@ import React, { useState } from "react"; -import { - Modal, - Form, - Input, - Select, - Button, - Space, - Radio, - Divider, -} from "antd"; +import { Modal, Form, Select, Radio, Divider, Typography } from "antd"; +import { Button } from "@tremor/react"; import { Policy, PolicyAttachmentCreateRequest } from "./types"; +import { createPolicyAttachmentCall } from "../networking"; +import NotificationsManager from "../molecules/notifications_manager"; + +const { Text } = Typography; interface AddAttachmentFormProps { visible: boolean; @@ -17,7 +13,6 @@ interface AddAttachmentFormProps { onSuccess: () => void; accessToken: string | null; policies: Policy[]; - onCreateAttachment: (data: PolicyAttachmentCreateRequest) => Promise; } const AddAttachmentForm: React.FC = ({ @@ -26,17 +21,31 @@ const AddAttachmentForm: React.FC = ({ onSuccess, accessToken, policies, - onCreateAttachment, }) => { const [form] = Form.useForm(); const [isSubmitting, setIsSubmitting] = useState(false); const [scopeType, setScopeType] = useState<"global" | "specific">("global"); - const handleSubmit = async (values: any) => { - if (!accessToken) return; + const resetForm = () => { + form.resetFields(); + setScopeType("global"); + }; - setIsSubmitting(true); + const handleClose = () => { + resetForm(); + onClose(); + }; + + const handleSubmit = async () => { try { + setIsSubmitting(true); + await form.validateFields(); + const values = form.getFieldsValue(true); + + if (!accessToken) { + throw new Error("No access token available"); + } + const data: PolicyAttachmentCreateRequest = { policy_name: values.policy_name, }; @@ -55,13 +64,17 @@ const AddAttachmentForm: React.FC = ({ } } - await onCreateAttachment(data); + await createPolicyAttachmentCall(accessToken, data); + NotificationsManager.success("Attachment created successfully"); + + resetForm(); onSuccess(); onClose(); - form.resetFields(); - setScopeType("global"); } catch (error) { - console.error("Error creating attachment:", error); + console.error("Failed to create attachment:", error); + NotificationsManager.fromBackend( + "Failed to create attachment: " + (error instanceof Error ? error.message : String(error)) + ); } finally { setIsSubmitting(false); } @@ -76,14 +89,13 @@ const AddAttachmentForm: React.FC = ({
= ({ filterOption={(input, option) => (option?.label ?? "").toLowerCase().includes(input.toLowerCase()) } + style={{ width: "100%" }} /> - Scope + + Scope + = ({ mode="tags" placeholder="Enter team aliases (e.g., healthcare-team)" tokenSeparators={[","]} + style={{ width: "100%" }} /> @@ -138,6 +154,7 @@ const AddAttachmentForm: React.FC = ({ mode="tags" placeholder="Enter key aliases (e.g., dev-key-*)" tokenSeparators={[","]} + style={{ width: "100%" }} /> @@ -150,19 +167,20 @@ const AddAttachmentForm: React.FC = ({ mode="tags" placeholder="Enter model names (e.g., gpt-4, bedrock/*)" tokenSeparators={[","]} + style={{ width: "100%" }} /> )} - - - - - - +
+ + +
); diff --git a/ui/litellm-dashboard/src/components/policies/add_policy_form.tsx b/ui/litellm-dashboard/src/components/policies/add_policy_form.tsx index c49754bc8ef..0c14cac418b 100644 --- a/ui/litellm-dashboard/src/components/policies/add_policy_form.tsx +++ b/ui/litellm-dashboard/src/components/policies/add_policy_form.tsx @@ -1,20 +1,13 @@ import React, { useState, useEffect } from "react"; -import { - Modal, - Form, - Input, - Select, - Button, - Space, - Typography, - Divider, -} from "antd"; -import { PlusOutlined, MinusCircleOutlined } from "@ant-design/icons"; +import { Form, Select, Modal, Divider, Typography } from "antd"; +import { Button, TextInput, Textarea } from "@tremor/react"; import { Policy, PolicyCreateRequest, PolicyUpdateRequest } from "./types"; import { Guardrail } from "../guardrails/types"; +import { createPolicyCall, updatePolicyCall } from "../networking"; +import NotificationsManager from "../molecules/notifications_manager"; -const { TextArea } = Input; const { Text } = Typography; +const { Option } = Select; interface AddPolicyFormProps { visible: boolean; @@ -24,8 +17,6 @@ interface AddPolicyFormProps { editingPolicy?: Policy | null; existingPolicies: Policy[]; availableGuardrails: Guardrail[]; - onCreatePolicy: (data: PolicyCreateRequest) => Promise; - onUpdatePolicy: (policyId: string, data: PolicyUpdateRequest) => Promise; } const AddPolicyForm: React.FC = ({ @@ -36,8 +27,6 @@ const AddPolicyForm: React.FC = ({ editingPolicy, existingPolicies, availableGuardrails, - onCreatePolicy, - onUpdatePolicy, }) => { const [form] = Form.useForm(); const [isSubmitting, setIsSubmitting] = useState(false); @@ -59,11 +48,25 @@ const AddPolicyForm: React.FC = ({ } }, [visible, editingPolicy, form]); - const handleSubmit = async (values: any) => { - if (!accessToken) return; + const resetForm = () => { + form.resetFields(); + }; - setIsSubmitting(true); + const handleClose = () => { + resetForm(); + onClose(); + }; + + const handleSubmit = async () => { try { + setIsSubmitting(true); + await form.validateFields(); + const values = form.getFieldsValue(true); + + if (!accessToken) { + throw new Error("No access token available"); + } + const data: PolicyCreateRequest | PolicyUpdateRequest = { policy_name: values.policy_name, description: values.description || undefined, @@ -76,16 +79,21 @@ const AddPolicyForm: React.FC = ({ }; if (isEditing && editingPolicy) { - await onUpdatePolicy(editingPolicy.policy_id, data as PolicyUpdateRequest); + await updatePolicyCall(accessToken, editingPolicy.policy_id, data as PolicyUpdateRequest); + NotificationsManager.success("Policy updated successfully"); } else { - await onCreatePolicy(data as PolicyCreateRequest); + await createPolicyCall(accessToken, data as PolicyCreateRequest); + NotificationsManager.success("Policy created successfully"); } + resetForm(); onSuccess(); onClose(); - form.resetFields(); } catch (error) { - console.error("Error saving policy:", error); + console.error("Failed to save policy:", error); + NotificationsManager.fromBackend( + "Failed to save policy: " + (error instanceof Error ? error.message : String(error)) + ); } finally { setIsSubmitting(false); } @@ -107,14 +115,13 @@ const AddPolicyForm: React.FC = ({
= ({ }, ]} > - -