ok this is my app backend and frontend in python can you patch it pls i got certains bug identify it and patch it and make it it more user friendly :
# api/app.py
import os
import json
from datetime import datetime, timedelta
from typing import Dict, Set, List, Optional

from fastapi import FastAPI, Depends, HTTPException, status, WebSocket, WebSocketDisconnect, UploadFile, File
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel, Field
from pymongo import MongoClient, ASCENDING, TEXT
from bson.objectid import ObjectId
import bcrypt
import jwt
from jwt.exceptions import PyJWTError as JWTError

from models import UserBase, UserCreate, UserInDB, User, Message, ConversationCreate, Conversation
from authentication import get_current_user, authenticate_user, create_access_token
from config import MONGODB_URI, SECRET_KEY, ALGORITHM, ACCESS_TOKEN_EXPIRE_MINUTES

# Initialisation de l'application FastAPI
app = FastAPI()

# Configuration CORS
origins = [
    "http://localhost:3000",  # Développement frontend
    "https://your-frontend-domain.com",  # Remplacez par votre domaine frontend en production
]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,  # Liste des origines autorisées
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Monture pour servir les fichiers uploadés
UPLOAD_DIRECTORY = "uploads"

if not os.path.exists(UPLOAD_DIRECTORY):
    os.makedirs(UPLOAD_DIRECTORY)

app.mount("/uploads", StaticFiles(directory=UPLOAD_DIRECTORY), name="uploads")

# Connexion à la base de données MongoDB
client = MongoClient(MONGODB_URI)
db = client["FIRSTSERv"]
users_collection = db["users"]
conversations_collection = db["conversations"]

# Création des index si nécessaire
users_collection.create_index([("name", ASCENDING)], unique=True)
conversations_collection.create_index([("participants", ASCENDING)])
conversations_collection.create_index([("messages.content", TEXT)])

# Dictionnaire pour stocker les connexions WebSocket par conversation
connections: Dict[str, Set[WebSocket]] = {}

# Modèle pour la mise à jour de la bio
class BioUpdate(BaseModel):
    bio: str

# Endpoint d'inscription
@app.post("/signup", response_model=User)
def signup(user: UserCreate):
    # Vérifier si l'utilisateur existe déjà
    if users_collection.find_one({"name": user.name}):
        raise HTTPException(status_code=400, detail="Un utilisateur avec ce nom existe déjà.")

    # Hasher le mot de passe
    hashed_password = bcrypt.hashpw(user.password.encode('utf-8'), bcrypt.gensalt())

    # Créer l'utilisateur
    user_doc = {
        "name": user.name,
        "password": hashed_password.decode('utf-8'),
        "bio": user.bio or ""  # Initialiser la bio
    }
    result = users_collection.insert_one(user_doc)
    return User(
        id=str(result.inserted_id),
        name=user.name,
        bio=user.bio or "",
        avatar_url=None
    )

# Endpoint de connexion
@app.post("/login")
def login(form_data: UserCreate):
    user = authenticate_user(users_collection, form_data.name, form_data.password)
    if not user:
        raise HTTPException(status_code=400, detail="Nom d'utilisateur ou mot de passe incorrect")

    access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
    access_token = create_access_token(
        data={"sub": user.name}, expires_delta=access_token_expires
    )
    return {
        "access_token": access_token,
        "token_type": "bearer"
    }

# Endpoint pour récupérer le profil de l'utilisateur courant
@app.get("/users/me", response_model=User)
def read_current_user(current_user: UserInDB = Depends(get_current_user)):
    user = users_collection.find_one({"name": current_user.name})
    return User(
        id=str(user["_id"]),
        name=user["name"],
        bio=user.get("bio", ""),
        avatar_url=user.get("avatar_url")
    )

# Endpoint pour mettre à jour la bio de l'utilisateur courant
@app.put("/users/me/bio", response_model=UserBase)
def update_bio(bio_update: BioUpdate, current_user: UserInDB = Depends(get_current_user)):
    users_collection.update_one(
        {"name": current_user.name},
        {"$set": {"bio": bio_update.bio}}
    )
    updated_user = users_collection.find_one({"name": current_user.name})
    return UserBase(
        name=updated_user["name"],
        bio=updated_user.get("bio", "")
    )

# Endpoint pour récupérer le profil d'un utilisateur par nom
@app.get("/users/{username}", response_model=UserBase)
def get_user_profile(username: str, current_user: UserInDB = Depends(get_current_user)):
    user = users_collection.find_one({"name": username})
    if not user:
        raise HTTPException(status_code=404, detail="Utilisateur non trouvé")
    return UserBase(
        name=user["name"],
        bio=user.get("bio", "")
    )

# Endpoint pour récupérer tous les utilisateurs
@app.get("/users", response_model=List[UserBase])
def get_all_users(current_user: UserInDB = Depends(get_current_user)):
    users = users_collection.find({})
    return [UserBase(name=user["name"], bio=user.get("bio", "")) for user in users]

# Modèle pour la création d'une conversation
# (déjà défini dans models.py)

# Endpoint pour créer une conversation
@app.post("/conversations", response_model=Conversation)
def create_conversation(convo: ConversationCreate, current_user: UserInDB = Depends(get_current_user)):
    participants = convo.participants
    if current_user.name not in participants:
        participants.append(current_user.name)

    # Vérifier que les utilisateurs existent
    for name in participants:
        if not users_collection.find_one({"name": name}):
            raise HTTPException(status_code=404, detail=f"Utilisateur {name} introuvable")

    # Si c'est une conversation individuelle sans nom, vérifier si elle existe déjà
    if len(participants) == 2 and not convo.name:
        existing_convo = conversations_collection.find_one({
            "participants": {"$all": participants},
            "name": {"$exists": False}
        })
        if existing_convo:
            raise HTTPException(status_code=400, detail="La conversation existe déjà")

    # Créer la conversation
    conversation_doc = {
        "participants": participants,
        "messages": [],
    }
    if convo.name:
        conversation_doc["name"] = convo.name

    result = conversations_collection.insert_one(conversation_doc)
    return Conversation(
        id=str(result.inserted_id),
        name=convo.name,
        participants=participants
    )

# Endpoint pour récupérer les conversations de l'utilisateur courant
@app.get("/conversations", response_model=List[Conversation])
def get_user_conversations(current_user: UserInDB = Depends(get_current_user)):
    conversations = conversations_collection.find({"participants": current_user.name})
    convo_list = []
    for convo in conversations:
        convo_list.append(Conversation(
            id=str(convo['_id']),
            name=convo.get("name"),
            participants=convo["participants"]
        ))
    return convo_list

