feat: add loading states to home page

This commit is contained in:
Rodrigo Verdiani 2026-04-05 19:45:59 -03:00
parent ee47a05f9f
commit db94d2480d
3 changed files with 78 additions and 8 deletions

View File

@ -0,0 +1,16 @@
import { Loader2Icon } from "lucide-react"
import { cn } from "@/lib/utils"
function Spinner({ className, ...props }: React.ComponentProps<"svg">) {
return (
<Loader2Icon
role="status"
aria-label="Loading"
className={cn("size-4 animate-spin", className)}
{...props}
/>
)
}
export { Spinner }

View File

@ -0,0 +1,40 @@
import { Spinner } from "@/components/ui/spinner";
import { useRef } from "react";
const CHEEKY_MESSAGES = [
"Sharpening katanas and downloading manga...",
"Searching for the One Piece... and your titles.",
"Summoning the Great Sage for faster loading...",
"Powering up to Super Saiyan level... please wait.",
"Collecting all seven Dragon Balls to fetch data...",
"Even Saitama takes a second to load... sometimes.",
"Naruto is training, wait for the results...",
"Loading... because we don't have a Death Note for bugs.",
"Entering the Hidden Leaf Village... of data.",
"Waiting for the next chapter... and your results.",
"Training in the Hyperbolic Time Chamber for better speed...",
"Collecting chakra for the ultimate data retrieval...",
"Waiting for the next hiatus to end... oh wait, just loading.",
"Reading the manga faster than you can... hold on.",
"Asking the Shinigami for the right data...",
"Is this a Jojo reference? No, it's just loading.",
"Surpassing our limits... Right here! Right now!",
"Hunting for the rarest manga volumes in the digital world...",
"Dodging spoilers while fetching your manga...",
"Preparing the transmutation circle for your results...",
];
export const MangaLoadingState = () => {
const loadingMessage = useRef(
CHEEKY_MESSAGES[Math.floor(Math.random() * CHEEKY_MESSAGES.length)],
);
return (
<div className="flex-1 flex flex-col items-center justify-center gap-4">
<Spinner className="size-12 text-primary" />
<p className="animate-pulse text-muted-foreground font-medium text-center px-4">
{loadingMessage.current}
</p>
</div>
);
};

View File

@ -3,12 +3,14 @@ import { useEffect, useRef } from "react";
import { useDebounce } from "use-debounce";
import { useGetMangas } from "@/api/generated/catalog/catalog.ts";
import { AuthHeader } from "@/components/AuthHeader.tsx";
import { Spinner } from "@/components/ui/spinner.tsx";
import { Pagination } from "@/components/Pagination.tsx";
import { ThemeToggle } from "@/components/ThemeToggle.tsx";
import { Input } from "@/components/ui/input.tsx";
import { useUIState } from "@/contexts/UIStateContext.tsx";
import { FilterSidebar } from "@/features/home/components/FilterSidebar.tsx";
import { MangaGrid } from "@/features/home/components/MangaGrid.tsx";
import { MangaLoadingState } from "@/features/home/components/MangaLoadingState.tsx";
import { SortDropdown } from "@/features/home/components/SortDropdown.tsx";
import { useDynamicPageSize } from "@/hooks/useDynamicPageSize.ts";
@ -37,7 +39,12 @@ const Home = () => {
const [debouncedSearchText] = useDebounce(searchText, 500);
const { data: mangasData, queryKey: mangasQueryKey } = useGetMangas({
const {
data: mangasData,
queryKey: mangasQueryKey,
isPending,
isFetching,
} = useGetMangas({
page: currentPage - 1,
size: itemsPerPage,
sort: ["id"],
@ -75,7 +82,7 @@ const Home = () => {
onShowAdultContentChange={setShowAdultContent}
/>
<div className="flex-1">
<div className="flex-1 flex flex-col">
<header className="border-b border-border bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/60">
<div className="px-8 py-6">
<div className="flex flex-col gap-6">
@ -86,9 +93,14 @@ const Home = () => {
<h1 className="text-3xl font-bold tracking-tight text-foreground">
MangaMochi
</h1>
<p className="mt-1 text-sm text-muted-foreground">
{mangasData?.data?.totalElements} titles available
</p>
<div className="mt-1 flex items-center gap-2">
<p className="text-sm text-muted-foreground">
{mangasData?.data?.totalElements ?? 0} titles available
</p>
{isFetching && (
<Spinner className="size-3 text-muted-foreground" />
)}
</div>
</div>
</div>
<div className="flex items-center gap-4">
@ -114,8 +126,10 @@ const Home = () => {
</div>
</header>
<main className="px-8 py-8">
{mangasData?.data?.content && mangasData.data.content.length > 0 ? (
<main className="flex-1 px-8 py-8 flex flex-col">
{isPending ? (
<MangaLoadingState />
) : mangasData?.data?.content && mangasData.data.content.length > 0 ? (
<>
{mangasData.data?.totalElements && (
<div className="mb-6 flex items-center justify-between">
@ -149,7 +163,7 @@ const Home = () => {
)}
</>
) : (
<div className="flex min-h-[400px] items-center justify-center">
<div className="flex-1 flex items-center justify-center">
<div className="text-center">
<p className="text-lg text-muted-foreground">
No manga found matching your filters.