mirror of
https://github.com/fosrl/pangolin.git
synced 2026-08-15 00:39:43 +02:00
improve model picker
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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={() =>
|
||||
|
||||
@@ -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 ? (
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user