import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbSeparator, } from "@/src/components/ui/breadcrumb"; import { Fragment } from "react"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/src/components/ui/dropdown-menu"; import { ChevronDownIcon, LoaderCircle, PlusIcon, Settings, Slash, } from "lucide-react"; import { Button } from "@/src/components/ui/button"; import { env } from "@/src/env.mjs"; import { useQueryProjectOrOrganization } from "@/src/features/projects/hooks"; import { useRouter } from "next/router"; import { useSession } from "next-auth/react"; import { useHasOrganizationAccess } from "@/src/features/rbac/utils/checkOrganizationAccess"; import { createOrganizationRoute, createProjectRoute, } from "@/src/features/setup/setupRoutes"; import { isCloudPlan, planLabels } from "@langfuse/shared"; import Link from "next/link"; import { Badge } from "@/src/components/ui/badge"; const LoadingMenuItem = () => ( Loading... ); const BreadcrumbComponent = ({ items, className, }: { items?: { name: string; href?: string }[]; className?: string; }) => { const router = useRouter(); const session = useSession(); const { organization, project } = useQueryProjectOrOrganization(); const organizations = session.data?.user?.organizations; const canCreateOrganizations = session.data?.user?.canCreateOrganizations; const canCreateProjects = useHasOrganizationAccess({ organizationId: organization?.id, scope: "projects:create", }); /** * Truncate the path before the first dynamic segment that is not allowlisted. * e.g. /project/[projectId]/traces/[traceId] -> /project/[projectId]/traces */ const truncatePathBeforeDynamicSegments = (path: string) => { const allowlistedIds = ["[projectId]", "[organizationId]", "[page]"]; const segments = router.route.split("/"); const idSegments = segments.filter( (segment) => segment.startsWith("[") && segment.endsWith("]"), ); const stopSegment = idSegments.filter((id) => !allowlistedIds.includes(id)); if (stopSegment.length === 0) return path; const stopIndex = segments.indexOf(stopSegment[0]); const truncatedPath = path.split("/").slice(0, stopIndex).join("/"); return truncatedPath; }; const getProjectPath = (projectId: string) => router.query.projectId ? truncatePathBeforeDynamicSegments(router.asPath).replace( router.query.projectId as string, projectId, ) : `/project/${projectId}`; const getOrgPath = (orgId: string) => router.query.organizationId ? truncatePathBeforeDynamicSegments(router.asPath).replace( router.query.organizationId as string, orgId, ) : `/organization/${orgId}`; return ( {organization && ( {organization?.name ?? "Organization"} {isCloudPlan(organization?.plan) && organization.id !== env.NEXT_PUBLIC_DEMO_ORG_ID && ( {planLabels[organization.plan]} )} Organizations
{organizations ? ( organizations .sort((a, b) => { // sort demo org to the bottom const isDemoA = env.NEXT_PUBLIC_DEMO_ORG_ID === a.id; const isDemoB = env.NEXT_PUBLIC_DEMO_ORG_ID === b.id; if (isDemoA) return 1; if (isDemoB) return -1; return 0; }) .map((dropdownOrg) => ( {env.NEXT_PUBLIC_DEMO_ORG_ID === dropdownOrg.id && ( )} {dropdownOrg.name} )) ) : ( )}
{canCreateOrganizations && ( <> )}
)} {organization && project && ( <> {project?.name ?? "Project"} Projects
{organizations ? ( organizations .find((org) => org.id === organization.id) ?.projects.map((dropdownProject) => ( {dropdownProject.name} )) ) : ( )}
{canCreateProjects && ( <> )}
)} {items?.map((item, index) => ( {item.href ? ( {item.name} ) : ( {item.name} )} ))}
); }; export default BreadcrumbComponent;