Use name for breadcrump

Signed-off-by: gearnode <bryan@frimin.fr>
This commit is contained in:
gearnode
2025-02-18 09:49:00 +01:00
parent 23dad14320
commit ba73ffca79
3 changed files with 114 additions and 47 deletions

View File

@@ -0,0 +1,33 @@
import React, { createContext, useContext, useState, useCallback } from 'react';
type BreadcrumbContextType = {
segments: Record<string, string>;
setBreadcrumbSegment: (path: string, display: string) => void;
};
const BreadcrumbContext = createContext<BreadcrumbContextType | undefined>(undefined);
export function BreadcrumbProvider({ children }: { children: React.ReactNode }) {
const [segments, setSegments] = useState<Record<string, string>>({});
const setBreadcrumbSegment = useCallback((path: string, display: string) => {
setSegments(prev => ({
...prev,
[path]: display
}));
}, []);
return (
<BreadcrumbContext.Provider value={{ segments, setBreadcrumbSegment }}>
{children}
</BreadcrumbContext.Provider>
);
}
export function useBreadcrumb() {
const context = useContext(BreadcrumbContext);
if (context === undefined) {
throw new Error('useBreadcrumb must be used within a BreadcrumbProvider');
}
return context;
}

View File

@@ -15,58 +15,83 @@ import {
SidebarProvider, SidebarProvider,
SidebarTrigger, SidebarTrigger,
} from "@/components/ui/sidebar"; } from "@/components/ui/sidebar";
import { BreadcrumbProvider, useBreadcrumb } from "@/contexts/BreadcrumbContext";
export default function ConsoleLayout() { function BreadcrumbNavigation() {
const location = useLocation(); const location = useLocation();
const pathSegments = location.pathname.split("/").filter(Boolean); const pathSegments = location.pathname.split("/").filter(Boolean);
const { segments } = useBreadcrumb();
const getBreadcrumbText = (segment: string, path: string) => {
// Check if we have a custom segment name
const fullPath = pathSegments
.slice(0, pathSegments.indexOf(segment) + 1)
.join('/');
// Look for patterns like 'vendors/:id' in our segments
const patternPath = fullPath.replace(/\/[^/]+$/, '/:id');
if (segments[patternPath]) {
return segments[patternPath];
}
// Default to capitalized segment
return segment.charAt(0).toUpperCase() + segment.slice(1);
};
return ( return (
<SidebarProvider> <Breadcrumb>
<AppSidebar /> <BreadcrumbList>
<SidebarInset> <BreadcrumbItem>
<header className="flex h-16 shrink-0 items-center gap-2"> <BreadcrumbLink asChild>
<div className="flex items-center gap-2 px-4"> <Link to="/">Home</Link>
<SidebarTrigger className="-ml-1" /> </BreadcrumbLink>
<Separator orientation="vertical" className="mr-2 h-4" /> </BreadcrumbItem>
<Breadcrumb> {pathSegments.map((segment, index) => {
<BreadcrumbList> const path = `/${pathSegments.slice(0, index + 1).join("/")}`;
<BreadcrumbItem> const isLast = index === pathSegments.length - 1;
<BreadcrumbLink asChild>
<Link to="/">Home</Link>
</BreadcrumbLink>
</BreadcrumbItem>
{pathSegments.map((segment, index) => {
const path = `/${pathSegments.slice(0, index + 1).join("/")}`;
const isLast = index === pathSegments.length - 1;
return ( return (
<React.Fragment key={path}> <React.Fragment key={path}>
<BreadcrumbSeparator /> <BreadcrumbSeparator />
<BreadcrumbItem> <BreadcrumbItem>
{isLast ? ( {isLast ? (
<BreadcrumbPage> <BreadcrumbPage>
{segment.charAt(0).toUpperCase() + segment.slice(1)} {getBreadcrumbText(segment, path)}
</BreadcrumbPage> </BreadcrumbPage>
) : ( ) : (
<BreadcrumbLink asChild> <BreadcrumbLink asChild>
<Link to={path}> <Link to={path}>
{segment.charAt(0).toUpperCase() + {getBreadcrumbText(segment, path)}
segment.slice(1)} </Link>
</Link> </BreadcrumbLink>
</BreadcrumbLink> )}
)} </BreadcrumbItem>
</BreadcrumbItem> </React.Fragment>
</React.Fragment> );
); })}
})} </BreadcrumbList>
</BreadcrumbList> </Breadcrumb>
</Breadcrumb> );
</div> }
</header>
<div className="flex flex-1 flex-col gap-4 p-4 pt-0"> export default function ConsoleLayout() {
<Outlet /> return (
</div> <BreadcrumbProvider>
</SidebarInset> <SidebarProvider>
</SidebarProvider> <AppSidebar />
<SidebarInset>
<header className="flex h-16 shrink-0 items-center gap-2">
<div className="flex items-center gap-2 px-4">
<SidebarTrigger className="-ml-1" />
<Separator orientation="vertical" className="mr-2 h-4" />
<BreadcrumbNavigation />
</div>
</header>
<div className="flex flex-1 flex-col gap-4 p-4 pt-0">
<Outlet />
</div>
</SidebarInset>
</SidebarProvider>
</BreadcrumbProvider>
); );
} }

View File

@@ -16,6 +16,8 @@ import { Suspense, useEffect } from "react";
import type { VendorOverviewPageQuery as VendorOverviewPageQueryType } from "./__generated__/VendorOverviewPageQuery.graphql"; import type { VendorOverviewPageQuery as VendorOverviewPageQueryType } from "./__generated__/VendorOverviewPageQuery.graphql";
import { useParams } from "react-router"; import { useParams } from "react-router";
import { Helmet } from "react-helmet-async"; import { Helmet } from "react-helmet-async";
import { useBreadcrumb } from "@/contexts/BreadcrumbContext";
const VendorOverviewPageQuery = graphql` const VendorOverviewPageQuery = graphql`
query VendorOverviewPageQuery($vendorId: ID!) { query VendorOverviewPageQuery($vendorId: ID!) {
node(id: $vendorId) { node(id: $vendorId) {
@@ -36,6 +38,13 @@ function VendorOverviewPageContent({
}) { }) {
const data = usePreloadedQuery(VendorOverviewPageQuery, queryRef); const data = usePreloadedQuery(VendorOverviewPageQuery, queryRef);
const [riskLevel, setRiskLevel] = useState("medium"); const [riskLevel, setRiskLevel] = useState("medium");
const { setBreadcrumbSegment } = useBreadcrumb();
useEffect(() => {
if (data.node?.name) {
setBreadcrumbSegment('vendors/:id', data.node.name);
}
}, [data.node?.name, setBreadcrumbSegment]);
return ( return (
<div className="space-y-6 p-4 md:p-6 lg:p-8"> <div className="space-y-6 p-4 md:p-6 lg:p-8">