Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bfd1f600de |
@@ -0,0 +1,40 @@
|
|||||||
|
import { createContext, useContext, useState } from 'react';
|
||||||
|
|
||||||
|
const ReviewContext = createContext(null);
|
||||||
|
|
||||||
|
export function ReviewProvider({ children }) {
|
||||||
|
const [reviews, setReviews] = useState(() => {
|
||||||
|
const saved = localStorage.getItem('reviews');
|
||||||
|
return saved ? JSON.parse(saved) : [];
|
||||||
|
});
|
||||||
|
|
||||||
|
function addReview(bookId, bookTitle, username, rating, comment) {
|
||||||
|
const review = {
|
||||||
|
reviewId: crypto.randomUUID(),
|
||||||
|
bookId,
|
||||||
|
bookTitle,
|
||||||
|
username,
|
||||||
|
rating,
|
||||||
|
comment,
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
const updated = [...reviews, review];
|
||||||
|
setReviews(updated);
|
||||||
|
localStorage.setItem('reviews', JSON.stringify(updated));
|
||||||
|
return review;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getReviewsByBook(bookId) {
|
||||||
|
return reviews.filter(r => String(r.bookId) === String(bookId));
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ReviewContext.Provider value={{ reviews, addReview, getReviewsByBook }}>
|
||||||
|
{children}
|
||||||
|
</ReviewContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useReviews() {
|
||||||
|
return useContext(ReviewContext);
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ 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 { ReservationProvider } from './context/ReservationContext';
|
||||||
|
import { ReviewProvider } from './context/ReviewContext';
|
||||||
import App from './App';
|
import App from './App';
|
||||||
import './styles/global.css';
|
import './styles/global.css';
|
||||||
|
|
||||||
@@ -12,7 +13,9 @@ root.render(
|
|||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
<ReservationProvider>
|
<ReservationProvider>
|
||||||
<App />
|
<ReviewProvider>
|
||||||
|
<App />
|
||||||
|
</ReviewProvider>
|
||||||
</ReservationProvider>
|
</ReservationProvider>
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
|
|||||||
@@ -3,10 +3,15 @@ import { useParams, Link } from 'react-router-dom';
|
|||||||
import { getBookById } from '../api/books';
|
import { getBookById } from '../api/books';
|
||||||
import { useAuth } from '../context/AuthContext';
|
import { useAuth } from '../context/AuthContext';
|
||||||
import { useReservations } from '../context/ReservationContext';
|
import { useReservations } from '../context/ReservationContext';
|
||||||
|
import { useReviews } from '../context/ReviewContext';
|
||||||
|
|
||||||
export default function BookDetail() {
|
export default function BookDetail() {
|
||||||
const { user } = useAuth();
|
const { user } = useAuth();
|
||||||
const { addReservation } = useReservations();
|
const { addReservation } = useReservations();
|
||||||
|
const { addReview, getReviewsByBook } = useReviews();
|
||||||
|
const [rating, setRating] = useState(5);
|
||||||
|
const [comment, setComment] = useState('');
|
||||||
|
const [reviewStatus, setReviewStatus] = useState(null);
|
||||||
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);
|
||||||
@@ -23,6 +28,14 @@ export default function BookDetail() {
|
|||||||
setPhoneNumber('');
|
setPhoneNumber('');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleReview(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
addReview(book.isbn, book.title, user.username, Number(rating), comment);
|
||||||
|
setReviewStatus({ success: true, message: 'Avis publié avec succès !' });
|
||||||
|
setComment('');
|
||||||
|
setRating(5);
|
||||||
|
}
|
||||||
|
|
||||||
if (loading) return <main><p>Chargement…</p></main>;
|
if (loading) return <main><p>Chargement…</p></main>;
|
||||||
if (error) return <main><p>{error}</p><Link to="/books">← Retour au catalogue</Link></main>;
|
if (error) return <main><p>{error}</p><Link to="/books">← Retour au catalogue</Link></main>;
|
||||||
|
|
||||||
@@ -40,6 +53,50 @@ export default function BookDetail() {
|
|||||||
<p>Catégories : {book.categories?.join(', ')}</p>
|
<p>Catégories : {book.categories?.join(', ')}</p>
|
||||||
{book.description && <p>{book.description}</p>}
|
{book.description && <p>{book.description}</p>}
|
||||||
|
|
||||||
|
<section>
|
||||||
|
<h2>Avis des lecteurs</h2>
|
||||||
|
{getReviewsByBook(book.isbn).length === 0 ? (
|
||||||
|
<p>Aucun avis pour le moment.</p>
|
||||||
|
) : (
|
||||||
|
<ul>
|
||||||
|
{getReviewsByBook(book.isbn).map(r => (
|
||||||
|
<li key={r.reviewId}>
|
||||||
|
<strong>{r.username}</strong> — {'*'.repeat(r.rating)}
|
||||||
|
<br />
|
||||||
|
{r.comment}
|
||||||
|
<br />
|
||||||
|
<small>{new Date(r.createdAt).toLocaleDateString('fr-FR')}</small>
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
{user?.role === 'user' && (
|
||||||
|
<form onSubmit={handleReview}>
|
||||||
|
<h3>Laisser un avis</h3>
|
||||||
|
<label>
|
||||||
|
Note :
|
||||||
|
<select value={rating} onChange={e => setRating(e.target.value)}>
|
||||||
|
<option value={1}>*</option>
|
||||||
|
<option value={2}>**</option>
|
||||||
|
<option value={3}>***</option>
|
||||||
|
<option value={4}>****</option>
|
||||||
|
<option value={5}>*****</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label>
|
||||||
|
Commentaire :
|
||||||
|
<textarea value={comment} onChange={e => setComment(e.target.value)} required />
|
||||||
|
</label>
|
||||||
|
<button type="submit">Publier l'avis</button>
|
||||||
|
</form>
|
||||||
|
)}
|
||||||
|
{reviewStatus && (
|
||||||
|
<p style={{ color: reviewStatus.success ? 'green' : 'red' }}>
|
||||||
|
{reviewStatus.message}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
|
</section>
|
||||||
|
|
||||||
{user?.role === 'user' && <section>
|
{user?.role === 'user' && <section>
|
||||||
<h2>Réserver ce livre</h2>
|
<h2>Réserver ce livre</h2>
|
||||||
<form onSubmit={handleReservation}>
|
<form onSubmit={handleReservation}>
|
||||||
|
|||||||
Reference in New Issue
Block a user