diff --git a/ui/litellm-dashboard/.eslintrc.json b/ui/litellm-dashboard/.eslintrc.json index 760b215c757..80d97495c0f 100644 --- a/ui/litellm-dashboard/.eslintrc.json +++ b/ui/litellm-dashboard/.eslintrc.json @@ -69,6 +69,7 @@ "src/components/model_add/credentials.tsx", "src/components/model_dashboard/health_check_columns.tsx", "src/components/UsagePage/components/EntityUsage/TopKeyView.tsx", + "src/components/policies/policy_templates.tsx", "src/components/permissions/VectorStorePermissions.tsx", "src/components/permissions/AgentPermissions.tsx", "src/components/permissions/MCPServerPermissions.tsx", diff --git a/ui/litellm-dashboard/src/components/policies/policy_templates.tsx b/ui/litellm-dashboard/src/components/policies/policy_templates.tsx index c4ea22651cf..8ee72f32195 100644 --- a/ui/litellm-dashboard/src/components/policies/policy_templates.tsx +++ b/ui/litellm-dashboard/src/components/policies/policy_templates.tsx @@ -1,19 +1,23 @@ import React, { useState, useEffect, useMemo } from "react"; -import { Card, Button, Spin, Checkbox } from "antd"; +import { Card } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Checkbox } from "@/components/ui/checkbox"; +import { cn } from "@/lib/utils"; import MessageManager from "@/components/molecules/message_manager"; import { - ShieldCheckIcon, - ShieldExclamationIcon, - BeakerIcon, - CurrencyDollarIcon, - CheckCircleIcon, -} from "@heroicons/react/outline"; + Beaker, + CheckCircle, + DollarSign, + Loader2, + Shield, + ShieldAlert, +} from "lucide-react"; import { getPolicyTemplates } from "../networking"; interface PolicyTemplateCardProps { title: string; description: string; - icon: React.ComponentType>; + icon: React.ComponentType<{ className?: string }>; iconColor: string; iconBg: string; guardrails: string[]; @@ -38,39 +42,41 @@ const PolicyTemplateCard: React.FC = ({ const getComplexityStyle = () => { switch (complexity) { case "Low": - return "bg-gray-50 text-gray-600 border-gray-200"; + return "bg-muted text-muted-foreground border-border"; case "Medium": - return "bg-blue-50 text-blue-600 border-blue-100"; + return "bg-blue-50 text-blue-600 border-blue-100 dark:bg-blue-950/30 dark:text-blue-300 dark:border-blue-900"; case "High": - return "bg-purple-50 text-purple-600 border-purple-100"; + return "bg-purple-50 text-purple-600 border-purple-100 dark:bg-purple-950/30 dark:text-purple-300 dark:border-purple-900"; } }; return ( - +
-
- +
+
{complexity} Complexity
-

{title}

-

{description}

+

{title}

+

+ {description} +

{tags.length > 0 && (
{tags.map((tag) => ( {tag} @@ -80,22 +86,22 @@ const PolicyTemplateCard: React.FC = ({ {inherits && (
- Inherits from: - + Inherits from: + {inherits}
)}
- + Included Guardrails
{guardrails.map((g) => ( {g} @@ -103,12 +109,7 @@ const PolicyTemplateCard: React.FC = ({
- @@ -116,27 +117,34 @@ const PolicyTemplateCard: React.FC = ({ }; interface PolicyTemplatesProps { + // eslint-disable-next-line @typescript-eslint/no-explicit-any onUseTemplate: (templateData: any) => void; onOpenAiSuggestion: () => void; + // eslint-disable-next-line @typescript-eslint/no-explicit-any onTemplatesLoaded?: (templates: any[]) => void; accessToken: string | null; } -// Map icon names from JSON to actual icon components -const iconMap: Record>> = { - ShieldCheckIcon: ShieldCheckIcon, - ShieldExclamationIcon: ShieldExclamationIcon, - BeakerIcon: BeakerIcon, - CurrencyDollarIcon: CurrencyDollarIcon, - CheckCircleIcon: CheckCircleIcon, +// Map icon names from JSON to lucide icon components. +const iconMap: Record> = { + ShieldCheckIcon: Shield, + ShieldExclamationIcon: ShieldAlert, + BeakerIcon: Beaker, + CurrencyDollarIcon: DollarSign, + CheckCircleIcon: CheckCircle, }; -const PolicyTemplates: React.FC = ({ onUseTemplate, onOpenAiSuggestion, onTemplatesLoaded, accessToken }) => { +const PolicyTemplates: React.FC = ({ + onUseTemplate, + onOpenAiSuggestion, + onTemplatesLoaded, + accessToken, +}) => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any const [templates, setTemplates] = useState([]); const [isLoading, setIsLoading] = useState(false); const [selectedTags, setSelectedTags] = useState>(new Set()); - // Compute all unique tags with counts const tagCounts = useMemo(() => { const counts: Record = {}; templates.forEach((t) => { @@ -145,16 +153,16 @@ const PolicyTemplates: React.FC = ({ onUseTemplate, onOpen counts[tag] = (counts[tag] || 0) + 1; }); }); - // Sort alphabetically return Object.entries(counts).sort(([a], [b]) => a.localeCompare(b)); }, [templates]); - // Filter templates: show templates that have ALL selected tags (AND logic) const filteredTemplates = useMemo(() => { if (selectedTags.size === 0) return templates; return templates.filter((t) => { const tags: string[] = t.tags || []; - return Array.from(selectedTags).every((selectedTag) => tags.includes(selectedTag)); + return Array.from(selectedTags).every((selectedTag) => + tags.includes(selectedTag), + ); }); }, [templates, selectedTags]); @@ -192,12 +200,16 @@ const PolicyTemplates: React.FC = ({ onUseTemplate, onOpen }; fetchTemplates(); + // eslint-disable-next-line react-hooks/exhaustive-deps }, [accessToken]); if (isLoading) { return ( -
- +
+ + + Loading policy templates... +
); } @@ -206,16 +218,16 @@ const PolicyTemplates: React.FC = ({ onUseTemplate, onOpen
-

+

Policy Templates

-

+

Start with a pre-configured policy template to quickly set up guardrails for your organization.

@@ -248,20 +261,21 @@ const PolicyTemplates: React.FC = ({ onUseTemplate, onOpen {tagCounts.map(([tag, count]) => ( @@ -274,7 +288,7 @@ const PolicyTemplates: React.FC = ({ onUseTemplate, onOpen {/* Right content - template cards */}
{selectedTags.size > 0 && ( -
+
Showing {filteredTemplates.length} of {templates.length} templates
)} @@ -284,7 +298,7 @@ const PolicyTemplates: React.FC = ({ onUseTemplate, onOpen key={template.id || index} title={template.title} description={template.description} - icon={iconMap[template.icon] || ShieldCheckIcon} + icon={iconMap[template.icon] || Shield} iconColor={template.iconColor} iconBg={template.iconBg} guardrails={template.guardrails} @@ -297,11 +311,12 @@ const PolicyTemplates: React.FC = ({ onUseTemplate, onOpen
{filteredTemplates.length === 0 && ( -
+

No templates match the selected filters.