Files
rheinsw-mono-repo/internal_frontend/components/dynamic-breadcrumb.tsx
Thatsaphorn Atchariyaphap e42b352216 Refactor navigation structure and API routes
- Centralize user menu, sidebar items, and breadcrumb logic.
- Map consistent API endpoints in `customerRoutes`.
- Replace inline route definitions with reusable constants.
- Refactor auth configuration file location.
- Improve `<Link>` usage to replace static `<a>` elements.
- Adjust sidebar and dropdown components to use dynamic navigation configurations.
2025-07-07 19:49:58 +02:00

123 lines
4.6 KiB
TypeScript

'use client';
import {usePathname} from 'next/navigation';
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from '@/components/ui/breadcrumb';
import {Skeleton} from "@/components/ui/skeleton";
import React, {useEffect, useState} from 'react';
import {getBreadcrumbs} from '@/services/navigation/breadcrumb-utils';
import {breadcrumbResolvers} from "@/lib/navigation/breadcrumb-map";
interface ResolvedBreadcrumb {
href: string;
label: string;
isCurrentPage: boolean;
}
export function DynamicBreadcrumb() {
const pathname = usePathname();
const [resolvedBreadcrumbs, setResolvedBreadcrumbs] = useState<ResolvedBreadcrumb[]>([]);
const [isLoading, setIsLoading] = useState(true);
// Get initial segments count for skeleton
const pathSegments = pathname.split('/').filter(Boolean);
useEffect(() => {
const resolveDynamicLabels = async () => {
setIsLoading(true);
const raw = getBreadcrumbs(pathname);
const enhanced = await Promise.all(
raw.map(async (b) => {
const pathSegments = b.href.split('/').filter(Boolean);
const lastSegment = pathSegments[pathSegments.length - 1];
const parentSegment = pathSegments[pathSegments.length - 2];
const isUUID = /^[0-9a-fA-F-]{36}$/.test(lastSegment);
const resolve = breadcrumbResolvers[parentSegment];
if (isUUID && resolve) {
try {
const dynamicLabel = await resolve(lastSegment);
return {
href: b.href,
label: dynamicLabel,
isCurrentPage: b.isCurrentPage ?? false,
};
} catch {
return {
href: b.href,
label: lastSegment,
isCurrentPage: b.isCurrentPage ?? false,
};
}
}
return {
href: b.href,
label: b.label,
isCurrentPage: b.isCurrentPage ?? false,
};
})
);
setResolvedBreadcrumbs(enhanced);
setIsLoading(false);
};
resolveDynamicLabels();
}, [pathname]);
if (isLoading) {
return (
<Breadcrumb>
<BreadcrumbList>
{/* Always show Home for empty path */}
<BreadcrumbItem className="hidden md:block">
<Skeleton className="h-4 w-[60px]"/>
</BreadcrumbItem>
{pathSegments.length > 0 && <BreadcrumbSeparator className="hidden md:block"/>}
{/* Show skeleton for each path segment */}
{pathSegments.map((_, index) => (
<React.Fragment key={index}>
<BreadcrumbItem className="hidden md:block">
<Skeleton className={`h-4 w-[${index === pathSegments.length - 1 ? '120' : '80'}px]`}/>
</BreadcrumbItem>
{index < pathSegments.length - 1 && (
<BreadcrumbSeparator className="hidden md:block"/>
)}
</React.Fragment>
))}
</BreadcrumbList>
</Breadcrumb>
);
}
return (
<Breadcrumb>
<BreadcrumbList>
{resolvedBreadcrumbs.map((breadcrumb, index) => (
<React.Fragment key={breadcrumb.href}>
<BreadcrumbItem className="hidden md:block">
{breadcrumb.isCurrentPage ? (
<BreadcrumbPage>{breadcrumb.label}</BreadcrumbPage>
) : (
<BreadcrumbLink href={breadcrumb.href}>{breadcrumb.label}</BreadcrumbLink>
)}
</BreadcrumbItem>
{index < resolvedBreadcrumbs.length - 1 && (
<BreadcrumbSeparator className="hidden md:block"/>
)}
</React.Fragment>
))}
</BreadcrumbList>
</Breadcrumb>
);
}