# Endpoint pour récupérer une conversation par son ID
@app.get("/conversations/{conversation_id}", response_model=Conversation)
def get_conversation(conversation_id: str, current_user: UserInDB = Depends(get_current_user)):
    try:
        conversation = conversations_collection.find_one({"_id": ObjectId(conversation_id)})
    except:
        raise HTTPException(status_code=400, detail="ID de conversation invalide")
    if not conversation:
        raise HTTPException(status_code=404, detail="Conversation non trouvée")
    if current_user.name not in conversation["participants"]:
        raise HTTPException(status_code=403, detail="Accès refusé")
    messages = [Message(**msg) for msg in conversation.get("messages", [])]
    return Conversation(
        id=str(conversation['_id']),
        name=conversation.get("name"),
        participants=conversation["participants"],
        messages=messages
    )

# Endpoint pour envoyer un message dans une conversation via REST (optionnel)
@app.post("/conversations/{conversation_id}/messages", response_model=Message)
def send_message(conversation_id: str, message: Message, current_user: UserInDB = Depends(get_current_user)):
    conversation = conversations_collection.find_one({"_id": ObjectId(conversation_id)})
    if not conversation:
        raise HTTPException(status_code=404, detail="Conversation non trouvée")
    if current_user.name not in conversation["participants"]:
        raise HTTPException(status_code=403, detail="Accès refusé")

    message_doc = message.dict()
    message_doc["timestamp"] = message.timestamp

    conversations_collection.update_one(
        {"_id": ObjectId(conversation_id)},
        {"$push": {"messages": message_doc}}
    )
    return message

# Endpoint pour supprimer une conversation
@app.delete("/conversations/{conversation_id}")
def delete_conversation(conversation_id: str, current_user: UserInDB = Depends(get_current_user)):
    conversation = conversations_collection.find_one({"_id": ObjectId(conversation_id)})
    if not conversation:
        raise HTTPException(status_code=404, detail="Conversation non trouvée")
    if current_user.name not in conversation["participants"]:
        raise HTTPException(status_code=403, detail="Accès refusé")

    conversations_collection.delete_one({"_id": ObjectId(conversation_id)})
    return {"detail": "Conversation supprimée"}

# Endpoint pour uploader un avatar ou autre média
@app.post("/upload/avatar/", response_model=dict)
async def upload_avatar(file: UploadFile = File(...), current_user: UserInDB = Depends(get_current_user)):
    # Vérifier le type de fichier
    if not file.content_type.startswith('image/'):
        raise HTTPException(status_code=400, detail="Only image files are allowed.")

    # Vérifier la taille du fichier (limité à 5MB)
    contents = await file.read()
    MAX_FILE_SIZE = 5 * 1024 * 1024  # 5 MB
    if len(contents) > MAX_FILE_SIZE:
        raise HTTPException(status_code=400, detail="File size exceeds the limit of 5MB.")
    await file.seek(0)  # Réinitialiser le pointeur du fichier après lecture

    # Générer un nom de fichier unique
    timestamp = datetime.utcnow().strftime("%Y%m%d%H%M%S")
    filename = f"{current_user.name}_{timestamp}_{file.filename}"
    file_path = os.path.join(UPLOAD_DIRECTORY, filename)

    # Sauvegarder le fichier
    with open(file_path, "wb") as buffer:
        buffer.write(await file.read())

    # Générer l'URL du fichier
    file_url = f"/uploads/{filename}"

    # Mettre à jour l'avatar_url de l'utilisateur
    users_collection.update_one(
        {"name": current_user.name},
        {"$set": {"avatar_url": file_url}}
    )
    return {"url": file_url}

# Endpoint WebSocket pour une conversation
@app.websocket("/ws/chat/{conversation_id}")
async def websocket_endpoint(websocket: WebSocket, conversation_id: str):
    token = websocket.query_params.get("token")
    if not token:
        await websocket.close(code=status.WS_1008_POLICY_VIOLATION)
        return

    # Authentifier le token
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise HTTPException(status_code=401, detail="Invalid token")
        user = get_user(users_collection, username)
        if user is None:
            raise HTTPException(status_code=401, detail="User not found")
    except JWTError:
        await websocket.close(code=status.WS_1008_POLICY_VIOLATION)
        return
    except HTTPException:
        await websocket.close(code=status.WS_1008_POLICY_VIOLATION)
        return

    # Vérifier que l'utilisateur est participant de la conversation
    try:
        conversation = conversations_collection.find_one({"_id": ObjectId(conversation_id)})
    except:
        await websocket.close(code=status.WS_1008_POLICY_VIOLATION)
        return

    if not conversation:
        await websocket.close(code=status.WS_1008_POLICY_VIOLATION)
        return
    if user.name not in conversation["participants"]:
        await websocket.close(code=status.WS_1008_POLICY_VIOLATION)
        return

    await websocket.accept()

    if conversation_id not in connections:
        connections[conversation_id] = set()
    connections[conversation_id].add(websocket)

    try:
        while True:
            data = await websocket.receive_text()
            message_data = json.loads(data)
            content = message_data.get("message")
            media = message_data.get("media")

            if not content and not media:
                continue

            message = Message(
                sender=user.name,
                content=content or "",
                timestamp=datetime.utcnow(),
                media=media or "",
                reactions={}
            )

            # Sauvegarder le message dans la base de données
            message_doc = message.dict()
            message_doc["timestamp"] = message.timestamp

            conversations_collection.update_one(
                {"_id": ObjectId(conversation_id)},
                {"$push": {"messages": message_doc}}
            )

            # Envoyer le message à tous les clients connectés
            if conversation_id in connections:
                for connection in connections[conversation_id]:
                    if connection != websocket:
                        await connection.send_json(message.dict())

    except WebSocketDisconnect:
        connections[conversation_id].remove(websocket)
        if not connections[conversation_id]:
            del connections[conversation_id]
    except Exception as e:
        print(f"WebSocket error: {e}")
        connections[conversation_id].remove(websocket)
        if not connections[conversation_id]:
            del connections[conversation_id]

# api/authentication.py
from datetime import datetime, timedelta
from typing import Optional
import jwt
from jwt.exceptions import PyJWTError as JWTError
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from database import users_collection
from models import UserInDB
from config import SECRET_KEY, ALGORITHM, ACCESS_TOKEN_EXPIRE_MINUTES
import bcrypt

oauth2_scheme = OAuth2PasswordBearer(tokenUrl="login")

def verify_password(plain_password, hashed_password):
    return bcrypt.checkpw(plain_password.encode('utf-8'), hashed_password.encode('utf-8'))

def get_user(db, username: str):
    user = db.find_one({"name": username})
    if user:
        return UserInDB(name=user["name"], hashed_password=user["password"], avatar_url=user.get("avatar_url"))
    return None

def authenticate_user(db, username: str, password: str):
    user = get_user(db, username)
    if not user:
        return False
    if not verify_password(password, user.hashed_password):
        return False
    return user

def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
    to_encode = data.copy()
    expire = datetime.utcnow() + (expires_delta if expires_delta else timedelta(minutes=15))
    to_encode.update({"exp": expire})
    encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt

