Component props with loading type and optional rate limit/retry info.
Animated loading card with spinner and contextual message.
export function LoadingStateDisplay({
type,
isRateLimited = false,
retryCount = 0,
maxRetries = 3,
}: Readonly<LoadingStateDisplayProps>) {
let loadingTitle: string;
if (type === "manga") {
loadingTitle = "Loading synchronization data";
} else if (isRateLimited) {
loadingTitle = "Synchronization paused";
} else {
loadingTitle = "Fetching your AniList library";
}
let description: string;
if (type === "manga") {
description =
"We're pulling in your matched manga and preparing preview data.";
} else if (isRateLimited) {
description =
"AniList asked us to slow down for a moment. We'll automatically resume once the cooldown ends.";
} else if (retryCount > 0) {
description = `Server hiccup detected. Retrying (${retryCount}/${maxRetries}) shortly...`;
} else {
description =
"Collecting your AniList entries so we can compare them against Kenmei.";
}
let accent: { glow: string; icon: string; text: string };
if (type === "manga") {
accent = {
glow: "from-emerald-300/60 to-transparent",
icon: "from-emerald-500 to-teal-500",
text: "text-emerald-500 dark:text-emerald-300",
};
} else if (isRateLimited) {
accent = {
glow: "from-amber-300/60 to-transparent",
icon: "from-amber-500 to-orange-500",
text: "text-amber-600 dark:text-amber-300",
};
} else {
accent = {
glow: "from-blue-300/60 to-transparent",
icon: "from-blue-500 to-indigo-500",
text: "text-blue-600 dark:text-blue-300",
};
}
return (
<div className="relative py-10">
<div className="pointer-events-none absolute inset-x-0 top-0 flex justify-center">
<div
className={`bg-linear-to-br h-56 w-56 rounded-full ${accent.glow} blur-3xl`}
/>
</div>
<motion.div
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
transition={{ duration: 0.4 }}
>
<Card className="mx-auto w-full max-w-md overflow-hidden border border-slate-200/70 bg-white/80 text-center shadow-xl backdrop-blur-xl dark:border-slate-800/60 dark:bg-slate-950/60">
<CardContent className="pb-8 pt-10">
<div
className={`bg-linear-to-br mx-auto mb-6 flex h-16 w-16 items-center justify-center rounded-full ${accent.icon} text-white shadow-lg`}
>
<div className="h-8 w-8 animate-spin rounded-full border-[3px] border-white/70 border-t-transparent" />
</div>
<h3 className={`text-lg font-semibold ${accent.text}`}>
{loadingTitle}
</h3>
<p className="mt-2 text-sm text-slate-600 dark:text-slate-400">
{description}
</p>
{isRateLimited && (
<p className="mt-2 text-xs text-amber-600 dark:text-amber-300">
You can keep browsing the preview while we wait for the cool
down.
</p>
)}
</CardContent>
</Card>
</motion.div>
</div>
);
}
Display animated loading state with context-specific messaging and visual feedback. Handles different loading scenarios: manga data preparation, library fetch, rate limiting, and retry states. Provides different visual treatment based on scenario (glow colors, icons, descriptions).