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:
ryan-crabbe-berri 2026-08-19 13:28:20 -07:00 • committed by GitHub
parent 4bb3152cc5
commit 73e7105e60
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 148 additions and 90 deletions

View file

@ -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

View file

@ -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>

View file

@ -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>

View file

@ -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]">
{(() => {

View file

@ -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>

View file

@ -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>

View file

@ -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>
)}

View file

@ -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

View file

@ -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}

View file

@ -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&apos;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>

View file

@ -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>

View file

@ -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(

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>