async def get_current_user(token: str = Depends(oauth2_scheme)):
    credentials_exception = HTTPException(
        status_code=status.HTTP_401_UNAUTHORIZED,
        detail="Unable to validate credentials",
        headers={"WWW-Authenticate": "Bearer"},
    )
    try:
        payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
        username: str = payload.get("sub")
        if username is None:
            raise credentials_exception
        user = get_user(users_collection, username)
        if user is None:
            raise credentials_exception
        return user
    except JWTError:
        raise credentials_exception


# api/config.py
import os
from dotenv import load_dotenv

load_dotenv()

MONGODB_URI = os.environ.get("MONGODB_URI")
SECRET_KEY = os.environ.get("SECRET_KEY")
ALGORITHM = os.environ.get("ALGORITHM", "HS256")
ACCESS_TOKEN_EXPIRE_MINUTES = int(os.environ.get("ACCESS_TOKEN_EXPIRE_MINUTES", 30))

# api/database.py
from pymongo import MongoClient, ASCENDING, TEXT
from config import MONGODB_URI

client = MongoClient(MONGODB_URI)
db = client["FIRSTSERv"]
users_collection = db["users"]
conversations_collection = db["conversations"]

# Créer des index si ils n'existent pas
users_collection.create_index([("name", ASCENDING)], unique=True)
conversations_collection.create_index([("participants", ASCENDING)])
conversations_collection.create_index([("messages.content", TEXT)])

# api/models.py
from pydantic import BaseModel, Field
from typing import List, Optional, Dict
from datetime import datetime

# User Models
class UserBase(BaseModel):
    name: str
    bio: Optional[str] = None  # Bio field

class UserCreate(UserBase):
    password: str

class UserInDB(UserBase):
    hashed_password: str
    avatar_url: Optional[str] = None  # Avatar URL

class User(UserBase):
    id: str
    avatar_url: Optional[str] = None

# Message Models
class Message(BaseModel):
    sender: str
    content: str
    timestamp: datetime = Field(default_factory=datetime.utcnow)
    media: Optional[str] = None  # URL to the media file
    reactions: Dict[str, int] = {}  # Emoji reactions

# Conversation Models
class ConversationCreate(BaseModel):
    participants: List[str]
    name: Optional[str] = None

class Conversation(BaseModel):
    id: str
    name: Optional[str] = None  # Optional group name
    participants: List[str]
    messages: List[Message] = []

this is the frontend : 

// src/components/Auth/Login.js
import React, { useState, useContext } from 'react';
import { AuthContext } from '../../contexts/AuthContext';
import { useNavigate } from 'react-router-dom';
import { motion } from 'framer-motion';
import styles from './Login.module.css';

function Login() {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const { login } = useContext(AuthContext);
  const [error, setError] = useState('');
  const navigate = useNavigate();

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      await login(username, password);
      navigate('/');
    } catch (err) {
      setError("Nom d'utilisateur ou mot de passe incorrect");
    }
  };

  return (
    <motion.div
      className={styles.loginContainer}
      initial={{ opacity: 0, scale: 0.9 }}
      animate={{ opacity: 1, scale: 1 }}
      transition={{ duration: 0.5 }}
    >
      <div className={styles.loginBox}>
        <h2 className={styles.title}>Se connecter</h2>
        <form onSubmit={handleSubmit} className={styles.form}>
          <div className={styles.inputGroup}>
            <label htmlFor="username">Nom d'utilisateur</label>
            <input
              id="username"
              type="text"
              value={username}
              onChange={(e) => setUsername(e.target.value)}
              required
              className={styles.input}
            />
          </div>
          <div className={styles.inputGroup}>
            <label htmlFor="password">Mot de passe</label>
            <input
              id="password"
              type="password"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
              required
              className={styles.input}
            />
          </div>
          <button type="submit" className={styles.submitButton}>
            Se connecter
          </button>
        </form>
        {error && <p className={styles.error}>{error}</p>}
      </div>
    </motion.div>
  );
}

export default Login;

// src/components/Auth/Signup.js
import React, { useState, useContext } from 'react';
import { AuthContext } from '../../contexts/AuthContext';
import { useNavigate, Link } from 'react-router-dom';
import { motion } from 'framer-motion';
import styles from './Signup.module.css';

function Signup() {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');
  const { signup } = useContext(AuthContext);
  const [error, setError] = useState('');
  const navigate = useNavigate();

  const handleSubmit = async (e) => {
    e.preventDefault();
    try {
      await signup(username, password);
      navigate('/');
    } catch (err) {
      setError("Erreur lors de l'inscription");
    }
  };

  return (
    <motion.div
      className={styles.container}
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      transition={{ duration: 0.5 }}
    >
      <div className={styles.overlay}></div>
      <div className={styles.formContainer}>
        <h1 className={styles.logo}>ChatApp</h1>
        <h2 className={styles.title}>Créer un compte</h2>

        <form onSubmit={handleSubmit} className={styles.form}>
          <div className={styles.inputField}>
            <input
              type="text"
              value={username}
              onChange={(e) => setUsername(e.target.value)}
              required
              placeholder="Nom d'utilisateur"
            />
          </div>

          <div className={styles.inputField}>
            <input
              type="password"
              value={password}
              onChange={(e) => setPassword(e.target.value)}
              required
              placeholder="Mot de passe"
            />
          </div>

          {error && <div className={styles.error}>{error}</div>}

          <button type="submit" className={styles.button}>
            S'inscrire
          </button>
        </form>

        <Link to="/login" className={styles.link}>
          Déjà un compte ? Se connecter
        </Link>
      </div>
    </motion.div>
  );
}

export default Signup;

// frontend/src/components/Chat/Chat.jsx

import React, { useEffect, useState, useContext, useRef } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { motion, AnimatePresence } from 'framer-motion';
import axios from '../../utils/api';
import { AuthContext } from '../../contexts/AuthContext';
import {
  IoSend,
  IoMenu,
  IoSearch,
  IoEllipsisVertical,
  IoAdd,
  IoImage,
  IoDocument
} from 'react-icons/io5';
import styles from './Chat.module.css';

// Import Modal and other components
import CreateConversationModal from './CreateConversationModal';
import MessageReactions from './MessageReactions';

