From 31c3be292a73dc4852e32edeccb9bbf81ae50c66 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 23 Apr 2026 09:09:07 +0000 Subject: [PATCH] feat(ui): migrate model_filters to shadcn - @tremor Card/Text \u2192 shadcn Card + plain + Tailwind labels. - Native kept (lightweight, no Radix portal in this filter bar). Replace 'border-gray-*'/text-gray-*'/text-blue-* raw classes with semantic tokens (border-input/text-muted-foreground/ text-primary). Gates: TS \u2713 | Build \u2713. Co-authored-by: yuneng-jiang --- .../src/components/model_filters.tsx | 97 +++++++++---------- 1 file changed, 44 insertions(+), 53 deletions(-) diff --git a/ui/litellm-dashboard/src/components/model_filters.tsx b/ui/litellm-dashboard/src/components/model_filters.tsx index 5b68d587599..19cb2d7fd94 100644 --- a/ui/litellm-dashboard/src/components/model_filters.tsx +++ b/ui/litellm-dashboard/src/components/model_filters.tsx @@ -1,5 +1,6 @@ import React, { useState, useEffect, useMemo, useRef } from "react"; -import { Card, Text } from "@tremor/react"; +import { Card } from "@/components/ui/card"; +import { cn } from "@/lib/utils"; interface ModelGroupInfo { model_group: string; @@ -16,6 +17,7 @@ interface ModelGroupInfo { supports_function_calling: boolean; supported_openai_params?: string[]; is_public_model_group: boolean; + // eslint-disable-next-line @typescript-eslint/no-explicit-any [key: string]: any; } @@ -38,19 +40,16 @@ const ModelFilters: React.FC = ({ const [selectedFeature, setSelectedFeature] = useState(""); const previousFilteredDataRef = useRef([]); - // Helper functions to get unique values const getUniqueProviders = (data: ModelGroupInfo[]) => { const providers = new Set(); - data.forEach((model) => { - model.providers.forEach((provider) => providers.add(provider)); - }); + data.forEach((m) => m.providers.forEach((p) => providers.add(p))); return Array.from(providers); }; const getUniqueModes = (data: ModelGroupInfo[]) => { const modes = new Set(); - data.forEach((model) => { - if (model.mode) modes.add(model.mode); + data.forEach((m) => { + if (m.mode) modes.add(m.mode); }); return Array.from(modes); }; @@ -64,7 +63,7 @@ const ModelFilters: React.FC = ({ const featureName = key .replace(/^supports_/, "") .split("_") - .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .map((w) => w.charAt(0).toUpperCase() + w.slice(1)) .join(" "); features.add(featureName); }); @@ -72,47 +71,51 @@ const ModelFilters: React.FC = ({ return Array.from(features).sort(); }; - // Memoized filtered data const filteredData = useMemo(() => { return ( modelHubData?.filter((model) => { - const matchesSearch = model.model_group.toLowerCase().includes(searchTerm.toLowerCase()); - const matchesProvider = selectedProvider === "" || model.providers.includes(selectedProvider); + const matchesSearch = model.model_group + .toLowerCase() + .includes(searchTerm.toLowerCase()); + const matchesProvider = + selectedProvider === "" || + model.providers.includes(selectedProvider); const matchesMode = selectedMode === "" || model.mode === selectedMode; - - // Check if model has the selected feature const matchesFeature = selectedFeature === "" || Object.entries(model) - .filter(([key, value]) => key.startsWith("supports_") && value === true) + .filter( + ([key, value]) => key.startsWith("supports_") && value === true, + ) .some(([key]) => { const featureName = key .replace(/^supports_/, "") .split("_") - .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .map((w) => w.charAt(0).toUpperCase() + w.slice(1)) .join(" "); return featureName === selectedFeature; }); - - return matchesSearch && matchesProvider && matchesMode && matchesFeature; + return ( + matchesSearch && matchesProvider && matchesMode && matchesFeature + ); }) || [] ); }, [modelHubData, searchTerm, selectedProvider, selectedMode, selectedFeature]); - // Update parent component when filtered data changes useEffect(() => { - // Only call the callback if the filtered data actually changed const hasChanged = filteredData.length !== previousFilteredDataRef.current.length || - filteredData.some((model, index) => model.model_group !== previousFilteredDataRef.current[index]?.model_group); - + filteredData.some( + (model, index) => + model.model_group !== + previousFilteredDataRef.current[index]?.model_group, + ); if (hasChanged) { previousFilteredDataRef.current = filteredData; onFilteredDataChange(filteredData); } }, [filteredData, onFilteredDataChange]); - // Reset filters function const resetFilters = () => { setSearchTerm(""); setSelectedProvider(""); @@ -120,88 +123,76 @@ const ModelFilters: React.FC = ({ setSelectedFeature(""); }; - // Expose filter values and reset function - const filterValues = { - searchTerm, - selectedProvider, - selectedMode, - selectedFeature, - resetFilters, - }; + const labelClass = "text-sm font-medium mb-2 block"; + const inputClass = + "border border-input bg-background rounded px-3 py-2 text-sm h-10"; const filtersContent = (
- Search Models: + Search Models: setSearchTerm(e.target.value)} - className="border rounded px-3 py-2 w-64 h-10 text-sm" + className={cn(inputClass, "w-64")} />
- Provider: + Provider:
- Mode: + Mode:
- Features: + Features:
- {/* Clear filters button */} {(searchTerm || selectedProvider || selectedMode || selectedFeature) && (
@@ -211,7 +202,7 @@ const ModelFilters: React.FC = ({ ); if (showFiltersCard) { - return {filtersContent}; + return {filtersContent}; } return
{filtersContent}
;