diff --git a/CHANGELOG.md b/CHANGELOG.md index 66af4bf..d65a317 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Fixed +- Fixed infinite spinning wheel on the home page: `authStore` no longer initialises `isLoading` as `true` unconditionally — it is now `false` when no access token exists in `localStorage`, so unauthenticated users see the landing page immediately instead of an endless spinner +- Home page now performs an auth check when a token is present in `localStorage`, redirecting authenticated users to the dashboard and clearing stale tokens on failure - Wrapped `useSearchParams()` in a `Suspense` boundary in `frontend/src/app/auth/callback/page.tsx` to fix the Next.js build error: "useSearchParams() should be wrapped in a suspense boundary at page /auth/callback" - Fixed TypeScript build error in `frontend/src/app/accounts/page.tsx`: replaced non-existent `account.username` with `account.email_address`, `account.last_checked_at` with `account.last_check_at`, and `account.last_error` with `account.last_error_message` (the backend intentionally excludes `username` from API responses for security) - Fixed TypeScript error in `frontend/src/app/auth/callback/page.tsx`: `TokenResponse` doesn't include `user`; now fetches user via `userApi.getCurrentUser()` after OAuth token exchange diff --git a/docs/TODO.md b/docs/TODO.md index 2dc4107..818ad17 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -195,6 +195,7 @@ because the API client layer is missing. - Exports: `authApi`, `mailAccountsApi`, `processingRunsApi`, `userApi` - Exports types: `User`, `MailAccount`, `MailAccountCreate`, `ProcessingRun` - 8 files import from `@/lib/api` — all compilation errors resolved +- [x] Fix infinite spinner on home page: `isLoading` now initialises based on token presence; home page performs auth check when token exists ### Existing Pages (UI done, need API wiring) 🔨 - [x] Landing page (`app/page.tsx`) diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 55558cb..6127f1f 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -4,17 +4,30 @@ import { useEffect } from 'react'; import Link from 'next/link'; import { useRouter } from 'next/navigation'; import { useAuthStore } from '@/store/authStore'; +import { userApi } from '@/lib/api'; import { Mail, ArrowRight, Shield, Zap, Clock } from 'lucide-react'; export default function Home() { const router = useRouter(); - const { user, isLoading } = useAuthStore(); + const { isLoading, setUser, setLoading } = useAuthStore(); useEffect(() => { - if (!isLoading && user) { - router.push('/dashboard'); - } - }, [user, isLoading, router]); + const token = + typeof window !== 'undefined' ? localStorage.getItem('access_token') : null; + if (!token) return; + + userApi + .getCurrentUser() + .then((userData) => { + setUser(userData); + router.push('/dashboard'); + }) + .catch((err) => { + console.error('Auth check failed on home page:', err); + localStorage.removeItem('access_token'); + setLoading(false); + }); + }, [router, setUser, setLoading]); if (isLoading) { return ( diff --git a/frontend/src/store/authStore.ts b/frontend/src/store/authStore.ts index f69b3f0..016b551 100644 --- a/frontend/src/store/authStore.ts +++ b/frontend/src/store/authStore.ts @@ -16,7 +16,7 @@ export const useAuthStore = create((set) => ({ user: null, token: typeof window !== 'undefined' ? localStorage.getItem('access_token') : null, isAuthenticated: typeof window !== 'undefined' ? !!localStorage.getItem('access_token') : false, - isLoading: true, + isLoading: typeof window !== 'undefined' ? !!localStorage.getItem('access_token') : false, setUser: (user) => set({ user,