mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-08 03:08:45 +00:00
fix(ui): restore tab strip styling and panel persistence lost in the shadcn migration (#37403)
Tremor's TabList defaulted to the underlined `line` variant and its TabPanel
rendered every panel, hiding the inactive ones with a class. The shadcn
TabsList defaults to a segmented pill and Base UI's TabsPanel unmounts a
hidden panel unless it carries `keepMounted`, so the conversion waves quietly
changed both on the pages that took tremor's defaults.
Restores the underline on the nine strips whose tremor markup carried no
`variant`, leaving the ones that were explicitly `variant="solid"` as pills,
and puts `keepMounted` back on the thirteen files whose panels used to stay
mounted, so filters, scroll position and in-progress input survive a tab
switch again.
Seeding the old usage page's activity state properly comes with it: it was
cast from `{}`, so the panel crashed on `data.length` the moment it mounted
before its fetch resolved, which only stayed hidden while the panel was
unmounted until first opened.
This commit is contained in:
parent
4bb3152cc5
commit
73e7105e60
17 changed files with 148 additions and 90 deletions
|
|
@ -46,7 +46,7 @@ const APIReferenceView: React.FC<ApiRefProps> = ({ proxySettings }) => {
|
|||
Langchain Py
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="openai">
|
||||
<TabsContent value="openai" keepMounted>
|
||||
<CodeBlock
|
||||
language="python"
|
||||
code={`import openai
|
||||
|
|
@ -69,7 +69,7 @@ print(response)`}
|
|||
/>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="llamaindex">
|
||||
<TabsContent value="llamaindex" keepMounted>
|
||||
<CodeBlock
|
||||
language="python"
|
||||
code={`import os, dotenv
|
||||
|
|
@ -103,7 +103,7 @@ print(response)`}
|
|||
/>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="langchain">
|
||||
<TabsContent value="langchain" keepMounted>
|
||||
<CodeBlock
|
||||
language="python"
|
||||
code={`from langchain.chat_models import ChatOpenAI
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ const BudgetPanel: React.FC<BudgetSettingsPageProps> = ({ accessToken }) => {
|
|||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</div>
|
||||
<TabsContent value="budgets" className="flex min-h-0 flex-1 flex-col">
|
||||
<TabsContent value="budgets" className="flex min-h-0 flex-1 flex-col" keepMounted>
|
||||
<div className="flex min-h-0 flex-1 flex-col pt-6">
|
||||
<BudgetModal isModalVisible={isCreateModelVisible} setIsModalVisible={setIsCreateModelVisible} />
|
||||
{selectedBudget && (
|
||||
|
|
@ -134,7 +134,7 @@ const BudgetPanel: React.FC<BudgetSettingsPageProps> = ({ accessToken }) => {
|
|||
/>
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="examples" className="min-h-0 flex-1 overflow-y-auto">
|
||||
<TabsContent value="examples" className="min-h-0 flex-1 overflow-y-auto" keepMounted>
|
||||
<div className="pt-6">
|
||||
<p className="text-base text-muted-foreground">How to use budget id</p>
|
||||
<Tabs defaultValue="assign-budget">
|
||||
|
|
@ -149,13 +149,13 @@ const BudgetPanel: React.FC<BudgetSettingsPageProps> = ({ accessToken }) => {
|
|||
Test it (OpenAI SDK)
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="assign-budget">
|
||||
<TabsContent value="assign-budget" keepMounted>
|
||||
<SyntaxHighlighter language="bash">{CREATE_END_USER_CURL_COMMAND}</SyntaxHighlighter>
|
||||
</TabsContent>
|
||||
<TabsContent value="curl">
|
||||
<TabsContent value="curl" keepMounted>
|
||||
<SyntaxHighlighter language="bash">{CHAT_COMPLETIONS_CURL_COMMAND}</SyntaxHighlighter>
|
||||
</TabsContent>
|
||||
<TabsContent value="openai-sdk">
|
||||
<TabsContent value="openai-sdk" keepMounted>
|
||||
<SyntaxHighlighter language="python">{OPENAI_SDK_PYTHON_CODE}</SyntaxHighlighter>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
|
|
|||
|
|
@ -140,18 +140,18 @@ const CacheDashboard: React.FC<CachePageProps> = ({ accessToken, token, userRole
|
|||
|
||||
return (
|
||||
<Tabs defaultValue="analytics" className="mt-2 mb-8 w-full gap-2 p-8">
|
||||
<div className="mt-2 flex w-full items-center justify-between">
|
||||
<TabsList>
|
||||
<TabsTrigger value="analytics" className="flex-none">
|
||||
<div className="mt-2 flex w-full items-center justify-between border-b">
|
||||
<TabsList variant="line" className="h-auto rounded-none p-0">
|
||||
<TabsTrigger value="analytics" className="flex-none rounded-none px-4 py-2">
|
||||
Cache Analytics
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="health" className="flex-none">
|
||||
<TabsTrigger value="health" className="flex-none rounded-none px-4 py-2">
|
||||
Cache Health
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="settings" className="flex-none">
|
||||
<TabsTrigger value="settings" className="flex-none rounded-none px-4 py-2">
|
||||
Cache Settings
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="coordination" className="flex-none">
|
||||
<TabsTrigger value="coordination" className="flex-none rounded-none px-4 py-2">
|
||||
Coordination Redis
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
|
@ -164,7 +164,7 @@ const CacheDashboard: React.FC<CachePageProps> = ({ accessToken, token, userRole
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<TabsContent value="analytics">
|
||||
<TabsContent value="analytics" keepMounted>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
|
|
@ -311,7 +311,7 @@ const CacheDashboard: React.FC<CachePageProps> = ({ accessToken, token, userRole
|
|||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="health">
|
||||
<TabsContent value="health" keepMounted>
|
||||
<CacheHealthTab
|
||||
accessToken={accessToken}
|
||||
healthCheckResponse={healthCheckResponse}
|
||||
|
|
@ -319,11 +319,11 @@ const CacheDashboard: React.FC<CachePageProps> = ({ accessToken, token, userRole
|
|||
/>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="settings">
|
||||
<TabsContent value="settings" keepMounted>
|
||||
<CacheSettings accessToken={accessToken} userRole={userRole} userID={userID} />
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="coordination">
|
||||
<TabsContent value="coordination" keepMounted>
|
||||
<CoordinationRedisSettings />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
|
|
|||
|
|
@ -164,7 +164,7 @@ const HealthCheckDetails: React.FC<{ response: any }> = ({ response }) => {
|
|||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="summary" className="p-4">
|
||||
<TabsContent value="summary" className="p-4" keepMounted>
|
||||
<div>
|
||||
<div className="mb-6 flex items-center">
|
||||
{response?.status === "healthy" ? (
|
||||
|
|
@ -228,7 +228,7 @@ const HealthCheckDetails: React.FC<{ response: any }> = ({ response }) => {
|
|||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="raw" className="p-4">
|
||||
<TabsContent value="raw" className="p-4" keepMounted>
|
||||
<div className="rounded-md bg-muted p-4 font-mono text-sm">
|
||||
<pre className="whitespace-pre-wrap wrap-break-word overflow-auto max-h-[500px]">
|
||||
{(() => {
|
||||
|
|
|
|||
|
|
@ -197,11 +197,15 @@ const CostTrackingSettings: React.FC<CostTrackingSettingsProps> = ({ userID, use
|
|||
/>
|
||||
<CollapsibleContent className="px-0">
|
||||
<Tabs defaultValue="discounts">
|
||||
<TabsList className="mx-6 mt-4">
|
||||
<TabsTrigger value="discounts">Discounts</TabsTrigger>
|
||||
<TabsTrigger value="test-it">Test It</TabsTrigger>
|
||||
<TabsList variant="line" className="mx-6 mt-4 h-auto justify-start rounded-none border-b p-0">
|
||||
<TabsTrigger value="discounts" className="flex-none rounded-none px-4 py-2">
|
||||
Discounts
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="test-it" className="flex-none rounded-none px-4 py-2">
|
||||
Test It
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="discounts">
|
||||
<TabsContent value="discounts" keepMounted>
|
||||
<div className="p-6">
|
||||
<div className="flex justify-end mb-4">
|
||||
<Button onClick={() => setIsModalVisible(true)}>+ Add Provider Discount</Button>
|
||||
|
|
@ -237,7 +241,7 @@ const CostTrackingSettings: React.FC<CostTrackingSettingsProps> = ({ userID, use
|
|||
)}
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="test-it">
|
||||
<TabsContent value="test-it" keepMounted>
|
||||
<div className="px-6 pb-4">
|
||||
<HowItWorks />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -130,22 +130,22 @@ export const MCPServerView: React.FC<MCPServerViewProps> = ({
|
|||
</div>
|
||||
|
||||
<Tabs value={String(selectedTabIndex)} onValueChange={(v: unknown) => setSelectedTabIndex(Number(v))}>
|
||||
<TabsList className="mb-4">
|
||||
<TabsTrigger value="0" className="flex-none">
|
||||
<TabsList variant="line" className="mb-4 h-auto w-full justify-start rounded-none border-b p-0">
|
||||
<TabsTrigger value="0" className="flex-none rounded-none px-4 py-2">
|
||||
Overview
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="1" className="flex-none">
|
||||
<TabsTrigger value="1" className="flex-none rounded-none px-4 py-2">
|
||||
MCP Tools
|
||||
</TabsTrigger>
|
||||
{isProxyAdmin && (
|
||||
<TabsTrigger value="2" className="flex-none">
|
||||
<TabsTrigger value="2" className="flex-none rounded-none px-4 py-2">
|
||||
Settings
|
||||
</TabsTrigger>
|
||||
)}
|
||||
</TabsList>
|
||||
|
||||
{/* Overview Panel */}
|
||||
<TabsContent value="0">
|
||||
<TabsContent value="0" keepMounted>
|
||||
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
|
||||
<Card className="p-4">
|
||||
<p className="text-xs font-medium tracking-wide text-muted-foreground uppercase">Transport</p>
|
||||
|
|
@ -192,7 +192,7 @@ export const MCPServerView: React.FC<MCPServerViewProps> = ({
|
|||
</TabsContent>
|
||||
|
||||
{/* Tool Panel */}
|
||||
<TabsContent value="1">
|
||||
<TabsContent value="1" keepMounted>
|
||||
<MCPToolsViewer
|
||||
serverId={mcpServer.server_id}
|
||||
accessToken={accessToken}
|
||||
|
|
@ -209,7 +209,7 @@ export const MCPServerView: React.FC<MCPServerViewProps> = ({
|
|||
</TabsContent>
|
||||
|
||||
{/* Settings Panel */}
|
||||
<TabsContent value="2">
|
||||
<TabsContent value="2" keepMounted>
|
||||
<Card className="p-6">
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<h2 className="text-lg font-medium">MCP Server Settings</h2>
|
||||
|
|
|
|||
|
|
@ -543,7 +543,7 @@ const MCPServers: React.FC<MCPServerProps> = ({ accessToken, userRole, userID })
|
|||
</TabsTrigger>
|
||||
)}
|
||||
</TabsList>
|
||||
<TabsContent value="servers">
|
||||
<TabsContent value="servers" keepMounted>
|
||||
{selectedServerId ? (
|
||||
<MCPServerView
|
||||
key={selectedServerId}
|
||||
|
|
@ -703,24 +703,24 @@ const MCPServers: React.FC<MCPServerProps> = ({ accessToken, userRole, userID })
|
|||
</div>
|
||||
)}
|
||||
</TabsContent>
|
||||
<TabsContent value="toolsets">
|
||||
<TabsContent value="toolsets" keepMounted>
|
||||
<MCPToolsetsTab accessToken={accessToken} userRole={userRole} />
|
||||
</TabsContent>
|
||||
<TabsContent value="connect">
|
||||
<TabsContent value="connect" keepMounted>
|
||||
<MCPConnect />
|
||||
</TabsContent>
|
||||
{isAdminRole(userRole) && (
|
||||
<TabsContent value="semantic-filter">
|
||||
<TabsContent value="semantic-filter" keepMounted>
|
||||
<MCPSemanticFilterSettings accessToken={accessToken} />
|
||||
</TabsContent>
|
||||
)}
|
||||
{isAdminRole(userRole) && (
|
||||
<TabsContent value="network-settings">
|
||||
<TabsContent value="network-settings" keepMounted>
|
||||
<MCPNetworkSettings accessToken={accessToken} />
|
||||
</TabsContent>
|
||||
)}
|
||||
{isAdminRole(userRole) && (
|
||||
<TabsContent value="submitted">
|
||||
<TabsContent value="submitted" keepMounted>
|
||||
<MCPSubmissionsTab accessToken={accessToken} />
|
||||
</TabsContent>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,8 @@ interface GlobalActivityData {
|
|||
daily_data: { date: string; api_requests: number; total_tokens: number }[];
|
||||
}
|
||||
|
||||
const EMPTY_GLOBAL_ACTIVITY: GlobalActivityData = { sum_api_requests: 0, sum_total_tokens: 0, daily_data: [] };
|
||||
|
||||
type UsageDateRange = { from?: Date; to?: Date };
|
||||
|
||||
type TeamSpendTotal = { name: string; value: number };
|
||||
|
|
@ -105,7 +107,7 @@ const UsagePage: React.FC<UsagePageProps> = ({ accessToken, token, userRole, use
|
|||
const [uniqueTeamIds, setUniqueTeamIds] = useState<any[]>([]);
|
||||
const [totalSpendPerTeam, setTotalSpendPerTeam] = useState<TeamSpendTotal[]>([]);
|
||||
const [spendByProvider, setSpendByProvider] = useState<any[]>([]);
|
||||
const [globalActivity, setGlobalActivity] = useState<GlobalActivityData>({} as GlobalActivityData);
|
||||
const [globalActivity, setGlobalActivity] = useState<GlobalActivityData>(EMPTY_GLOBAL_ACTIVITY);
|
||||
const [globalActivityPerModel, setGlobalActivityPerModel] = useState<any[]>([]);
|
||||
const [selectedKeyToken, setSelectedKeyToken] = useState<string | null>(null);
|
||||
const [selectedTags, setSelectedTags] = useState<string[]>([ALL_TAGS]);
|
||||
|
|
@ -560,14 +562,14 @@ const UsagePage: React.FC<UsagePageProps> = ({ accessToken, token, userRole, use
|
|||
)}
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="all-up">
|
||||
<TabsContent value="all-up" keepMounted>
|
||||
<Tabs defaultValue="cost">
|
||||
<TabsList className="mt-1">
|
||||
<TabsTrigger value="cost">Cost</TabsTrigger>
|
||||
<TabsTrigger value="activity">Activity</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="cost">
|
||||
<TabsContent value="cost" keepMounted>
|
||||
<div className="grid h-screen w-full grid-cols-2 gap-2">
|
||||
<div className="col-span-2">
|
||||
<p className="mt-2 mb-2 text-lg text-muted-foreground">
|
||||
|
|
@ -671,7 +673,7 @@ const UsagePage: React.FC<UsagePageProps> = ({ accessToken, token, userRole, use
|
|||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="activity">
|
||||
<TabsContent value="activity" keepMounted>
|
||||
<div className="grid h-[75vh] w-full grid-cols-1 gap-2">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
|
|
@ -751,7 +753,7 @@ const UsagePage: React.FC<UsagePageProps> = ({ accessToken, token, userRole, use
|
|||
</Tabs>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="team-based-usage">
|
||||
<TabsContent value="team-based-usage" keepMounted>
|
||||
<div className="grid h-[75vh] w-full grid-cols-2 gap-2">
|
||||
<div className="col-span-2">
|
||||
<Card className="mb-2">
|
||||
|
|
@ -782,7 +784,7 @@ const UsagePage: React.FC<UsagePageProps> = ({ accessToken, token, userRole, use
|
|||
</div>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="customer-usage">
|
||||
<TabsContent value="customer-usage" keepMounted>
|
||||
<p className="mb-2 text-[12px] text-muted-foreground italic">
|
||||
Customers of your LLM API calls. Tracked when a `user` param is passed in your LLM calls{" "}
|
||||
<a
|
||||
|
|
@ -860,7 +862,7 @@ const UsagePage: React.FC<UsagePageProps> = ({ accessToken, token, userRole, use
|
|||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="tag-based-usage">
|
||||
<TabsContent value="tag-based-usage" keepMounted>
|
||||
<div className="grid grid-cols-2">
|
||||
<div className="col-span-1">
|
||||
<AdvancedDatePicker
|
||||
|
|
|
|||
|
|
@ -63,7 +63,11 @@ export default function PlaygroundPage() {
|
|||
Agent Builder (Experimental)
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="chat" className="mt-0 h-full min-h-0 min-w-0 overflow-hidden data-hidden:hidden">
|
||||
<TabsContent
|
||||
value="chat"
|
||||
className="mt-0 h-full min-h-0 min-w-0 overflow-hidden data-hidden:hidden"
|
||||
keepMounted
|
||||
>
|
||||
<ChatUI
|
||||
accessToken={accessToken}
|
||||
token={token}
|
||||
|
|
@ -73,13 +77,13 @@ export default function PlaygroundPage() {
|
|||
proxySettings={proxySettings}
|
||||
/>
|
||||
</TabsContent>
|
||||
<TabsContent value="compare" className="mt-0 h-full data-hidden:hidden">
|
||||
<TabsContent value="compare" className="mt-0 h-full data-hidden:hidden" keepMounted>
|
||||
<CompareUI accessToken={accessToken} disabledPersonalKeyCreation={disabledPersonalKeyCreation} />
|
||||
</TabsContent>
|
||||
<TabsContent value="compliance" className="mt-0 h-full data-hidden:hidden">
|
||||
<TabsContent value="compliance" className="mt-0 h-full data-hidden:hidden" keepMounted>
|
||||
<ComplianceUI accessToken={accessToken} disabledPersonalKeyCreation={disabledPersonalKeyCreation} />
|
||||
</TabsContent>
|
||||
<TabsContent value="agent-builder" className="mt-0 h-full data-hidden:hidden">
|
||||
<TabsContent value="agent-builder" className="mt-0 h-full data-hidden:hidden" keepMounted>
|
||||
<DeprecationBanner featureName="The Playground's Agent Builder" />
|
||||
<AgentBuilderView
|
||||
accessToken={accessToken}
|
||||
|
|
|
|||
|
|
@ -409,22 +409,22 @@ const PoliciesPanel: React.FC<PoliciesPanelProps> = ({ accessToken, userRole })
|
|||
return (
|
||||
<div className="m-8 mx-auto w-full flex-auto overflow-y-auto p-2">
|
||||
<Tabs value={activeTab} onValueChange={setActiveTab}>
|
||||
<TabsList className="mb-4">
|
||||
<TabsTrigger value="templates" className="flex-none">
|
||||
<TabsList variant="line" className="mb-4 h-auto w-full justify-start rounded-none border-b p-0">
|
||||
<TabsTrigger value="templates" className="flex-none rounded-none px-4 py-2">
|
||||
Templates
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="policies" className="flex-none">
|
||||
<TabsTrigger value="policies" className="flex-none rounded-none px-4 py-2">
|
||||
Policies
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="attachments" className="flex-none">
|
||||
<TabsTrigger value="attachments" className="flex-none rounded-none px-4 py-2">
|
||||
Attachments
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="simulator" className="flex-none">
|
||||
<TabsTrigger value="simulator" className="flex-none rounded-none px-4 py-2">
|
||||
Policy Simulator
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<TabsContent value="templates">
|
||||
<TabsContent value="templates" keepMounted>
|
||||
<AboutPoliciesAlert />
|
||||
<PolicyTemplates
|
||||
onUseTemplate={handleUseTemplate}
|
||||
|
|
@ -434,7 +434,7 @@ const PoliciesPanel: React.FC<PoliciesPanelProps> = ({ accessToken, userRole })
|
|||
/>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="policies">
|
||||
<TabsContent value="policies" keepMounted>
|
||||
<AboutPoliciesAlert />
|
||||
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
|
|
@ -503,7 +503,7 @@ const PoliciesPanel: React.FC<PoliciesPanelProps> = ({ accessToken, userRole })
|
|||
/>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="attachments">
|
||||
<TabsContent value="attachments" keepMounted>
|
||||
<DismissibleAlert title="About Policy Attachments" icon={<Info />}>
|
||||
<p className="mb-3">
|
||||
Policy attachments control where your policies apply. Policies don't do anything until you attach
|
||||
|
|
@ -571,7 +571,7 @@ const PoliciesPanel: React.FC<PoliciesPanelProps> = ({ accessToken, userRole })
|
|||
/>
|
||||
</TabsContent>
|
||||
|
||||
<TabsContent value="simulator">
|
||||
<TabsContent value="simulator" keepMounted>
|
||||
<PolicyTestPanel accessToken={accessToken} />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
|
|
|||
|
|
@ -261,19 +261,19 @@ const GeneralSettings: React.FC<GeneralSettingsPageProps> = ({ accessToken, user
|
|||
<TabsTrigger value="prompt-caching">Prompt Caching</TabsTrigger>
|
||||
<TabsTrigger value="general">General</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="loadbalancing" className="px-8 py-6">
|
||||
<TabsContent value="loadbalancing" className="px-8 py-6" keepMounted>
|
||||
<RouterSettings accessToken={accessToken} userRole={userRole} userID={userID} />
|
||||
</TabsContent>
|
||||
<TabsContent value="routing-groups" className="px-8 py-6">
|
||||
<TabsContent value="routing-groups" className="px-8 py-6" keepMounted>
|
||||
<RoutingGroups />
|
||||
</TabsContent>
|
||||
<TabsContent value="fallbacks" className="px-8 py-6">
|
||||
<TabsContent value="fallbacks" className="px-8 py-6" keepMounted>
|
||||
<Fallbacks accessToken={accessToken} userRole={userRole} userID={userID} />
|
||||
</TabsContent>
|
||||
<TabsContent value="prompt-caching" className="px-8 py-6">
|
||||
<TabsContent value="prompt-caching" className="px-8 py-6" keepMounted>
|
||||
<PromptCachingPanel accessToken={accessToken} settings={generalSettings} onChange={handleInputChange} />
|
||||
</TabsContent>
|
||||
<TabsContent value="general" className="px-8 py-6">
|
||||
<TabsContent value="general" className="px-8 py-6" keepMounted>
|
||||
<Card>
|
||||
<CardContent>
|
||||
<Table>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import * as networking from "@/components/networking";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import { renderWithProviders, screen, waitFor } from "../../../tests/test-utils";
|
||||
import ModelHubTable from "./ModelHubTable";
|
||||
|
|
@ -201,6 +202,41 @@ describe("ModelHubTable", () => {
|
|||
expect(getUiConfigCallOrder).toBeLessThan(modelHubPublicModelsCallOrder);
|
||||
});
|
||||
|
||||
describe("hub tabs", () => {
|
||||
const renderHub = async () => {
|
||||
vi.mocked(networking.modelHubCall).mockResolvedValue({
|
||||
data: [{ model_group: "claude-opus-4-8", providers: ["anthropic"], mode: "chat" }],
|
||||
});
|
||||
vi.mocked(networking.getConfigFieldSetting).mockResolvedValue({ field_value: false });
|
||||
vi.mocked(networking.getAgentsList).mockResolvedValue({ agents: [] });
|
||||
vi.mocked(networking.fetchMCPServers).mockResolvedValue([]);
|
||||
vi.mocked(networking.getUiSettings).mockResolvedValue({ values: {} });
|
||||
mockUseUISettings.mockReturnValue({ data: { values: {} }, isLoading: false });
|
||||
|
||||
const user = userEvent.setup();
|
||||
renderWithProviders(
|
||||
<ModelHubTable accessToken="test-token" publicPage={false} premiumUser={false} userRole="Admin" />,
|
||||
);
|
||||
return { user, search: await screen.findByPlaceholderText("Search model names...") };
|
||||
};
|
||||
|
||||
it("keeps the model filter typed on the Model Hub tab after visiting another hub", async () => {
|
||||
const { user, search } = await renderHub();
|
||||
|
||||
await user.type(search, "opus");
|
||||
await user.click(screen.getByRole("tab", { name: "Agent Hub" }));
|
||||
await user.click(screen.getByRole("tab", { name: "Model Hub" }));
|
||||
|
||||
expect(await screen.findByPlaceholderText("Search model names...")).toHaveValue("opus");
|
||||
});
|
||||
|
||||
it("renders the hub strip as underlined tabs rather than a segmented pill", async () => {
|
||||
await renderHub();
|
||||
|
||||
expect(screen.getByRole("tablist")).toHaveAttribute("data-variant", "line");
|
||||
});
|
||||
});
|
||||
|
||||
describe("authentication redirect behavior", () => {
|
||||
// Test cases where requireAuth is true - should redirect on invalid tokens
|
||||
testAuthRedirect(
|
||||
|
|
|
|||
|
|
@ -428,16 +428,24 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
|
||||
{/* Tab System for Model Hub, Agent Hub, MCP Hub, and Plugin Marketplace */}
|
||||
<Tabs defaultValue="models">
|
||||
<TabsList className="mb-4">
|
||||
<TabsTrigger value="models">Model Hub</TabsTrigger>
|
||||
<TabsTrigger value="agents">Agent Hub</TabsTrigger>
|
||||
<TabsTrigger value="mcp">MCP Hub</TabsTrigger>
|
||||
<TabsTrigger value="skills">Skill Hub</TabsTrigger>
|
||||
<TabsList variant="line" className="mb-4 h-auto w-full justify-start rounded-none border-b p-0">
|
||||
<TabsTrigger value="models" className="flex-none rounded-none px-4 py-2">
|
||||
Model Hub
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="agents" className="flex-none rounded-none px-4 py-2">
|
||||
Agent Hub
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="mcp" className="flex-none rounded-none px-4 py-2">
|
||||
MCP Hub
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="skills" className="flex-none rounded-none px-4 py-2">
|
||||
Skill Hub
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<div>
|
||||
{/* Model Hub Tab */}
|
||||
<TabsContent value="models">
|
||||
<TabsContent value="models" keepMounted>
|
||||
{/* Model Filters and Table */}
|
||||
<Card className="px-6">
|
||||
{/* Header with Make Public Button */}
|
||||
|
|
@ -482,7 +490,7 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
</TabsContent>
|
||||
|
||||
{/* Agent Hub Tab */}
|
||||
<TabsContent value="agents">
|
||||
<TabsContent value="agents" keepMounted>
|
||||
<Card className="px-6">
|
||||
{/* Header with Make Public Button */}
|
||||
{publicPage == false && canModify && (
|
||||
|
|
@ -516,7 +524,7 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
</TabsContent>
|
||||
|
||||
{/* MCP Hub Tab */}
|
||||
<TabsContent value="mcp">
|
||||
<TabsContent value="mcp" keepMounted>
|
||||
<Card className="px-6">
|
||||
{/* Header with Make Public Button */}
|
||||
{publicPage == false && canModify && (
|
||||
|
|
@ -553,7 +561,7 @@ const ModelHubTable: React.FC<ModelHubTableProps> = ({ accessToken, publicPage,
|
|||
</TabsContent>
|
||||
|
||||
{/* Skill Hub Tab */}
|
||||
<TabsContent value="skills">
|
||||
<TabsContent value="skills" keepMounted>
|
||||
{publicPage == false && canModify && (
|
||||
<div className="flex justify-end mb-4">
|
||||
<Button onClick={() => setIsMakeSkillPublicModalVisible(true)}>Select Skills to Make Public</Button>
|
||||
|
|
|
|||
|
|
@ -124,11 +124,11 @@ const PerUserUsage: React.FC<PerUserUsageProps> = ({ accessToken, selectedTags,
|
|||
<p className="text-sm text-muted-foreground">Individual developer usage metrics</p>
|
||||
|
||||
<Tabs defaultValue="details">
|
||||
<TabsList className="mb-6">
|
||||
<TabsTrigger value="details" className="flex-none px-3">
|
||||
<TabsList variant="line" className="mb-6 h-auto w-full justify-start rounded-none border-b p-0">
|
||||
<TabsTrigger value="details" className="flex-none rounded-none px-4 py-2">
|
||||
User Details
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="distribution" className="flex-none px-3">
|
||||
<TabsTrigger value="distribution" className="flex-none rounded-none px-4 py-2">
|
||||
Usage Distribution
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
|
|
|||
|
|
@ -491,7 +491,7 @@ const Settings: React.FC<SettingsPageProps> = ({ accessToken, userRole, userID,
|
|||
<TabsTrigger value="alerting-settings">Alerting Settings</TabsTrigger>
|
||||
<TabsTrigger value="email-alerts">Email Alerts</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="logging-callbacks">
|
||||
<TabsContent value="logging-callbacks" keepMounted>
|
||||
<LoggingCallbacksTable
|
||||
callbacks={callbacks}
|
||||
availableCallbacks={allCallbacks}
|
||||
|
|
@ -512,12 +512,12 @@ const Settings: React.FC<SettingsPageProps> = ({ accessToken, userRole, userID,
|
|||
}}
|
||||
/>
|
||||
</TabsContent>
|
||||
<TabsContent value="cloudzero-cost-tracking">
|
||||
<TabsContent value="cloudzero-cost-tracking" keepMounted>
|
||||
<div className="p-8">
|
||||
<CloudZeroCostTracking />
|
||||
</div>
|
||||
</TabsContent>
|
||||
<TabsContent value="alerting-types">
|
||||
<TabsContent value="alerting-types" keepMounted>
|
||||
<Card className="p-6">
|
||||
<p className="my-2">
|
||||
Alerts are only supported for Slack Webhook URLs. Get your webhook urls from{" "}
|
||||
|
|
@ -601,10 +601,10 @@ const Settings: React.FC<SettingsPageProps> = ({ accessToken, userRole, userID,
|
|||
</Button>
|
||||
</Card>
|
||||
</TabsContent>
|
||||
<TabsContent value="alerting-settings">
|
||||
<TabsContent value="alerting-settings" keepMounted>
|
||||
<AlertingSettings accessToken={accessToken} premiumUser={premiumUser} />
|
||||
</TabsContent>
|
||||
<TabsContent value="email-alerts">
|
||||
<TabsContent value="email-alerts" keepMounted>
|
||||
<EmailSettings accessToken={accessToken} premiumUser={premiumUser} alerts={alerts} />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
|
|
|||
|
|
@ -599,9 +599,13 @@ export default function KeyInfoView({
|
|||
</Dialog>
|
||||
|
||||
<Tabs defaultValue="overview">
|
||||
<TabsList className="mb-4">
|
||||
<TabsTrigger value="overview">Overview</TabsTrigger>
|
||||
<TabsTrigger value="settings">Settings</TabsTrigger>
|
||||
<TabsList variant="line" className="mb-4 h-auto w-full justify-start rounded-none border-b p-0">
|
||||
<TabsTrigger value="overview" className="flex-none rounded-none px-4 py-2">
|
||||
Overview
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="settings" className="flex-none rounded-none px-4 py-2">
|
||||
Settings
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -485,11 +485,11 @@ const UserAgentActivity: React.FC<UserAgentActivityProps> = ({ accessToken, user
|
|||
<Card>
|
||||
<CardContent>
|
||||
<Tabs defaultValue="active-users">
|
||||
<TabsList className="mb-6">
|
||||
<TabsTrigger value="active-users" className="flex-none px-3">
|
||||
<TabsList variant="line" className="mb-6 h-auto w-full justify-start rounded-none border-b p-0">
|
||||
<TabsTrigger value="active-users" className="flex-none rounded-none px-4 py-2">
|
||||
DAU/WAU/MAU
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="per-user" className="flex-none px-3">
|
||||
<TabsTrigger value="per-user" className="flex-none rounded-none px-4 py-2">
|
||||
Per User Usage (Last 30 Days)
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
|
@ -502,14 +502,14 @@ const UserAgentActivity: React.FC<UserAgentActivityProps> = ({ accessToken, user
|
|||
</div>
|
||||
|
||||
<Tabs defaultValue="dau">
|
||||
<TabsList className="mb-6">
|
||||
<TabsTrigger value="dau" className="flex-none px-3">
|
||||
<TabsList variant="line" className="mb-6 h-auto w-full justify-start rounded-none border-b p-0">
|
||||
<TabsTrigger value="dau" className="flex-none rounded-none px-4 py-2">
|
||||
DAU
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="wau" className="flex-none px-3">
|
||||
<TabsTrigger value="wau" className="flex-none rounded-none px-4 py-2">
|
||||
WAU
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="mau" className="flex-none px-3">
|
||||
<TabsTrigger value="mau" className="flex-none rounded-none px-4 py-2">
|
||||
MAU
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue