Add dynamic breadcrumb navigation and update kanzlei routes under /demo
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
export default function Home() {
|
||||
return (
|
||||
<div>
|
||||
Bilanzbuchhalter
|
||||
</div>
|
||||
);
|
||||
}
|
||||
7
internal_frontend/app/demo/kanzlei/rechtsanwalt/page.tsx
Normal file
7
internal_frontend/app/demo/kanzlei/rechtsanwalt/page.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
export default function Home() {
|
||||
return (
|
||||
<div>
|
||||
Rechtsanwalt
|
||||
</div>
|
||||
);
|
||||
}
|
||||
7
internal_frontend/app/demo/kanzlei/steuer/page.tsx
Normal file
7
internal_frontend/app/demo/kanzlei/steuer/page.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
export default function Home() {
|
||||
return (
|
||||
<div>
|
||||
Steuer
|
||||
</div>
|
||||
);
|
||||
}
|
||||
10
internal_frontend/app/demo/settings/page.tsx
Normal file
10
internal_frontend/app/demo/settings/page.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import {SidebarGroupLabel} from "@/components/ui/sidebar";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div>
|
||||
<SidebarGroupLabel>Documents</SidebarGroupLabel>
|
||||
Settings
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import type {Metadata} from "next";
|
||||
import "./globals.css";
|
||||
import {ThemeProvider} from "@/components/theme-provider";
|
||||
import {SidebarProvider, SidebarTrigger} from "@/components/ui/sidebar"
|
||||
import {SidebarInset, SidebarProvider, SidebarTrigger} from "@/components/ui/sidebar"
|
||||
import {AppSidebar} from "@/components/app-sidebar"
|
||||
import React from "react";
|
||||
import {Separator} from "@/components/ui/separator";
|
||||
import {DynamicBreadcrumb} from "@/components/dynamic-breadcrumb";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Internal | Rhein Software",
|
||||
@@ -16,7 +18,6 @@ export default function RootLayout({
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
|
||||
<html lang="de" suppressHydrationWarning>
|
||||
<body>
|
||||
<ThemeProvider
|
||||
@@ -25,10 +26,29 @@ export default function RootLayout({
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<SidebarProvider>
|
||||
<SidebarProvider
|
||||
style={
|
||||
{
|
||||
"--sidebar-width": "calc(var(--spacing) * 72)",
|
||||
"--header-height": "calc(var(--spacing) * 12)",
|
||||
} as React.CSSProperties
|
||||
}
|
||||
>
|
||||
<AppSidebar/>
|
||||
<main>
|
||||
<SidebarTrigger/>
|
||||
<SidebarInset>
|
||||
<header
|
||||
className="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-data-[collapsible=icon]/sidebar-wrapper:h-12">
|
||||
<div className="flex items-center gap-2 px-4">
|
||||
<SidebarTrigger className="-ml-1"/>
|
||||
<Separator
|
||||
orientation="vertical"
|
||||
className="mr-2 data-[orientation=vertical]:h-4"
|
||||
/>
|
||||
<DynamicBreadcrumb/>
|
||||
</div>
|
||||
</header>
|
||||
</SidebarInset>
|
||||
{children}
|
||||
</main>
|
||||
</SidebarProvider>
|
||||
@@ -36,4 +56,4 @@ export default function RootLayout({
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -116,17 +116,17 @@ export function AppSidebar() {
|
||||
<CollapsibleContent>
|
||||
<SidebarMenuSub className="ml-4 border-l border-border pl-4 flex flex-col gap-y-1">
|
||||
<SidebarMenuSubItem>
|
||||
<SidebarMenuSubButton href="/kanzlei/steuer">
|
||||
<SidebarMenuSubButton href="/demo/kanzlei/steuer">
|
||||
Steuer
|
||||
</SidebarMenuSubButton>
|
||||
</SidebarMenuSubItem>
|
||||
<SidebarMenuSubItem>
|
||||
<SidebarMenuSubButton href="/kanzlei/rechtsanwalt">
|
||||
<SidebarMenuSubButton href="/demo/kanzlei/rechtsanwalt">
|
||||
Rechtsanwalt
|
||||
</SidebarMenuSubButton>
|
||||
</SidebarMenuSubItem>
|
||||
<SidebarMenuSubItem>
|
||||
<SidebarMenuSubButton href="/kanzlei/bilanzbuchhalter">
|
||||
<SidebarMenuSubButton href="/demo/kanzlei/bilanzbuchhalter">
|
||||
Bilanzbuchhalter
|
||||
</SidebarMenuSubButton>
|
||||
</SidebarMenuSubItem>
|
||||
|
||||
42
internal_frontend/components/dynamic-breadcrumb.tsx
Normal file
42
internal_frontend/components/dynamic-breadcrumb.tsx
Normal file
@@ -0,0 +1,42 @@
|
||||
// components/dynamic-breadcrumb.tsx
|
||||
'use client';
|
||||
|
||||
import {usePathname} from 'next/navigation';
|
||||
import {
|
||||
Breadcrumb,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbList,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
} from "@/components/ui/breadcrumb";
|
||||
import React from 'react';
|
||||
import {getBreadcrumbs} from "@/utils/BreadcrumbUtils";
|
||||
|
||||
export function DynamicBreadcrumb() {
|
||||
const pathname = usePathname();
|
||||
const breadcrumbs = getBreadcrumbs(pathname);
|
||||
|
||||
return (
|
||||
<Breadcrumb>
|
||||
<BreadcrumbList>
|
||||
{breadcrumbs.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 < breadcrumbs.length - 1 && (
|
||||
<BreadcrumbSeparator className="hidden md:block"/>
|
||||
)}
|
||||
</React.Fragment>
|
||||
))}
|
||||
</BreadcrumbList>
|
||||
</Breadcrumb>
|
||||
);
|
||||
}
|
||||
109
internal_frontend/components/ui/breadcrumb.tsx
Normal file
109
internal_frontend/components/ui/breadcrumb.tsx
Normal file
@@ -0,0 +1,109 @@
|
||||
import * as React from "react"
|
||||
import { Slot } from "@radix-ui/react-slot"
|
||||
import { ChevronRight, MoreHorizontal } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
function Breadcrumb({ ...props }: React.ComponentProps<"nav">) {
|
||||
return <nav aria-label="breadcrumb" data-slot="breadcrumb" {...props} />
|
||||
}
|
||||
|
||||
function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
|
||||
return (
|
||||
<ol
|
||||
data-slot="breadcrumb-list"
|
||||
className={cn(
|
||||
"text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
|
||||
return (
|
||||
<li
|
||||
data-slot="breadcrumb-item"
|
||||
className={cn("inline-flex items-center gap-1.5", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function BreadcrumbLink({
|
||||
asChild,
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"a"> & {
|
||||
asChild?: boolean
|
||||
}) {
|
||||
const Comp = asChild ? Slot : "a"
|
||||
|
||||
return (
|
||||
<Comp
|
||||
data-slot="breadcrumb-link"
|
||||
className={cn("hover:text-foreground transition-colors", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
|
||||
return (
|
||||
<span
|
||||
data-slot="breadcrumb-page"
|
||||
role="link"
|
||||
aria-disabled="true"
|
||||
aria-current="page"
|
||||
className={cn("text-foreground font-normal", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function BreadcrumbSeparator({
|
||||
children,
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"li">) {
|
||||
return (
|
||||
<li
|
||||
data-slot="breadcrumb-separator"
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
className={cn("[&>svg]:size-3.5", className)}
|
||||
{...props}
|
||||
>
|
||||
{children ?? <ChevronRight />}
|
||||
</li>
|
||||
)
|
||||
}
|
||||
|
||||
function BreadcrumbEllipsis({
|
||||
className,
|
||||
...props
|
||||
}: React.ComponentProps<"span">) {
|
||||
return (
|
||||
<span
|
||||
data-slot="breadcrumb-ellipsis"
|
||||
role="presentation"
|
||||
aria-hidden="true"
|
||||
className={cn("flex size-9 items-center justify-center", className)}
|
||||
{...props}
|
||||
>
|
||||
<MoreHorizontal className="size-4" />
|
||||
<span className="sr-only">More</span>
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
export {
|
||||
Breadcrumb,
|
||||
BreadcrumbList,
|
||||
BreadcrumbItem,
|
||||
BreadcrumbLink,
|
||||
BreadcrumbPage,
|
||||
BreadcrumbSeparator,
|
||||
BreadcrumbEllipsis,
|
||||
}
|
||||
8
internal_frontend/lib/breadcrumb-map.ts
Normal file
8
internal_frontend/lib/breadcrumb-map.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
// lib/breadcrumb-map.ts
|
||||
export const breadcrumbMap: Record<string, string> = {
|
||||
'dashboard': 'Dashboard',
|
||||
'settings': 'Settings',
|
||||
'demo': 'Demo',
|
||||
'users': 'User Management',
|
||||
// Add more mappings as needed
|
||||
};
|
||||
28
internal_frontend/utils/BreadcrumbUtils.ts
Normal file
28
internal_frontend/utils/BreadcrumbUtils.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
// utils/getBreadcrumbs.ts
|
||||
import {breadcrumbMap} from '@/lib/breadcrumb-map';
|
||||
|
||||
export type Breadcrumb = {
|
||||
label: string;
|
||||
href: string;
|
||||
isCurrentPage?: boolean;
|
||||
};
|
||||
|
||||
export function getBreadcrumbs(path: string): Breadcrumb[] {
|
||||
const pathSegments = path.split('/').filter(Boolean);
|
||||
|
||||
return pathSegments.map((segment, index) => {
|
||||
const href = `/${pathSegments.slice(0, index + 1).join('/')}`;
|
||||
// Use the mapping if it exists, otherwise format the segment
|
||||
const label = breadcrumbMap[segment.toLowerCase()] ||
|
||||
segment
|
||||
.split('-')
|
||||
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
||||
.join(' ');
|
||||
|
||||
return {
|
||||
label,
|
||||
href,
|
||||
isCurrentPage: index === pathSegments.length - 1
|
||||
};
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user