improve model picker

This commit is contained in:
miloschwartz
2026-08-14 11:38:11 -04:00
parent c8f170d197
commit 7375b9efae
7 changed files with 45 additions and 197 deletions
+2 -1
View File
@@ -80,7 +80,8 @@ export default async function SettingsLayout(props: SettingsLayoutProps) {
orgId={params.orgId}
orgs={orgs}
navItems={orgNavSections(env, {
isPrimaryOrg: primaryOrg
isPrimaryOrg: primaryOrg,
isServerAdmin: user.serverAdmin
})}
commandNavItems={commandBarNavSections(env, {
isPrimaryOrg: primaryOrg
+22
View File
@@ -47,6 +47,7 @@ export type SidebarNavSection = {
export type OrgNavSectionsOptions = {
isPrimaryOrg?: boolean;
isServerAdmin?: boolean;
};
// Merged from 'user-management-and-resources' branch
@@ -67,6 +68,27 @@ export const orgNavSections = (
env?: Env,
options?: OrgNavSectionsOptions
): SidebarNavSection[] => [
{
heading: "sidebarOverview",
items: [
{
title: "resourceSidebarLauncherTitle",
href: "/{orgId}",
icon: <LayoutGrid className="size-4 flex-none" />,
exact: true
},
...(options?.isServerAdmin
? [
{
title: "serverAdmin",
href: "/admin",
icon: <Server className="size-4 flex-none" />,
exact: true
}
]
: [])
]
},
{
heading: "network",
items: [
@@ -476,11 +476,6 @@ export default function CreateVirtualApiKeyForm({
</div>
<div className="space-y-4 mt-4">
<p className="text-sm text-muted-foreground">
{t(
"virtualApiKeysInferenceBudgetDescription"
)}
</p>
<BudgetRowsFields
rows={pendingBudgetRows}
onChange={setPendingBudgetRows}
-5
View File
@@ -524,11 +524,6 @@ export default function EditVirtualApiKeyForm({
</div>
<div className="space-y-4 mt-4">
<p className="text-sm text-muted-foreground">
{t(
"virtualApiKeysInferenceBudgetDescription"
)}
</p>
<BudgetRowsFields
rows={pendingBudgetRows}
onChange={setPendingBudgetRows}
+1 -65
View File
@@ -14,13 +14,11 @@ import {
SheetTitle,
SheetTrigger
} from "@app/components/ui/sheet";
import { useUserContext } from "@app/hooks/useUserContext";
import { cn } from "@app/lib/cn";
import { ListUserOrgsResponse } from "@server/routers/org";
import { Menu, Server, Settings, LayoutGrid } from "lucide-react";
import { Menu, Settings } from "lucide-react";
import { useTranslations } from "next-intl";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useState } from "react";
interface LayoutMobileMenuProps {
@@ -45,18 +43,8 @@ export function LayoutMobileMenu({
showViewAsAdmin = false
}: LayoutMobileMenuProps) {
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
const pathname = usePathname();
const isAdminPage = pathname?.startsWith("/admin");
const { user } = useUserContext();
const t = useTranslations();
const showMobileNav = showSidebar || launcherMode;
const currentOrg = orgs?.find((org) => org.orgId === orgId);
const isSettingsPage = Boolean(
orgId && pathname?.includes(`/${orgId}/settings`)
);
const canViewResourceLauncher = Boolean(
currentOrg?.isAdmin || currentOrg?.isOwner
);
const mobileNavLinkClassName = cn(
"flex items-center rounded transition-colors text-muted-foreground hover:text-foreground text-sm w-full hover:bg-secondary/50 dark:hover:bg-secondary/20 rounded-md px-3 py-1.5"
@@ -183,58 +171,6 @@ export function LayoutMobileMenu({
</div>
<div className="flex-1 overflow-y-auto relative">
<div className="px-3">
{!isAdminPage &&
isSettingsPage &&
canViewResourceLauncher &&
orgId && (
<div className="mb-1">
<Link
href={`/${orgId}`}
className={
mobileNavLinkClassName
}
onClick={() =>
setIsMobileMenuOpen(
false
)
}
>
<span className="flex-shrink-0 w-5 h-5 flex items-center justify-center text-muted-foreground mr-3">
<LayoutGrid className="h-4 w-4" />
</span>
<span className="flex-1">
{t(
"resourceSidebarLauncherTitle"
)}
</span>
</Link>
</div>
)}
{!isAdminPage &&
user.serverAdmin && (
<div className="mb-1">
<Link
href="/admin"
className={
mobileNavLinkClassName
}
onClick={() =>
setIsMobileMenuOpen(
false
)
}
>
<span className="flex-shrink-0 w-5 h-5 flex items-center justify-center text-muted-foreground mr-3">
<Server className="h-4 w-4" />
</span>
<span className="flex-1">
{t(
"serverAdmin"
)}
</span>
</Link>
</div>
)}
<SidebarNav
sections={navItems}
onItemClick={() =>
+4 -117
View File
@@ -18,13 +18,7 @@ import { approvalQueries } from "@app/lib/queries";
import { build } from "@server/build";
import { useQuery } from "@tanstack/react-query";
import { ListUserOrgsResponse } from "@server/routers/org";
import {
ArrowRight,
ExternalLink,
LayoutGrid,
PanelRightOpen,
Server
} from "lucide-react";
import { ExternalLink, PanelRightOpen } from "lucide-react";
import { useTranslations } from "next-intl";
import dynamic from "next/dynamic";
import Link from "next/link";
@@ -136,13 +130,6 @@ export function LayoutSidebar({
const showTrial =
build === "saas" && Boolean(orgId) && subscriptionContext?.isTrial;
const isSettingsPage = Boolean(
orgId && pathname?.includes(`/${orgId}/settings`)
);
const canViewResourceLauncher = Boolean(
currentOrg?.isAdmin || currentOrg?.isOwner
);
return (
<div
className={cn(
@@ -165,107 +152,6 @@ export function LayoutSidebar({
/>
<div className="flex-1 overflow-y-auto relative">
<div className="px-2 pt-3">
{!isAdminPage &&
isSettingsPage &&
canViewResourceLauncher &&
orgId && (
<div
className={cn(
"shrink-0",
isSidebarCollapsed ? "mb-4" : "mb-1"
)}
>
{isSidebarCollapsed ? (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Link
href={`/${orgId}`}
className={cn(
"flex items-center transition-colors text-muted-foreground hover:text-foreground text-sm w-full hover:bg-sidebar-accent dark:hover:bg-sidebar-accent/50 rounded-md px-2 py-2 justify-center"
)}
>
<span className="flex-shrink-0 w-5 h-5 flex items-center justify-center text-muted-foreground">
<LayoutGrid className="h-4 w-4" />
</span>
</Link>
</TooltipTrigger>
<TooltipContent
side="right"
sideOffset={8}
>
<p>
{t(
"resourceSidebarLauncherTitle"
)}
</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
) : (
<Link
href={`/${orgId}`}
className={cn(
"flex items-center transition-colors text-muted-foreground hover:text-foreground text-sm w-full hover:bg-sidebar-accent dark:hover:bg-sidebar-accent/50 rounded-md px-3 py-1.5"
)}
>
<span className="flex-shrink-0 mr-3 w-5 h-5 flex items-center justify-center text-muted-foreground">
<LayoutGrid className="h-4 w-4" />
</span>
<span className="flex-1">
{t("resourceSidebarLauncherTitle")}
</span>
</Link>
)}
</div>
)}
{!isAdminPage && user.serverAdmin && (
<div
className={cn(
"shrink-0",
isSidebarCollapsed ? "mb-4" : "mb-1"
)}
>
{isSidebarCollapsed ? (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Link
href="/admin"
className={cn(
"flex items-center transition-colors text-muted-foreground hover:text-foreground text-sm w-full hover:bg-sidebar-accent dark:hover:bg-sidebar-accent/50 rounded-md px-2 py-2 justify-center"
)}
>
<span className="flex-shrink-0 w-5 h-5 flex items-center justify-center text-muted-foreground">
<Server className="h-4 w-4" />
</span>
</Link>
</TooltipTrigger>
<TooltipContent
side="right"
sideOffset={8}
>
<p>{t("serverAdmin")}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
) : (
<Link
href="/admin"
className={cn(
"flex items-center transition-colors text-muted-foreground hover:text-foreground text-sm w-full hover:bg-sidebar-accent dark:hover:bg-sidebar-accent/50 rounded-md px-3 py-1.5"
)}
>
<span className="flex-shrink-0 mr-3 w-5 h-5 flex items-center justify-center text-muted-foreground">
<Server className="h-4 w-4" />
</span>
<span className="flex-1">
{t("serverAdmin")}
</span>
</Link>
)}
</div>
)}
<SidebarNav
sections={navItems}
isCollapsed={isSidebarCollapsed}
@@ -304,8 +190,9 @@ export function LayoutSidebar({
<div
className={cn(
"pt-1 flex flex-col shrink-0 gap-2 w-full border-t border-border",
isSidebarCollapsed && "pb-2"
"pt-1 flex flex-col shrink-0 gap-2 w-full",
!isSidebarCollapsed && "border-t border-border",
isSidebarCollapsed && "pb-4"
)}
>
{canShowProductUpdates ? (
+16 -4
View File
@@ -34,6 +34,7 @@ export type SidebarNavItem = {
showEE?: boolean;
isBeta?: boolean;
items?: SidebarNavItem[];
exact?: boolean;
};
export type SidebarNavSection = {
@@ -49,7 +50,14 @@ export interface SidebarNavProps extends React.HTMLAttributes<HTMLElement> {
notificationCounts?: Record<string, number | undefined>;
}
function isPathActive(pathname: string, href: string): boolean {
function isPathActive(
pathname: string,
href: string,
exact?: boolean
): boolean {
if (exact) {
return pathname === href;
}
return pathname === href || pathname.startsWith(`${href}/`);
}
@@ -289,7 +297,11 @@ function CollapsedNavItemWithPopover({
childItem.href
);
const childIsActive = childHydratedHref
? isPathActive(pathname, childHydratedHref)
? isPathActive(
pathname,
childHydratedHref,
childItem.exact
)
: false;
const childIsEE =
build === "enterprise" &&
@@ -396,7 +408,7 @@ export function SidebarNav({
function isItemOrChildActive(item: SidebarNavItem): boolean {
const hydratedHref = hydrateHref(item.href);
if (hydratedHref && isPathActive(pathname, hydratedHref)) {
if (hydratedHref && isPathActive(pathname, hydratedHref, item.exact)) {
return true;
}
if (item.items) {
@@ -412,7 +424,7 @@ export function SidebarNav({
const hydratedHref = hydrateHref(item.href);
const hasNestedItems = item.items && item.items.length > 0;
const isActive = hydratedHref
? isPathActive(pathname, hydratedHref)
? isPathActive(pathname, hydratedHref, item.exact)
: false;
const isChildActive = hasNestedItems
? isItemOrChildActive(item)