teqo-backup/src/components/SiteHeader.tsx
Francisco Ignácio Fontoura Solla 28647e4760 Add Post and Tag collections for news and publications
- Add Post + Tag collections (notícias/publicações) with tag-based
  "Esconder" visibility control for electoral-law compliance
- Add static SEO routes /[type]/[category]/[slug] plus /[type] and
  /[type]/[category] listings, with shared header/breadcrumb and PostCard
- Recreate the homepage and refresh the site color palette (styles.css)
- Add posts/slug/seo utilities and revalidation wiring (documents.ts)
- Add live-fetch seed script (scripts/seed-posts.mjs + seed-loader.mjs,
  db:seed:posts)
- Upgrade Payload 3.79.0 -> 3.82.0 (fixes admin relationship-filter crash)
- Remove obsolete Header component; add shadcn card/badge/breadcrumb;
  update AGENTS.md and components rule docs
2026-07-15 19:12:11 -03:00

98 lines
3.5 KiB
TypeScript

import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from '@/components/ui/breadcrumb'
import { getCachedGlobal } from '@/utilities/globals'
import Link from 'next/link'
import { Fragment } from 'react'
import { SocialLinks } from './socialLinks'
export type Crumb = {
label: string
/** Omit on the current (last) crumb so it renders as plain text. */
href?: string
}
type SiteHeaderProps = {
/**
* Breadcrumb trail rendered on the light sub-bar. When empty (e.g. on the
* home page) no breadcrumb bar is shown.
*/
breadcrumbs?: Crumb[]
/**
* `solid` renders a brand-red bar that participates in the layout flow and
* sticks to the top of its scroll container (used on the Posts pages).
* `overlay` floats transparently over hero content (used on the home page).
*/
variant?: 'solid' | 'overlay'
}
const HOME_LABEL = 'Início'
export const SiteHeader = async ({ breadcrumbs = [], variant = 'solid' }: SiteHeaderProps) => {
const settings = await getCachedGlobal('site-settings', 2)()
const title = settings.headerTitle?.trim() || 'Jorge Solla'
if (variant === 'overlay') {
return (
<header className="absolute top-0 right-0 left-0 z-30 flex items-center justify-between p-4">
<Link href="/" className="text-inherit no-underline">
<h4>{title}</h4>
</Link>
<SocialLinks ariaLabel="Redes sociais" />
</header>
)
}
const trail: Crumb[] = [{ label: HOME_LABEL, href: '/' }, ...breadcrumbs]
return (
<header className="sticky top-0 z-30">
<div className="bg-(--site-header) text-(--site-header-foreground)">
<div className="mx-auto flex w-full max-w-5xl items-center justify-between gap-4 px-4 py-3 sm:px-6 lg:px-8">
<Link
href="/"
className="text-lg font-semibold tracking-tight text-(--site-header-foreground) no-underline transition-opacity hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-white/70"
>
{title}
</Link>
<SocialLinks
ariaLabel="Redes sociais"
className="text-(--site-header-muted) [&_a]:text-(--site-header-muted) [&_a:hover]:text-(--site-header-foreground)"
/>
</div>
</div>
{trail.length > 1 ? (
<div className="border-b border-border bg-background/95 backdrop-blur supports-backdrop-filter:bg-background/80">
<div className="mx-auto w-full max-w-5xl px-4 py-2.5 sm:px-6 lg:px-8">
<Breadcrumb>
<BreadcrumbList>
{trail.map((crumb, index) => {
const isLast = index === trail.length - 1
return (
<Fragment key={`${crumb.label}-${index}`}>
<BreadcrumbItem>
{isLast || !crumb.href ? (
<BreadcrumbPage className="line-clamp-1">{crumb.label}</BreadcrumbPage>
) : (
<BreadcrumbLink asChild>
<Link href={crumb.href}>{crumb.label}</Link>
</BreadcrumbLink>
)}
</BreadcrumbItem>
{isLast ? null : <BreadcrumbSeparator />}
</Fragment>
)
})}
</BreadcrumbList>
</Breadcrumb>
</div>
</div>
) : null}
</header>
)
}