Initial commit: spreewaldzeit + Dockerfile for Coolify (Next.js + Prisma/SQLite)
This commit is contained in:
95
components/layout/Footer.tsx
Normal file
95
components/layout/Footer.tsx
Normal file
@@ -0,0 +1,95 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { usePathname } from "next/navigation";
|
||||
|
||||
export function Footer() {
|
||||
const pathname = usePathname();
|
||||
if (pathname?.startsWith("/admin")) return null;
|
||||
|
||||
const year = new Date().getFullYear();
|
||||
return (
|
||||
<footer className="mt-24 md:mt-32 border-t border-ink/10">
|
||||
{/* Big editorial tagline band */}
|
||||
<div className="bg-moss-800 text-parchment overflow-hidden">
|
||||
<div className="container py-16 md:py-24">
|
||||
<p className="font-display text-[clamp(2.5rem,8vw,6rem)] leading-[0.95] tracking-tight">
|
||||
Zeit haben.<br />
|
||||
Luft holen.<br />
|
||||
<span className="italic text-moss-300">Bleiben.</span>
|
||||
</p>
|
||||
<div className="mt-10 flex flex-wrap gap-3">
|
||||
<Link
|
||||
href="/anfrage"
|
||||
className="inline-flex items-center gap-2 bg-parchment text-ink px-7 py-3.5 rounded-full text-sm font-medium hover:bg-cream transition-colors"
|
||||
>
|
||||
Jetzt anfragen →
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Links & info row */}
|
||||
<div className="bg-cream/60">
|
||||
<div className="container py-12 md:py-16 grid grid-cols-2 md:grid-cols-12 gap-10 md:gap-12">
|
||||
<div className="col-span-2 md:col-span-4">
|
||||
<div className="font-display text-2xl mb-3">Spreewaldzeit</div>
|
||||
<p className="text-ink/65 text-sm leading-relaxed max-w-xs">
|
||||
Zwei private Ferienwohnungen im Spreewald. Keine Rezeption, keine Masse —
|
||||
nur Sie, das Wasser und die Bäume.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="col-span-2 md:col-span-3 md:col-start-6">
|
||||
<div className="eyebrow mb-4">Kontakt</div>
|
||||
<address className="not-italic text-sm leading-relaxed text-ink/75">
|
||||
Spreewaldzeit<br />
|
||||
Familie Musterfrau<br />
|
||||
Kraftwerkstraße 10<br />
|
||||
03226 Vetschau/Spreewald
|
||||
</address>
|
||||
<div className="mt-4 text-sm text-ink/75">
|
||||
<a href="mailto:hallo@spreewaldzeit.de" className="link-underline">
|
||||
hallo@spreewaldzeit.de
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="md:col-span-2 md:col-start-9 col-span-1">
|
||||
<div className="eyebrow mb-4">Navigation</div>
|
||||
<ul className="space-y-2 text-sm text-ink/75">
|
||||
<li><Link href="/#wohnungen" className="link-underline">Die Wohnungen</Link></li>
|
||||
<li><Link href="/#umgebung" className="link-underline">Umgebung</Link></li>
|
||||
<li><Link href="/anfrage" className="link-underline">Anfrage senden</Link></li>
|
||||
<li><Link href="/datenschutz" className="link-underline">Datenschutz</Link></li>
|
||||
<li><Link href="/impressum" className="link-underline">Impressum</Link></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className="md:col-span-2 md:col-start-11 col-span-1">
|
||||
<div className="eyebrow mb-4">Plattformen</div>
|
||||
<ul className="space-y-2 text-sm text-ink/75">
|
||||
<li>
|
||||
<a href="https://www.airbnb.de" target="_blank" rel="noopener noreferrer" className="link-underline">
|
||||
Airbnb ↗
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="https://www.booking.com" target="_blank" rel="noopener noreferrer" className="link-underline">
|
||||
Booking.com ↗
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border-t border-ink/10">
|
||||
<div className="container py-5 flex flex-col md:flex-row justify-between gap-2 text-xs text-ink/40">
|
||||
<span>© {year} Spreewaldzeit. Alle Rechte vorbehalten.</span>
|
||||
<span>Mit Sorgfalt gemacht im Spreewald.</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
);
|
||||
}
|
||||
145
components/layout/Header.tsx
Normal file
145
components/layout/Header.tsx
Normal file
@@ -0,0 +1,145 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { useEffect, useState } from "react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const navItems = [
|
||||
{ href: "/#wohnungen", label: "Wohnungen" },
|
||||
{ href: "/#umgebung", label: "Umgebung" },
|
||||
{ href: "/anfrage", label: "Anfrage" },
|
||||
];
|
||||
|
||||
export function Header() {
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
const [mobileOpen, setMobileOpen] = useState(false);
|
||||
const pathname = usePathname();
|
||||
const isHome = pathname === "/";
|
||||
|
||||
useEffect(() => {
|
||||
const onScroll = () => setScrolled(window.scrollY > 12);
|
||||
onScroll();
|
||||
window.addEventListener("scroll", onScroll, { passive: true });
|
||||
return () => window.removeEventListener("scroll", onScroll);
|
||||
}, []);
|
||||
|
||||
// On non-home pages the header is always opaque
|
||||
const transparent = isHome && !scrolled;
|
||||
|
||||
useEffect(() => {
|
||||
setMobileOpen(false);
|
||||
}, [pathname]);
|
||||
|
||||
if (pathname?.startsWith("/admin")) return null;
|
||||
|
||||
return (
|
||||
<header
|
||||
className={cn(
|
||||
"fixed top-0 left-0 right-0 z-40 transition-all duration-300",
|
||||
transparent
|
||||
? "bg-transparent"
|
||||
: "bg-parchment/95 backdrop-blur-md border-b border-ink/8 shadow-[0_1px_12px_rgba(28,38,32,0.06)]"
|
||||
)}
|
||||
>
|
||||
<div className="container flex items-center justify-between py-5 md:py-6">
|
||||
<Link href="/" className="flex items-baseline gap-2.5 group">
|
||||
<span className={cn(
|
||||
"font-display text-2xl md:text-[1.7rem] tracking-tight leading-none transition-colors duration-200",
|
||||
transparent ? "text-parchment group-hover:text-moss-200" : "text-ink group-hover:text-moss-700"
|
||||
)}>
|
||||
Spreewaldzeit
|
||||
</span>
|
||||
<span
|
||||
className="hidden sm:inline-block h-1.5 w-1.5 rounded-full bg-moss-400 opacity-70 group-hover:opacity-100 group-hover:scale-110 transition-all duration-200"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</Link>
|
||||
|
||||
<nav className="hidden md:flex items-center gap-9 text-sm">
|
||||
{navItems.map((item) => (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className={cn(
|
||||
"link-underline transition-colors duration-200",
|
||||
transparent ? "text-parchment/70 hover:text-parchment" : "text-ink/70 hover:text-ink"
|
||||
)}
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
<Link
|
||||
href="/anfrage"
|
||||
className={cn(
|
||||
"inline-flex items-center gap-2 px-5 py-2.5 rounded-full text-sm transition-colors",
|
||||
transparent
|
||||
? "bg-parchment/15 text-parchment border border-parchment/25 hover:bg-parchment/25"
|
||||
: "bg-ink text-parchment hover:bg-moss-700"
|
||||
)}
|
||||
>
|
||||
Jetzt anfragen
|
||||
<span aria-hidden="true">→</span>
|
||||
</Link>
|
||||
</nav>
|
||||
|
||||
<button
|
||||
className="md:hidden p-2 -mr-2 rounded-sm transition"
|
||||
onClick={() => setMobileOpen((v) => !v)}
|
||||
aria-label={mobileOpen ? "Menü schließen" : "Menü öffnen"}
|
||||
aria-expanded={mobileOpen}
|
||||
>
|
||||
<span className="relative block w-6 h-[2px]">
|
||||
<span
|
||||
className={cn(
|
||||
"absolute inset-x-0 h-[2px] transition-all duration-300",
|
||||
transparent ? "bg-parchment" : "bg-ink",
|
||||
mobileOpen ? "top-0 rotate-45" : "-top-2"
|
||||
)}
|
||||
/>
|
||||
<span
|
||||
className={cn(
|
||||
"absolute inset-x-0 h-[2px] top-0 transition-opacity",
|
||||
transparent ? "bg-parchment" : "bg-ink",
|
||||
mobileOpen && "opacity-0"
|
||||
)}
|
||||
/>
|
||||
<span
|
||||
className={cn(
|
||||
"absolute inset-x-0 h-[2px] transition-all duration-300",
|
||||
transparent ? "bg-parchment" : "bg-ink",
|
||||
mobileOpen ? "top-0 -rotate-45" : "top-2"
|
||||
)}
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Mobile menu */}
|
||||
<div
|
||||
className={cn(
|
||||
"md:hidden overflow-hidden border-t border-ink/8 bg-parchment/95 backdrop-blur-md transition-[max-height,opacity] duration-300",
|
||||
mobileOpen ? "max-h-[400px] opacity-100" : "max-h-0 opacity-0"
|
||||
)}
|
||||
>
|
||||
<nav className="container flex flex-col py-5 gap-1">
|
||||
{navItems.map((item) => (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className="py-3 text-lg font-display hover:text-moss-600 transition-colors"
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
))}
|
||||
<Link
|
||||
href="/anfrage"
|
||||
className="mt-3 inline-flex items-center justify-center bg-ink text-parchment px-5 py-3 rounded-full text-sm hover:bg-moss-700 transition-colors"
|
||||
>
|
||||
Jetzt anfragen →
|
||||
</Link>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user