since3-portal/modules/templates/ui/components/template-grid.tsx
2026-10-10 02:38:50 +02:00

88 lines
5 KiB
TypeScript

"use client"
import Image from "next/image"
import { useState } from "react"
import { EyeIcon, SearchIcon } from "lucide-react"
import { buttonVariants } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { cn } from "@/lib/utils"
import { templates } from "../../lib/registry"
import { CreateVariantButton } from "./create-variant-button"
// Seiten-Typen für bestimmte Situationen, alles andere sind Branchen-Webseiten
const PAGE_TYPES = new Set(["karriere", "kampagne", "event", "shop", "linkinbio", "speisekarte"])
const GROUPS = ["Alle", "Branchen", "Seiten-Typen"] as const
export function TemplateGrid() {
const [query, setQuery] = useState("")
const [group, setGroup] = useState<(typeof GROUPS)[number]>("Alle")
const term = query.trim().toLowerCase()
const visible = templates.filter((template) => {
const inGroup = group === "Alle" || (group === "Seiten-Typen") === PAGE_TYPES.has(template.key)
const text = `${template.name} ${template.industry} ${template.description}`.toLowerCase()
return inGroup && (!term || text.includes(term))
})
return (
<section className="flex flex-col gap-4">
<div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
<div className="flex gap-1 rounded-lg border p-1">
{GROUPS.map((option) => (
<button
key={option}
type="button"
aria-pressed={group === option}
onClick={() => setGroup(option)}
className={cn("cursor-pointer rounded-md px-3 py-1.5 text-sm transition-colors", group === option ? "bg-surface font-medium" : "text-muted-foreground hover:text-foreground")}
>
{option}
</button>
))}
</div>
<label className="relative sm:w-72">
<SearchIcon className="pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-muted-foreground" />
<Input value={query} onChange={(event) => setQuery(event.target.value)} placeholder="Branche suchen, z. B. Auto" aria-label="Vorlagen durchsuchen" className="pl-9" />
</label>
</div>
<div className="grid gap-4 sm:grid-cols-2 xl:grid-cols-4">
{visible.map((template) => (
<article key={template.key} className="group flex flex-col overflow-hidden rounded-xl border bg-card">
<div className="relative aspect-[4/3] overflow-hidden">
<Image
src={template.cover}
alt=""
fill
sizes="(min-width: 1280px) 25vw, (min-width: 640px) 50vw, 100vw"
className="object-cover transition-transform duration-700 group-hover:scale-105"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/70 via-black/10 to-transparent" />
<span className="absolute top-3 left-3 rounded-full bg-black/50 px-2.5 py-1 text-xs text-white backdrop-blur">{template.industry}</span>
<div className="absolute bottom-3 left-3 flex gap-1.5">
{template.palettes.map((palette) => (
<span
key={palette.key}
title={palette.label}
className="size-4 rounded-full ring-2 ring-white/70"
style={{ background: `linear-gradient(135deg, ${palette.bg} 50%, ${palette.accent} 50%)` }}
/>
))}
</div>
</div>
<div className="flex flex-1 flex-col gap-2 p-4">
<h2 className="font-semibold">{template.name}</h2>
<p className="flex-1 text-sm text-muted-foreground">{template.description}</p>
<div className="mt-3 flex flex-wrap gap-2">
<a href={`/preview/${template.key}`} target="_blank" rel="noreferrer" className={buttonVariants({ variant: "outline", size: "sm" })}>
<EyeIcon /> Ansehen
</a>
<CreateVariantButton templateKey={template.key} />
</div>
</div>
</article>
))}
</div>
{visible.length === 0 && <p className="rounded-xl border border-dashed p-8 text-center text-sm text-muted-foreground">Keine passende Vorlage gefunden.</p>}
</section>
)
}