101 lines
2.7 KiB
TypeScript
101 lines
2.7 KiB
TypeScript
|
|
"use client";
|
||
|
|
|
||
|
|
import { useEffect, useRef } from "react";
|
||
|
|
import Link from "next/link";
|
||
|
|
import gsap from "gsap";
|
||
|
|
|
||
|
|
export default function RouteError({
|
||
|
|
error,
|
||
|
|
retry,
|
||
|
|
}: {
|
||
|
|
error: Error & { digest?: string };
|
||
|
|
retry: () => void;
|
||
|
|
}) {
|
||
|
|
const root = useRef<HTMLElement>(null);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
console.error(error);
|
||
|
|
}, [error]);
|
||
|
|
|
||
|
|
useEffect(() => {
|
||
|
|
const reduce = window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
||
|
|
if (reduce) return;
|
||
|
|
|
||
|
|
const ctx = gsap.context(() => {
|
||
|
|
gsap.set("[data-fade]", { autoAlpha: 0, y: 14 });
|
||
|
|
|
||
|
|
gsap
|
||
|
|
.timeline({ defaults: { ease: "power3.out" } })
|
||
|
|
.from("[data-icon]", { autoAlpha: 0, scale: 0.6, duration: 0.4, ease: "back.out(2)" })
|
||
|
|
.to("[data-icon]", {
|
||
|
|
keyframes: { rotate: [0, -9, 8, -6, 4, 0] },
|
||
|
|
duration: 0.55,
|
||
|
|
ease: "power1.inOut",
|
||
|
|
})
|
||
|
|
.to("[data-fade]", { autoAlpha: 1, y: 0, duration: 0.5, stagger: 0.08 }, "-=0.3")
|
||
|
|
.to("[data-icon]", {
|
||
|
|
scale: 1.05,
|
||
|
|
duration: 1.6,
|
||
|
|
ease: "sine.inOut",
|
||
|
|
repeat: -1,
|
||
|
|
yoyo: true,
|
||
|
|
});
|
||
|
|
}, root);
|
||
|
|
|
||
|
|
return () => ctx.revert();
|
||
|
|
}, []);
|
||
|
|
|
||
|
|
return (
|
||
|
|
<main
|
||
|
|
ref={root}
|
||
|
|
className="flex min-h-screen flex-col items-center justify-center gap-6 px-6 text-center"
|
||
|
|
>
|
||
|
|
<svg
|
||
|
|
data-icon
|
||
|
|
viewBox="0 0 24 24"
|
||
|
|
className="size-20 text-danger"
|
||
|
|
fill="none"
|
||
|
|
stroke="currentColor"
|
||
|
|
strokeWidth="1.75"
|
||
|
|
strokeLinecap="round"
|
||
|
|
strokeLinejoin="round"
|
||
|
|
aria-hidden="true"
|
||
|
|
>
|
||
|
|
<path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0Z" />
|
||
|
|
<line x1="12" y1="9" x2="12" y2="13" />
|
||
|
|
<line x1="12" y1="17" x2="12.01" y2="17" />
|
||
|
|
</svg>
|
||
|
|
|
||
|
|
<div data-fade className="space-y-2">
|
||
|
|
<h1 className="text-2xl font-semibold">Quelque chose a planté</h1>
|
||
|
|
<p className="max-w-[46ch] text-muted">
|
||
|
|
Une erreur inattendue s'est produite pendant le rendu de cette
|
||
|
|
page.
|
||
|
|
</p>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
<div data-fade className="flex flex-wrap justify-center gap-3">
|
||
|
|
<button
|
||
|
|
type="button"
|
||
|
|
onClick={() => retry()}
|
||
|
|
className="rounded-md border border-line bg-surface px-4 py-2 text-sm font-semibold hover:border-muted"
|
||
|
|
>
|
||
|
|
Réessayer
|
||
|
|
</button>
|
||
|
|
<Link
|
||
|
|
href="/"
|
||
|
|
className="rounded-md border border-line px-4 py-2 text-sm font-semibold text-accent hover:border-muted"
|
||
|
|
>
|
||
|
|
Retour à l'accueil
|
||
|
|
</Link>
|
||
|
|
</div>
|
||
|
|
|
||
|
|
{error.digest && (
|
||
|
|
<p data-fade className="font-mono text-xs text-muted">
|
||
|
|
ref: {error.digest}
|
||
|
|
</p>
|
||
|
|
)}
|
||
|
|
</main>
|
||
|
|
);
|
||
|
|
}
|