Skip to main content
Glama
project-dashboard-layout-header.tsx8.77 kB
import { t } from 'i18next'; import { ChevronDown, History, Link2, ListTodo, Package, Table2, Workflow, } from 'lucide-react'; import { useState } from 'react'; import { useLocation, useNavigate } from 'react-router-dom'; import { McpSvg } from '@/assets/img/custom/mcp'; import { useEmbedding } from '@/components/embed-provider'; import { Button } from '@/components/ui/button'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu'; import { Separator } from '@/components/ui/separator'; import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { useAuthorization } from '@/hooks/authorization-hooks'; import { platformHooks } from '@/hooks/platform-hooks'; import { projectHooks } from '@/hooks/project-hooks'; import { authenticationSession } from '@/lib/authentication-session'; import { cn } from '@/lib/utils'; import { Permission } from '@activepieces/shared'; import { ProjectDashboardPageHeader } from './project-dashboard-page-header'; import { ProjectDashboardLayoutHeaderTab } from '.'; export const ProjectDashboardLayoutHeader = () => { const { project } = projectHooks.useCurrentProject(); const { platform } = platformHooks.useCurrentPlatform(); const { checkAccess } = useAuthorization(); const { embedState } = useEmbedding(); const location = useLocation(); const navigate = useNavigate(); const isEmbedded = embedState.isEmbedded; const flowsLink: ProjectDashboardLayoutHeaderTab = { to: authenticationSession.appendProjectRoutePrefix('/flows'), label: t('Flows'), icon: Workflow, hasPermission: checkAccess(Permission.READ_FLOW), show: true, }; const tablesLink: ProjectDashboardLayoutHeaderTab = { to: authenticationSession.appendProjectRoutePrefix('/tables'), label: t('Tables'), show: platform.plan.tablesEnabled, icon: Table2, hasPermission: checkAccess(Permission.READ_TABLE), }; const runsLink: ProjectDashboardLayoutHeaderTab = { to: authenticationSession.appendProjectRoutePrefix('/runs'), label: t('Runs'), icon: History, hasPermission: checkAccess(Permission.READ_RUN), show: true, }; const moreItems: ProjectDashboardLayoutHeaderTab[] = [ { to: authenticationSession.appendProjectRoutePrefix('/connections'), label: t('Connections'), icon: Link2, hasPermission: checkAccess(Permission.READ_APP_CONNECTION), show: true, }, { to: authenticationSession.appendProjectRoutePrefix('/mcps'), label: t('MCP'), show: platform.plan.mcpsEnabled, hasPermission: checkAccess(Permission.READ_MCP), icon: McpSvg, }, { to: authenticationSession.appendProjectRoutePrefix('/todos'), label: t('Todos'), show: platform.plan.todosEnabled, icon: ListTodo, hasPermission: checkAccess(Permission.READ_TODOS), }, { to: authenticationSession.appendProjectRoutePrefix('/releases'), icon: Package, label: t('Releases'), hasPermission: project.releasesEnabled && checkAccess(Permission.READ_PROJECT_RELEASE) && !isEmbedded, show: project.releasesEnabled, }, ]; const [pinnedItem, setPinnedItem] = useState<ProjectDashboardLayoutHeaderTab | null>(() => { const matchedItem = moreItems.find( (item) => item.show && item.hasPermission !== false && location.pathname.includes(item.to), ); return matchedItem || null; }); return ( <div className="flex flex-col px-4 gap-1"> {!isEmbedded && ( <ProjectDashboardPageHeader title={project?.displayName} /> )} <Tabs> {!embedState.hideSideNav && ( <TabsList variant="outline"> {flowsLink.show && flowsLink.hasPermission && ( <TabsTrigger value="flows" variant="outline" className="pb-3" onClick={() => navigate(flowsLink.to)} data-state={ location.pathname.includes(flowsLink.to) ? 'active' : 'inactive' } > <flowsLink.icon className="h-4 w-4 mr-2" /> {t('Flows')} </TabsTrigger> )} {tablesLink.show && tablesLink.hasPermission && ( <TabsTrigger value="tables" variant="outline" className="pb-3" onClick={() => navigate(tablesLink.to)} data-state={ location.pathname.includes(tablesLink.to) ? 'active' : 'inactive' } > <tablesLink.icon className="h-4 w-4 mr-2" /> {t('Tables')} </TabsTrigger> )} <Separator orientation="vertical" className="mx-2 h-5 self-center mb-2" /> {runsLink.show && runsLink.hasPermission && ( <TabsTrigger value="runs" variant="outline" className="pb-3" onClick={() => navigate(runsLink.to)} data-state={ location.pathname.includes(runsLink.to) ? 'active' : 'inactive' } > <runsLink.icon className="h-4 w-4 mr-2" /> {t('Runs')} </TabsTrigger> )} {pinnedItem && pinnedItem.show && pinnedItem.hasPermission && ( <TabsTrigger value="pinned" variant="outline" className="pb-3" onClick={() => navigate(pinnedItem.to)} data-state={ location.pathname.includes(pinnedItem.to) ? 'active' : 'inactive' } > <pinnedItem.icon className="h-4 w-4 mr-2" /> {pinnedItem.label} </TabsTrigger> )} <DropdownMenu> <DropdownMenuTrigger asChild> {(() => { const filteredMoreItems = moreItems.filter( (item) => item.to !== pinnedItem?.to, ); const activeItem = filteredMoreItems.find((item) => location.pathname.includes(item.to), ); if (activeItem) { return ( <TabsTrigger value="more" variant="outline" className="pb-3" data-state="active" > <activeItem.icon className="h-4 w-4 mr-2" /> {activeItem.label} <ChevronDown className="h-4 w-4 ml-1" /> </TabsTrigger> ); } return ( <Button variant="ghost" size="sm" className="ml-2 h-auto text-muted-foreground hover:text-foreground mb-2" > {t('More')} <ChevronDown className="h-4 w-4" /> </Button> ); })()} </DropdownMenuTrigger> <DropdownMenuContent align="start" className="w-64"> {moreItems .filter( (item) => item.show && item.hasPermission !== false && item.to !== pinnedItem?.to, ) .map((item) => { return ( <DropdownMenuItem key={item.to} onClick={() => { setPinnedItem(item); navigate(item.to); }} > <div className="flex items-center gap-2"> <item.icon className={cn('size-4')} /> <span>{item.label}</span> </div> </DropdownMenuItem> ); })} </DropdownMenuContent> </DropdownMenu> </TabsList> )} </Tabs> </div> ); }; ProjectDashboardLayoutHeader.displayName = 'ProjectDashboardLayoutHeader'; export default ProjectDashboardLayoutHeader;

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/activepieces/activepieces'

If you have feedback or need assistance with the MCP directory API, please join our Discord server