mirror of
https://github.com/Since-3/since3-portal.git
synced 2026-10-12 20:51:17 +00:00
97 lines
5.6 KiB
TypeScript
97 lines
5.6 KiB
TypeScript
"use client"
|
|
|
|
import Image from "next/image"
|
|
import { useState } from "react"
|
|
import { BlocksIcon, 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 type { TemplateDefinition } from "../../lib/types"
|
|
import { CreateVariantButton } from "./create-variant-button"
|
|
|
|
// Seiten-Typen für bestimmte Situationen, Baukasten-Vorlagen sind frei aus Bausteinen, alles andere sind Branchen-Webseiten
|
|
const PAGE_TYPES = new Set(["karriere", "kampagne", "event", "shop", "linkinbio", "speisekarte"])
|
|
const GROUPS = ["Alle", "Baukasten", "Branchen", "Seiten-Typen"] as const
|
|
|
|
const groupOf = (template: TemplateDefinition): (typeof GROUPS)[number] =>
|
|
template.builder ? "Baukasten" : PAGE_TYPES.has(template.key) ? "Seiten-Typen" : "Branchen"
|
|
|
|
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" || groupOf(template) === group
|
|
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>
|
|
{template.builder && (
|
|
<span className="absolute top-3 right-3 flex items-center gap-1 rounded-full bg-white/90 px-2.5 py-1 text-xs font-medium text-black">
|
|
<BlocksIcon className="size-3.5" /> Bausteine
|
|
</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>
|
|
)
|
|
}
|