/** * Routing Rules Table * Displays all routing rules with CRUD actions */ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alertDialog"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { ProviderIconType, RenderProviderIcon } from "@/lib/constants/icons"; import { getProviderLabel } from "@/lib/constants/logs"; import { getErrorMessage } from "@/lib/store"; import { useDeleteRoutingRuleMutation } from "@/lib/store/apis/routingRulesApi"; import { RoutingRule, RoutingTarget } from "@/lib/types/routingRules"; import { getPriorityBadgeClass, getScopeLabel, truncateCELExpression } from "@/lib/utils/routingRules"; import { ChevronLeft, ChevronRight, Edit, Search, Trash2 } from "lucide-react"; import { useState } from "react"; import { toast } from "sonner"; interface RoutingRulesTableProps { rules: RoutingRule[] | undefined; totalCount: number; isLoading: boolean; onEdit: (rule: RoutingRule) => void; onRowClick: (rule: RoutingRule) => void; /** When false, delete button is hidden and deletion is disabled (e.g. for read-only users). */ canDelete?: boolean; search: string; onSearchChange: (value: string) => void; offset: number; limit: number; onOffsetChange: (offset: number) => void; } export function RoutingRulesTable({ rules, totalCount, isLoading, onEdit, onRowClick, canDelete = false, search, onSearchChange, offset, limit, onOffsetChange, }: RoutingRulesTableProps) { const [deleteRuleId, setDeleteRuleId] = useState(null); const [deleteRoutingRule, { isLoading: isDeleting }] = useDeleteRoutingRuleMutation(); const handleDelete = async () => { if (!canDelete || !deleteRuleId) return; try { await deleteRoutingRule(deleteRuleId).unwrap(); toast.success("Routing rule deleted successfully"); setDeleteRuleId(null); } catch (error: any) { toast.error(getErrorMessage(error)); } }; if (isLoading) { return (
Name Targets Scope Priority Expression Status Actions {[...Array(5)].map((_, i) => (
))}
); } const sortedRules = rules ? [...rules].sort((a, b) => a.priority - b.priority) : []; const ruleToDelete = sortedRules.find((r) => r.id === deleteRuleId); return ( <> {/* Toolbar: Search */}
onSearchChange(e.target.value)} className="pl-9" data-testid="routing-rules-search-input" />
Name Targets Scope Priority Expression Status Actions {sortedRules.length === 0 ? ( No matching routing rules found. ) : ( sortedRules.map((rule) => ( onRowClick(rule)}>
{rule.name} {rule.description && ( {rule.description} )}
{getScopeLabel(rule.scope)}
{rule.priority}
{truncateCELExpression(rule.cel_expression)} {rule.enabled ? "Enabled" : "Disabled"} e.stopPropagation()}>
{canDelete && ( )}
)) )}
{/* Pagination */} {totalCount > 0 && (

Showing {offset + 1}-{Math.min(offset + limit, totalCount)} of {totalCount}

)} !open && setDeleteRuleId(null)}> Delete Routing Rule Are you sure you want to delete "{ruleToDelete?.name}"? This action cannot be undone. Cancel {isDeleting ? "Deleting..." : "Delete"} ); } function TargetsSummary({ targets }: { targets: RoutingTarget[] }) { if (!targets || targets.length === 0) { return -; } const first = targets[0]; const label = [first.provider ? getProviderLabel(first.provider) : "Any", first.model || "Any model"].join(" / "); return (
{first.provider && } {label}
{targets.length > 1 && ( +{targets.length - 1} more target{targets.length > 2 ? "s" : ""} )}
); }