diff --git a/frontend/contexts/AuthContext.tsx b/frontend/contexts/AuthContext.tsx index 81a13e2..416226f 100644 --- a/frontend/contexts/AuthContext.tsx +++ b/frontend/contexts/AuthContext.tsx @@ -36,8 +36,9 @@ const MOCK_USERS: User[] = [ interface AuthContextType { user: User | null; - login: (email: string) => Promise; + login: (email: string, password?: string) => Promise; logout: () => void; + register: (data: { nome: string; email: string; senha: string; telefone: string }) => Promise; availableUsers: User[]; // Helper for the login screen demo } @@ -46,24 +47,78 @@ const AuthContext = createContext(undefined); export const AuthProvider: React.FC<{ children: ReactNode }> = ({ children }) => { const [user, setUser] = useState(null); - const login = async (email: string) => { - // Simulate API call - await new Promise(resolve => setTimeout(resolve, 800)); - - const foundUser = MOCK_USERS.find(u => u.email === email); - if (foundUser) { - setUser(foundUser); + const login = async (email: string, password?: string) => { + // 1. Check for Demo/Mock users first + const mockUser = MOCK_USERS.find(u => u.email === email); + if (mockUser) { + await new Promise(resolve => setTimeout(resolve, 800)); // Simulate delay + setUser({ ...mockUser, ativo: true }); return true; } - return false; + + // 2. Try Real API + try { + const response = await fetch(`${import.meta.env.VITE_API_URL}/auth/login`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ email, senha: password }) + }); + + if (!response.ok) { + console.error('Login failed:', response.statusText); + return false; + } + + const data = await response.json(); + + // Store token (optional, if you need it for other requests outside cookies) + // localStorage.setItem('token', data.access_token); + + const backendUser = data.user; + // Map backend user to frontend User type + const mappedUser: User = { + id: backendUser.id, + email: backendUser.email, + name: backendUser.email.split('@')[0], // Fallback name or from profile if available + role: backendUser.role as UserRole, + ativo: backendUser.ativo, + // ... propagate other fields if needed or fetch profile + }; + + setUser(mappedUser); + return true; + } catch (err) { + console.error('Login error:', err); + return false; + } }; const logout = () => { setUser(null); }; + const register = async (data: { nome: string; email: string; senha: string; telefone: string }) => { + try { + const response = await fetch(`${import.meta.env.VITE_API_URL}/auth/register`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(data) + }); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error(errorData.error || 'Falha no cadastro'); + } + + return true; + } catch (err) { + console.error('Registration error:', err); + throw err; + } + }; + return ( - + {children} ); diff --git a/frontend/pages/Login.tsx b/frontend/pages/Login.tsx index 6fc9f7b..2ea83b3 100644 --- a/frontend/pages/Login.tsx +++ b/frontend/pages/Login.tsx @@ -12,6 +12,8 @@ interface LoginProps { export const Login: React.FC = ({ onNavigate }) => { const { login, availableUsers } = useAuth(); const [email, setEmail] = useState(''); + const [password, setPassword] = useState(''); + const [showPassword, setShowPassword] = useState(false); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(''); @@ -20,7 +22,7 @@ export const Login: React.FC = ({ onNavigate }) => { setIsLoading(true); setError(''); - const success = await login(email); + const success = await login(email, password); if (!success) { setError('Usuário não encontrado. Tente um dos e-mails de demonstração.'); } @@ -29,6 +31,7 @@ export const Login: React.FC = ({ onNavigate }) => { const fillCredentials = (userEmail: string) => { setEmail(userEmail); + setPassword('123456'); // Dummy password to pass HTML5 validation }; const getRoleLabel = (role: UserRole) => { @@ -95,16 +98,31 @@ export const Login: React.FC = ({ onNavigate }) => {
setPassword(e.target.value)} + className="w-full px-3 sm:px-4 py-2.5 sm:py-3 text-sm sm:text-base border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:border-transparent transition-all" + style={{ focusRing: '2px solid #5A4B81' }} + onFocus={(e) => e.target.style.borderColor = '#5A4B81'} + onBlur={(e) => e.target.style.borderColor = '#d1d5db'} /> -
diff --git a/frontend/pages/Register.tsx b/frontend/pages/Register.tsx index 4549098..eeb0c01 100644 --- a/frontend/pages/Register.tsx +++ b/frontend/pages/Register.tsx @@ -1,8 +1,8 @@ - import React, { useState } from 'react'; import { Button } from '../components/Button'; import { Input } from '../components/Input'; import { InstitutionForm } from '../components/InstitutionForm'; +import { useAuth } from '../contexts/AuthContext'; import { useData } from '../contexts/DataContext'; interface RegisterProps { @@ -10,7 +10,8 @@ interface RegisterProps { } export const Register: React.FC = ({ onNavigate }) => { - const { addInstitution, registerPendingUser } = useData(); + const { register } = useAuth(); + const { addInstitution } = useData(); const [formData, setFormData] = useState({ name: '', email: '', @@ -64,21 +65,25 @@ export const Register: React.FC = ({ onNavigate }) => { return; } - // Simular registro - usuário ficará pendente de aprovação - setTimeout(() => { - registerPendingUser({ - id: tempUserId, - name: formData.name, + try { + await register({ + nome: formData.name, email: formData.email, - phone: formData.phone, - registeredInstitution: undefined + senha: formData.password, + telefone: formData.phone }); setIsLoading(false); setIsPending(true); - }, 1500); + } catch (err: any) { + setIsLoading(false); + setError(err.message || 'Erro ao realizar cadastro'); + } }; - const handleInstitutionSubmit = (institutionData: any) => { + const handleInstitutionSubmit = async (institutionData: any) => { + // Logic for adding institution remains (mock or need API for it too?), + // but user registration must be real. + // Assuming institution addition is still mock for now in DataContext. const newInstitution = { ...institutionData, id: `inst-${Date.now()}`, @@ -88,19 +93,25 @@ export const Register: React.FC = ({ onNavigate }) => { setRegisteredInstitutionName(newInstitution.name); setShowInstitutionForm(false); - // Complete registration - usuário ficará pendente de aprovação + // Complete registration via API setIsLoading(true); - setTimeout(() => { - registerPendingUser({ - id: tempUserId, - name: formData.name, + try { + await register({ + nome: formData.name, email: formData.email, - phone: formData.phone, - registeredInstitution: newInstitution.name + senha: formData.password, + telefone: formData.phone + // Note: The backend register endpoint currently doesn't accept institution data directly. + // We'd need to create the institution separately after logging in, but the user won't be able to login yet (pending). + // Or update backend to accept it. + // For now, we perform the user registration. }); setIsLoading(false); setIsPending(true); - }, 1500); + } catch (err: any) { + setIsLoading(false); + setError(err.message || 'Erro ao realizar cadastro'); + } }; // Show institution form modal diff --git a/frontend/types.ts b/frontend/types.ts index 6729ca2..558a479 100644 --- a/frontend/types.ts +++ b/frontend/types.ts @@ -43,6 +43,7 @@ export interface User { registeredInstitution?: string; // Nome da instituição cadastrada durante o registro (se houver) phone?: string; // Telefone do usuário createdAt?: string; // Data de criação do cadastro + ativo?: boolean; } export interface Institution {