fix(nav): improve navigation for authorization routes

This commit is contained in:
shafin-r
2025-09-08 14:15:27 +06:00
parent 99d6c15e38
commit 53a2228dc9
9 changed files with 55 additions and 63 deletions

View File

@ -29,7 +29,7 @@ const LoginPage = () => {
setIsLoading(true); setIsLoading(true);
setError(null); setError(null);
await login(form, setToken); await login(form, setToken);
router.push("/home"); router.replace("/home");
} catch (err: unknown) { } catch (err: unknown) {
console.error(err); console.error(err);

View File

@ -88,7 +88,7 @@ export default function RegisterPage() {
try { try {
await register(form, setToken); await register(form, setToken);
router.push("/login"); router.replace("/login");
} catch (err: unknown) { } catch (err: unknown) {
setError(formatError(err)); setError(formatError(err));
console.error("User creation error: ", err); console.error("User creation error: ", err);

View File

@ -73,7 +73,10 @@ export default function MockScreen() {
<Header displayTabTitle="Mocks" /> <Header displayTabTitle="Mocks" />
<div className="overflow-y-auto"> <div className="overflow-y-auto">
<div className="mt-5 px-5"> <div className="mt-5 px-5">
<DestructibleAlert text={errorMsg} /> <h1 className="text-2xl font-semibold my-5">
{user?.preparation_unit}
</h1>
<DestructibleAlert text={errorMsg} variant="error" />
</div> </div>
<div className="flex justify-center mt-4"> <div className="flex justify-center mt-4">
<button <button

View File

@ -68,7 +68,10 @@ export default function PaperScreen() {
<Header displayTabTitle="Subjects" /> <Header displayTabTitle="Subjects" />
<div className="overflow-y-auto"> <div className="overflow-y-auto">
<div className="mt-5 px-5"> <div className="mt-5 px-5">
<DestructibleAlert text={errorMsg} /> <h1 className="text-2xl font-semibold my-5">
{user?.preparation_unit}
</h1>
<DestructibleAlert text={errorMsg} variant="error" />
</div> </div>
<div className="flex justify-center mt-4"> <div className="flex justify-center mt-4">
<button <button

View File

@ -72,7 +72,10 @@ export default function TopicScreen() {
<Header displayTabTitle="Subjects" /> <Header displayTabTitle="Subjects" />
<div className="overflow-y-auto"> <div className="overflow-y-auto">
<div className="mt-5 px-5"> <div className="mt-5 px-5">
<DestructibleAlert text={errorMsg} /> <h1 className="text-2xl font-semibold my-5">
{user?.preparation_unit}
</h1>
<DestructibleAlert text={errorMsg} variant="error" />
</div> </div>
<div className="flex justify-center mt-4"> <div className="flex justify-center mt-4">
<button <button

View File

@ -9,32 +9,41 @@ export default function AuthInitializer({
}: { }: {
children: React.ReactNode; children: React.ReactNode;
}) { }) {
const { initializeAuth, isLoading, token } = useAuthStore(); const { initializeAuth, token, hydrated } = useAuthStore();
const router = useRouter(); const router = useRouter();
const pathname = usePathname(); const pathname = usePathname();
// 1⃣ Run initialization once
useEffect(() => { useEffect(() => {
const run = async () => { initializeAuth();
await initializeAuth(); }, [initializeAuth]);
// Routing logic based on auth state // 2⃣ Run routing logic only after hydration
const publicPages = ["/", "/login", "/register"]; useEffect(() => {
if (!hydrated) return;
if (token) { const publicPages = ["/", "/login", "/register"];
if (publicPages.includes(pathname)) {
router.replace("/home"); if (token) {
} if (publicPages.includes(pathname)) {
} else { router.replace("/home");
if (!publicPages.includes(pathname)) {
router.replace("/");
}
} }
}; } else {
if (!publicPages.includes(pathname)) {
router.replace("/login");
}
}
}, [pathname, token, hydrated, router]);
run(); // 3⃣ Show loading until hydrated
}, [pathname, token, initializeAuth, router]); if (!hydrated) {
return (
if (isLoading) return <p>Loading...</p>; <div className="min-h-screen flex flex-col justify-center items-center gap-3">
<div className="animate-spin rounded-full h-20 w-20 border-b-2 border-blue-500"></div>
<p className="text-2xl font-semibold tracking-tighter">Loading...</p>
</div>
);
}
return <>{children}</>; return <>{children}</>;
} }

View File

@ -46,9 +46,9 @@ const Header = ({
const seconds = timeRemaining % 60; const seconds = timeRemaining % 60;
return ( return (
<header className={styles.header}> <header className="bg-[#113768] h-[100px] w-full pt-7 px-6 flex items-center justify-between sticky top-0 z-50">
{displayUser && ( {displayUser && (
<div className={styles.profile}> <div className="flex items-center gap-3">
<Avatar className="bg-gray-200 w-10 h-10"> <Avatar className="bg-gray-200 w-10 h-10">
<AvatarFallback className="flex items-center justify-center h-10 text-lg"> <AvatarFallback className="flex items-center justify-center h-10 text-lg">
{user?.username ? ( {user?.username ? (
@ -58,24 +58,26 @@ const Header = ({
)} )}
</AvatarFallback> </AvatarFallback>
</Avatar> </Avatar>
<span className={styles.text}> <span className="text-md font-bold text-white">
Hello, {user?.username ? user.username.split(" ")[0] : "User"} Hello, {user?.username ? user.username.split(" ")[0] : "User"}
</span> </span>
</div> </div>
)} )}
{displayTabTitle && ( {displayTabTitle && (
<div className={styles.profile}> <div className="flex items-center gap-3">
<button onClick={handleBackClick} className={styles.iconButton}> <button onClick={handleBackClick} className="bg-none border-none p-1">
<ChevronLeft size={24} color="white" /> <ChevronLeft size={24} color="white" />
</button> </button>
<span className={styles.text}>{displayTabTitle}</span> <span className="text-md font-bold text-white">
{displayTabTitle}
</span>
</div> </div>
)} )}
{displaySubject && ( {displaySubject && (
<div className={styles.profile}> <div className="flex items-center gap-3">
<span className={styles.text}>{displaySubject}</span> <span className="text-md font-bold text-white">{displaySubject}</span>
</div> </div>
)} )}

View File

@ -127,32 +127,4 @@ export const getLinkedViews = (): GalleryViews[] => [
</Link> </Link>
), ),
}, },
{
id: 2,
content: (
<Link
href="https://www.facebook.com/share/g/15jdqESvWV/?mibextid=wwXIfr"
className="lock "
>
<div className="w-full h-full p-6 flex text-black bg-blue-50 rounded-3xl border-[0.5px] border-[#113768]/30">
<div className="">
<h3 className="text-2xl text-[#113768] font-black">
Meet, Share, and Learn!
</h3>
<p className="font-bold text-sm text-[#113768] ">
Join Facebook Community
</p>
</div>
<div className="flex justify-center items-center shrink-0">
<Image
src="/images/static/facebook-logo.png"
alt="Facebook Logo"
width={150}
height={150}
/>
</div>
</div>
</Link>
),
},
]; ];

View File

@ -35,6 +35,7 @@ const setCookie = (
interface AuthState { interface AuthState {
token: string | null; token: string | null;
isLoading: boolean; isLoading: boolean;
hydrated: boolean;
user: UserData | null; user: UserData | null;
setToken: (token: string | null) => void; setToken: (token: string | null) => void;
@ -46,6 +47,7 @@ interface AuthState {
export const useAuthStore = create<AuthState>((set, get) => ({ export const useAuthStore = create<AuthState>((set, get) => ({
token: null, token: null,
isLoading: true, isLoading: true,
hydrated: false,
user: null, user: null,
setToken: (newToken) => { setToken: (newToken) => {
@ -59,9 +61,7 @@ export const useAuthStore = create<AuthState>((set, get) => ({
try { try {
const res = await fetch(`${API_URL}/me/profile/`, { const res = await fetch(`${API_URL}/me/profile/`, {
headers: { headers: { Authorization: `Bearer ${token}` },
Authorization: `Bearer ${token}`,
},
}); });
if (!res.ok) throw new Error("Failed to fetch user info"); if (!res.ok) throw new Error("Failed to fetch user info");
@ -85,6 +85,6 @@ export const useAuthStore = create<AuthState>((set, get) => ({
set({ token: storedToken }); set({ token: storedToken });
await get().fetchUser(); await get().fetchUser();
} }
set({ isLoading: false }); set({ isLoading: false, hydrated: true });
}, },
})); }));