feat: Sistema de autenticación frontend (Login + Register + Dashboard) #3
4
Frontend/.dockerignore
Normal file
4
Frontend/.dockerignore
Normal file
@@ -0,0 +1,4 @@
|
||||
node_modules
|
||||
dist
|
||||
.env
|
||||
.env.local
|
||||
1
Frontend/.env.example
Normal file
1
Frontend/.env.example
Normal file
@@ -0,0 +1 @@
|
||||
VITE_API_URL=http://localhost:5000/api
|
||||
@@ -1,16 +1,18 @@
|
||||
import { Routes, Route, Navigate } from 'react-router-dom';
|
||||
import { AuthProvider } from './hooks/useAuth';
|
||||
import { ProtectedRoute } from './components/ProtectedRoute';
|
||||
import { AuthProvider, useAuth } from './hooks/useAuth';
|
||||
import ProtectedRoute from './components/ProtectedRoute';
|
||||
import LoginPage from './pages/LoginPage';
|
||||
import RegisterPage from './pages/RegisterPage';
|
||||
import DashboardPage from './pages/DashboardPage';
|
||||
|
||||
function App() {
|
||||
const { isAuthenticated } = useAuth();
|
||||
|
||||
return (
|
||||
<AuthProvider>
|
||||
<Routes>
|
||||
<Route path="/" element={
|
||||
<Navigate to={localStorage.getItem('token') ? '/dashboard' : '/login'} replace />
|
||||
<Navigate to={isAuthenticated ? '/dashboard' : '/login'} replace />
|
||||
} />
|
||||
<Route path="/login" element={<LoginPage />} />
|
||||
<Route path="/register" element={<RegisterPage />} />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { LoginRequest, RegisterRequest, AuthResponse, RegisterResponse } from '../types/auth';
|
||||
import { User } from '../types/user';
|
||||
import type { LoginRequest, RegisterRequest, AuthResponse, RegisterResponse } from '../types/auth';
|
||||
import type { User } from '../types/user';
|
||||
|
||||
const API_URL = import.meta.env.VITE_API_URL || '';
|
||||
|
||||
@@ -26,7 +26,9 @@ class ApiClient {
|
||||
|
||||
if (!response.ok) {
|
||||
const errorData = await response.json().catch(() => ({}));
|
||||
throw new Error(errorData.message || `Error ${response.status}`);
|
||||
// Handle both { error: "message" } and { message: "..." } formats
|
||||
const errorMessage = errorData.error || errorData.message || `Error ${response.status}`;
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
import { Navigate, Outlet } from 'react-router-dom';
|
||||
import { Navigate } from 'react-router-dom';
|
||||
import { useAuth } from '../hooks/useAuth';
|
||||
import type { PropsWithChildren } from 'react';
|
||||
|
||||
const ProtectedRoute = () => {
|
||||
const ProtectedRoute = ({ children }: PropsWithChildren<{}>) => {
|
||||
const { isAuthenticated } = useAuth();
|
||||
|
||||
if (!isAuthenticated) {
|
||||
return <Navigate to="/login" replace />;
|
||||
}
|
||||
|
||||
return <Outlet />;
|
||||
return children;
|
||||
};
|
||||
|
||||
export default ProtectedRoute;
|
||||
@@ -1,7 +1,8 @@
|
||||
import { createContext, useContext, useState, useEffect, ReactNode } from 'react';
|
||||
import { createContext, useContext, useState, useEffect } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { apiClient } from '../api/client';
|
||||
import { RegisterResponse, LoginRequest, RegisterRequest } from '../types/auth';
|
||||
import { User } from '../types/user';
|
||||
import type { LoginRequest, RegisterRequest } from '../types/auth';
|
||||
import type { User } from '../types/user';
|
||||
|
||||
interface AuthContextProps {
|
||||
user: User | null;
|
||||
|
||||
@@ -18,8 +18,12 @@ const LoginPage = () => {
|
||||
try {
|
||||
await login({ username, password });
|
||||
navigate('/dashboard', { replace: true });
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
setError(err.message || 'Error al iniciar sesión');
|
||||
} else {
|
||||
setError('Error al iniciar sesión');
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
@@ -20,12 +20,14 @@ const RegisterPage = () => {
|
||||
try {
|
||||
await register({ username, password, email, nombreCompleto });
|
||||
navigate('/dashboard', { replace: true });
|
||||
} catch (err: any) {
|
||||
} catch (err) {
|
||||
// Handle 409 Conflict for duplicate username/email
|
||||
if (err.message?.includes('409')) {
|
||||
if (err instanceof Error && err.message?.includes('409')) {
|
||||
setError('El usuario o correo electrónico ya existe');
|
||||
} else {
|
||||
} else if (err instanceof Error) {
|
||||
setError(err.message || 'Error al registrarse');
|
||||
} else {
|
||||
setError('Error al registrarse');
|
||||
}
|
||||
} finally {
|
||||
setLoading(false);
|
||||
|
||||
Reference in New Issue
Block a user