Feature: Fehlerseiten 401, 403, 500, 502, 503, 504
Gemeinsames Layout in components/error-page.tsx, 404 darauf umgestellt. Jede Route app/<code>/page.tsx erzeugt landingpage/<code>.html; die Deploy-Skripte kopieren alle Fehlerseiten in den Webroot. README dokumentiert die nötigen ErrorDocument-Zeilen (Server-Konfig unverändert). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
ea2bcfbd27
commit
9c7e1ce4db
1 changed files with 427 additions and 120 deletions
157
components/error-page.tsx
Normal file
157
components/error-page.tsx
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
import type { ReactNode } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { ArrowLeft, FolderGit2 } from 'lucide-react'
|
||||
import { Button } from '@/components/ui/button'
|
||||
|
||||
/* Terminal lines — each renders one line incl. trailing newline inside <pre> */
|
||||
|
||||
export function Cmd({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<>
|
||||
<span className="text-accent">$</span> {children}{'\n'}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export function Out({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<>
|
||||
<span className="text-muted-foreground">{children}</span>{'\n'}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export function Err({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<>
|
||||
<span className="text-destructive dark:text-red-400">{children}</span>{'\n'}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export function Code({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<>
|
||||
<span className="text-cyan-600 dark:text-cyan-400">{children}</span>{'\n'}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export function Cursor() {
|
||||
return (
|
||||
<>
|
||||
<span className="text-accent">$</span>{' '}
|
||||
<span className="inline-block w-2 h-4 align-middle bg-foreground animate-pulse" />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
type ErrorPageProps = {
|
||||
code: number
|
||||
label: string
|
||||
title: string
|
||||
children: ReactNode
|
||||
terminal: ReactNode
|
||||
tip: ReactNode
|
||||
}
|
||||
|
||||
export function ErrorPage({ code, label, title, children, terminal, tip }: ErrorPageProps) {
|
||||
return (
|
||||
<main className="relative min-h-screen flex flex-col overflow-hidden">
|
||||
{/* Fly.io-style gradient mesh background (dark mode only) */}
|
||||
<div className="absolute inset-0 bg-gradient-mesh hidden dark:block" />
|
||||
|
||||
{/* Subtle grid overlay */}
|
||||
<div
|
||||
className="absolute inset-0 opacity-[0.02] dark:opacity-[0.04]"
|
||||
style={{
|
||||
backgroundImage: `
|
||||
linear-gradient(to right, currentColor 1px, transparent 1px),
|
||||
linear-gradient(to bottom, currentColor 1px, transparent 1px)
|
||||
`,
|
||||
backgroundSize: '60px 60px',
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Logo */}
|
||||
<div className="relative max-w-6xl w-full mx-auto px-6 lg:px-8">
|
||||
<div className="flex items-center h-16 lg:h-20">
|
||||
<Link
|
||||
href="/"
|
||||
className="font-mono text-lg tracking-tight text-foreground hover:text-accent transition-colors"
|
||||
>
|
||||
Jamulix
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative flex-1 flex items-center">
|
||||
<div className="max-w-6xl w-full mx-auto px-6 lg:px-8 py-12 lg:py-20">
|
||||
<div className="grid lg:grid-cols-12 gap-12 lg:gap-16 items-center">
|
||||
{/* Text Content */}
|
||||
<div className="lg:col-span-6 space-y-8">
|
||||
<div>
|
||||
<span className="inline-block font-mono text-xs tracking-wider text-accent uppercase mb-4">
|
||||
Fehler {code} · {label}
|
||||
</span>
|
||||
|
||||
<p
|
||||
aria-hidden="true"
|
||||
className="font-serif font-bold text-8xl sm:text-9xl leading-none tracking-tighter text-foreground/90 dark:bg-gradient-to-r dark:from-cyan-400 dark:to-violet-400 dark:bg-clip-text dark:text-transparent"
|
||||
>
|
||||
{code}
|
||||
</p>
|
||||
|
||||
<h1 className="mt-6 font-serif text-3xl sm:text-4xl lg:text-5xl leading-[1.1] tracking-tight text-balance">
|
||||
{title}
|
||||
</h1>
|
||||
</div>
|
||||
|
||||
<p className="text-lg text-muted-foreground leading-relaxed max-w-xl">
|
||||
{children}
|
||||
</p>
|
||||
|
||||
<div className="flex flex-col sm:flex-row gap-4">
|
||||
<Button asChild size="lg" className="group">
|
||||
<Link href="/">
|
||||
<ArrowLeft className="mr-2 size-4 transition-transform group-hover:-translate-x-0.5" />
|
||||
cd ~ — Zur Startseite
|
||||
</Link>
|
||||
</Button>
|
||||
<Button asChild variant="outline" size="lg" className="group">
|
||||
<Link href="/#projekte">
|
||||
<FolderGit2 className="mr-2 size-4" />
|
||||
Projekte ansehen
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Terminal */}
|
||||
<div className="lg:col-span-6">
|
||||
<div className="rounded-lg border border-border bg-card shadow-xl dark:shadow-[0_0_40px_oklch(0.70_0.22_280/0.3)] overflow-hidden">
|
||||
<div className="flex items-center gap-2 px-4 py-3 border-b border-border bg-muted/50">
|
||||
<span className="size-3 rounded-full bg-red-500/80" />
|
||||
<span className="size-3 rounded-full bg-yellow-500/80" />
|
||||
<span className="size-3 rounded-full bg-green-500/80" />
|
||||
<span className="ml-3 font-mono text-xs text-muted-foreground">
|
||||
jamulix@jamulix.de: ~
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<pre className="p-5 sm:p-6 font-mono text-xs sm:text-sm leading-relaxed whitespace-pre-wrap text-card-foreground">
|
||||
{terminal}
|
||||
<Cursor />
|
||||
</pre>
|
||||
</div>
|
||||
|
||||
<p className="mt-4 font-mono text-xs text-muted-foreground text-center lg:text-left">
|
||||
{tip}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
)
|
||||
}
|
||||
Loading…
Reference in a new issue