reservation livres
This commit is contained in:
@@ -9,6 +9,7 @@ import AddBook from './pages/AddBook';
|
|||||||
import BookDetail from './pages/BookDetail';
|
import BookDetail from './pages/BookDetail';
|
||||||
import Customers from './pages/Customers';
|
import Customers from './pages/Customers';
|
||||||
import Login from './pages/Login';
|
import Login from './pages/Login';
|
||||||
|
import Reservations from './pages/Reservations';
|
||||||
import { useAuth } from './context/AuthContext';
|
import { useAuth } from './context/AuthContext';
|
||||||
|
|
||||||
function RequireAuth({ children }) {
|
function RequireAuth({ children }) {
|
||||||
@@ -33,6 +34,7 @@ export default function App() {
|
|||||||
<Route path="books/:bookId" element={<RequireAuth><BookDetail /></RequireAuth>} />
|
<Route path="books/:bookId" element={<RequireAuth><BookDetail /></RequireAuth>} />
|
||||||
<Route path="books/new" element={<RequireAdmin><AddBook /></RequireAdmin>} />
|
<Route path="books/new" element={<RequireAdmin><AddBook /></RequireAdmin>} />
|
||||||
<Route path="orders" element={<RequireAuth><Orders /></RequireAuth>} />
|
<Route path="orders" element={<RequireAuth><Orders /></RequireAuth>} />
|
||||||
|
<Route path="reservations" element={<RequireAuth><Reservations /></RequireAuth>} />
|
||||||
<Route path="profile" element={<RequireAuth><Profile /></RequireAuth>} />
|
<Route path="profile" element={<RequireAuth><Profile /></RequireAuth>} />
|
||||||
<Route path="customers" element={<RequireAdmin><Customers /></RequireAdmin>} />
|
<Route path="customers" element={<RequireAdmin><Customers /></RequireAdmin>} />
|
||||||
<Route path="*" element={<NotFound />} />
|
<Route path="*" element={<NotFound />} />
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ export default function Navbar() {
|
|||||||
<li><Link to="/">Accueil</Link></li>
|
<li><Link to="/">Accueil</Link></li>
|
||||||
<li><Link to="/books">Catalogue</Link></li>
|
<li><Link to="/books">Catalogue</Link></li>
|
||||||
{user && <li><Link to="/orders">Commandes</Link></li>}
|
{user && <li><Link to="/orders">Commandes</Link></li>}
|
||||||
|
{user?.role === 'user' && <li><Link to="/reservations">Mes réservations</Link></li>}
|
||||||
{user && <li><Link to="/profile">Mon compte</Link></li>}
|
{user && <li><Link to="/profile">Mon compte</Link></li>}
|
||||||
{user?.role === 'admin' && <li><Link to="/customers">Clients</Link></li>}
|
{user?.role === 'admin' && <li><Link to="/customers">Clients</Link></li>}
|
||||||
</ul>
|
</ul>
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
import { createContext, useContext, useState } from 'react';
|
||||||
|
|
||||||
|
const ReservationContext = createContext(null);
|
||||||
|
|
||||||
|
export function ReservationProvider({ children }) {
|
||||||
|
const [reservations, setReservations] = useState(() => {
|
||||||
|
const saved = localStorage.getItem('reservations');
|
||||||
|
return saved ? JSON.parse(saved) : [];
|
||||||
|
});
|
||||||
|
|
||||||
|
function addReservation(book, phoneNumber) {
|
||||||
|
const reservation = {
|
||||||
|
reservationId: crypto.randomUUID(),
|
||||||
|
bookId: book.isbn,
|
||||||
|
bookTitle: book.title,
|
||||||
|
bookAuthor: book.author,
|
||||||
|
phoneNumber,
|
||||||
|
status: 'CONFIRMED',
|
||||||
|
reservedAt: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
const updated = [...reservations, reservation];
|
||||||
|
setReservations(updated);
|
||||||
|
localStorage.setItem('reservations', JSON.stringify(updated));
|
||||||
|
return reservation;
|
||||||
|
}
|
||||||
|
|
||||||
|
function cancelReservation(reservationId) {
|
||||||
|
const updated = reservations.filter(r => r.reservationId !== reservationId);
|
||||||
|
setReservations(updated);
|
||||||
|
localStorage.setItem('reservations', JSON.stringify(updated));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ReservationContext.Provider value={{ reservations, addReservation, cancelReservation }}>
|
||||||
|
{children}
|
||||||
|
</ReservationContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useReservations() {
|
||||||
|
return useContext(ReservationContext);
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import React from 'react';
|
|||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from 'react-dom/client';
|
||||||
import { BrowserRouter } from 'react-router-dom';
|
import { BrowserRouter } from 'react-router-dom';
|
||||||
import { AuthProvider } from './context/AuthContext';
|
import { AuthProvider } from './context/AuthContext';
|
||||||
|
import { ReservationProvider } from './context/ReservationContext';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import './styles/global.css';
|
import './styles/global.css';
|
||||||
|
|
||||||
@@ -10,7 +11,9 @@ root.render(
|
|||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
<App />
|
<ReservationProvider>
|
||||||
|
<App />
|
||||||
|
</ReservationProvider>
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
</React.StrictMode>
|
</React.StrictMode>
|
||||||
|
|||||||
@@ -1,34 +1,26 @@
|
|||||||
import { useState, useEffect } from 'react';
|
import { useState, useEffect } from 'react';
|
||||||
import { useParams, Link } from 'react-router-dom';
|
import { useParams, Link } from 'react-router-dom';
|
||||||
import { getBookById, reserveBook } from '../api/books';
|
import { getBookById } from '../api/books';
|
||||||
import { useAuth } from '../context/AuthContext';
|
import { useAuth } from '../context/AuthContext';
|
||||||
|
import { useReservations } from '../context/ReservationContext';
|
||||||
|
|
||||||
export default function BookDetail() {
|
export default function BookDetail() {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
|
const { addReservation } = useReservations();
|
||||||
const { bookId } = useParams();
|
const { bookId } = useParams();
|
||||||
const [book, setBook] = useState(null);
|
const [book, setBook] = useState(null);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [error, setError] = useState(null);
|
const [error, setError] = useState(null);
|
||||||
const [phoneNumber, setPhoneNumber] = useState('');
|
const [phoneNumber, setPhoneNumber] = useState('');
|
||||||
const [reservationStatus, setReservationStatus] = useState(null);
|
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) {
|
function handleReservation(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setReserving(true);
|
addReservation(book, phoneNumber);
|
||||||
setReservationStatus(null);
|
setReservationStatus({ success: true, message: 'Réservation effectuée avec succès !' });
|
||||||
reserveBook(bookId, { phoneNumber })
|
setPhoneNumber('');
|
||||||
.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 <main><p>Chargement…</p></main>;
|
if (loading) return <main><p>Chargement…</p></main>;
|
||||||
@@ -61,9 +53,7 @@ export default function BookDetail() {
|
|||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</label>
|
</label>
|
||||||
<button type="submit" disabled={reserving}>
|
<button type="submit">Réserver</button>
|
||||||
{reserving ? 'Réservation…' : 'Réserver'}
|
|
||||||
</button>
|
|
||||||
</form>
|
</form>
|
||||||
{reservationStatus && (
|
{reservationStatus && (
|
||||||
<p style={{ color: reservationStatus.success ? 'green' : 'red' }}>
|
<p style={{ color: reservationStatus.success ? 'green' : 'red' }}>
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
import { useReservations } from '../context/ReservationContext';
|
||||||
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
export default function Reservations() {
|
||||||
|
const { reservations, cancelReservation } = useReservations();
|
||||||
|
|
||||||
|
if (reservations.length === 0) {
|
||||||
|
return (
|
||||||
|
<main>
|
||||||
|
<h1>Mes réservations</h1>
|
||||||
|
<p>Aucune réservation pour le moment.</p>
|
||||||
|
<Link to="/books">← Retour au catalogue</Link>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main>
|
||||||
|
<h1>Mes réservations</h1>
|
||||||
|
<ul>
|
||||||
|
{reservations.map(r => (
|
||||||
|
<li key={r.reservationId}>
|
||||||
|
<strong>{r.bookTitle}</strong> — {r.bookAuthor}
|
||||||
|
<br />
|
||||||
|
Téléphone : {r.phoneNumber}
|
||||||
|
<br />
|
||||||
|
Réservé le : {new Date(r.reservedAt).toLocaleDateString('fr-FR')}
|
||||||
|
<br />
|
||||||
|
Statut : {r.status}
|
||||||
|
<br />
|
||||||
|
<button onClick={() => cancelReservation(r.reservationId)}>Annuler</button>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</main>
|
||||||
|
);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user