function Chat() {
  const { conversationId } = useParams();
  const { user, token } = useContext(AuthContext); // Ensure 'token' is available if needed
  const navigate = useNavigate();

  const [conversations, setConversations] = useState([]);
  const [currentConversation, setCurrentConversation] = useState(null);
  const [message, setMessage] = useState('');
  const [searchQuery, setSearchQuery] = useState('');
  const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);
  const [isCreateModalOpen, setIsCreateModalOpen] = useState(false);
  const [typingStatus, setTypingStatus] = useState('');
  const messagesEndRef = useRef(null);
  const socketRef = useRef(null);
  const typingTimeoutRef = useRef(null);
  const [selectedFile, setSelectedFile] = useState(null); // Single declaration for selectedFile
  const [searchResults, setSearchResults] = useState([]);
  const [usersMap, setUsersMap] = useState({}); // Mapping of username to avatar_url

  // Animation variants
  const sidebarVariants = {
    hidden: { x: -300, opacity: 0 },
    visible: { x: 0, opacity: 1, transition: { type: 'spring', damping: 25 } }
  };

  const messageVariants = {
    hidden: { opacity: 0, y: 20 },
    visible: { opacity: 1, y: 0 },
    exit: { opacity: 0, x: -100 }
  };

  // Fetch all users to map usernames to their avatars
  useEffect(() => {
    const fetchUsers = async () => {
      try {
        const response = await axios.get('/users');
        const map = {};
        response.data.forEach(user => {
          map[user.name] = user.avatar_url;
        });
        setUsersMap(map);
      } catch (error) {
        console.error('Error fetching users:', error);
      }
    };

    fetchUsers();
  }, []);

  useEffect(() => {
    fetchConversations();
    const interval = setInterval(fetchConversations, 5000);
    return () => clearInterval(interval);
  }, []);

  useEffect(() => {
    if (conversationId) {
      fetchCurrentConversation();
      setupWebSocket();
      const interval = setInterval(fetchCurrentConversation, 3000);
      return () => {
        clearInterval(interval);
        if (socketRef.current) {
          socketRef.current.close();
        }
      };
    } else {
      setCurrentConversation(null); // Reset if no conversation selected
    }
  }, [conversationId, token]); // Include 'token' if it's used in WebSocket setup

  useEffect(() => {
    scrollToBottom();
  }, [currentConversation?.messages]);

  const fetchConversations = async () => {
    try {
      const response = await axios.get('/conversations');
      setConversations(response.data);
    } catch (error) {
      console.error('Error fetching conversations:', error);
    }
  };

  const fetchCurrentConversation = async () => {
    try {
      const response = await axios.get(`/conversations/${conversationId}`);
      setCurrentConversation(response.data);
    } catch (error) {
      console.error('Error fetching conversation:', error);
    }
  };

  const setupWebSocket = () => {
    if (!conversationId || !token) return;

    const wsProtocol = window.location.protocol === 'https:' ? 'wss' : 'ws';
    const socket = new WebSocket(
      `${wsProtocol}://${window.location.hostname}:8000/ws/chat/${conversationId}?token=${token}`
    );

    socket.onopen = () => {
      console.log('WebSocket connected');
    };

    socket.onmessage = (event) => {
      const data = JSON.parse(event.data);
      if (data.sender && (data.content || data.media)) {
        setCurrentConversation(prev => ({
          ...prev,
          messages: [...prev.messages, data]
        }));
      }
      if (data.event === 'typing') {
        setTypingStatus(`${data.data.user} is typing...`);
        if (typingTimeoutRef.current) clearTimeout(typingTimeoutRef.current);
        typingTimeoutRef.current = setTimeout(() => {
          setTypingStatus('');
        }, 3000);
      }
    };

    socket.onclose = () => {
      console.log('WebSocket disconnected');
    };

    socket.onerror = (error) => {
      console.error('WebSocket error:', error);
    };

    socketRef.current = socket;
  };

  const handleSendMessage = async (e) => {
    e.preventDefault();
    if (!message.trim() && !selectedFile) return;

    let mediaUrl = null;
    if (selectedFile) {
      try {
        const formData = new FormData();
        formData.append('file', selectedFile);

        const uploadResponse = await axios.post('/upload/avatar/', formData, {
          headers: { 'Content-Type': 'multipart/form-data' }
        });

        mediaUrl = uploadResponse.data.url;
        setSelectedFile(null);
      } catch (error) {
        console.error('Error uploading media:', error);
        alert('Failed to upload media.');
        return;
      }
    }

    const payload = {
      message: message.trim(),
      media: mediaUrl
    };

    // Send message via WebSocket
    if (socketRef.current && socketRef.current.readyState === WebSocket.OPEN) {
      socketRef.current.send(JSON.stringify(payload));
    }

    setMessage('');
    // No need to fetchConversation again as WebSocket will handle it
  };

  const scrollToBottom = () => {
    messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
  };

  const filteredConversations = conversations.filter(conv => {
    const conversationName = conv.name || conv.participants.join(', ');
    return conversationName.toLowerCase().includes(searchQuery.toLowerCase());
  });

  const formatTime = (timestamp) => {
    const date = new Date(timestamp);
    return date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
  };

  // Typing Indicators
  const handleTyping = () => {
    if (socketRef.current && socketRef.current.readyState === WebSocket.OPEN) {
      socketRef.current.send(JSON.stringify({ event: 'typing', data: { user: user.name } }));
    }
    if (typingTimeoutRef.current) clearTimeout(typingTimeoutRef.current);
    typingTimeoutRef.current = setTimeout(() => {
      setTypingStatus('');
    }, 3000);
  };

  // Handle File Selection
  const handleFileChange = (e) => {
    const file = e.target.files[0];
    setSelectedFile(file);
  };

  // Search Messages
  const handleSearch = async (e) => {
    const query = e.target.value;
    setSearchQuery(query);
    if (query.length < 3) {
      setSearchResults([]);
      return;
    }
    try {
      const response = await axios.get(`/search?query=${encodeURIComponent(query)}`);
      setSearchResults(response.data);
    } catch (error) {
      console.error('Error searching messages:', error);
    }
  };

  // Modal handlers
  const openCreateModal = () => {
    setIsCreateModalOpen(true);
  };

  const closeCreateModal = () => {
    setIsCreateModalOpen(false);
  };

  // Reaction handler
  const handleAddReaction = async (messageId, emoji) => {
    try {
      await axios.post(`/conversations/${conversationId}/messages/${messageId}/reactions`, { emoji });
      // Optionally, you can update the message reaction locally or refetch
    } catch (error) {
      console.error('Error adding reaction:', error);
    }
  };

  return (
    <motion.div
      className={styles.container}
      initial={{ opacity: 0 }}
      animate={{ opacity: 1 }}
      exit={{ opacity: 0 }}
      transition={{ duration: 0.5 }}
    >
      {/* Sidebar */}
      <motion.aside
        className={`${styles.sidebar} ${isMobileMenuOpen ? styles.sidebarOpen : ''}`}
        variants={sidebarVariants}
        initial="hidden"
        animate="visible"
      >
        <div className={styles.sidebarHeader}>
          <motion.div
            className={styles.userProfile}
            whileHover={{ scale: 1.05 }}
            whileTap={{ scale: 0.95 }}
            onClick={() => navigate('/profile')}
          >
            <div className={styles.userAvatar}>
              {user.avatar_url ? (
                <img src={user.avatar_url} alt="Avatar" className={styles.avatarImage} />
              ) : (
                user.name.charAt(0).toUpperCase()
              )}
            </div>
            <h3>{user.name}</h3>
          </motion.div>
        </div>

        <div className={styles.searchContainer}>
          <IoSearch className={styles.searchIcon} />
          <input
            type="text"
            placeholder="Search conversations..."
            value={searchQuery}
            onChange={handleSearch}
            className={styles.searchInput}
          />
        </div>

        <div className={styles.newConversationButton}>
          <button onClick={openCreateModal}>
            <IoAdd /> New Conversation
          </button>
        </div>

        <div className={styles.conversationsList}>
          <AnimatePresence>
            {filteredConversations.map((conv) => (
              <motion.div
                key={conv.id}
                className={`${styles.conversationItem} ${conv.id === conversationId ? styles.active : ''}`}
                variants={messageVariants}
                initial="hidden"
                animate="visible"
                exit="exit"
                whileHover={{ x: 5 }}
                onClick={() => {
                  navigate(`/chat/${conv.id}`);
                  setIsMobileMenuOpen(false);
                }}
              >
                <div className={styles.conversationAvatar}>
                  {conv.name
                    ? conv.name.charAt(0).toUpperCase()
                    : conv.participants[0].charAt(0).toUpperCase()}
                </div>
                <div className={styles.conversationInfo}>
                  <h4>{conv.name || conv.participants.join(', ')}</h4>
                  <p>
                    {conv.messages.length > 0
                      ? `${conv.messages[conv.messages.length - 1].content.substring(0, 20)}...`
                      : 'No messages yet'}
                  </p>
                </div>
                {/* Optional: Add unread count or other info here */}
              </motion.div>
            ))}
          </AnimatePresence>
        </div>
      </motion.aside>

      {/* Main Chat Area */}
      <main className={styles.chatArea}>
        {currentConversation ? (
          <>
            <header className={styles.chatHeader}>
              <button
                className={styles.menuButton}
                onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
              >
                <IoMenu />
              </button>
              <div className={styles.chatInfo}>
                <h2>{currentConversation.name || currentConversation.participants.join(', ')}</h2>
                <span>{currentConversation.participants.length} participants</span>
              </div>
              <button className={styles.optionsButton}>
                <IoEllipsisVertical />
              </button>
            </header>

            <div className={styles.messagesContainer}>
              <AnimatePresence>
                {currentConversation.messages.map((msg, index) => (
                  <motion.div
                    key={index}
                    className={`${styles.message} ${msg.sender === user.name ? styles.sent : styles.received}`}
                    variants={messageVariants}
                    initial="hidden"
                    animate="visible"
                    exit="exit"
                  >
                    <div className={styles.userAvatar}>
                      {msg.sender === user.name ? (
                        user.avatar_url ? (
                          <img src={user.avatar_url} alt="Your Avatar" className={styles.avatarImage} />
                        ) : (
                          user.name.charAt(0).toUpperCase()
                        )
                      ) : (
                        usersMap[msg.sender] ? (
                          <img src={usersMap[msg.sender]} alt={`${msg.sender} Avatar`} className={styles.avatarImage} />
                        ) : (
                          msg.sender.charAt(0).toUpperCase()
                        )
                      )}
                    </div>
                    <div className={styles.messageContent}>
                      {msg.media && (
                        <div className={styles.mediaContent}>
                          {/\.(jpeg|jpg|gif|png)$/.test(msg.media) ? (
                            <img src={msg.media} alt="Media" className={styles.mediaImage} />
                          ) : (
                            <a href={msg.media} target="_blank" rel="noopener noreferrer" className={styles.mediaLink}>
                              View Document <IoDocument />
                            </a>
                          )}
                        </div>
                      )}
                      <p>{msg.content}</p>
                      {msg.reactions && Object.keys(msg.reactions).length > 0 && (
                        <div className={styles.reactions}>
                          {Object.entries(msg.reactions).map(([emoji, count]) => (
                            <span key={emoji}>{emoji} {count}</span>
                          ))}
                        </div>
                      )}
                      <span className={styles.timestamp}>
                        {formatTime(msg.timestamp)}
                      </span>
                      <MessageReactions onAddReaction={(emoji) => handleAddReaction(msg._id, emoji)} />
                    </div>
                  </motion.div>
                ))}
              </AnimatePresence>
              <div ref={messagesEndRef} />
            </div>

            {typingStatus && <div className={styles.typingStatus}>{typingStatus}</div>}

            <form onSubmit={handleSendMessage} className={styles.messageForm}>
              <input
                type="text"
                value={message}
                onChange={(e) => {
                  setMessage(e.target.value);
                  handleTyping();
                }}
                placeholder="Write your message..."
                className={styles.messageInput}
              />
              <input
                type="file"
                accept="image/*,video/*,application/pdf"
                onChange={handleFileChange}
                className={styles.fileInput}
                id="file-upload"
                style={{ display: 'none' }}
              />
              <label htmlFor="file-upload" className={styles.uploadButton}>
                <IoImage />
              </label>
              <button type="submit" disabled={!message.trim() && !selectedFile} className={styles.sendButton}>
                <IoSend />
              </button>
            </form>
          </>
        ) : (
          <motion.div
            className={styles.welcomeScreen}
            initial={{ opacity: 0, y: 20 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.5 }}
          >
            <div className={styles.welcomeContent}>
              <h2>Welcome to your Messenger</h2>
              <p>Select a conversation to start chatting</p>
            </div>
          </motion.div>
        )}

        {/* Search Results */}
        {searchResults.length > 0 && (
          <div className={styles.searchResults}>
            <h3>Search Results</h3>
            {searchResults.map((conv) => (
              <div key={conv.id} className={styles.searchResultItem} onClick={() => navigate(`/chat/${conv.id}`)}>
                <div className={styles.conversationAvatar}>
                  {conv.name
                    ? conv.name.charAt(0).toUpperCase()
                    : conv.participants[0].charAt(0).toUpperCase()}
                </div>
                <div className={styles.conversationInfo}>
                  <h4>{conv.name || conv.participants.join(', ')}</h4>
                  <p>
                    {conv.messages.length > 0
                      ? `${conv.messages[conv.messages.length - 1].content.substring(0, 20)}...`
                      : 'No messages yet'}
                  </p>
                </div>
              </div>
            ))}
          </div>
        )}
      </main>

      {/* Mobile Overlay */}
      {isMobileMenuOpen && (
        <motion.div
          className={styles.overlay}
          initial={{ opacity: 0 }}
          animate={{ opacity: 1 }}
          exit={{ opacity: 0 }}
          onClick={() => setIsMobileMenuOpen(false)}
        />
      )}

      {/* Modal for Creating Conversation */}
      {isCreateModalOpen && (
        <CreateConversationModal
          onClose={closeCreateModal}
          onCreate={fetchConversations}
        />
      )}
    </motion.div>
  );
}

