138 lines
4.3 KiB
TypeScript
138 lines
4.3 KiB
TypeScript
import type { ReactNode } from "react"
|
|
import Link from "next/link"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Breadcrumbs, type BreadcrumbItem } from "@/components/breadcrumbs"
|
|
import {
|
|
PublicInset,
|
|
PublicPageHeader,
|
|
PublicProse,
|
|
PublicSurface,
|
|
} from "@/components/public-surface"
|
|
import { cn } from "@/lib/utils"
|
|
|
|
type ShellAction = {
|
|
label: string
|
|
href: string
|
|
variant?: "default" | "outline"
|
|
}
|
|
|
|
interface DropdownPageShellProps {
|
|
breadcrumbs: BreadcrumbItem[]
|
|
eyebrow?: string
|
|
title: string
|
|
description?: string
|
|
headerContent?: ReactNode
|
|
contentIntro?: ReactNode
|
|
content: ReactNode
|
|
contentClassName?: string
|
|
contentSurfaceClassName?: string
|
|
sections?: ReactNode
|
|
cta?: {
|
|
eyebrow?: string
|
|
title: string
|
|
description: string
|
|
actions: ShellAction[]
|
|
note?: ReactNode
|
|
}
|
|
className?: string
|
|
}
|
|
|
|
export function DropdownPageShell({
|
|
breadcrumbs,
|
|
eyebrow,
|
|
title,
|
|
description,
|
|
headerContent,
|
|
contentIntro,
|
|
content,
|
|
contentClassName,
|
|
contentSurfaceClassName,
|
|
sections,
|
|
cta,
|
|
className,
|
|
}: DropdownPageShellProps) {
|
|
return (
|
|
<div className={cn("public-page", className)}>
|
|
<Breadcrumbs className="mb-6" items={breadcrumbs} />
|
|
|
|
<PublicPageHeader
|
|
align="center"
|
|
eyebrow={eyebrow}
|
|
title={title}
|
|
description={description}
|
|
>
|
|
{headerContent}
|
|
</PublicPageHeader>
|
|
|
|
{contentIntro ? (
|
|
<section className="mt-10 grid gap-5 lg:grid-cols-2">{contentIntro}</section>
|
|
) : null}
|
|
|
|
<section className={cn(contentIntro ? "mt-8" : "mt-10")}>
|
|
<PublicSurface
|
|
className={cn(
|
|
"relative overflow-hidden p-0 md:p-0",
|
|
contentSurfaceClassName
|
|
)}
|
|
>
|
|
<div className="absolute inset-x-0 top-0 h-24 bg-[radial-gradient(circle_at_top,rgba(41,160,71,0.09),transparent_74%)]" />
|
|
<div className="relative p-5 md:p-7 lg:p-10">
|
|
<div className="mb-8 flex items-center justify-between gap-4 border-b border-border/55 pb-5">
|
|
<div>
|
|
<p className="text-[0.72rem] font-semibold uppercase tracking-[0.22em] text-primary/80">
|
|
Location Guide
|
|
</p>
|
|
<p className="mt-2 max-w-2xl text-sm leading-6 text-muted-foreground">
|
|
How Rocky Mountain Vending typically approaches this type of
|
|
location, from placement fit to service expectations.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<PublicProse className={cn("mx-auto max-w-3xl", contentClassName)}>
|
|
{content}
|
|
</PublicProse>
|
|
</div>
|
|
</PublicSurface>
|
|
</section>
|
|
|
|
{sections ? <div className="mt-14 space-y-14">{sections}</div> : null}
|
|
|
|
{cta ? (
|
|
<section className="mt-14">
|
|
<PublicSurface className="overflow-hidden text-center">
|
|
<div className="absolute inset-x-0 top-0 h-20 bg-[radial-gradient(circle_at_top,rgba(41,160,71,0.10),transparent_70%)]" />
|
|
{cta.eyebrow ? (
|
|
<p className="text-xs font-semibold uppercase tracking-[0.2em] text-primary/80">
|
|
{cta.eyebrow}
|
|
</p>
|
|
) : null}
|
|
<h2 className="mt-3 text-3xl font-semibold tracking-tight text-balance">
|
|
{cta.title}
|
|
</h2>
|
|
<p className="mx-auto mt-4 max-w-2xl text-base leading-7 text-muted-foreground md:text-lg md:leading-8">
|
|
{cta.description}
|
|
</p>
|
|
<div className="mt-6 flex flex-col justify-center gap-3 sm:flex-row">
|
|
{cta.actions.map((action) => (
|
|
<Button
|
|
key={`${action.href}-${action.label}`}
|
|
asChild
|
|
size="lg"
|
|
variant={action.variant ?? "default"}
|
|
className="min-h-11 rounded-full px-6"
|
|
>
|
|
<Link href={action.href}>{action.label}</Link>
|
|
</Button>
|
|
))}
|
|
</div>
|
|
{cta.note ? (
|
|
<PublicInset className="mx-auto mt-6 max-w-2xl border-primary/10 text-left sm:text-center">
|
|
{cta.note}
|
|
</PublicInset>
|
|
) : null}
|
|
</PublicSurface>
|
|
</section>
|
|
) : null}
|
|
</div>
|
|
)
|
|
}
|