+ SUSPENDED AUTH CHECK

feat: add suspended account handling with dedicated page and redirect logic
This commit is contained in:
seaznCode 2026-01-30 15:34:43 +01:00
parent 1090010169
commit 40d626437c
4 changed files with 60 additions and 4 deletions

View File

@ -1,4 +1,5 @@
import { useState, useEffect, useCallback } from 'react'
import { useRouter } from 'next/navigation'
import useAuthStore from '../store/authStore'
export interface UserStatus {
@ -20,6 +21,7 @@ export interface UserStatusResponse {
}
export const useUserStatus = () => {
const router = useRouter()
const [userStatus, setUserStatus] = useState<UserStatus | null>(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(null)
@ -86,6 +88,14 @@ export const useUserStatus = () => {
}
})
if (response.status === 403) {
useAuthStore.getState().clearAuth()
setUserStatus(null)
setError(null)
router.push('/suspended')
return
}
// If 401, try token refresh once
if (response.status === 401) {
console.log('Got 401, attempting token refresh...')
@ -104,6 +114,14 @@ export const useUserStatus = () => {
}
})
if (retryResponse.status === 403) {
useAuthStore.getState().clearAuth()
setUserStatus(null)
setError(null)
router.push('/suspended')
return
}
if (!retryResponse.ok) {
throw new Error(`Failed to fetch user status: ${retryResponse.status}`)
}
@ -138,7 +156,7 @@ export const useUserStatus = () => {
} finally {
setLoading(false)
}
}, [isClient, accessToken, user])
}, [isClient, accessToken, user, router])
// Fetch status on mount and when auth changes
useEffect(() => {

View File

@ -81,6 +81,9 @@ export default function LoginForm() {
const redirectPath = (result as any).redirectPath || '/dashboard'
// instant redirect; toast persists via global store
router.push(redirectPath)
} else if ((result as any)?.redirectPath) {
router.push((result as any).redirectPath)
return
} else {
showToast({
variant: 'error',

View File

@ -46,8 +46,13 @@ export function useLogin() {
console.log('Login response status:', response.status)
const data = await response.json().catch(() => null)
if (!response.ok) {
// Handle HTTP errors
if (response.status === 403) {
return { success: false, error: 'Account suspended', redirectPath: '/suspended' }
}
if (response.status === 401) {
throw new Error('Invalid credentials')
} else if (response.status === 404) {
@ -58,8 +63,6 @@ export function useLogin() {
throw new Error('Login failed. Please try again.')
}
}
const data = await response.json()
console.log('Login response data:', data)
if (data.success && data.accessToken && data.user) {

View File

@ -0,0 +1,32 @@
'use client'
import Link from 'next/link'
import PageLayout from '../components/PageLayout'
import PageTransitionEffect from '../components/animation/pageTransitionEffect'
export default function SuspendedPage() {
return (
<PageTransitionEffect>
<PageLayout showFooter={true} className="bg-[#F5F5F0] text-slate-900">
<div className="min-h-[70vh] flex items-center justify-center px-6">
<div className="max-w-xl w-full rounded-3xl border border-slate-200 bg-white/80 shadow-xl p-8 text-center">
<div className="mx-auto mb-4 h-12 w-12 rounded-full bg-rose-100 text-rose-700 flex items-center justify-center text-2xl">!</div>
<h1 className="text-2xl md:text-3xl font-bold text-slate-900">Account suspended</h1>
<p className="mt-3 text-slate-700">
Your account has been suspended. For more information, contact
{' '}<a className="text-[#8D6B1D] font-semibold" href="mailto:office@profit-planet.com">office@profit-planet.com</a>.
</p>
<div className="mt-6">
<Link
href="/login"
className="inline-flex items-center justify-center rounded-xl border border-slate-300 px-5 py-2.5 text-sm font-semibold text-slate-800 hover:bg-slate-50"
>
Back to login
</Link>
</div>
</div>
</div>
</PageLayout>
</PageTransitionEffect>
)
}