export default Chat;

// frontend/src/components/Chat/CreateConversationModal.jsx
import React, { useState, useEffect, useContext } from 'react';
import { motion } from 'framer-motion';
import styles from './CreateConversationModal.module.css';
import axios from '../../utils/api';
import { AuthContext } from '../../contexts/AuthContext';

function CreateConversationModal({ onClose, onCreate }) {
  const { user } = useContext(AuthContext);
  const [name, setName] = useState('');
  const [participants, setParticipants] = useState([]);
  const [allUsers, setAllUsers] = useState([]);
  const [searchUser, setSearchUser] = useState('');
  const [error, setError] = useState('');

  useEffect(() => {
    fetchAllUsers();
  }, []);

  const fetchAllUsers = async () => {
    try {
      const response = await axios.get('/users');
      // Exclude current user from participants
      const otherUsers = response.data.filter(u => u.name !== user.name);
      setAllUsers(otherUsers);
    } catch (error) {
      console.error('Error fetching users:', error);
      setError('Error loading users.');
    }
  };

  const handleAddParticipant = (userName) => {
    if (!participants.includes(userName)) {
      setParticipants([...participants, userName]);
    }
  };

  const handleRemoveParticipant = (userName) => {
    setParticipants(participants.filter(p => p !== userName));
  };

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (participants.length === 0) {
      setError('Please add at least one participant.');
      return;
    }
    try {
      const conversationData = {
        name: name.trim() || null,
        participants
      };
      await axios.post('/conversations', conversationData);
      onCreate();
      onClose();
    } catch (error) {
      console.error('Error creating conversation:', error);
      setError('Failed to create conversation.');
    }
  };

  const filteredUsers = allUsers.filter(u =>
    u.name.toLowerCase().includes(searchUser.toLowerCase())
  );

  return (
    <div className={styles.overlay}>
      <motion.div
        className={styles.modal}
        initial={{ opacity: 0, scale: 0.9 }}
        animate={{ opacity: 1, scale: 1 }}
        exit={{ opacity: 0, scale: 0.9 }}
        transition={{ duration: 0.3 }}
      >
        <h2>Create a New Conversation</h2>
        <form onSubmit={handleSubmit}>
          <div className={styles.inputGroup}>
            <label>Conversation Name (Optional)</label>
            <input
              type="text"
              value={name}
              onChange={(e) => setName(e.target.value)}
              placeholder="Enter conversation name"
            />
          </div>

          <div className={styles.inputGroup}>
            <label>Add Participants</label>
            <input
              type="text"
              placeholder="Search users..."
              value={searchUser}
              onChange={(e) => setSearchUser(e.target.value)}
            />
            <div className={styles.usersList}>
              {filteredUsers.map(user => (
                <div key={user.id} className={styles.userItem}>
                  <span>{user.name}</span>
                  <button type="button" onClick={() => handleAddParticipant(user.name)}>
                    Add
                  </button>
                </div>
              ))}
            </div>
          </div>

          <div className={styles.participants}>
            <h3>Selected Participants:</h3>
            {participants.map(name => (
              <div key={name} className={styles.participantItem}>
                <span>{name}</span>
                <button type="button" onClick={() => handleRemoveParticipant(name)}>
                  Remove
                </button>
              </div>
            ))}
          </div>

          {error && <div className={styles.error}>{error}</div>}

          <div className={styles.buttonGroup}>
            <button type="button" onClick={onClose} className={styles.cancelButton}>
              Cancel
            </button>
            <button type="submit" className={styles.createButton}>
              Create
            </button>
          </div>
        </form>
      </motion.div>
    </div>
  );
}

