diff --git a/README.md b/README.md index 7abbb99..cd2cd37 100644 --- a/README.md +++ b/README.md @@ -4,95 +4,137 @@ Una aplicación de chat en tiempo real construida con React y Material-UI que pe ## Características -- Autenticación de usuarios -- Chat en tiempo real -- Búsqueda de conversaciones -- Interfaz oscura moderna -- Sistema de respuestas a mensajes -- Indicadores de estado de conexión -- Interfaz responsive +- Autenticación de usuarios segura +- Chat en tiempo real con WebSocket +- Sistema de mensajería instantánea +- Interfaz oscura moderna y personalizable +- Sistema de respuestas a mensajes con threading +- Indicadores de estado de conexión en tiempo real +- Diseño totalmente responsive +- Búsqueda avanzada de conversaciones +- Notificaciones en tiempo real +- Emojis y reacciones a mensajes ## Tecnologías Utilizadas -- React.js -- Material-UI +- React 18 +- Material-UI v5 - WebSocket (@stomp/stompjs) -- React Router +- React Router v6 - Axios -- Bootstrap -- Socket.io +- Context API +- Custom Hooks +- Jest para testing ## Requisitos Previos -- Node.js (versión 14 o superior) -- NPM (incluido con Node.js) +- Node.js (versión 16 o superior) +- NPM (versión 8 o superior) +- Conexión a Internet ## Configuración del Proyecto 1. Clona el repositorio: -```git clone https://github.com/guerreroFacundo/ChatAppFrontend.git``` +```bash +git clone https://github.com/guerreroFacundo/ChatAppFrontend.git +``` 2. Navega al directorio del proyecto: -```cd chat-application``` +```bash +cd chat-application +``` 3. Instala las dependencias: -```npm install``` +```bash +npm install +``` -4. Crea un archivo .env en la raíz del proyecto y configura las variables de entorno necesarias: +4. Configura las variables de entorno: +Crea un archivo `.env` en la raíz del proyecto con: +``` REACT_APP_API_URL=tu_url_api REACT_APP_WS_URL=tu_url_websocket +``` - -5. Inicia la aplicación en modo desarrollo: -```npm start``` +5. Inicia la aplicación: +```bash +npm start +``` La aplicación estará disponible en http://localhost:3000 ## Estructura del Proyecto ``` src/ -├── components/ # Componentes React -├── context/ # Contextos de React -├── css/ # Estilos CSS -├── hooks/ # Hooks personalizados -├── services/ # Servicios de API y autenticación -└── utils/ # Utilidades y helpers +├── components/ # Componentes React reutilizables +│ ├── routing/ # Componentes de enrutamiento +├── context/ # Contextos de React (Auth, Theme, etc.) +├── css/ # Estilos modulares +├── hooks/ # Hooks personalizados +├── services/ # Servicios de API y autenticación +└── utils/ # Utilidades y helpers ``` ## Componentes Principales -- `ChatLayout`: Layout principal de la aplicación -- `ChatComponent`: Maneja la lógica del chat -- `MessageList`: Lista de mensajes -- `InputArea`: Área de entrada de mensajes -- `LoginComponent`: Manejo de autenticación +- `ChatLayout`: Estructura principal de la aplicación +- `ChatComponent`: Gestión de la lógica del chat +- `MessageList`: Visualización de mensajes +- `InputArea`: Entrada de mensajes con funciones avanzadas +- `LoginComponent`: Sistema de autenticación +- `ReplyBox`: Sistema de respuestas +- `MessageBubble`: Visualización de mensajes individuales + +## Scripts Disponibles + +- `npm start`: Inicia el servidor de desarrollo +- `npm test`: Ejecuta las pruebas +- `npm run build`: Construye la app para producción +- `npm run eject`: Expone la configuración de webpack -## Hooks Personalizados +## Testing -- `useAuth`: Manejo de autenticación -- `useChat`: Lógica del chat y WebSocket +El proyecto incluye: +- Tests unitarios con Jest +- Tests de integración +- Tests de componentes con React Testing Library + +Para ejecutar las pruebas: +```bash +npm test +``` ## Despliegue -Para construir la aplicación para producción: +1. Construye la aplicación: +```bash +npm run build +``` + +2. Los archivos de producción estarán en `build/` -```npm run build``` +## Contribuir -Los archivos de producción estarán en el directorio `build/` +1. Fork del proyecto +2. Crea tu rama de feature (`git checkout -b feature/NuevaCaracteristica`) +3. Commit de cambios (`git commit -m 'Añade nueva característica'`) +4. Push a la rama (`git push origin feature/NuevaCaracteristica`) +5. Crea un Pull Request -## Pruebas +## Guía de Estilo -Para ejecutar las pruebas: +- Utiliza ESLint y Prettier para el formato del código +- Sigue las convenciones de nombres de React +- Documenta los componentes principales +- Escribe tests para nuevas características -```npm test``` +## Soporte -## Contribuir +Para reportar bugs o solicitar nuevas características, por favor: +1. Revisa los issues existentes +2. Crea un nuevo issue con detalles específicos +3. Incluye pasos para reproducir el problema -1. Haz fork del proyecto -2. Crea una rama para tu feature (`git checkout -b feature/AmazingFeature`) -3. Commit tus cambios (`git commit -m 'Add some AmazingFeature'`) -4. Push a la rama (`git push origin feature/AmazingFeature`) -5. Abre un Pull Request ## Contacto diff --git a/src/App.js b/src/App.js index 6c72024..4f5920b 100644 --- a/src/App.js +++ b/src/App.js @@ -1,32 +1,37 @@ import React from 'react'; import { BrowserRouter as Router } from 'react-router-dom'; -import { useAuth } from './hooks/useAuth'; import { useChat } from './hooks/useChat'; import { AppRouter } from './components/routing/AppRouter'; +import { AuthProvider, useAuth } from './context/AuthContext'; import './css/app.css'; - -function App() { - const { authToken, setAuthToken, currentUser, setCurrentUser, logout } = useAuth(); +function AppContent() { + const { authToken, currentUser, logout } = useAuth(); const { chats, messages, selectedChat, setSelectedChat, fetchMessages } = useChat(authToken, currentUser); return ( - -
- -
-
+
+ +
+ ); +} + +function App() { + return ( + + + + + ); } diff --git a/src/components/LoginComponent.js b/src/components/LoginComponent.js index cdbba88..bcdb089 100644 --- a/src/components/LoginComponent.js +++ b/src/components/LoginComponent.js @@ -1,38 +1,30 @@ import React, { useState } from 'react'; -import axios from 'axios'; import { TextField, Button, Container, Typography, Box, Card, CardContent } from '@mui/material'; import { useNavigate } from 'react-router-dom'; +import { useAuth } from '../context/AuthContext'; +import { authService } from '../services/auth'; -function LoginComponent({ setAuthToken, setCurrentUser }) { +function LoginComponent() { const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [error, setError] = useState(''); - const baseURL = `${process.env.REACT_APP_API_BASE_URL}/api/users`; const navigate = useNavigate(); + const { login } = useAuth(); const handleSubmit = async (e) => { e.preventDefault(); try { - const response = await axios.post(`${baseURL}/login`, { - username, - password, - }); - - const { token, username: loggedUsername, userId, type } = response.data; - + const response = await authService.login(username, password); + const { token, userId, type } = response; const fullToken = `${type} ${token}`; - localStorage.setItem("authToken", fullToken); - localStorage.setItem("userId", userId); - localStorage.setItem("username", loggedUsername); - setAuthToken(fullToken); - setCurrentUser({ id: userId, username: loggedUsername }); + await login(fullToken, userId, username); navigate('/chat-layout'); + } catch (error) { console.log(error); if (error.response && error.response.data) { - const errorMessage = error.response.data; - setError(errorMessage); + setError(error.response.data); } else { const status = error.response?.status; switch (status) { @@ -107,4 +99,4 @@ function LoginComponent({ setAuthToken, setCurrentUser }) { ); } -export default LoginComponent; +export default LoginComponent; \ No newline at end of file diff --git a/src/components/routing/AppRouter.js b/src/components/routing/AppRouter.js index dbda84f..bb9edbd 100644 --- a/src/components/routing/AppRouter.js +++ b/src/components/routing/AppRouter.js @@ -18,7 +18,7 @@ export const AppRouter = ({ if (!authToken) { return ( - } /> + } /> } /> ); diff --git a/src/context/AuthContext.js b/src/context/AuthContext.js index 174e5bc..145cbff 100644 --- a/src/context/AuthContext.js +++ b/src/context/AuthContext.js @@ -1,4 +1,4 @@ -import React, { createContext, useState, useContext, useEffect } from 'react'; +import React, { createContext, useState, useContext } from 'react'; import { authService } from '../services/auth'; const AuthContext = createContext(null); @@ -10,11 +10,13 @@ export const AuthProvider = ({ children }) => { username: localStorage.getItem('username') }); - const login = async (credentials) => { - const data = await authService.login(credentials); - authService.setAuthData(data.token, data.userId, data.username); - setAuthToken(data.token); - setCurrentUser({ id: data.userId, username: data.username }); + const login = async (token, userId, username) => { + return new Promise((resolve) => { + authService.setAuthData(token, userId, username); + setAuthToken(token); + setCurrentUser({ id: userId, username: username }); + resolve(); + }); }; const logout = () => { diff --git a/src/hooks/useAuth.js b/src/hooks/useAuth.js deleted file mode 100644 index 73102d5..0000000 --- a/src/hooks/useAuth.js +++ /dev/null @@ -1,34 +0,0 @@ -import { useState, useEffect } from 'react'; - -export const useAuth = () => { - const [authToken, setAuthToken] = useState(localStorage.getItem("authToken")); - const [currentUser, setCurrentUser] = useState({ - id: localStorage.getItem("userId"), - username: localStorage.getItem("username"), - }); - - const login = (token, userId, username) => { - localStorage.setItem("authToken", token); - localStorage.setItem("userId", userId); - localStorage.setItem("username", username); - setAuthToken(token); - setCurrentUser({ id: userId, username: username }); - }; - - const logout = () => { - localStorage.removeItem("authToken"); - localStorage.removeItem("userId"); - localStorage.removeItem("username"); - setAuthToken(null); - setCurrentUser({ id: null, username: null }); - }; - - return { - authToken, - setAuthToken, - currentUser, - login, - logout, - setCurrentUser - }; -}; diff --git a/src/services/api.js b/src/services/api.js index 9e92254..d4ece8c 100644 --- a/src/services/api.js +++ b/src/services/api.js @@ -4,6 +4,15 @@ const BASE_URL = process.env.REACT_APP_API_BASE_URL; export const api = { + + login: async (username, password) => { + const response = await axios.post(`${BASE_URL}/api/users/login`, { + username, + password, + }); + return response.data; + }, + getChats: async (userId, authToken) => { console.log(BASE_URL); const response = await axios.get(`${BASE_URL}/api/messages/chats`, { diff --git a/src/services/auth.js b/src/services/auth.js index ff5f186..3fa6eb4 100644 --- a/src/services/auth.js +++ b/src/services/auth.js @@ -3,11 +3,20 @@ import axios from 'axios'; const BASE_URL = process.env.REACT_APP_API_BASE_URL; export const authService = { - login: async (credentials) => { - const response = await axios.post(`${BASE_URL}/api/auth/login`, credentials); + login: async (username,password) => { + const response = await axios.post(`${BASE_URL}/api/users/login`, { + username, + password, + }, { + headers: { + 'Content-Type': 'application/json', // Asegúrate de que se envíe como JSON + }, + }); + return response.data; }, + logout: () => { localStorage.removeItem('authToken'); localStorage.removeItem('userId');