feat(admin): add mcp as main navigation item when enabled, and make it route-aware

This commit is contained in:
dswbx
2025-12-05 15:25:06 +01:00
parent 9958dd7308
commit 40b70e7a20
7 changed files with 136 additions and 111 deletions

View File

@@ -474,6 +474,7 @@ type SectionHeaderAccordionItemProps = {
ActiveIcon?: any;
children?: React.ReactNode;
renderHeaderRight?: (props: { open: boolean }) => React.ReactNode;
scrollContainerRef?: React.RefObject<HTMLDivElement>;
};
export const SectionHeaderAccordionItem = ({
@@ -483,6 +484,7 @@ export const SectionHeaderAccordionItem = ({
ActiveIcon = IconChevronUp,
children,
renderHeaderRight,
scrollContainerRef,
}: SectionHeaderAccordionItemProps) => (
<div
style={{ minHeight: 49 }}
@@ -493,6 +495,8 @@ export const SectionHeaderAccordionItem = ({
: "flex-initial cursor-pointer hover:bg-primary/5",
)}
>
{/** biome-ignore lint/a11y/noStaticElementInteractions: . */}
{/** biome-ignore lint/a11y/useKeyWithClickEvents: . */}
<div
className={twMerge(
"flex flex-row bg-muted/10 border-muted border-b h-14 py-4 pr-4 pl-2 items-center gap-2",
@@ -501,14 +505,12 @@ export const SectionHeaderAccordionItem = ({
>
<IconButton Icon={open ? ActiveIcon : IconChevronDown} disabled={open} />
<h2 className="text-lg dark:font-bold font-semibold select-text">{title}</h2>
<div className="flex flex-grow" />
<div className="flex grow" />
{renderHeaderRight?.({ open })}
</div>
<div
className={twMerge(
"overflow-y-scroll transition-all",
open ? " flex-grow" : "h-0 opacity-0",
)}
ref={scrollContainerRef}
className={twMerge("overflow-y-scroll transition-all", open ? " grow" : "h-0 opacity-0")}
>
{children}
</div>
@@ -518,14 +520,25 @@ export const SectionHeaderAccordionItem = ({
export const RouteAwareSectionHeaderAccordionItem = ({
routePattern,
identifier,
renderHeaderRight,
...props
}: Omit<SectionHeaderAccordionItemProps, "open" | "toggle"> & {
}: Omit<SectionHeaderAccordionItemProps, "open" | "toggle" | "renderHeaderRight"> & {
renderHeaderRight?: (props: { open: boolean; active: boolean }) => React.ReactNode;
// it's optional because it could be provided using the context
routePattern?: string;
identifier: string;
}) => {
const { active, toggle } = useRoutePathState(routePattern, identifier);
return <SectionHeaderAccordionItem {...props} open={active} toggle={toggle} />;
return (
<SectionHeaderAccordionItem
{...props}
open={active}
toggle={toggle}
renderHeaderRight={
renderHeaderRight && ((props) => renderHeaderRight?.({ open: props.open, active }))
}
/>
);
};
export const Separator = ({ className, ...props }: ComponentPropsWithoutRef<"hr">) => (

View File

@@ -30,28 +30,29 @@ import { useAppShellAdminOptions } from "ui/options";
export function HeaderNavigation() {
const [location, navigate] = useLocation();
const { config } = useBknd();
const items: {
label: string;
href: string;
Icon: any;
Icon?: any;
exact?: boolean;
tooltip?: string;
disabled?: boolean;
}[] = [
/*{
label: "Base",
href: "#",
exact: true,
Icon: TbLayoutDashboard,
disabled: true,
tooltip: "Coming soon"
},*/
{ label: "Data", href: "/data", Icon: TbDatabase },
{ label: "Auth", href: "/auth", Icon: TbFingerprint },
{ label: "Media", href: "/media", Icon: TbPhoto },
{ label: "Flows", href: "/flows", Icon: TbHierarchy2 },
];
if (import.meta.env.DEV || Object.keys(config.flows?.flows ?? {}).length > 0) {
items.push({ label: "Flows", href: "/flows", Icon: TbHierarchy2 });
}
if (config.server.mcp.enabled) {
items.push({ label: "MCP", href: "/tools/mcp", Icon: McpIcon });
}
const activeItem = items.find((item) =>
item.exact ? location === item.href : location.startsWith(item.href),
);