Add email verification, personal calendar feed, and full SEO implementation

- Customer accounts now require email verification (hashed, single-use,
  time-limited tokens) before they can request/confirm bookings, with
  resend flows on login/account/booking widget and rate limiting.
- Admins get a private, rotatable iCalendar (ICS) subscription feed of
  their confirmed bookings and public events, timezone-correct for
  Europe/Berlin including DST, never exposing meeting passwords.
- Adds a full SEO layer: per-page canonical/OG/Twitter metadata with
  CMS-editable overrides and content-derived fallbacks, a dynamic
  sitemap.xml and robots.txt driven by real published content, JSON-LD
  (Organization/LocalBusiness, WebSite, WebPage, BreadcrumbList, Service,
  Event, BlogPosting) that never fabricates data, and a CMS-managed
  redirect table for changed slugs.
- Global ANOUMA-naming audit: the brand name is never used to label
  personal account/calendar areas anywhere in the app, CMS, or emails.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-25 22:57:31 +02:00
co-authored by Claude Sonnet 5
parent 50c39a70e0
commit 1cd15aff25
72 changed files with 2835 additions and 257 deletions
+6 -2
View File
@@ -25,6 +25,7 @@ const inter = Inter({
export const metadata: Metadata = {
title: "Seite nicht gefunden",
robots: { index: false, follow: false },
};
export default function GlobalNotFound() {
@@ -47,14 +48,17 @@ export default function GlobalNotFound() {
Diesen Weg gibt es hier nicht
</h1>
<p className="mt-5 max-w-md text-lg leading-relaxed text-anouma-plum">
Die gesuchte Seite konnte nicht gefunden werden. Vielleicht findest du deinen Weg
über die Startseite oder die Angebote weiter.
Diese Seite wurde nicht gefunden. Vielleicht findest du deinen Weg über die
Startseite, die Angebote oder den Kontakt weiter.
</p>
<div className="mt-8 flex flex-wrap gap-4">
<Button href="/">Zur Startseite</Button>
<Button href="/angebote" variant="secondary">
Angebote ansehen
</Button>
<Button href="/kontakt" variant="secondary">
Kontakt
</Button>
</div>
</div>
<div className="relative mx-auto aspect-square w-full max-w-sm">