extracted AllModelsTab, eliminating props

This commit is contained in:
Achintya Rajan
2025-10-10 15:02:38 -07:00
parent c8b93c3940
commit ff06e6d4ea
4 changed files with 411 additions and 359 deletions
@@ -0,0 +1,373 @@
import { Grid, Select, SelectItem, TabPanel, Text } from "@tremor/react";
import { InfoCircleOutlined } from "@ant-design/icons";
import { ModelDataTable } from "@/components/model_dashboard/table";
import { columns } from "@/components/molecules/models/columns";
import { getDisplayModelName } from "@/components/view_model/model_name_display";
import React, { useEffect, useMemo, useRef, useState } from "react";
import useTeams from "@/app/(dashboard)/hooks/useTeams";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import { Table as TableInstance, PaginationState } from "@tanstack/react-table";
type ModelViewMode = "all" | "current_team";
interface AllModelsTabProps {
modelNameSearch: string;
setModelNameSearch: (nameSearch: string) => void;
selectedModelGroup: string | null;
setSelectedModelGroup: (selectedModelGroup: string) => void;
availableModelGroups: string[];
selectedModelAccessGroupFilter: string | null;
setSelectedModelAccessGroupFilter: (selectedModelAccessGroupFilter: string | null) => void;
availableModelAccessGroups: string[];
setSelectedModelId: (id: string) => void;
setSelectedTeamId: (id: string) => void;
setEditModel: (edit: boolean) => void;
modelData: any;
}
const AllModelsTab = ({
modelNameSearch,
setModelNameSearch,
selectedModelGroup,
setSelectedModelGroup,
availableModelGroups,
selectedModelAccessGroupFilter,
setSelectedModelAccessGroupFilter,
availableModelAccessGroups,
setSelectedModelId,
setSelectedTeamId,
setEditModel,
modelData,
}: AllModelsTabProps) => {
const { userId, userRole, premiumUser } = useAuthorized();
const { teams } = useTeams();
const [modelViewMode, setModelViewMode] = useState<ModelViewMode>("current_team");
const [currentTeam, setCurrentTeam] = useState<string>("personal"); // 'personal' or team_id
const [showFilters, setShowFilters] = useState<boolean>(false);
const [expandedRows, setExpandedRows] = useState<Set<string>>(new Set());
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 50,
});
const tableRef = useRef<TableInstance<any>>(null);
const filteredData = useMemo(() => {
if (!modelData || !modelData.data || modelData.data.length === 0) {
return [];
}
return modelData.data.filter((model: any) => {
const searchMatch =
modelNameSearch === "" || model.model_name.toLowerCase().includes(modelNameSearch.toLowerCase());
const modelNameMatch =
selectedModelGroup === "all" ||
model.model_name === selectedModelGroup ||
!selectedModelGroup ||
(selectedModelGroup === "wildcard" && model.model_name?.includes("*"));
const accessGroupMatch =
selectedModelAccessGroupFilter === "all" ||
model.model_info["access_groups"]?.includes(selectedModelAccessGroupFilter) ||
!selectedModelAccessGroupFilter;
let teamAccessMatch = true;
if (modelViewMode === "current_team") {
if (currentTeam === "personal") {
teamAccessMatch = model.model_info?.direct_access === true;
} else {
teamAccessMatch = model.model_info?.access_via_team_ids?.includes(currentTeam) === true;
}
}
return searchMatch && modelNameMatch && accessGroupMatch && teamAccessMatch;
});
}, [modelData, modelNameSearch, selectedModelGroup, selectedModelAccessGroupFilter, currentTeam, modelViewMode]);
const paginatedData = useMemo(() => {
const startIndex = pagination.pageIndex * pagination.pageSize;
const endIndex = startIndex + pagination.pageSize;
return filteredData.slice(startIndex, endIndex);
}, [filteredData, pagination.pageIndex, pagination.pageSize]);
useEffect(() => {
setPagination((prev: PaginationState) => ({ ...prev, pageIndex: 0 }));
}, [modelNameSearch, selectedModelGroup, selectedModelAccessGroupFilter, currentTeam, modelViewMode]);
const resetFilters = () => {
setModelNameSearch("");
setSelectedModelGroup("all");
setSelectedModelAccessGroupFilter(null);
setCurrentTeam("personal");
setModelViewMode("current_team");
setPagination({ pageIndex: 0, pageSize: 50 });
};
return (
<TabPanel>
<Grid>
<div className="flex flex-col space-y-4">
<div className="bg-white rounded-lg shadow">
{/* 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-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>
<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="/public?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 &quot;
{currentTeam}&quot; on the{" "}
<a
href="/public?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="w-64">
<Select
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>
{availableModelGroups.map((group, idx) => (
<SelectItem key={idx} value={group}>
{group}
</SelectItem>
))}
</Select>
</div>
{/* Model Access Group Filter */}
<div className="w-64">
<Select
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) => (
<SelectItem key={idx} value={accessGroup}>
{accessGroup}
</SelectItem>
))}
</Select>
</div>
</div>
)}
{/* Results Count and Pagination Controls */}
<div className="flex justify-between items-center">
<span className="text-sm text-gray-700">
{filteredData.length > 0
? `Showing ${pagination.pageIndex * pagination.pageSize + 1} - ${Math.min(
(pagination.pageIndex + 1) * pagination.pageSize,
filteredData.length,
)} of ${filteredData.length} results`
: "Showing 0 results"}
</span>
{/* Pagination Controls */}
{filteredData.length > pagination.pageSize && (
<div className="flex items-center space-x-2">
<button
onClick={() =>
setPagination((prev: PaginationState) => ({ ...prev, pageIndex: prev.pageIndex - 1 }))
}
disabled={pagination.pageIndex === 0}
className={`px-3 py-1 text-sm border rounded-md ${
pagination.pageIndex === 0
? "bg-gray-100 text-gray-400 cursor-not-allowed"
: "hover:bg-gray-50"
}`}
>
Previous
</button>
<button
onClick={() =>
setPagination((prev: PaginationState) => ({ ...prev, pageIndex: prev.pageIndex + 1 }))
}
disabled={pagination.pageIndex >= Math.ceil(filteredData.length / pagination.pageSize) - 1}
className={`px-3 py-1 text-sm border rounded-md ${
pagination.pageIndex >= Math.ceil(filteredData.length / pagination.pageSize) - 1
? "bg-gray-100 text-gray-400 cursor-not-allowed"
: "hover:bg-gray-50"
}`}
>
Next
</button>
</div>
)}
</div>
</div>
</div>
<ModelDataTable
columns={columns(
userRole,
userId,
premiumUser,
setSelectedModelId,
setSelectedTeamId,
getDisplayModelName,
() => {},
() => {},
setEditModel,
expandedRows,
setExpandedRows,
)}
data={paginatedData}
isLoading={false}
table={tableRef}
/>
</div>
</div>
</Grid>
</TabPanel>
);
};
export default AllModelsTab;
@@ -1,4 +1,4 @@
import React, { useState, useEffect, useRef, useMemo } from "react";
import React, { useState, useEffect, useRef } from "react";
import {
Card,
Title,
@@ -13,15 +13,15 @@ import {
Grid,
Col,
} from "@tremor/react";
import { CredentialItem, credentialListCall, CredentialsResponse } from "../networking";
import { CredentialItem, credentialListCall, CredentialsResponse } from "../../../components/networking";
import { handleAddModelSubmit } from "../add_model/handle_add_model_submit";
import { handleAddModelSubmit } from "../../../components/add_model/handle_add_model_submit";
import CredentialsPanel from "@/components/model_add/credentials";
import { getDisplayModelName } from "../view_model/model_name_display";
import { getDisplayModelName } from "../../../components/view_model/model_name_display";
import { TabPanel, TabPanels, TabGroup, TabList, Tab, Icon } from "@tremor/react";
import { Select, SelectItem, DateRangePickerValue } from "@tremor/react";
import UsageDatePicker from "../shared/usage_date_picker";
import UsageDatePicker from "../../../components/shared/usage_date_picker";
import {
modelInfoCall,
modelCostMap,
@@ -36,30 +36,27 @@ import {
adminGlobalActivityExceptions,
adminGlobalActivityExceptionsPerDeployment,
allEndUsersCall,
} from "../networking";
} from "../../../components/networking";
import { BarChart, AreaChart } from "@tremor/react";
import { Popover, Form, InputNumber } from "antd";
import { Button } from "@tremor/react";
import { Typography } from "antd";
import { RefreshIcon, FilterIcon } from "@heroicons/react/outline";
import { InfoCircleOutlined } from "@ant-design/icons";
import type { UploadProps } from "antd";
import TimeToFirstToken from "../model_metrics/time_to_first_token";
import { Team } from "../key_team_helpers/key_list";
import TeamInfoView from "../team/team_info";
import { Providers, provider_map, getPlaceholder, getProviderModels } from "../provider_info_helpers";
import ModelInfoView from "../model_info_view";
import AddModelTab from "../add_model/add_model_tab";
import TimeToFirstToken from "../../../components/model_metrics/time_to_first_token";
import { Team } from "../../../components/key_team_helpers/key_list";
import TeamInfoView from "../../../components/team/team_info";
import { Providers, provider_map, getPlaceholder, getProviderModels } from "../../../components/provider_info_helpers";
import ModelInfoView from "../../../components/model_info_view";
import AddModelTab from "../../../components/add_model/add_model_tab";
import { ModelDataTable } from "../model_dashboard/table";
import { columns } from "../molecules/models/columns";
import PriceDataReload from "../price_data_reload";
import HealthCheckComponent from "../model_dashboard/HealthCheckComponent";
import PassThroughSettings from "../pass_through_settings";
import ModelGroupAliasSettings from "../model_group_alias_settings";
import PriceDataReload from "../../../components/price_data_reload";
import HealthCheckComponent from "../../../components/model_dashboard/HealthCheckComponent";
import PassThroughSettings from "../../../components/pass_through_settings";
import ModelGroupAliasSettings from "../../../components/model_group_alias_settings";
import { all_admin_roles } from "@/utils/roles";
import { Table as TableInstance, PaginationState } from "@tanstack/react-table";
import NotificationsManager from "../molecules/notifications_manager";
import NotificationsManager from "../../../components/molecules/notifications_manager";
import AllModelsTab from "@/app/(dashboard)/models-and-endpoints/components/AllModelsTab";
interface ModelDashboardProps {
accessToken: string | null;
@@ -109,7 +106,7 @@ const retry_policy_map: Record<string, string> = {
"InternalServerError (500)": "InternalServerErrorRetries",
};
const ModelDashboard: React.FC<ModelDashboardProps> = ({
const ModelsAndEndpointsView: React.FC<ModelDashboardProps> = ({
accessToken,
token,
userRole,
@@ -184,25 +181,14 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
const [modelNameSearch, setModelNameSearch] = useState<string>("");
// Add new state for current team and model view mode
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);
const [expandedRows, setExpandedRows] = useState<Set<string>>(new Set());
const dropdownRef = useRef<HTMLDivElement>(null);
const tableRef = useRef<TableInstance<any>>(null);
// Pagination state
const [pagination, setPagination] = useState<PaginationState>({
pageIndex: 0,
pageSize: 50,
});
const [selectedTabIndex, setSelectedTabIndex] = useState(0);
const handleCreateNewModelClick = () => {
@@ -212,61 +198,6 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
setSelectedTabIndex(1);
};
const resetFilters = () => {
setModelNameSearch("");
setSelectedModelGroup("all");
setSelectedModelAccessGroupFilter(null);
setCurrentTeam("personal");
setModelViewMode("current_team");
setPagination({ pageIndex: 0, pageSize: 50 });
};
// Memoize filtered data to prevent unnecessary re-calculations
const filteredData = useMemo(() => {
if (!modelData || !modelData.data || modelData.data.length === 0) {
return [];
}
return modelData.data.filter((model: any) => {
const searchMatch =
modelNameSearch === "" || model.model_name.toLowerCase().includes(modelNameSearch.toLowerCase());
const modelNameMatch =
selectedModelGroup === "all" ||
model.model_name === selectedModelGroup ||
!selectedModelGroup ||
(selectedModelGroup === "wildcard" && model.model_name?.includes("*"));
const accessGroupMatch =
selectedModelAccessGroupFilter === "all" ||
model.model_info["access_groups"]?.includes(selectedModelAccessGroupFilter) ||
!selectedModelAccessGroupFilter;
let teamAccessMatch = true;
if (modelViewMode === "current_team") {
if (currentTeam === "personal") {
teamAccessMatch = model.model_info?.direct_access === true;
} else {
teamAccessMatch = model.model_info?.access_via_team_ids?.includes(currentTeam) === true;
}
}
return searchMatch && modelNameMatch && accessGroupMatch && teamAccessMatch;
});
}, [modelData, modelNameSearch, selectedModelGroup, selectedModelAccessGroupFilter, currentTeam, modelViewMode]);
// Memoize paginated data
const paginatedData = useMemo(() => {
const startIndex = pagination.pageIndex * pagination.pageSize;
const endIndex = startIndex + pagination.pageSize;
return filteredData.slice(startIndex, endIndex);
}, [filteredData, pagination.pageIndex, pagination.pageSize]);
// Reset pagination when filters change
useEffect(() => {
setPagination((prev) => ({ ...prev, pageIndex: 0 }));
}, [modelNameSearch, selectedModelGroup, selectedModelAccessGroupFilter, currentTeam, modelViewMode]);
const setProviderModelsFn = (provider: Providers) => {
const _providerModels = getProviderModels(provider, modelMap);
setProviderModels(_providerModels);
@@ -1065,272 +996,20 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
</div>
</TabList>
<TabPanels>
<TabPanel>
<Grid>
<div className="flex flex-col space-y-4">
<div className="bg-white rounded-lg shadow">
{/* 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-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>
<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 &quot;
{currentTeam}&quot; 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="w-64">
<Select
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>
{availableModelGroups.map((group, idx) => (
<SelectItem key={idx} value={group}>
{group}
</SelectItem>
))}
</Select>
</div>
{/* Model Access Group Filter */}
<div className="w-64">
<Select
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) => (
<SelectItem key={idx} value={accessGroup}>
{accessGroup}
</SelectItem>
))}
</Select>
</div>
</div>
)}
{/* Results Count and Pagination Controls */}
<div className="flex justify-between items-center">
<span className="text-sm text-gray-700">
{filteredData.length > 0
? `Showing ${pagination.pageIndex * pagination.pageSize + 1} - ${Math.min(
(pagination.pageIndex + 1) * pagination.pageSize,
filteredData.length,
)} of ${filteredData.length} results`
: "Showing 0 results"}
</span>
{/* Pagination Controls */}
{filteredData.length > pagination.pageSize && (
<div className="flex items-center space-x-2">
<button
onClick={() =>
setPagination((prev) => ({ ...prev, pageIndex: prev.pageIndex - 1 }))
}
disabled={pagination.pageIndex === 0}
className={`px-3 py-1 text-sm border rounded-md ${
pagination.pageIndex === 0
? "bg-gray-100 text-gray-400 cursor-not-allowed"
: "hover:bg-gray-50"
}`}
>
Previous
</button>
<button
onClick={() =>
setPagination((prev) => ({ ...prev, pageIndex: prev.pageIndex + 1 }))
}
disabled={
pagination.pageIndex >= Math.ceil(filteredData.length / pagination.pageSize) - 1
}
className={`px-3 py-1 text-sm border rounded-md ${
pagination.pageIndex >= Math.ceil(filteredData.length / pagination.pageSize) - 1
? "bg-gray-100 text-gray-400 cursor-not-allowed"
: "hover:bg-gray-50"
}`}
>
Next
</button>
</div>
)}
</div>
</div>
</div>
<ModelDataTable
columns={columns(
userRole,
userID,
premiumUser,
setSelectedModelId,
setSelectedTeamId,
getDisplayModelName,
handleEditClick,
handleRefreshClick,
setEditModel,
expandedRows,
setExpandedRows,
)}
data={paginatedData}
isLoading={false}
table={tableRef}
/>
</div>
</div>
</Grid>
</TabPanel>
<AllModelsTab
modelNameSearch={modelNameSearch}
setModelNameSearch={setModelNameSearch}
selectedModelGroup={selectedModelGroup}
setSelectedModelGroup={setSelectedModelGroup}
availableModelGroups={availableModelGroups}
selectedModelAccessGroupFilter={selectedModelAccessGroupFilter}
setSelectedModelAccessGroupFilter={setSelectedModelAccessGroupFilter}
availableModelAccessGroups={availableModelAccessGroups}
setSelectedModelId={setSelectedModelId}
setSelectedTeamId={setSelectedTeamId}
setEditModel={setEditModel}
modelData={modelData}
/>
<TabPanel className="h-full">
<AddModelTab
form={addModelForm}
@@ -1754,4 +1433,4 @@ const ModelDashboard: React.FC<ModelDashboardProps> = ({
);
};
export default ModelDashboard;
export default ModelsAndEndpointsView;
@@ -1,9 +1,9 @@
"use client";
import ModelDashboard from "@/components/templates/model_dashboard";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
import useTeams from "@/app/(dashboard)/hooks/useTeams";
import { useState } from "react";
import ModelsAndEndpointsView from "@/app/(dashboard)/models-and-endpoints/model_dashboard";
const ModelsAndEndpointsPage = () => {
const { token, accessToken, userRole, userId, premiumUser } = useAuthorized();
@@ -12,7 +12,7 @@ const ModelsAndEndpointsPage = () => {
const { teams } = useTeams();
return (
<ModelDashboard
<ModelsAndEndpointsView
accessToken={accessToken}
token={token}
userRole={userRole}
+2 -2
View File
@@ -8,7 +8,7 @@ import { Team } from "@/components/key_team_helpers/key_list";
import Navbar from "@/components/navbar";
import { ThemeProvider } from "@/contexts/ThemeContext";
import UserDashboard from "@/components/user_dashboard";
import ModelDashboard from "@/components/templates/model_dashboard";
import OldModelDashboard from "@/app/(dashboard)/models-and-endpoints/model_dashboard";
import ViewUserDashboard from "@/components/view_users";
import TeamsView from "@/app/(dashboard)/teams/TeamsView";
import Organizations from "@/components/organizations";
@@ -347,7 +347,7 @@ export default function CreateKeyPage() {
createClicked={createClicked}
/>
) : page == "models" ? (
<ModelDashboard
<OldModelDashboard
userID={userID}
userRole={userRole}
token={token}