feat: Add bot-proof Impressum page

- Email displayed as user[at]domain with onClick handler
- Phone displayed split with dashes
- All contact info clickable but not easily scraped
- Ready to fill in at src/app/impressum/page.tsx
This commit is contained in:
2025-12-26 21:58:37 +01:00
parent f21b2738ad
commit f1990ebf2d

241
src/app/impressum/page.tsx Normal file
View File

@@ -0,0 +1,241 @@
import { Metadata } from 'next';
import { ArrowLeft, Mail, Phone, MapPin, Building2, User, FileText } from 'lucide-react';
import Link from 'next/link';
export const metadata: Metadata = {
title: 'Impressum',
description: 'Impressum und rechtliche Angaben',
};
/**
* Bot-proof Impressum page
* Contact info is rendered via CSS/JS to prevent simple scraping
*
* TO FILL IN: Edit the IMPRESSUM_DATA object below
*/
// ==========================================
// HIER AUSFÜLLEN - Deine Impressumsdaten
// ==========================================
const IMPRESSUM_DATA = {
// Verantwortlicher
name: 'Max Mustermann', // TODO: Dein Name
// Adresse
street: 'Musterstraße 123', // TODO: Straße + Hausnummer
city: '12345 Musterstadt', // TODO: PLZ + Stadt
country: 'Deutschland',
// Kontakt (wird bot-sicher dargestellt)
email: {
user: 'kontakt', // TODO: Teil vor dem @
domain: 'example.com', // TODO: Teil nach dem @
},
phone: {
prefix: '+49', // TODO: Ländervorwahl
area: '123', // TODO: Vorwahl
number: '4567890', // TODO: Rufnummer
},
// Optional: Firma
company: '', // TODO: Firmenname (leer lassen wenn Privatperson)
// Optional: Handelsregister
registry: '', // z.B. 'HRB 12345'
registryCourt: '', // z.B. 'Amtsgericht München'
// Optional: USt-IdNr.
vatId: '', // z.B. 'DE123456789'
// Optional: Berufsbezeichnung / Aufsichtsbehörde (für bestimmte Berufe)
profession: '',
supervisoryAuthority: '',
};
// ==========================================
/**
* Bot-proof email display component
* Renders email in a way that's hard for bots to scrape
*/
function BotProofEmail({ user, domain }: { user: string; domain: string }) {
return (
<span
className="inline-flex items-center gap-1.5 text-orange-500 cursor-pointer hover:underline"
onClick={() => {
window.location.href = `mailto:${user}@${domain}`;
}}
title="E-Mail senden"
>
<Mail size={16} />
<span className="select-none" aria-hidden="true">
{user}<span className="text-zinc-500">[at]</span>{domain}
</span>
<span className="sr-only">{user}@{domain}</span>
</span>
);
}
/**
* Bot-proof phone display component
*/
function BotProofPhone({ prefix, area, number }: { prefix: string; area: string; number: string }) {
const fullNumber = `${prefix}${area}${number}`;
return (
<span
className="inline-flex items-center gap-1.5 text-orange-500 cursor-pointer hover:underline"
onClick={() => {
window.location.href = `tel:${fullNumber}`;
}}
title="Anrufen"
>
<Phone size={16} />
<span className="select-none" aria-hidden="true">
{prefix} ({area}) {number.slice(0, 3)}-{number.slice(3)}
</span>
<span className="sr-only">{fullNumber}</span>
</span>
);
}
export default function ImpressumPage() {
const data = IMPRESSUM_DATA;
return (
<div className="min-h-screen bg-zinc-950">
{/* Header */}
<header className="sticky top-0 z-40 bg-zinc-950/80 backdrop-blur-lg border-b border-zinc-800">
<div className="max-w-2xl mx-auto px-4 py-4 flex items-center gap-4">
<Link
href="/"
className="p-2 -ml-2 text-zinc-400 hover:text-white transition-colors"
>
<ArrowLeft size={20} />
</Link>
<div className="flex items-center gap-2">
<FileText size={20} className="text-orange-500" />
<h1 className="text-lg font-bold text-white">Impressum</h1>
</div>
</div>
</header>
{/* Content */}
<main className="max-w-2xl mx-auto px-4 py-8 space-y-8">
{/* Anbieter */}
<section className="bg-zinc-900 border border-zinc-800 rounded-2xl p-6">
<h2 className="text-sm font-bold uppercase tracking-widest text-zinc-500 mb-4 flex items-center gap-2">
<User size={14} />
Angaben gemäß § 5 TMG
</h2>
<div className="space-y-3">
{data.company && (
<div className="flex items-center gap-2 text-white">
<Building2 size={16} className="text-zinc-500" />
<span className="font-bold">{data.company}</span>
</div>
)}
<div className="text-white font-bold">
{data.name}
</div>
<div className="flex items-start gap-2 text-zinc-400">
<MapPin size={16} className="text-zinc-500 mt-0.5" />
<div>
<div>{data.street}</div>
<div>{data.city}</div>
<div>{data.country}</div>
</div>
</div>
</div>
</section>
{/* Kontakt */}
<section className="bg-zinc-900 border border-zinc-800 rounded-2xl p-6">
<h2 className="text-sm font-bold uppercase tracking-widest text-zinc-500 mb-4">
Kontakt
</h2>
<div className="space-y-3">
<div>
<BotProofEmail user={data.email.user} domain={data.email.domain} />
</div>
{data.phone.number && (
<div>
<BotProofPhone
prefix={data.phone.prefix}
area={data.phone.area}
number={data.phone.number}
/>
</div>
)}
</div>
</section>
{/* Registerdaten */}
{(data.registry || data.vatId) && (
<section className="bg-zinc-900 border border-zinc-800 rounded-2xl p-6">
<h2 className="text-sm font-bold uppercase tracking-widest text-zinc-500 mb-4">
Registereintrag
</h2>
<div className="space-y-2 text-zinc-400 text-sm">
{data.registry && (
<div>
<span className="text-zinc-500">Handelsregister:</span>{' '}
{data.registry}
{data.registryCourt && `, ${data.registryCourt}`}
</div>
)}
{data.vatId && (
<div>
<span className="text-zinc-500">USt-IdNr.:</span>{' '}
{data.vatId}
</div>
)}
</div>
</section>
)}
{/* Berufshaftpflicht / Aufsicht */}
{(data.profession || data.supervisoryAuthority) && (
<section className="bg-zinc-900 border border-zinc-800 rounded-2xl p-6">
<h2 className="text-sm font-bold uppercase tracking-widest text-zinc-500 mb-4">
Berufsrechtliche Angaben
</h2>
<div className="space-y-2 text-zinc-400 text-sm">
{data.profession && (
<div>
<span className="text-zinc-500">Berufsbezeichnung:</span>{' '}
{data.profession}
</div>
)}
{data.supervisoryAuthority && (
<div>
<span className="text-zinc-500">Zuständige Aufsichtsbehörde:</span>{' '}
{data.supervisoryAuthority}
</div>
)}
</div>
</section>
)}
{/* Hinweis */}
<div className="text-center text-xs text-zinc-600 py-4">
Stand: {new Date().toLocaleDateString('de-DE', { month: 'long', year: 'numeric' })}
</div>
{/* Links */}
<div className="flex justify-center gap-6 text-sm">
<Link href="/privacy" className="text-zinc-500 hover:text-orange-500 transition-colors">
Datenschutz
</Link>
<Link href="/" className="text-zinc-500 hover:text-orange-500 transition-colors">
Zurück zur App
</Link>
</div>
</main>
</div>
);
}