feat(dependencies): add Lottie animation libraries and refactor NotFoundPage

- 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.
This commit is contained in:
AmirReza Jamali
2026-05-13 17:07:47 +03:30
parent d1bc56dc60
commit 25f8bde229
49 changed files with 3511 additions and 2197 deletions
+185
View File
@@ -0,0 +1,185 @@
"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>
);
}