export default CreateConversationModal;

// frontend/src/components/Chat/MessageReactions.jsx
import React from 'react';
import { IoHappy, IoThumbsUp, IoHeart } from 'react-icons/io5';
import styles from './MessageReactions.module.css';

const availableReactions = [
  { emoji: '😊', label: 'Happy' },
  { emoji: '👍', label: 'Thumbs Up' },
  { emoji: '❤️', label: 'Heart' },
];

function MessageReactions({ onAddReaction }) {
  return (
    <div className={styles.reactionsContainer}>
      {availableReactions.map((reaction) => (
        <button
          key={reaction.emoji}
          className={styles.reactionButton}
          onClick={() => onAddReaction(reaction.emoji)}
          aria-label={reaction.label}
        >
          {reaction.emoji}
        </button>
      ))}
    </div>
  );
}

export default MessageReactions;

// frontend/src/components/Profile/Profile.jsx

import React, { useContext, useEffect, useState } from 'react';
import axios from '../../utils/api';
import { AuthContext } from '../../contexts/AuthContext';
import styles from './Profile.module.css';

function Profile() {
  const { user, setUser, token } = useContext(AuthContext); // Ensure 'token' is accessible
  const [bio, setBio] = useState('');
  const [editingBio, setEditingBio] = useState(false);
  const [error, setError] = useState('');

  useEffect(() => {
    setBio(user.bio || '');
  }, [user.bio]);

  const handleSaveBio = async () => {
    try {
      const response = await axios.put('/users/me/bio', { bio });
      setEditingBio(false);
      // Update user context
      setUser({ ...user, bio: response.data.bio });
    } catch (error) {
      console.error('Error saving bio:', error);
      setError('Failed to save bio.');
    }
  };

  const handleAvatarUpload = async (e) => {
    const file = e.target.files[0];
    if (!file) return;

    try {
      // Basic client-side validation
      if (!['image/jpeg', 'image/png', 'image/gif'].includes(file.type)) {
        setError('Unsupported file type. Please upload a JPEG, PNG, or GIF image.');
        return;
      }

      const formData = new FormData();
      formData.append('file', file);

      const response = await axios.post('/upload/avatar/', formData, {
        headers: { 'Content-Type': 'multipart/form-data' },
      });

      const { avatar_url } = response.data;

      // Update user context with new avatar
      setUser({ ...user, avatar_url });

      setError('');
    } catch (error) {
      console.error('Error uploading avatar:', error);
      setError('Failed to upload avatar.');
    }
  };

  return (
    <div className={styles.container}>
      <div className={styles.background}>
        <div className={styles.gradient1}></div>
        <div className={styles.gradient2}></div>
        <div className={styles.gradient3}></div>
      </div>
      
      <div className={styles.glassOverlay}>
        <div className={styles.profileHeader}>
          <div className={styles.avatarContainer}>
            <div className={styles.avatarRing}></div>
            <div className={styles.avatar}>
              {user.avatar_url ? (
                <img src={user.avatar_url} alt="Avatar" className={styles.avatarImage} />
              ) : (
                user.name.charAt(0).toUpperCase()
              )}
            </div>
            <input
              type="file"
              accept="image/*"
              onChange={handleAvatarUpload}
              id="avatar-upload"
              style={{ display: 'none' }}
            />
            <label htmlFor="avatar-upload" className={styles.uploadLabel}>
              Change Avatar
            </label>
          </div>
          <h1 className={styles.username}>{user.name}</h1>
          <div className={styles.statusBadge}>Online</div>
        </div>

        <div className={styles.bioSection}>
          {editingBio ? (
            <div className={styles.editContainer}>
              <textarea
                value={bio || ''}
                onChange={(e) => setBio(e.target.value)}
                className={styles.bioInput}
                placeholder="Write something about yourself..."
              />
              <div className={styles.buttonGroup}>
                <button onClick={() => setEditingBio(false)} className={styles.cancelButton}>
                  Cancel
                </button>
                <button onClick={handleSaveBio} className={styles.saveButton}>
                  Save
                </button>
              </div>
            </div>
          ) : (
            <div className={styles.bioContainer}>
              <p className={styles.bioText}>{bio || "No bio set."}</p>
              <button onClick={() => setEditingBio(true)} className={styles.editButton}>
                Edit Profile
              </button>
            </div>
          )}
          {error && <div className={styles.error}>{error}</div>}
        </div>
      </div>
    </div>
  );
}

