Fix authentication state persistence and admin role display
- Implement complete authentication system with JWT token validation - Add auth provider with persistent login state across page refreshes - Create multilingual login/register forms with Material-UI components - Fix token validation using raw SQL queries to bypass Prisma sync issues - Add comprehensive error handling for expired/invalid tokens - Create profile and settings pages with full i18n support - Add proper user role management (admin/user) with database sync - Implement secure middleware with CSRF protection and auth checks - Add debug endpoints for troubleshooting authentication issues - Fix Zustand store persistence for authentication state 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -30,10 +30,12 @@ import {
|
||||
Home,
|
||||
Settings,
|
||||
Logout,
|
||||
Login,
|
||||
} from '@mui/icons-material'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { useTranslations, useLocale } from 'next-intl'
|
||||
import { LanguageSwitcher } from './language-switcher'
|
||||
import { useAuth } from '@/hooks/use-auth'
|
||||
|
||||
export function Navigation() {
|
||||
const [anchorElNav, setAnchorElNav] = useState<null | HTMLElement>(null)
|
||||
@@ -44,6 +46,7 @@ export function Navigation() {
|
||||
const isMobile = useMediaQuery(theme.breakpoints.down('md'))
|
||||
const t = useTranslations('navigation')
|
||||
const locale = useLocale()
|
||||
const { user, isAuthenticated, logout } = useAuth()
|
||||
|
||||
const pages = [
|
||||
{ name: t('home'), path: '/', icon: <Home /> },
|
||||
@@ -53,9 +56,9 @@ export function Navigation() {
|
||||
]
|
||||
|
||||
const settings = [
|
||||
{ name: t('profile'), icon: <AccountCircle /> },
|
||||
{ name: t('settings'), icon: <Settings /> },
|
||||
{ name: t('logout'), icon: <Logout /> },
|
||||
{ name: t('profile'), icon: <AccountCircle />, action: 'profile' },
|
||||
{ name: t('settings'), icon: <Settings />, action: 'settings' },
|
||||
{ name: t('logout'), icon: <Logout />, action: 'logout' },
|
||||
]
|
||||
|
||||
const handleOpenNavMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||
@@ -81,6 +84,29 @@ export function Navigation() {
|
||||
setDrawerOpen(false)
|
||||
}
|
||||
|
||||
const handleUserMenuAction = (action: string) => {
|
||||
handleCloseUserMenu()
|
||||
|
||||
switch (action) {
|
||||
case 'profile':
|
||||
router.push(`/${locale}/profile`)
|
||||
break
|
||||
case 'settings':
|
||||
router.push(`/${locale}/settings`)
|
||||
break
|
||||
case 'logout':
|
||||
logout()
|
||||
router.push(`/${locale}`)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const handleLogin = () => {
|
||||
router.push(`/${locale}/auth/login`)
|
||||
}
|
||||
|
||||
const toggleDrawer = (open: boolean) => {
|
||||
setDrawerOpen(open)
|
||||
}
|
||||
@@ -191,38 +217,56 @@ export function Navigation() {
|
||||
|
||||
{/* User Menu */}
|
||||
<Box sx={{ flexGrow: 0 }}>
|
||||
<Tooltip title={t('settings')}>
|
||||
<IconButton onClick={handleOpenUserMenu} sx={{ p: 0 }}>
|
||||
<Avatar sx={{ bgcolor: 'secondary.main' }}>
|
||||
<AccountCircle />
|
||||
</Avatar>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Menu
|
||||
sx={{ mt: '45px' }}
|
||||
id="menu-appbar"
|
||||
anchorEl={anchorElUser}
|
||||
anchorOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
keepMounted
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
open={Boolean(anchorElUser)}
|
||||
onClose={handleCloseUserMenu}
|
||||
>
|
||||
{settings.map((setting) => (
|
||||
<MenuItem key={setting.name} onClick={handleCloseUserMenu}>
|
||||
<ListItemIcon>
|
||||
{setting.icon}
|
||||
</ListItemIcon>
|
||||
<Typography textAlign="center">{setting.name}</Typography>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
{isAuthenticated ? (
|
||||
<>
|
||||
<Tooltip title={user?.name || user?.email || t('profile')}>
|
||||
<IconButton onClick={handleOpenUserMenu} sx={{ p: 0 }}>
|
||||
<Avatar sx={{ bgcolor: 'secondary.main' }}>
|
||||
{user?.name ? user.name.charAt(0).toUpperCase() : <AccountCircle />}
|
||||
</Avatar>
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<Menu
|
||||
sx={{ mt: '45px' }}
|
||||
id="menu-appbar"
|
||||
anchorEl={anchorElUser}
|
||||
anchorOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
keepMounted
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'right',
|
||||
}}
|
||||
open={Boolean(anchorElUser)}
|
||||
onClose={handleCloseUserMenu}
|
||||
>
|
||||
<MenuItem disabled>
|
||||
<Typography variant="body2" color="text.secondary">
|
||||
{user?.name || user?.email}
|
||||
</Typography>
|
||||
</MenuItem>
|
||||
{settings.map((setting) => (
|
||||
<MenuItem key={setting.name} onClick={() => handleUserMenuAction(setting.action)}>
|
||||
<ListItemIcon>
|
||||
{setting.icon}
|
||||
</ListItemIcon>
|
||||
<Typography textAlign="center">{setting.name}</Typography>
|
||||
</MenuItem>
|
||||
))}
|
||||
</Menu>
|
||||
</>
|
||||
) : (
|
||||
<Button
|
||||
onClick={handleLogin}
|
||||
variant="outlined"
|
||||
startIcon={<Login />}
|
||||
sx={{ color: 'white', borderColor: 'white', '&:hover': { borderColor: 'white', bgcolor: 'primary.dark' } }}
|
||||
>
|
||||
Login
|
||||
</Button>
|
||||
)}
|
||||
</Box>
|
||||
</Toolbar>
|
||||
</Container>
|
||||
|
||||
Reference in New Issue
Block a user