25f8bde229
- Added `@lottiefiles/dotlottie-react` and `lottie-react` dependencies for enhanced animation capabilities. - Refactored the NotFoundPage component to utilize a dedicated NotFoundContent component, improving code organization and readability. - Updated package.json and pnpm-lock.yaml to reflect new dependencies and their versions.
186 lines
8.7 KiB
TypeScript
186 lines
8.7 KiB
TypeScript
"use client";
|
|
|
|
import { DotLottieReact } from "@lottiefiles/dotlottie-react";
|
|
import { cn } from "@/lib/utils";
|
|
import Link from "next/link";
|
|
import { useTheme } from "next-themes";
|
|
import { useEffect, useState } from "react";
|
|
|
|
/** Served from `public/lottie/404.lottie` (mirrored in `src/assets/lottie/404.lottie` for the repo). */
|
|
const LOTTIE_URL = "/lottie/404.lottie";
|
|
|
|
export default function NotFoundContent() {
|
|
const { resolvedTheme } = useTheme();
|
|
const [mounted, setMounted] = useState(false);
|
|
const [loadError, setLoadError] = useState(false);
|
|
|
|
useEffect(() => {
|
|
setMounted(true);
|
|
}, []);
|
|
|
|
const mode = mounted && resolvedTheme === "dark" ? "dark" : "light";
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"relative isolate flex min-h-svh w-full flex-col overflow-hidden",
|
|
"bg-gray-1 dark:bg-dark",
|
|
)}
|
|
>
|
|
{/* Ambient mesh */}
|
|
<div
|
|
className="pointer-events-none absolute -left-1/4 top-0 h-[min(85vh,720px)] w-[min(85vw,720px)] rounded-full bg-primary/[0.22] blur-[100px] dark:bg-primary/[0.12]"
|
|
aria-hidden
|
|
/>
|
|
<div
|
|
className="pointer-events-none absolute -right-1/4 bottom-0 h-[min(70vh,560px)] w-[min(70vw,560px)] rounded-full bg-blue/[0.18] blur-[90px] dark:bg-blue/[0.1]"
|
|
aria-hidden
|
|
/>
|
|
<div
|
|
className="pointer-events-none absolute left-1/2 top-1/3 h-64 w-64 -translate-x-1/2 rounded-full bg-primary/[0.08] blur-3xl dark:bg-primary/[0.06]"
|
|
aria-hidden
|
|
/>
|
|
|
|
{/* Vignette + fine grid */}
|
|
<div
|
|
className="pointer-events-none absolute inset-0 bg-[radial-gradient(ellipse_80%_55%_at_50%_38%,transparent_0%,rgba(17,25,40,0.04)_55%,rgba(17,25,40,0.09)_100%)] dark:bg-[radial-gradient(ellipse_80%_55%_at_50%_38%,transparent_0%,rgba(0,0,0,0.35)_55%,rgba(0,0,0,0.65)_100%)]"
|
|
aria-hidden
|
|
/>
|
|
<div
|
|
className={cn(
|
|
"pointer-events-none absolute inset-0 opacity-[0.45] dark:opacity-[0.22]",
|
|
"[background-image:linear-gradient(to_right,rgba(17,25,40,0.06)_1px,transparent_1px),linear-gradient(to_bottom,rgba(17,25,40,0.06)_1px,transparent_1px)]",
|
|
"dark:[background-image:linear-gradient(to_right,rgba(148,163,184,0.08)_1px,transparent_1px),linear-gradient(to_bottom,rgba(148,163,184,0.08)_1px,transparent_1px)]",
|
|
)}
|
|
style={{
|
|
backgroundSize: "28px 28px",
|
|
backgroundPosition: "0 0",
|
|
}}
|
|
aria-hidden
|
|
/>
|
|
|
|
{/* Frame accents */}
|
|
<div className="pointer-events-none absolute left-5 top-5 h-16 w-16 border-l-2 border-t-2 border-primary/40 dark:border-primary/35 sm:left-8 sm:top-8" />
|
|
<div className="pointer-events-none absolute bottom-5 right-5 h-16 w-16 border-b-2 border-r-2 border-primary/40 dark:border-primary/35 sm:bottom-8 sm:right-8" />
|
|
|
|
<div className="relative z-10 flex flex-1 flex-col items-center justify-center px-4 py-14 sm:px-6 sm:py-16">
|
|
<div className="w-full max-w-xl">
|
|
{/* Gradient hairline frame */}
|
|
<div
|
|
className={cn(
|
|
"rounded-[1.75rem] p-[1px] shadow-5",
|
|
"bg-gradient-to-br from-primary/55 via-primary/[0.12] to-blue/35",
|
|
"dark:from-primary/45 dark:via-primary/[0.08] dark:to-blue/25",
|
|
)}
|
|
>
|
|
<div
|
|
className={cn(
|
|
"relative overflow-hidden rounded-[1.7rem]",
|
|
"bg-white/[0.92] backdrop-blur-2xl dark:bg-dark-2/[0.88]",
|
|
"border border-white/60 dark:border-dark-3/80",
|
|
)}
|
|
>
|
|
<div
|
|
className="pointer-events-none absolute inset-x-0 top-0 h-px bg-gradient-to-r from-transparent via-primary/35 to-transparent"
|
|
aria-hidden
|
|
/>
|
|
|
|
<div className="relative px-6 pb-8 pt-7 sm:px-10 sm:pb-10 sm:pt-9">
|
|
<div className="mb-6 flex flex-col items-center gap-3 text-center sm:mb-8">
|
|
<span className="inline-flex items-center gap-2 rounded-full border border-stroke/90 bg-gray-1/90 px-3 py-1 font-mono text-[10px] font-semibold uppercase tracking-[0.28em] text-gray-6 shadow-theme-xs dark:border-dark-3 dark:bg-dark/60 dark:text-dark-6">
|
|
<span
|
|
className="h-1.5 w-1.5 shrink-0 rounded-full bg-primary shadow-[0_0_10px_rgba(87,80,241,0.65)]"
|
|
aria-hidden
|
|
/>
|
|
Route not found
|
|
</span>
|
|
|
|
<h1 className="text-[clamp(3.25rem,12vw,5.5rem)] font-bold leading-none tracking-tight">
|
|
<span className="bg-gradient-to-br from-dark via-dark to-gray-7 bg-clip-text text-transparent dark:from-white dark:via-white dark:to-dark-6">
|
|
4
|
|
</span>
|
|
<span className="bg-gradient-to-br from-primary via-primary to-blue bg-clip-text text-transparent">
|
|
0
|
|
</span>
|
|
<span className="bg-gradient-to-br from-dark via-dark to-gray-7 bg-clip-text text-transparent dark:from-white dark:via-white dark:to-dark-6">
|
|
4
|
|
</span>
|
|
</h1>
|
|
|
|
<p className="max-w-sm text-body-sm text-gray-6 dark:text-dark-6">
|
|
This URL is not mapped in the panel. The page may have moved
|
|
or the link could be mistyped.
|
|
</p>
|
|
</div>
|
|
|
|
<div className="relative mx-auto w-full max-w-[min(100%,400px)]">
|
|
<div
|
|
className="pointer-events-none absolute -inset-3 rounded-2xl bg-gradient-to-b from-primary/[0.07] to-transparent dark:from-primary/[0.05]"
|
|
aria-hidden
|
|
/>
|
|
<div className="relative [&_canvas]:mx-auto [&_canvas]:block [&_canvas]:h-auto [&_canvas]:w-full">
|
|
{loadError ? (
|
|
<div
|
|
className={cn(
|
|
"flex min-h-[200px] w-full items-center justify-center rounded-2xl border border-dashed border-error/40 bg-gray-1/50 dark:border-error/35 dark:bg-dark/40",
|
|
"animate-pulse",
|
|
)}
|
|
role="alert"
|
|
>
|
|
<span className="text-body-xs font-medium text-gray-6 dark:text-dark-6">
|
|
Could not load illustration
|
|
</span>
|
|
</div>
|
|
) : (
|
|
<DotLottieReact
|
|
key={mode}
|
|
src={LOTTIE_URL}
|
|
loop
|
|
autoplay
|
|
className="h-auto w-full max-w-full drop-shadow-[0_12px_40px_rgba(87,80,241,0.12)] dark:drop-shadow-[0_12px_48px_rgba(0,0,0,0.35)]"
|
|
aria-hidden
|
|
onError={() => setLoadError(true)}
|
|
/>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-8 flex flex-col items-stretch gap-3 sm:mt-10 sm:flex-row sm:items-center sm:justify-center sm:gap-4">
|
|
<Link
|
|
href="/"
|
|
className={cn(
|
|
"shadow-theme-xs relative isolate inline-flex w-full items-center justify-center overflow-hidden rounded-xl bg-gradient-to-r from-primary to-primary/85 py-[11px] px-6 text-center text-body-sm font-semibold text-white transition hover:opacity-95",
|
|
"focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-primary",
|
|
"sm:flex-1 sm:max-w-[200px]",
|
|
)}
|
|
>
|
|
Back to dashboard
|
|
</Link>
|
|
<button
|
|
type="button"
|
|
onClick={() =>
|
|
typeof window !== "undefined" && window.history.back()
|
|
}
|
|
className={cn(
|
|
"rounded-xl border border-stroke bg-white/90 px-6 py-3 text-body-sm font-semibold text-dark shadow-theme-xs",
|
|
"transition hover:border-primary/40 hover:text-primary",
|
|
"dark:border-dark-3 dark:bg-dark/80 dark:text-white dark:hover:border-primary/35 dark:hover:bg-dark-3",
|
|
"sm:flex-1 sm:max-w-[200px]",
|
|
)}
|
|
>
|
|
Go back
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<p className="mt-6 text-center font-mono text-[10px] uppercase tracking-[0.35em] text-gray-5 dark:text-dark-5">
|
|
Opp lens · admin
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|