mirror of
https://github.com/fosrl/pangolin.git
synced 2026-08-15 08:49:59 +02:00
improve model picker
This commit is contained in:
@@ -80,7 +80,8 @@ export default async function SettingsLayout(props: SettingsLayoutProps) {
|
|||||||
orgId={params.orgId}
|
orgId={params.orgId}
|
||||||
orgs={orgs}
|
orgs={orgs}
|
||||||
navItems={orgNavSections(env, {
|
navItems={orgNavSections(env, {
|
||||||
isPrimaryOrg: primaryOrg
|
isPrimaryOrg: primaryOrg,
|
||||||
|
isServerAdmin: user.serverAdmin
|
||||||
})}
|
})}
|
||||||
commandNavItems={commandBarNavSections(env, {
|
commandNavItems={commandBarNavSections(env, {
|
||||||
isPrimaryOrg: primaryOrg
|
isPrimaryOrg: primaryOrg
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ export type SidebarNavSection = {
|
|||||||
|
|
||||||
export type OrgNavSectionsOptions = {
|
export type OrgNavSectionsOptions = {
|
||||||
isPrimaryOrg?: boolean;
|
isPrimaryOrg?: boolean;
|
||||||
|
isServerAdmin?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Merged from 'user-management-and-resources' branch
|
// Merged from 'user-management-and-resources' branch
|
||||||
@@ -67,6 +68,27 @@ export const orgNavSections = (
|
|||||||
env?: Env,
|
env?: Env,
|
||||||
options?: OrgNavSectionsOptions
|
options?: OrgNavSectionsOptions
|
||||||
): SidebarNavSection[] => [
|
): 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",
|
heading: "network",
|
||||||
items: [
|
items: [
|
||||||
|
|||||||
@@ -476,11 +476,6 @@ export default function CreateVirtualApiKeyForm({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-4 mt-4">
|
<div className="space-y-4 mt-4">
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
{t(
|
|
||||||
"virtualApiKeysInferenceBudgetDescription"
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
<BudgetRowsFields
|
<BudgetRowsFields
|
||||||
rows={pendingBudgetRows}
|
rows={pendingBudgetRows}
|
||||||
onChange={setPendingBudgetRows}
|
onChange={setPendingBudgetRows}
|
||||||
|
|||||||
@@ -524,11 +524,6 @@ export default function EditVirtualApiKeyForm({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-4 mt-4">
|
<div className="space-y-4 mt-4">
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
{t(
|
|
||||||
"virtualApiKeysInferenceBudgetDescription"
|
|
||||||
)}
|
|
||||||
</p>
|
|
||||||
<BudgetRowsFields
|
<BudgetRowsFields
|
||||||
rows={pendingBudgetRows}
|
rows={pendingBudgetRows}
|
||||||
onChange={setPendingBudgetRows}
|
onChange={setPendingBudgetRows}
|
||||||
|
|||||||
@@ -14,13 +14,11 @@ import {
|
|||||||
SheetTitle,
|
SheetTitle,
|
||||||
SheetTrigger
|
SheetTrigger
|
||||||
} from "@app/components/ui/sheet";
|
} from "@app/components/ui/sheet";
|
||||||
import { useUserContext } from "@app/hooks/useUserContext";
|
|
||||||
import { cn } from "@app/lib/cn";
|
import { cn } from "@app/lib/cn";
|
||||||
import { ListUserOrgsResponse } from "@server/routers/org";
|
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 { useTranslations } from "next-intl";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { usePathname } from "next/navigation";
|
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
|
|
||||||
interface LayoutMobileMenuProps {
|
interface LayoutMobileMenuProps {
|
||||||
@@ -45,18 +43,8 @@ export function LayoutMobileMenu({
|
|||||||
showViewAsAdmin = false
|
showViewAsAdmin = false
|
||||||
}: LayoutMobileMenuProps) {
|
}: LayoutMobileMenuProps) {
|
||||||
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
|
||||||
const pathname = usePathname();
|
|
||||||
const isAdminPage = pathname?.startsWith("/admin");
|
|
||||||
const { user } = useUserContext();
|
|
||||||
const t = useTranslations();
|
const t = useTranslations();
|
||||||
const showMobileNav = showSidebar || launcherMode;
|
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(
|
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"
|
"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>
|
||||||
<div className="flex-1 overflow-y-auto relative">
|
<div className="flex-1 overflow-y-auto relative">
|
||||||
<div className="px-3">
|
<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
|
<SidebarNav
|
||||||
sections={navItems}
|
sections={navItems}
|
||||||
onItemClick={() =>
|
onItemClick={() =>
|
||||||
|
|||||||
@@ -18,13 +18,7 @@ import { approvalQueries } from "@app/lib/queries";
|
|||||||
import { build } from "@server/build";
|
import { build } from "@server/build";
|
||||||
import { useQuery } from "@tanstack/react-query";
|
import { useQuery } from "@tanstack/react-query";
|
||||||
import { ListUserOrgsResponse } from "@server/routers/org";
|
import { ListUserOrgsResponse } from "@server/routers/org";
|
||||||
import {
|
import { ExternalLink, PanelRightOpen } from "lucide-react";
|
||||||
ArrowRight,
|
|
||||||
ExternalLink,
|
|
||||||
LayoutGrid,
|
|
||||||
PanelRightOpen,
|
|
||||||
Server
|
|
||||||
} from "lucide-react";
|
|
||||||
import { useTranslations } from "next-intl";
|
import { useTranslations } from "next-intl";
|
||||||
import dynamic from "next/dynamic";
|
import dynamic from "next/dynamic";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
@@ -136,13 +130,6 @@ export function LayoutSidebar({
|
|||||||
const showTrial =
|
const showTrial =
|
||||||
build === "saas" && Boolean(orgId) && subscriptionContext?.isTrial;
|
build === "saas" && Boolean(orgId) && subscriptionContext?.isTrial;
|
||||||
|
|
||||||
const isSettingsPage = Boolean(
|
|
||||||
orgId && pathname?.includes(`/${orgId}/settings`)
|
|
||||||
);
|
|
||||||
const canViewResourceLauncher = Boolean(
|
|
||||||
currentOrg?.isAdmin || currentOrg?.isOwner
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
@@ -165,107 +152,6 @@ export function LayoutSidebar({
|
|||||||
/>
|
/>
|
||||||
<div className="flex-1 overflow-y-auto relative">
|
<div className="flex-1 overflow-y-auto relative">
|
||||||
<div className="px-2 pt-3">
|
<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
|
<SidebarNav
|
||||||
sections={navItems}
|
sections={navItems}
|
||||||
isCollapsed={isSidebarCollapsed}
|
isCollapsed={isSidebarCollapsed}
|
||||||
@@ -304,8 +190,9 @@ export function LayoutSidebar({
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"pt-1 flex flex-col shrink-0 gap-2 w-full border-t border-border",
|
"pt-1 flex flex-col shrink-0 gap-2 w-full",
|
||||||
isSidebarCollapsed && "pb-2"
|
!isSidebarCollapsed && "border-t border-border",
|
||||||
|
isSidebarCollapsed && "pb-4"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
{canShowProductUpdates ? (
|
{canShowProductUpdates ? (
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ export type SidebarNavItem = {
|
|||||||
showEE?: boolean;
|
showEE?: boolean;
|
||||||
isBeta?: boolean;
|
isBeta?: boolean;
|
||||||
items?: SidebarNavItem[];
|
items?: SidebarNavItem[];
|
||||||
|
exact?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type SidebarNavSection = {
|
export type SidebarNavSection = {
|
||||||
@@ -49,7 +50,14 @@ export interface SidebarNavProps extends React.HTMLAttributes<HTMLElement> {
|
|||||||
notificationCounts?: Record<string, number | undefined>;
|
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}/`);
|
return pathname === href || pathname.startsWith(`${href}/`);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,7 +297,11 @@ function CollapsedNavItemWithPopover({
|
|||||||
childItem.href
|
childItem.href
|
||||||
);
|
);
|
||||||
const childIsActive = childHydratedHref
|
const childIsActive = childHydratedHref
|
||||||
? isPathActive(pathname, childHydratedHref)
|
? isPathActive(
|
||||||
|
pathname,
|
||||||
|
childHydratedHref,
|
||||||
|
childItem.exact
|
||||||
|
)
|
||||||
: false;
|
: false;
|
||||||
const childIsEE =
|
const childIsEE =
|
||||||
build === "enterprise" &&
|
build === "enterprise" &&
|
||||||
@@ -396,7 +408,7 @@ export function SidebarNav({
|
|||||||
|
|
||||||
function isItemOrChildActive(item: SidebarNavItem): boolean {
|
function isItemOrChildActive(item: SidebarNavItem): boolean {
|
||||||
const hydratedHref = hydrateHref(item.href);
|
const hydratedHref = hydrateHref(item.href);
|
||||||
if (hydratedHref && isPathActive(pathname, hydratedHref)) {
|
if (hydratedHref && isPathActive(pathname, hydratedHref, item.exact)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (item.items) {
|
if (item.items) {
|
||||||
@@ -412,7 +424,7 @@ export function SidebarNav({
|
|||||||
const hydratedHref = hydrateHref(item.href);
|
const hydratedHref = hydrateHref(item.href);
|
||||||
const hasNestedItems = item.items && item.items.length > 0;
|
const hasNestedItems = item.items && item.items.length > 0;
|
||||||
const isActive = hydratedHref
|
const isActive = hydratedHref
|
||||||
? isPathActive(pathname, hydratedHref)
|
? isPathActive(pathname, hydratedHref, item.exact)
|
||||||
: false;
|
: false;
|
||||||
const isChildActive = hasNestedItems
|
const isChildActive = hasNestedItems
|
||||||
? isItemOrChildActive(item)
|
? isItemOrChildActive(item)
|
||||||
|
|||||||
Reference in New Issue
Block a user