BillTracker/client/pages/NotFoundPage.jsx

179 lines
5.4 KiB
React
Raw Normal View History

import { useEffect, useRef } from 'react';
import { Link, useNavigate, useLocation } from 'react-router-dom';
import { ArrowLeft, Home, FileQuestion } from 'lucide-react';
import { Button } from '@/components/ui/button';
import { useAuth } from '@/hooks/useAuth';
// Animated digit — cycles through random numbers before settling
function GlitchDigit({ value, delay = 0 }) {
const ref = useRef(null);
useEffect(() => {
const el = ref.current;
if (!el) return;
const frames = 14;
const digits = '0123456789';
let frame = 0;
let start = null;
function tick(ts) {
if (!start) start = ts + delay;
const elapsed = ts - start;
if (elapsed < 0) { requestAnimationFrame(tick); return; }
if (frame < frames) {
el.textContent = digits[Math.floor(Math.random() * 10)];
frame++;
setTimeout(() => requestAnimationFrame(tick), 40 + frame * 6);
} else {
el.textContent = value;
el.classList.add('settled');
}
}
requestAnimationFrame(tick);
}, [value, delay]);
return (
<span
ref={ref}
style={{
display: 'inline-block',
fontVariantNumeric: 'tabular-nums',
transition: 'opacity 0.15s',
}}
>
{value}
</span>
);
}
export default function NotFoundPage() {
const { user } = useAuth();
const navigate = useNavigate();
const location = useLocation();
const canGoBack = window.history.length > 1;
const badPath = location.pathname;
return (
<div
className="relative flex min-h-screen flex-col items-center justify-center overflow-hidden bg-background text-foreground"
style={{
background: `
radial-gradient(ellipse 80% 60% at 50% -10%, oklch(var(--primary) / 0.12), transparent),
radial-gradient(ellipse 60% 40% at 100% 100%, oklch(var(--primary) / 0.06), transparent),
oklch(var(--background))
`,
}}
>
{/* Grid overlay */}
<div
aria-hidden="true"
className="pointer-events-none absolute inset-0"
style={{
backgroundImage: `
linear-gradient(oklch(var(--border) / 0.35) 1px, transparent 1px),
linear-gradient(90deg, oklch(var(--border) / 0.35) 1px, transparent 1px)
`,
backgroundSize: '48px 48px',
maskImage: 'radial-gradient(ellipse 80% 80% at 50% 50%, black 30%, transparent 100%)',
WebkitMaskImage: 'radial-gradient(ellipse 80% 80% at 50% 50%, black 30%, transparent 100%)',
}}
/>
{/* Glow orb */}
<div
aria-hidden="true"
className="pointer-events-none absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 rounded-full blur-3xl"
style={{
width: '36rem',
height: '20rem',
background: 'oklch(var(--primary) / 0.07)',
}}
/>
{/* Content */}
<div className="relative z-10 flex flex-col items-center gap-6 px-6 text-center">
{/* Icon badge */}
<div
className="flex h-14 w-14 items-center justify-center rounded-2xl border"
style={{
background: 'oklch(var(--primary) / 0.08)',
borderColor: 'oklch(var(--primary) / 0.25)',
}}
>
<FileQuestion
className="h-6 w-6"
style={{ color: 'oklch(var(--primary))' }}
/>
</div>
{/* 404 */}
<div
className="select-none font-mono font-black leading-none tracking-tighter"
style={{
fontSize: 'clamp(6rem, 22vw, 14rem)',
background: `linear-gradient(
135deg,
oklch(var(--foreground)) 0%,
oklch(var(--foreground) / 0.55) 50%,
oklch(var(--primary) / 0.5) 100%
)`,
WebkitBackgroundClip: 'text',
WebkitTextFillColor: 'transparent',
backgroundClip: 'text',
letterSpacing: '-0.06em',
}}
>
<GlitchDigit value="4" delay={0} />
<GlitchDigit value="0" delay={180} />
<GlitchDigit value="4" delay={360} />
</div>
{/* Headline */}
<div className="space-y-2">
<h1 className="text-2xl font-semibold tracking-tight sm:text-3xl">
Nothing here but debt
</h1>
<p className="max-w-sm text-sm leading-relaxed text-muted-foreground">
{badPath !== '/'
? <>The page <code className="rounded bg-muted px-1.5 py-0.5 font-mono text-xs">{badPath}</code> doesn't exist.</>
: <>That page doesn't exist.</>
}
{' '}Check the URL or head back somewhere useful.
</p>
</div>
{/* CTAs */}
<div className="flex flex-wrap items-center justify-center gap-3">
<Button asChild size="default">
<Link to={user ? '/' : '/login'}>
<Home className="h-4 w-4" />
{user ? 'Dashboard' : 'Sign in'}
</Link>
</Button>
{canGoBack && (
<Button
variant="outline"
size="default"
onClick={() => navigate(-1)}
>
<ArrowLeft className="h-4 w-4" />
Go back
</Button>
)}
</div>
{/* Path hint */}
<p className="text-xs text-muted-foreground/40">
error 404 · page not found
</p>
</div>
</div>
);
}