Add Suspense fallback for loading states in multiple pages and layouts

This commit is contained in:
2026-01-20 07:43:19 -05:00
parent 775a351425
commit 3ab697b58d
7 changed files with 94 additions and 13 deletions
+19 -1
View File
@@ -1,6 +1,8 @@
// app/(builder)/builder/page.tsx // app/(builder)/builder/page.tsx
"use client"; "use client";
import { Suspense } from "react";
/** /**
* Battl Builder - Main builder page * Battl Builder - Main builder page
* *
@@ -329,7 +331,7 @@ function buildAssemblyRows(params: {
return rows; return rows;
} }
export default function GunbuilderPage() { function GunbuilderPageContent() {
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const router = useRouter(); const router = useRouter();
@@ -1704,3 +1706,19 @@ export default function GunbuilderPage() {
</main> </main>
); );
} }
export default function GunbuilderPage() {
return (
<Suspense fallback={
<main className="min-h-screen bg-white text-zinc-900 dark:bg-black dark:text-zinc-50">
<div className="mx-auto max-w-6xl px-4 py-6 lg:py-10">
<div className="rounded-lg border border-zinc-800 bg-zinc-950/80 p-4">
<div className="text-sm text-zinc-400">Loading builder</div>
</div>
</div>
</main>
}>
<GunbuilderPageContent />
</Suspense>
);
}
+4 -2
View File
@@ -1,16 +1,18 @@
// app/(builder)/layout.tsx // app/(builder)/layout.tsx
import type { ReactNode } from "react"; import type { ReactNode } from "react";
import { Suspense } from "react";
import { BuilderNav } from "@/components/BuilderNav"; import { BuilderNav } from "@/components/BuilderNav";
import { TopNav } from "@/components/TopNav"; import { TopNav } from "@/components/TopNav";
import { Import } from "lucide-react";
import { Footer } from "@/components/Footer"; import { Footer } from "@/components/Footer";
export default function BuilderLayout({ children }: { children: ReactNode }) { export default function BuilderLayout({ children }: { children: ReactNode }) {
return ( return (
<div className="min-h-screen bg-white text-zinc-900 dark:bg-neutral-950 dark:text-zinc-50"> <div className="min-h-screen bg-white text-zinc-900 dark:bg-neutral-950 dark:text-zinc-50">
<TopNav /> <TopNav />
<BuilderNav /> <Suspense fallback={<div className="h-[52px]" />}>
<BuilderNav />
</Suspense>
<main className="min-h-screen">{children}</main> <main className="min-h-screen">{children}</main>
<Footer /> <Footer />
</div> </div>
+17 -2
View File
@@ -1,14 +1,14 @@
// app/beta/confirm/page.tsx // app/beta/confirm/page.tsx
"use client"; "use client";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState, Suspense } from "react";
import { useRouter, useSearchParams } from "next/navigation"; import { useRouter, useSearchParams } from "next/navigation";
import { useAuth } from "@/context/AuthContext"; import { useAuth } from "@/context/AuthContext";
const API_BASE_URL = const API_BASE_URL =
process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:8080"; process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:8080";
export default function BetaConfirmPage() { function BetaConfirmPageContent() {
const params = useSearchParams(); const params = useSearchParams();
const token = params.get("token"); const token = params.get("token");
const router = useRouter(); const router = useRouter();
@@ -88,4 +88,19 @@ export default function BetaConfirmPage() {
</div> </div>
</main> </main>
); );
}
export default function BetaConfirmPage() {
return (
<Suspense fallback={
<main className="min-h-screen flex items-center justify-center bg-black text-white px-6">
<div className="max-w-md w-full rounded-xl border border-white/10 bg-white/5 p-6 text-center">
<h1 className="text-lg font-semibold">Battl Builders</h1>
<p className="mt-2 text-sm text-zinc-300">Loading</p>
</div>
</main>
}>
<BetaConfirmPageContent />
</Suspense>
);
} }
+17 -2
View File
@@ -1,10 +1,10 @@
"use client"; "use client";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState, Suspense } from "react";
import { useRouter, useSearchParams } from "next/navigation"; import { useRouter, useSearchParams } from "next/navigation";
import { useAuth } from "@/context/AuthContext"; import { useAuth } from "@/context/AuthContext";
export default function BetaMagicPage() { function BetaMagicPageContent() {
const params = useSearchParams(); const params = useSearchParams();
const token = params.get("token"); const token = params.get("token");
const router = useRouter(); const router = useRouter();
@@ -75,4 +75,19 @@ export default function BetaMagicPage() {
</div> </div>
</main> </main>
); );
}
export default function BetaMagicPage() {
return (
<Suspense fallback={
<main className="min-h-screen bg-black text-zinc-50 flex items-center justify-center px-4">
<div className="max-w-md w-full rounded-xl border border-white/10 bg-white/5 p-6">
<h1 className="text-lg font-semibold">Battl Builders</h1>
<p className="mt-2 text-sm text-zinc-300">Loading</p>
</div>
</main>
}>
<BetaMagicPageContent />
</Suspense>
);
} }
+4 -1
View File
@@ -1,5 +1,6 @@
// app/(builder)/layout.tsx // app/(builder)/layout.tsx
import type { ReactNode } from "react"; import type { ReactNode } from "react";
import { Suspense } from "react";
import { AuthProvider } from "@/context/AuthContext"; import { AuthProvider } from "@/context/AuthContext";
import { BuilderNav } from "@/components/BuilderNav"; import { BuilderNav } from "@/components/BuilderNav";
@@ -10,7 +11,9 @@ export default function BuilderLayout({ children }: { children: ReactNode }) {
<div className="min-h-screen bg-neutral-950 text-zinc-50"> <div className="min-h-screen bg-neutral-950 text-zinc-50">
<AuthProvider> <AuthProvider>
<TopNav /> <TopNav />
<BuilderNav /> <Suspense fallback={<div className="h-[52px]" />}>
<BuilderNav />
</Suspense>
<main className="min-h-screen">{children}</main> <main className="min-h-screen">{children}</main>
</AuthProvider> </AuthProvider>
</div> </div>
+16 -2
View File
@@ -1,7 +1,7 @@
// app/login/page.tsx // app/login/page.tsx
"use client"; "use client";
import { FormEvent, useMemo, useState } from "react"; import { FormEvent, useMemo, useState, Suspense } from "react";
import { useRouter, useSearchParams } from "next/navigation"; import { useRouter, useSearchParams } from "next/navigation";
import Link from "next/link"; import Link from "next/link";
import { useAuth } from "@/context/AuthContext"; import { useAuth } from "@/context/AuthContext";
@@ -10,7 +10,7 @@ import { Button, Field, Input } from "@/components/ui/form";
const API_BASE_URL = const API_BASE_URL =
process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:8080"; process.env.NEXT_PUBLIC_API_BASE_URL ?? "http://localhost:8080";
export default function LoginPage() { function LoginPageContent() {
const router = useRouter(); const router = useRouter();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const { login, loading } = useAuth(); const { login, loading } = useAuth();
@@ -186,3 +186,17 @@ export default function LoginPage() {
</main> </main>
); );
} }
export default function LoginPage() {
return (
<Suspense fallback={
<main className="min-h-screen bg-black text-zinc-50">
<div className="mx-auto flex max-w-md flex-col px-4 py-10">
<div className="h-8 w-48 animate-pulse bg-zinc-800 rounded" />
</div>
</main>
}>
<LoginPageContent />
</Suspense>
);
}
+17 -3
View File
@@ -1,7 +1,7 @@
// app/register/page.tsx // app/register/page.tsx
"use client"; "use client";
import { FormEvent, useState } from "react"; import { FormEvent, useState, Suspense } from "react";
import { useRouter, useSearchParams } from "next/navigation"; import { useRouter, useSearchParams } from "next/navigation";
import Link from "next/link"; import Link from "next/link";
import { useAuth } from "@/context/AuthContext"; import { useAuth } from "@/context/AuthContext";
@@ -9,7 +9,7 @@ import { Button, Field, Input } from "@/components/ui/form";
const TOS_VERSION = "2025-12-27"; const TOS_VERSION = "2025-12-27";
export default function RegisterPage() { function RegisterPageContent() {
const router = useRouter(); const router = useRouter();
const searchParams = useSearchParams(); const searchParams = useSearchParams();
const { register, loading } = useAuth(); const { register, loading } = useAuth();
@@ -125,7 +125,7 @@ export default function RegisterPage() {
> >
Privacy Policy Privacy Policy
</Link> </Link>
. I understand Battl Builders is informational only and Im . I understand Battl Builders is informational only and I'm
solely responsible for legality, compatibility, and safe solely responsible for legality, compatibility, and safe
assembly/use of any firearm or components. assembly/use of any firearm or components.
</span> </span>
@@ -147,4 +147,18 @@ export default function RegisterPage() {
</div> </div>
</main> </main>
); );
}
export default function RegisterPage() {
return (
<Suspense fallback={
<main className="min-h-screen bg-black text-zinc-50">
<div className="mx-auto flex max-w-md flex-col px-4 py-10">
<div className="h-8 w-64 animate-pulse bg-zinc-800 rounded" />
</div>
</main>
}>
<RegisterPageContent />
</Suspense>
);
} }