export default Profile;

// frontend/src/components/Home.jsx
import React, { useState, useEffect, useRef, useContext } from 'react';
import { AuthContext } from '../contexts/AuthContext';
import { useNavigate } from 'react-router-dom';
import styles from './Home.module.css';
import backgroundVideo from './Space.mp4';

function Home() {
  const { user } = useContext(AuthContext);
  const navigate = useNavigate();
  const [scrollProgress, setScrollProgress] = useState(0);
  const sectionsRef = useRef([]);

  const handleLogin = () => navigate('/login');
  const handleSignUp = () => navigate('/signup');
  const handleStartChat = () => navigate('/conversations');

  useEffect(() => {
    const handleScroll = () => {
      // Calculate scroll progress (0 to 1)
      const totalHeight = document.documentElement.scrollHeight - window.innerHeight;
      const progress = window.scrollY / totalHeight;
      setScrollProgress(progress);

      // Check each section for visibility
      sectionsRef.current.forEach(section => {
        if (!section) return;
        const rect = section.getBoundingClientRect();
        const isVisible = rect.top < window.innerHeight * 0.75;
        if (isVisible) {
          section.classList.add(styles.revealed);
        }
      });
    };

    window.addEventListener('scroll', handleScroll);
    handleScroll(); // Initial check

    return () => window.removeEventListener('scroll', handleScroll);
  }, []);

  // Progress line width calculation
  const progressWidth = `${scrollProgress * 100}%`;

  return (
    <div className={styles.mainContainer}>
      {/* Progress Line */}
      <div className={styles.progressLine}>
        <div className={styles.progressFill} style={{ width: progressWidth }} />
      </div>

      {/* Background Video */}
      <div className={styles.videoOverlay} style={{ 
        opacity: Math.min(0.4 + scrollProgress * 0.4, 0.8) 
      }} />
      <video
        autoPlay
        muted
        loop
        playsInline
        className={styles.backgroundVideo}
      >
        <source src={backgroundVideo} type="video/mp4" />
      </video>

      {/* Hero Section */}
      <section 
        className={`${styles.section} ${styles.heroSection}`}
        ref={el => sectionsRef.current[0] = el}
      >
        <div className={styles.contentWrapper}>
          {user.name ? (
            <div className={styles.authenticatedContent}>
              <div className={styles.avatarWrapper}>
                <div className={styles.avatar}>
                  {user.name.charAt(0).toUpperCase()}
                </div>
              </div>
              <h1 className={styles.welcomeText}>
                Welcome back, <span className={styles.userName}>{user.name}</span>
              </h1>
              <button className={styles.mainButton} onClick={handleStartChat}>
                Go to Conversations
              </button>
            </div>
          ) : (
            <div className={styles.unauthenticatedContent}>
              <h1 className={styles.heroTitle}>Connect to Start Chatting</h1>
              <p className={styles.heroText}>
                Join a dynamic community and communicate in real-time
              </p>
              <div className={styles.actionButtons}>
                <button className={styles.mainButton} onClick={handleLogin}>
                  Login
                </button>
                <button className={styles.mainButton} onClick={handleSignUp}>
                  Signup
                </button>
              </div>
            </div>
          )}
        </div>
      </section>

      {/* Features Section */}
      <section 
        className={`${styles.section} ${styles.featuresSection}`}
        ref={el => sectionsRef.current[1] = el}
      >
        <div className={styles.contentWrapper}>
          <h2 className={styles.sectionTitle}>Our Features</h2>
          <div className={styles.featuresGrid}>
            {[
              { icon: '🔒', title: 'Secure', desc: 'End-to-end encryption' },
              { icon: '⚡', title: 'Fast', desc: 'Real-time communication' },
              { icon: '🌐', title: 'Global', desc: 'Accessible everywhere' },
              { icon: '📱', title: 'Responsive', desc: 'Mobile-friendly interface' },
              { icon: '🎨', title: 'Customizable', desc: 'Themes and avatars' },
              { icon: '📂', title: 'Media Sharing', desc: 'Share images, videos, and documents' },
            ].map((feature, index) => (
              <div 
                key={index}
                className={styles.featureCard}
                style={{ transitionDelay: `${index * 0.1}s` }}
              >
                <span className={styles.featureIcon}>{feature.icon}</span>
                <h3>{feature.title}</h3>
                <p>{feature.desc}</p>
              </div>
            ))}
          </div>
        </div>
      </section>

      {/* Stats Section */}
      <section 
        className={`${styles.section} ${styles.statsSection}`}
        ref={el => sectionsRef.current[2] = el}
      >
        <div className={styles.contentWrapper}>
          <h2 className={styles.sectionTitle}>Our Stats</h2>
          <div className={styles.statsGrid}>
            {[
              { number: '10k+', label: 'Active Users' },
              { number: '500k+', label: 'Messages Sent' },
              { number: '99.9%', label: 'Uptime' },
            ].map((stat, index) => (
              <div 
                key={index}
                className={styles.statCard}
                style={{ transitionDelay: `${index * 0.1}s` }}
              >
                <span className={styles.statNumber}>{stat.number}</span>
                <span className={styles.statLabel}>{stat.label}</span>
              </div>
            ))}
          </div>
        </div>
      </section>

      {/* Contact Section */}
      <section 
        className={`${styles.section} ${styles.contactSection}`}
        ref={el => sectionsRef.current[3] = el}
      >
        <div className={styles.contentWrapper}>
          <h2 className={styles.sectionTitle}>Get in Touch</h2>
          <div className={styles.contactContent}>
            <p className={styles.contactText}>
              Ready to join us?
            </p>
            <button className={styles.mainButton}>
              Start Chatting
            </button>
          </div>
        </div>
      </section>
    </div>
  );
}

export default Home;

