BookMind / frontend /src /components /BookDetailsModal.js
BookMind Deployer
Deploy BookMind to HF Spaces - exclude large files
806d445
Raw History Blame Contribute Delete
11.5 kB
import React, { useState } from 'react';
import { X, Star, Heart, Share2, Calendar, Building, User, BookOpen, TrendingUp } from 'lucide-react';
const BookDetailsModal = ({ book, onClose, onRecommend }) => {
const [isFavorite, setIsFavorite] = useState(false);
const [rating, setRating] = useState(0);
const [hoverRating, setHoverRating] = useState(0);
const handleAddToFavorites = () => {
setIsFavorite(!isFavorite);
// Save to localStorage
const favorites = JSON.parse(localStorage.getItem('bookFavorites') || '[]');
if (!isFavorite) {
favorites.push(book);
localStorage.setItem('bookFavorites', JSON.stringify(favorites));
} else {
const filtered = favorites.filter(fav => fav.title !== book.title);
localStorage.setItem('bookFavorites', JSON.stringify(filtered));
}
};
const handleShare = () => {
const shareText = `Check out "${book.title}" by ${book.author} on BookMind! 📚`;
if (navigator.share) {
navigator.share({
title: book.title,
text: shareText,
url: window.location.href,
});
} else {
// Fallback: Copy to clipboard
navigator.clipboard.writeText(shareText);
alert('Recommendation copied to clipboard!');
}
};
const handleRating = (value) => {
setRating(value);
// Save rating to localStorage
const ratings = JSON.parse(localStorage.getItem('bookRatings') || '{}');
ratings[book.title] = value;
localStorage.setItem('bookRatings', JSON.stringify(ratings));
};
if (!book) return null;
return (
<>
{/* Backdrop */}
<div
className="fixed inset-0 bg-black/50 backdrop-blur-sm z-40 transition-opacity"
onClick={onClose}
></div>
{/* Modal */}
<div className="fixed inset-0 z-50 flex items-center justify-center p-4 overflow-y-auto">
<div
className="bg-white rounded-3xl shadow-2xl max-w-2xl w-full my-8 overflow-hidden animate-in fade-in scale-95"
onClick={(e) => e.stopPropagation()}
>
{/* Close Button */}
<button
onClick={onClose}
className="absolute top-4 right-4 z-10 p-2 bg-bg-body rounded-full hover:bg-border transition-all"
>
<X size={24} className="text-text-primary" />
</button>
{/* Modal Content */}
<div className="p-8">
{/* Header with Book Image and Info */}
<div className="flex gap-8 mb-8">
{/* Book Cover */}
<div className="flex-shrink-0">
<img
src={book.image_url}
alt={book.title}
className="w-40 h-56 object-cover rounded-2xl shadow-lg border-4 border-border"
/>
</div>
{/* Book Details */}
<div className="flex-1">
<h2 className="text-3xl font-bold text-text-primary mb-2">
{book.title}
</h2>
<p className="text-xl text-text-secondary mb-6 flex items-center gap-2">
<User size={18} className="text-[#0891b2]" />
{book.author}
</p>
{/* Rating Stars */}
<div className="flex items-center gap-3 mb-6">
<div className="flex gap-1">
{[1, 2, 3, 4, 5].map((star) => (
<button
key={star}
onClick={() => handleRating(star)}
onMouseEnter={() => setHoverRating(star)}
onMouseLeave={() => setHoverRating(0)}
className="transition-transform hover:scale-110"
>
<Star
size={24}
className={`${
star <= (hoverRating || rating)
? 'fill-yellow-400 text-yellow-400'
: 'text-gray-300'
}`}
/>
</button>
))}
</div>
<span className="text-sm text-text-secondary">
{rating > 0 ? `You rated: ${rating}/5` : 'Rate this book'}
</span>
</div>
{/* Book Metadata */}
<div className="space-y-2 mb-6">
{book.year && (
<div className="flex items-center gap-3 text-text-secondary">
<Calendar size={16} className="text-[#0891b2]" />
<span className="font-medium">{book.year}</span>
</div>
)}
{book.publisher && (
<div className="flex items-center gap-3 text-text-secondary">
<Building size={16} className="text-[#0891b2]" />
<span className="font-medium">{book.publisher}</span>
</div>
)}
</div>
{/* Action Buttons */}
<div className="flex gap-3 flex-wrap">
<button
onClick={handleAddToFavorites}
className={`flex items-center gap-2 px-4 py-2 rounded-lg font-semibold transition-all ${
isFavorite
? 'bg-red-500 text-white'
: 'bg-bg-body text-text-secondary hover:bg-border'
}`}
>
<Heart size={18} fill={isFavorite ? 'currentColor' : 'none'} />
{isFavorite ? 'Saved' : 'Save'}
</button>
<button
onClick={handleShare}
className="flex items-center gap-2 px-4 py-2 bg-bg-body text-text-secondary rounded-lg font-semibold hover:bg-border transition-all"
>
<Share2 size={18} />
Share
</button>
</div>
</div>
</div>
{/* Description Section */}
<div className="mb-8">
<h3 className="text-lg font-bold text-text-primary mb-3 flex items-center gap-2">
<BookOpen size={20} className="text-[#0891b2]" />
About This Book
</h3>
<p className="text-text-secondary leading-relaxed bg-bg-body p-4 rounded-xl">
{book.description || 'No description available for this book.'}
</p>
</div>
{/* Recommendation Methods */}
<div className="mb-6">
<h3 className="text-lg font-bold text-text-primary mb-4 flex items-center gap-2">
<TrendingUp size={20} className="text-[#0891b2]" />
Get Recommendations
</h3>
<div className="grid grid-cols-3 gap-3">
<button
onClick={() => {
onRecommend(book.title, 'hybrid');
onClose();
}}
className="bg-gradient-to-r from-[#06b6d4] to-[#0891b2] text-white px-4 py-3 rounded-lg font-bold text-sm hover:shadow-lg transition-all hover:-translate-y-0.5"
>
Hybrid
</button>
<button
onClick={() => {
onRecommend(book.title, 'collaborative');
onClose();
}}
className="bg-gradient-to-r from-[#0369a1] to-[#0284c7] text-white px-4 py-3 rounded-lg font-bold text-sm hover:shadow-lg transition-all hover:-translate-y-0.5"
>
Collaborative
</button>
<button
onClick={() => {
onRecommend(book.title, 'content');
onClose();
}}
className="bg-gradient-to-r from-[#0891b2] to-[#06d6a0] text-white px-4 py-3 rounded-lg font-bold text-sm hover:shadow-lg transition-all hover:-translate-y-0.5"
>
Content
</button>
</div>
</div>
{/* Close Button */}
<button
onClick={onClose}
className="w-full bg-text-primary text-white py-3 rounded-lg font-bold hover:bg-text-secondary transition-all"
>
Close
</button>
</div>
</div>
</div>
</>
);
};
export default BookDetailsModal;