refactor: Remove AccountRouteGuard and adjust loading state handling in AppShell and views
This commit is contained in:
parent
69e07ecef2
commit
8d9c954230
@ -1,31 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect } from "react";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import { useAuthStore } from "@/features/auth/stores/auth.store";
|
||||
|
||||
export function AccountRouteGuard() {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const isAuthenticated = useAuthStore(state => state.isAuthenticated);
|
||||
const hasCheckedAuth = useAuthStore(state => state.hasCheckedAuth);
|
||||
const loading = useAuthStore(state => state.loading);
|
||||
const checkAuth = useAuthStore(state => state.checkAuth);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasCheckedAuth) {
|
||||
void checkAuth();
|
||||
}
|
||||
}, [checkAuth, hasCheckedAuth]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasCheckedAuth || loading || isAuthenticated) {
|
||||
return;
|
||||
}
|
||||
|
||||
const destination = pathname || "/account";
|
||||
router.replace(`/auth/login?redirect=${encodeURIComponent(destination)}`);
|
||||
}, [hasCheckedAuth, isAuthenticated, loading, pathname, router]);
|
||||
|
||||
return null;
|
||||
}
|
||||
@ -2,12 +2,10 @@ import type { ReactNode } from "react";
|
||||
import { AppShell } from "@/components/organisms";
|
||||
import { ErrorBoundary, PageErrorFallback } from "@/components/molecules";
|
||||
import { AccountEventsListener } from "@/features/realtime/components/AccountEventsListener";
|
||||
import { AccountRouteGuard } from "./AccountRouteGuard";
|
||||
|
||||
export default function AccountLayout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<AppShell>
|
||||
<AccountRouteGuard />
|
||||
<AccountEventsListener />
|
||||
<ErrorBoundary fallback={<PageErrorFallback />}>{children}</ErrorBoundary>
|
||||
</AppShell>
|
||||
|
||||
@ -147,7 +147,10 @@ export function AppShell({ children }: AppShellProps) {
|
||||
}
|
||||
}, [navigation, router]);
|
||||
|
||||
// Do not block the shell; sidebar/header remain mounted. The content area can handle its own loading.
|
||||
// Determine if we should show loading state in content area
|
||||
// Auth must be checked before rendering protected content
|
||||
const isAuthReady = hasCheckedAuth && isAuthenticated;
|
||||
const isCheckingAuth = !hasCheckedAuth || loading;
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -194,7 +197,25 @@ export function AppShell({ children }: AppShellProps) {
|
||||
/>
|
||||
|
||||
{/* Main content area */}
|
||||
<main className="flex-1 relative overflow-y-auto focus:outline-none">{children}</main>
|
||||
<main className="flex-1 relative overflow-y-auto focus:outline-none">
|
||||
{isCheckingAuth ? (
|
||||
<div className="p-6 md:p-8 lg:p-10">
|
||||
<div className="space-y-6 animate-pulse">
|
||||
<div className="space-y-3">
|
||||
<div className="h-4 bg-muted rounded w-24" />
|
||||
<div className="h-8 bg-muted rounded w-48" />
|
||||
</div>
|
||||
<div className="h-32 bg-muted rounded-xl" />
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
<div className="h-40 bg-muted rounded-xl" />
|
||||
<div className="h-40 bg-muted rounded-xl" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : isAuthReady ? (
|
||||
children
|
||||
) : null}
|
||||
</main>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -145,7 +145,7 @@ export const useAuthStore = create<AuthState>()((set, get) => {
|
||||
if (!parsed.success) {
|
||||
throw new Error(parsed.error.issues?.[0]?.message ?? "Login failed");
|
||||
}
|
||||
applyAuthResponse(parsed.data, true); // Keep loading for redirect
|
||||
applyAuthResponse(parsed.data); // Let destination page handle loading
|
||||
} catch (error) {
|
||||
const parsed = parseError(error);
|
||||
set({ loading: false, error: parsed.message, isAuthenticated: false });
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { Suspense, useEffect, useMemo, useState } from "react";
|
||||
import { Suspense, useEffect, useMemo, useRef, useState } from "react";
|
||||
import { useSearchParams } from "next/navigation";
|
||||
|
||||
import { AuthLayout } from "../components";
|
||||
@ -14,6 +14,7 @@ import {
|
||||
resolveLogoutMessage,
|
||||
type LogoutReason,
|
||||
} from "@/features/auth/utils/logout-reason";
|
||||
import { getPostLoginRedirect } from "@/features/auth/utils/route-protection";
|
||||
|
||||
function LoginContent() {
|
||||
const { loading, isAuthenticated } = useAuthStore();
|
||||
@ -42,6 +43,19 @@ function LoginContent() {
|
||||
}
|
||||
}, [logoutReason]);
|
||||
|
||||
// Track if redirect has been initiated to prevent multiple attempts
|
||||
const hasRedirected = useRef(false);
|
||||
|
||||
// Redirect authenticated users away from login page
|
||||
useEffect(() => {
|
||||
if (isAuthenticated && !loading && !hasRedirected.current) {
|
||||
hasRedirected.current = true;
|
||||
const redirectTo = searchParams ? getPostLoginRedirect(searchParams) : "/account";
|
||||
// Force hard navigation - auth state is persisted via cookies
|
||||
window.location.assign(redirectTo);
|
||||
}
|
||||
}, [isAuthenticated, loading, searchParams]);
|
||||
|
||||
const logoutMessage = logoutReason ? resolveLogoutMessage(logoutReason) : null;
|
||||
|
||||
return (
|
||||
|
||||
@ -30,8 +30,8 @@ export function DashboardView() {
|
||||
const { tasks, isLoading: tasksLoading, taskCount } = useDashboardTasks();
|
||||
const { data: eligibility } = useInternetEligibility({ enabled: isAuthenticated });
|
||||
|
||||
// Combined loading state
|
||||
const isLoading = authLoading || summaryLoading || !isAuthenticated;
|
||||
// Combined loading state - AppShell handles unauthenticated redirect
|
||||
const isLoading = authLoading || summaryLoading;
|
||||
|
||||
useEffect(() => {
|
||||
if (!isAuthenticated || !user?.id) return;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user