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:
241
src/app/impressum/page.tsx
Normal file
241
src/app/impressum/page.tsx
Normal 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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user