// frontend/src/components/Navbar/Navbar.jsx
import React, { useContext } from 'react';
import { Link, useLocation } from 'react-router-dom';
import { AuthContext } from '../contexts/AuthContext';
import { motion, AnimatePresence } from 'framer-motion';
import styles from './Navbar.module.css';
import ThemeToggle from './ThemeToggle';

function Navbar() {
  const { user, logout } = useContext(AuthContext);
  const location = useLocation();

  // Define routes where navbar should not be displayed
  const hideNavbarRoutes = ['/chat']; // Add other routes if necessary
  const shouldHideNavbar = hideNavbarRoutes.some(route => location.pathname.startsWith(route));

  return (
    <AnimatePresence>
      {!shouldHideNavbar && (
        <motion.nav
          className={styles.navbar}
          initial={{ y: -100, opacity: 0 }}
          animate={{ y: 0, opacity: 1 }}
          exit={{ y: -100, opacity: 0 }}
          transition={{ duration: 0.5 }}
        >
          <div className={styles.navbarContainer}>
            <Link to="/" className={styles.navbarLogo}>
              ChatApp
            </Link>

            <div className={styles.navbarContent}>
              <ThemeToggle />
              {user.name ? (
                <div className={styles.authenticatedNav}>
                  <div className={styles.navLinks}>
                    <Link to="/conversations" className={styles.navLink}>
                      Conversations
                    </Link>
                    <Link to="/profile" className={styles.navLink}>
                      Profile
                    </Link>
                  </div>

                  <div className={styles.userSection}>
                    <div className={styles.userInfo}>
                      <div className={styles.userAvatar}>
                        {user.avatar_url ? (
                          <img src={user.avatar_url} alt="Avatar" className={styles.avatarImage} />
                        ) : (
                          user.name.charAt(0).toUpperCase()
                        )}
                      </div>
                      <span className={styles.userName}>{user.name}</span>
                    </div>
                    <button onClick={logout} className={styles.logoutButton}>
                      Logout
                    </button>
                  </div>
                </div>
              ) : (
                <div className={styles.authButtons}>
                  <Link to="/login" className={styles.loginButton}>
                    Login
                  </Link>
                  <Link to="/signup" className={styles.signupButton}>
                    Signup
                  </Link>
                </div>
              )}
            </div>
          </div>
        </motion.nav>
      )}
    </AnimatePresence>
  );
}

export default Navbar;

// frontend/src/components/ThemeToggle.jsx
import React, { useEffect, useState } from 'react';
import { IoSunny, IoMoon } from 'react-icons/io5';
import styles from './ThemeToggle.module.css';

function ThemeToggle() {
  const [theme, setTheme] = useState('light');

  useEffect(() => {
    // Check for saved user preference
    const storedTheme = localStorage.getItem('app-theme') || 'light';
    setTheme(storedTheme);
    document.documentElement.setAttribute('data-theme', storedTheme);
  }, []);

  const toggleTheme = () => {
    const newTheme = theme === 'light' ? 'dark' : 'light';
    setTheme(newTheme);
    document.documentElement.setAttribute('data-theme', newTheme);
    localStorage.setItem('app-theme', newTheme);
  };

  return (
    <button className={styles.toggleButton} onClick={toggleTheme} aria-label="Toggle Theme">
      {theme === 'light' ? <IoMoon /> : <IoSunny />}
    </button>
  );
}

export default ThemeToggle;

// frontend/src/contexts/AuthContext.jsx
import React, { createContext, useState, useEffect } from 'react';
import axios from '../utils/api';

export const AuthContext = createContext();

export const AuthProvider = ({ children }) => {
  const [user, setUser] = useState({
    id: '',
    name: '',
    bio: '',
    avatar_url: ''
  });

  // Load user from localStorage on mount
  useEffect(() => {
    const storedUser = localStorage.getItem('user');
    const storedToken = localStorage.getItem('token');
    if (storedUser && storedToken) {
      const parsedUser = JSON.parse(storedUser);
      setUser({
        id: parsedUser.id || '',
        name: parsedUser.name || '',
        bio: parsedUser.bio || '',
        avatar_url: parsedUser.avatar_url || ''
      });
      axios.defaults.headers.common['Authorization'] = `Bearer ${storedToken}`;
    }
  }, []);

  const login = async (username, password) => {
    const response = await axios.post('/login', {
      name: username,
      password: password,
    });
    const { access_token } = response.data;
    localStorage.setItem('token', access_token);
    axios.defaults.headers.common['Authorization'] = `Bearer ${access_token}`;

    // Fetch current user data
    const userResponse = await axios.get('/users/me');
    const fetchedUser = userResponse.data;
    localStorage.setItem('user', JSON.stringify(fetchedUser));
    setUser({
      id: fetchedUser.id || '',
      name: fetchedUser.name || '',
      bio: fetchedUser.bio || '',
      avatar_url: fetchedUser.avatar_url || ''
    });
  };

  const signup = async (username, password) => {
    await axios.post('/signup', {
      name: username,
      password: password,
    });
    // After signup, login the user
    await login(username, password);
  };

  const logout = () => {
    localStorage.removeItem('user');
    localStorage.removeItem('token');
    delete axios.defaults.headers.common['Authorization'];
    setUser({
      id: '',
      name: '',
      bio: '',
      avatar_url: ''
    });
  };

  return (
    <AuthContext.Provider value={{ user, login, signup, logout, setUser }}>
      {children}
    </AuthContext.Provider>
  );
};

// src/utils/api.js
import axios from 'axios';

const instance = axios.create({
  baseURL: process.env.REACT_APP_API_URL,
});

export default instance;

import React, { useContext } from 'react';
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
import { AuthContext } from './contexts/AuthContext';
import Login from './components/Auth/Login';
import Signup from './components/Auth/Signup';
import Home from './components/Home';
import Chat from './components/Chat/Chat';
import Profile from './components/Profile/Profile';
import Navbar from './components/Navbar';


function App() {
  const { user } = useContext(AuthContext);

  return (
    <Router>
      <Navbar />
      <Routes>
        <Route path="/" element={user.name ? <Navigate to="/conversations" /> : <Home />} />
        <Route path="/login" element={user.name ? <Navigate to="/conversations" /> : <Login />} />
        <Route path="/signup" element={user.name ? <Navigate to="/conversations" /> : <Signup />} />
        <Route path="/profile" element={user.name ? <Profile /> : <Navigate to="/login" />} />
        <Route path="/conversations" element={user.name ? <Chat /> : <Navigate to="/login" />} />
        <Route path="/chat/:conversationId" element={user.name ? <Chat /> : <Navigate to="/login" />} />
        {/* Redirection pour les chemins non définis */}
        <Route path="*" element={<Navigate to="/" replace />} />
      </Routes>
    </Router>
  );
}

export default App;

// frontend/src/index.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { AuthProvider } from './contexts/AuthContext';
import './index.css';

ReactDOM.render(
  <AuthProvider>
    <App />
  </AuthProvider>,
  document.getElementById('root')
);