From 4ae227d79dac7b9d397ba169ced5d79f03af5570 Mon Sep 17 00:00:00 2001 From: AISSI-JUDE-CHRIST Date: Thu, 11 Jun 2026 22:23:39 +0200 Subject: [PATCH] ajout authentification admin/user --- my-library/src/App.jsx | 27 ++++++++++--- my-library/src/components/Navbar.jsx | 26 +++++++++--- my-library/src/context/AuthContext.js | 39 ++++++++++++++++++ my-library/src/index.js | 5 ++- my-library/src/pages/BookDetail.jsx | 51 ++++++++++++++++++++++-- my-library/src/pages/Books.jsx | 4 +- my-library/src/pages/Login.jsx | 57 +++++++++++++++++++++++++++ 7 files changed, 193 insertions(+), 16 deletions(-) create mode 100644 my-library/src/context/AuthContext.js create mode 100644 my-library/src/pages/Login.jsx diff --git a/my-library/src/App.jsx b/my-library/src/App.jsx index ff39111..a10b954 100644 --- a/my-library/src/App.jsx +++ b/my-library/src/App.jsx @@ -1,4 +1,4 @@ -import { Routes, Route } from 'react-router-dom'; +import { Routes, Route, Navigate } from 'react-router-dom'; import Layout from './components/Layout'; import Home from './pages/Home'; import Books from './pages/Books'; @@ -8,19 +8,34 @@ import NotFound from './pages/NotFound'; import AddBook from './pages/AddBook'; import BookDetail from './pages/BookDetail'; import Customers from './pages/Customers'; +import Login from './pages/Login'; +import { useAuth } from './context/AuthContext'; + +function RequireAuth({ children }) { + const { user } = useAuth(); + return user ? children : ; +} + +function RequireAdmin({ children }) { + const { user } = useAuth(); + if (!user) return ; + if (user.role !== 'admin') return ; + return children; +} export default function App() { return ( + } /> }> } /> } /> - } /> - } /> + } /> + } /> + } /> + } /> + } /> } /> - } /> - } /> - } /> ); diff --git a/my-library/src/components/Navbar.jsx b/my-library/src/components/Navbar.jsx index c60e798..84dce88 100644 --- a/my-library/src/components/Navbar.jsx +++ b/my-library/src/components/Navbar.jsx @@ -1,7 +1,16 @@ -import { Link } from 'react-router-dom'; +import { Link, useNavigate } from 'react-router-dom'; +import { useAuth } from '../context/AuthContext'; import '../styles/navbar.css'; export default function Navbar() { + const { user, logout } = useAuth(); + const navigate = useNavigate(); + + function handleLogout() { + logout(); + navigate('/login'); + } + return ( ); diff --git a/my-library/src/context/AuthContext.js b/my-library/src/context/AuthContext.js new file mode 100644 index 0000000..c9e5c41 --- /dev/null +++ b/my-library/src/context/AuthContext.js @@ -0,0 +1,39 @@ +import { createContext, useContext, useState } from 'react'; + +const USERS = [ + { username: 'admin', password: 'admin', role: 'admin' }, + { username: 'alice', password: 'bob', role: 'user' }, +]; + +const AuthContext = createContext(null); + +export function AuthProvider({ children }) { + const [user, setUser] = useState(() => { + const saved = localStorage.getItem('auth'); + return saved ? JSON.parse(saved) : null; + }); + + function login(username, password) { + const found = USERS.find(u => u.username === username && u.password === password); + if (!found) return false; + const { password: _, ...safe } = found; + setUser(safe); + localStorage.setItem('auth', JSON.stringify(safe)); + return true; + } + + function logout() { + setUser(null); + localStorage.removeItem('auth'); + } + + return ( + + {children} + + ); +} + +export function useAuth() { + return useContext(AuthContext); +} diff --git a/my-library/src/index.js b/my-library/src/index.js index 7b56730..71c19cb 100644 --- a/my-library/src/index.js +++ b/my-library/src/index.js @@ -1,6 +1,7 @@ import React from 'react'; import ReactDOM from 'react-dom/client'; import { BrowserRouter } from 'react-router-dom'; +import { AuthProvider } from './context/AuthContext'; import App from './App'; import './styles/global.css'; @@ -8,7 +9,9 @@ const root = ReactDOM.createRoot(document.getElementById('root')); root.render( - + + + ); \ No newline at end of file diff --git a/my-library/src/pages/BookDetail.jsx b/my-library/src/pages/BookDetail.jsx index 68194d6..77cf45c 100644 --- a/my-library/src/pages/BookDetail.jsx +++ b/my-library/src/pages/BookDetail.jsx @@ -1,14 +1,35 @@ import { useState, useEffect } from 'react'; import { useParams, Link } from 'react-router-dom'; -import { getBookById } from '../api/books'; +import { getBookById, reserveBook } from '../api/books'; +import { useAuth } from '../context/AuthContext'; export default function BookDetail() { - const { bookId } = useParams(); + const { user } = useAuth(); + const { bookId } = useParams(); const [book, setBook] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); + const [phoneNumber, setPhoneNumber] = useState(''); + const [reservationStatus, setReservationStatus] = useState(null); + const [reserving, setReserving] = useState(false); - useEffect(() => {getBookById(bookId).then((response) => setBook(response.data)).catch((err) => {console.error(err);setError('Livre introuvable.');}).finally(() => setLoading(false));}, [bookId]); + useEffect(() => { getBookById(bookId).then((response) => setBook(response.data)).catch((err) => { console.error(err); setError('Livre introuvable.'); }).finally(() => setLoading(false)); }, [bookId]); + + function handleReservation(e) { + e.preventDefault(); + setReserving(true); + setReservationStatus(null); + reserveBook(bookId, { phoneNumber }) + .then(() => { + setReservationStatus({ success: true, message: 'Réservation effectuée avec succès !' }); + setPhoneNumber(''); + }) + .catch((err) => { + const msg = err.response?.data || 'Erreur lors de la réservation.'; + setReservationStatus({ success: false, message: msg }); + }) + .finally(() => setReserving(false)); + } if (loading) return

Chargement…

; if (error) return

{error}

← Retour au catalogue
; @@ -26,6 +47,30 @@ export default function BookDetail() {

Langue : {book.language}

Catégories : {book.categories?.join(', ')}

{book.description &&

{book.description}

} + + {user?.role === 'user' &&
+

Réserver ce livre

+
+ + +
+ {reservationStatus && ( +

+ {reservationStatus.message} +

+ )} +
} ); } \ No newline at end of file diff --git a/my-library/src/pages/Books.jsx b/my-library/src/pages/Books.jsx index 0c283e5..e4d4115 100644 --- a/my-library/src/pages/Books.jsx +++ b/my-library/src/pages/Books.jsx @@ -1,8 +1,10 @@ import { useState, useEffect } from 'react'; import { getBooks } from '../api/books'; import { Link } from 'react-router-dom'; +import { useAuth } from '../context/AuthContext'; export default function Books() { + const { user } = useAuth(); const [books, setBooks] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); @@ -28,7 +30,7 @@ export default function Books() { return (

Catalogue

- + Ajouter un livre + {user?.role === 'admin' && + Ajouter un livre}
    {books.map((book) => (
  • diff --git a/my-library/src/pages/Login.jsx b/my-library/src/pages/Login.jsx new file mode 100644 index 0000000..1e0689d --- /dev/null +++ b/my-library/src/pages/Login.jsx @@ -0,0 +1,57 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { useAuth } from '../context/AuthContext'; + +export default function Login() { + const { login } = useAuth(); + const navigate = useNavigate(); + const [form, setForm] = useState({ username: '', password: '' }); + const [error, setError] = useState(null); + + function handleSubmit(e) { + e.preventDefault(); + const ok = login(form.username, form.password); + if (ok) { + navigate('/'); + } else { + setError('Identifiants incorrects.'); + } + } + + return ( +
    +

    Connexion

    +
    +
    + +
    +
    + +
    + + {error &&

    {error}

    } +
    +

    Comptes disponibles :

    +
      +
    • admin / admin (administrateur)
    • +
    • user / user (utilisateur)
    • +
    +
    + ); +}