mirror of
https://github.com/tiennm99/litellm.git
synced 2026-07-11 17:05:43 +00:00
model filters ui matches the users page
This commit is contained in:
@@ -191,6 +191,9 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
|
||||
const [currentTeam, setCurrentTeam] = useState<string>("personal") // 'personal' or team_id
|
||||
const [modelViewMode, setModelViewMode] = useState<"current_team" | "all">("current_team")
|
||||
|
||||
// Add state for showing/hiding filters
|
||||
const [showFilters, setShowFilters] = useState<boolean>(false)
|
||||
|
||||
const [showColumnDropdown, setShowColumnDropdown] = useState(false)
|
||||
|
||||
const [isDropdownOpen, setIsDropdownOpen] = useState(false)
|
||||
@@ -206,6 +209,14 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
|
||||
setSelectedTabIndex(1)
|
||||
}
|
||||
|
||||
const resetFilters = () => {
|
||||
setModelNameSearch("")
|
||||
setSelectedModelGroup("all")
|
||||
setSelectedModelAccessGroupFilter(null)
|
||||
setCurrentTeam("personal")
|
||||
setModelViewMode("current_team")
|
||||
}
|
||||
|
||||
const setProviderModelsFn = (provider: Providers) => {
|
||||
const _providerModels = getProviderModels(provider, modelMap)
|
||||
setProviderModels(_providerModels)
|
||||
@@ -1006,119 +1017,166 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
|
||||
<div className="flex flex-col space-y-4">
|
||||
|
||||
<div className="bg-white rounded-lg shadow">
|
||||
<div className="border-b px-6 py-4">
|
||||
<div className="flex flex-col space-y-4">
|
||||
{/* Current Team Selector - Prominent */}
|
||||
<div className="flex items-center justify-between bg-gray-50 rounded-lg p-4 border-2 border-gray-200">
|
||||
<div>
|
||||
<div className="flex items-center gap-4">
|
||||
<Text className="text-lg font-semibold text-gray-900">Current Team:</Text>
|
||||
<Select
|
||||
className="w-80"
|
||||
defaultValue="personal"
|
||||
value={currentTeam}
|
||||
onValueChange={(value) => setCurrentTeam(value)}
|
||||
>
|
||||
<SelectItem value="personal">
|
||||
{/* Current Team and View Mode Selector - Prominent Section */}
|
||||
<div className="border-b px-6 py-4 bg-gray-50">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-4">
|
||||
<Text className="text-lg font-semibold text-gray-900">Current Team:</Text>
|
||||
<Select
|
||||
className="w-80"
|
||||
defaultValue="personal"
|
||||
value={currentTeam}
|
||||
onValueChange={(value) => setCurrentTeam(value)}
|
||||
>
|
||||
<SelectItem value="personal">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-2 h-2 bg-blue-500 rounded-full"></div>
|
||||
<span className="font-medium">Personal</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
{teams
|
||||
?.filter((team) => team.team_id)
|
||||
.map((team) => (
|
||||
<SelectItem key={team.team_id} value={team.team_id}>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-2 h-2 bg-blue-500 rounded-full"></div>
|
||||
<span className="font-medium">Personal</span>
|
||||
<div className="w-2 h-2 bg-green-500 rounded-full"></div>
|
||||
<span className="font-medium">
|
||||
{team.team_alias
|
||||
? `${team.team_alias.slice(0, 30)}...`
|
||||
: `Team ${team.team_id.slice(0, 30)}...`}
|
||||
</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
{teams
|
||||
?.filter((team) => team.team_id)
|
||||
.map((team) => (
|
||||
<SelectItem key={team.team_id} value={team.team_id}>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-2 h-2 bg-green-500 rounded-full"></div>
|
||||
<span className="font-medium">
|
||||
{team.team_alias
|
||||
? `${team.team_alias.slice(0, 30)}...`
|
||||
: `Team ${team.team_id.slice(0, 30)}...`}
|
||||
</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
{modelViewMode === "current_team" && (
|
||||
<div className="flex items-start gap-2 mt-2 bg-gray-50 rounded">
|
||||
<InfoCircleOutlined className="text-gray-400 mt-0.5 flex-shrink-0 text-xs" />
|
||||
<div className="text-xs text-gray-500">
|
||||
{currentTeam === "personal" ? (
|
||||
<span>
|
||||
To access these models: Create a Virtual Key without selecting a team on the{" "}
|
||||
<a
|
||||
href="/?login=success&page=api-keys"
|
||||
className="text-gray-600 hover:text-gray-800 underline"
|
||||
>
|
||||
Virtual Keys page
|
||||
</a>
|
||||
</span>
|
||||
) : (
|
||||
<span>
|
||||
To access these models: Create a Virtual Key and select Team as "
|
||||
{currentTeam}" on the{" "}
|
||||
<a
|
||||
href="/?login=success&page=api-keys"
|
||||
className="text-gray-600 hover:text-gray-800 underline"
|
||||
>
|
||||
Virtual Keys page
|
||||
</a>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Model View Mode Toggle - Also prominent */}
|
||||
<div className="flex items-center gap-4">
|
||||
<Text className="text-lg font-semibold text-gray-900">View:</Text>
|
||||
<Select
|
||||
className="w-64"
|
||||
defaultValue="current_team"
|
||||
value={modelViewMode}
|
||||
onValueChange={(value) => setModelViewMode(value as "current_team" | "all")}
|
||||
>
|
||||
<SelectItem value="current_team">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-2 h-2 bg-purple-500 rounded-full"></div>
|
||||
<span className="font-medium">Current Team Models</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
<SelectItem value="all">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-2 h-2 bg-gray-500 rounded-full"></div>
|
||||
<span className="font-medium">All Available Models</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
</Select>
|
||||
</div>
|
||||
))}
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
{/* Other Filters */}
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
{/* Model Name Search */}
|
||||
<div className="flex items-center gap-2">
|
||||
<Text>Search Public Model Name:</Text>
|
||||
<TextInput
|
||||
className="w-64"
|
||||
placeholder="Search model names..."
|
||||
value={modelNameSearch}
|
||||
onValueChange={setModelNameSearch}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<Text className="text-lg font-semibold text-gray-900">View:</Text>
|
||||
<Select
|
||||
className="w-64"
|
||||
defaultValue="current_team"
|
||||
value={modelViewMode}
|
||||
onValueChange={(value) => setModelViewMode(value as "current_team" | "all")}
|
||||
>
|
||||
<SelectItem value="current_team">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-2 h-2 bg-purple-500 rounded-full"></div>
|
||||
<span className="font-medium">Current Team Models</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
<SelectItem value="all">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-2 h-2 bg-gray-500 rounded-full"></div>
|
||||
<span className="font-medium">All Available Models</span>
|
||||
</div>
|
||||
</SelectItem>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{modelViewMode === "current_team" && (
|
||||
<div className="flex items-start gap-2 mt-3">
|
||||
<InfoCircleOutlined className="text-gray-400 mt-0.5 flex-shrink-0 text-xs" />
|
||||
<div className="text-xs text-gray-500">
|
||||
{currentTeam === "personal" ? (
|
||||
<span>
|
||||
To access these models: Create a Virtual Key without selecting a team on the{" "}
|
||||
<a
|
||||
href="/?login=success&page=api-keys"
|
||||
className="text-gray-600 hover:text-gray-800 underline"
|
||||
>
|
||||
Virtual Keys page
|
||||
</a>
|
||||
</span>
|
||||
) : (
|
||||
<span>
|
||||
To access these models: Create a Virtual Key and select Team as "
|
||||
{currentTeam}" on the{" "}
|
||||
<a
|
||||
href="/?login=success&page=api-keys"
|
||||
className="text-gray-600 hover:text-gray-800 underline"
|
||||
>
|
||||
Virtual Keys page
|
||||
</a>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Search and Filter Controls */}
|
||||
<div className="border-b px-6 py-4">
|
||||
<div className="flex flex-col space-y-4">
|
||||
{/* Search and Filter Controls */}
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
{/* Model Name Search */}
|
||||
<div className="relative w-64">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search model names..."
|
||||
className="w-full px-3 py-2 pl-8 border rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
|
||||
value={modelNameSearch}
|
||||
onChange={(e) => setModelNameSearch(e.target.value)}
|
||||
/>
|
||||
<svg
|
||||
className="absolute left-2.5 top-2.5 h-4 w-4 text-gray-500"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
{/* Filter Button */}
|
||||
<button
|
||||
className={`px-3 py-2 text-sm border rounded-md hover:bg-gray-50 flex items-center gap-2 ${showFilters ? "bg-gray-100" : ""}`}
|
||||
onClick={() => setShowFilters(!showFilters)}
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z"
|
||||
/>
|
||||
</svg>
|
||||
Filters
|
||||
</button>
|
||||
|
||||
{/* Reset Filters Button */}
|
||||
<button
|
||||
className="px-3 py-2 text-sm border rounded-md hover:bg-gray-50 flex items-center gap-2"
|
||||
onClick={resetFilters}
|
||||
>
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
|
||||
/>
|
||||
</svg>
|
||||
Reset Filters
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Additional Filters */}
|
||||
{showFilters && (
|
||||
<div className="flex flex-wrap items-center gap-3 mt-3">
|
||||
{/* Model Name Filter */}
|
||||
<div className="flex items-center gap-2">
|
||||
<Text>Filter by Public Model Name:</Text>
|
||||
<div className="w-64">
|
||||
<Select
|
||||
className="w-64"
|
||||
defaultValue={selectedModelGroup ?? "all"}
|
||||
onValueChange={(value) => setSelectedModelGroup(value === "all" ? "all" : value)}
|
||||
value={selectedModelGroup ?? "all"}
|
||||
onValueChange={(value) => setSelectedModelGroup(value === "all" ? "all" : value)}
|
||||
placeholder="Filter by Public Model Name"
|
||||
>
|
||||
<SelectItem value="all">All Models</SelectItem>
|
||||
<SelectItem value="wildcard">Wildcard Models (*)</SelectItem>
|
||||
@@ -1130,15 +1188,14 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-2">
|
||||
<Text>Filter by Model Access Group:</Text>
|
||||
{/* Model Access Group Filter */}
|
||||
<div className="w-64">
|
||||
<Select
|
||||
className="w-64"
|
||||
defaultValue="all"
|
||||
value={selectedModelAccessGroupFilter ?? "all"}
|
||||
onValueChange={(value) =>
|
||||
setSelectedModelAccessGroupFilter(value === "all" ? null : value)
|
||||
}
|
||||
placeholder="Filter by Model Access Group"
|
||||
>
|
||||
<SelectItem value="all">All Model Access Groups</SelectItem>
|
||||
{availableModelAccessGroups.map((accessGroup, idx) => (
|
||||
@@ -1149,11 +1206,11 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Results Count */}
|
||||
<div className="flex justify-between items-center">
|
||||
<Text className="text-sm text-gray-700">
|
||||
<span className="text-sm text-gray-700">
|
||||
Showing{" "}
|
||||
{modelData && modelData.data.length > 0
|
||||
? modelData.data.filter((model: any) => {
|
||||
@@ -1164,7 +1221,8 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
|
||||
const modelNameMatch =
|
||||
selectedModelGroup === "all" ||
|
||||
model.model_name === selectedModelGroup ||
|
||||
!selectedModelGroup
|
||||
!selectedModelGroup ||
|
||||
(selectedModelGroup === "wildcard" && model.model_name?.includes("*"))
|
||||
const accessGroupMatch =
|
||||
selectedModelAccessGroupFilter === "all" ||
|
||||
model.model_info["access_groups"]?.includes(selectedModelAccessGroupFilter) ||
|
||||
@@ -1183,7 +1241,7 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
|
||||
}).length
|
||||
: 0}{" "}
|
||||
results
|
||||
</Text>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user