feat: frontend auth — login/register modal + user badge
- API service (api.js): fetch wrapper with JWT, auto-refresh on 401 - AuthContext: user state, login/register/logout, loading, roles - AuthModal: tabbed login/register form matching .pen design - User badge in toolbar (Sandbox + WorldMap) with initial avatar - "Entrar" button when not logged in - CSS: auth overlay, card, tabs, inputs, error state, user badge - Auth is opt-in: app works fully without login Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import React, { useState, useRef } from 'react';
|
||||
import MobileTabBar from '../components/MobileTabBar.jsx';
|
||||
import { useIsMobile } from '../hooks/useIsMobile.js';
|
||||
import { useAuth } from '../services/AuthContext.jsx';
|
||||
import { WORLD_1 } from './levels/world1.js';
|
||||
import { WORLD_2 } from './levels/world2.js';
|
||||
import { WORLD_3 } from './levels/world3.js';
|
||||
@@ -53,6 +54,7 @@ export default function WorldMap({ onSelectLevel, onSandbox, onAdmin }) {
|
||||
const [search, setSearch] = useState('');
|
||||
const searchRef = useRef(null);
|
||||
const isMobile = useIsMobile();
|
||||
const { user, isLoggedIn, openAuth, logout } = useAuth();
|
||||
|
||||
const query = search.trim().toLowerCase();
|
||||
|
||||
@@ -90,6 +92,14 @@ export default function WorldMap({ onSelectLevel, onSandbox, onAdmin }) {
|
||||
🛠
|
||||
</button>
|
||||
)}
|
||||
{isLoggedIn ? (
|
||||
<div className="user-badge" onClick={logout} title="Cerrar sesion">
|
||||
<div className="user-avatar">{user.username?.[0]?.toUpperCase()}</div>
|
||||
<span className="user-name">{user.username}</span>
|
||||
</div>
|
||||
) : (
|
||||
<button className="login-btn" onClick={openAuth}>Entrar</button>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user