diff --git a/app/@breadcrumbs/about/page.tsx b/app/@breadcrumbs/about/page.tsx new file mode 100644 index 0000000..ed78d5e --- /dev/null +++ b/app/@breadcrumbs/about/page.tsx @@ -0,0 +1,15 @@ +import { Breadcrumbs } from '@/components/layout/Breadcrumbs'; + +export default function AboutBreadcrumb() { + return ( + + ); +} diff --git a/app/@breadcrumbs/blog/[...slug]/page.tsx b/app/@breadcrumbs/blog/[...slug]/page.tsx new file mode 100644 index 0000000..a2dc242 --- /dev/null +++ b/app/@breadcrumbs/blog/[...slug]/page.tsx @@ -0,0 +1,53 @@ +import { Breadcrumbs } from '@/components/layout/Breadcrumbs'; +import { getPostBySlug } from '@/lib/markdown'; + +interface BreadcrumbItem { + label: string; + href: string; + current?: boolean; +} + +function formatDirectoryName(name: string): string { + const directoryNames: { [key: string]: string } = { + tech: 'Tehnologie', + design: 'Design', + tutorial: 'Tutoriale', + }; + + return directoryNames[name] || name.charAt(0).toUpperCase() + name.slice(1); +} + +export default async function BlogPostBreadcrumb({ + params, +}: { + params: Promise<{ slug: string[] }>; +}) { + const { slug } = await params; + const slugPath = slug.join('/'); + const post = getPostBySlug(slugPath); + + const items: BreadcrumbItem[] = [ + { + label: 'Blog', + href: '/blog', + }, + ]; + + if (slug.length > 1) { + for (let i = 0; i < slug.length - 1; i++) { + const segmentPath = slug.slice(0, i + 1).join('/'); + items.push({ + label: formatDirectoryName(slug[i]), + href: `/blog/${segmentPath}`, + }); + } + } + + items.push({ + label: post ? post.frontmatter.title : slug[slug.length - 1], + href: `/blog/${slugPath}`, + current: true, + }); + + return ; +} diff --git a/app/@breadcrumbs/blog/page.tsx b/app/@breadcrumbs/blog/page.tsx new file mode 100644 index 0000000..6abd4ba --- /dev/null +++ b/app/@breadcrumbs/blog/page.tsx @@ -0,0 +1,15 @@ +import { Breadcrumbs } from '@/components/layout/Breadcrumbs'; + +export default function BlogBreadcrumb() { + return ( + + ); +} diff --git a/app/@breadcrumbs/default.tsx b/app/@breadcrumbs/default.tsx new file mode 100644 index 0000000..8f1d385 --- /dev/null +++ b/app/@breadcrumbs/default.tsx @@ -0,0 +1,7 @@ +'use client'; + +import { Breadcrumbs } from '@/components/layout/Breadcrumbs'; + +export default function DefaultBreadcrumb() { + return ; +} diff --git a/app/@breadcrumbs/tags/[tag]/page.tsx b/app/@breadcrumbs/tags/[tag]/page.tsx new file mode 100644 index 0000000..9451e8d --- /dev/null +++ b/app/@breadcrumbs/tags/[tag]/page.tsx @@ -0,0 +1,29 @@ +import { Breadcrumbs } from '@/components/layout/Breadcrumbs'; + +export default async function TagBreadcrumb({ + params, +}: { + params: Promise<{ tag: string }>; +}) { + const { tag } = await params; + const tagName = tag + .split('-') + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); + + return ( + + ); +} diff --git a/app/@breadcrumbs/tags/page.tsx b/app/@breadcrumbs/tags/page.tsx new file mode 100644 index 0000000..8d74cd3 --- /dev/null +++ b/app/@breadcrumbs/tags/page.tsx @@ -0,0 +1,15 @@ +import { Breadcrumbs } from '@/components/layout/Breadcrumbs'; + +export default function TagsBreadcrumb() { + return ( + + ); +} diff --git a/app/globals.css b/app/globals.css index f1d8c73..d5487a6 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1 +1,11 @@ @import "tailwindcss"; + +@layer utilities { + .scrollbar-hide { + -ms-overflow-style: none; + scrollbar-width: none; + } + .scrollbar-hide::-webkit-scrollbar { + display: none; + } +} diff --git a/app/layout.tsx b/app/layout.tsx index da72438..56b8ff5 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -27,8 +27,10 @@ export const metadata: Metadata = { export default function RootLayout({ children, + breadcrumbs, }: { children: React.ReactNode + breadcrumbs: React.ReactNode }) { return ( @@ -48,6 +50,7 @@ export default function RootLayout({ + {breadcrumbs}
{children}
diff --git a/components/layout/Breadcrumbs.tsx b/components/layout/Breadcrumbs.tsx new file mode 100644 index 0000000..1dfd000 --- /dev/null +++ b/components/layout/Breadcrumbs.tsx @@ -0,0 +1,145 @@ +'use client'; + +import Link from 'next/link'; +import { usePathname } from 'next/navigation'; +import { Fragment } from 'react'; +import { BreadcrumbsSchema } from './BreadcrumbsSchema'; + +interface BreadcrumbItem { + label: string; + href: string; + current?: boolean; +} + +function HomeIcon({ className }: { className?: string }) { + return ( + + + + ); +} + +function ChevronIcon({ className }: { className?: string }) { + return ( + + + + ); +} + +function formatSegmentLabel(segment: string): string { + const specialCases: { [key: string]: string } = { + blog: 'Blog', + tags: 'Tag-uri', + about: 'Despre', + }; + + if (specialCases[segment]) { + return specialCases[segment]; + } + + return segment + .split('-') + .map(word => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); +} + +export function Breadcrumbs({ items }: { items?: BreadcrumbItem[] }) { + const pathname = usePathname(); + + let breadcrumbs: BreadcrumbItem[] = items || []; + + if (!items) { + const segments = pathname.split('/').filter(Boolean); + breadcrumbs = segments.map((segment, index) => { + const href = '/' + segments.slice(0, index + 1).join('/'); + const label = formatSegmentLabel(segment); + const current = index === segments.length - 1; + + return { label, href, current }; + }); + } + + if (pathname === '/') { + return null; + } + + const schemaItems = [ + { position: 1, name: 'Acasă', item: '/' }, + ...breadcrumbs.map((item, index) => ({ + position: index + 2, + name: item.label, + item: item.href, + })), + ]; + + return ( + <> + + + + ); +} diff --git a/components/layout/BreadcrumbsSchema.tsx b/components/layout/BreadcrumbsSchema.tsx new file mode 100644 index 0000000..18c1467 --- /dev/null +++ b/components/layout/BreadcrumbsSchema.tsx @@ -0,0 +1,25 @@ +interface BreadcrumbSchemaItem { + position: number; + name: string; + item: string; +} + +export function BreadcrumbsSchema({ items }: { items: BreadcrumbSchemaItem[] }) { + const structuredData = { + '@context': 'https://schema.org', + '@type': 'BreadcrumbList', + itemListElement: items.map((item) => ({ + '@type': 'ListItem', + position: item.position, + name: item.name, + item: `http://localhost:3000${item.item}`, + })), + }; + + return ( +