diff --git a/templates/dashboard.templ b/templates/dashboard.templ index 5a7214e..a2d4c47 100644 --- a/templates/dashboard.templ +++ b/templates/dashboard.templ @@ -41,6 +41,33 @@ templ Dashboard(user User) { .btn-primary:hover { opacity: 0.8; } + .rating-star { + display: inline-block; + transition: all 0.2s ease; + cursor: pointer; + position: relative; + } + .rating-star:hover { + transform: scale(1.1); + } + .half-star { + position: relative; + display: inline-block; + } + .half-star::before { + content: '★'; + position: absolute; + left: 0; + top: 0; + width: 50%; + overflow: hidden; + color: #facc15; /* yellow-400 */ + } + .rating-text { + font-size: 0.875rem; + margin-left: 0.5rem; + opacity: 0.8; + }
@@ -170,16 +197,62 @@ templ Dashboard(user User) { } } + // Convert 10-point rating to 5-point with half-star precision + function convertToFivePoint(rating) { + return rating / 2; + } + + // Convert 5-point with half-stars back to 10-point + function convertToTenPoint(fivePointRating) { + return Math.round(fivePointRating * 2); + } + function renderRatingStars(ebookId, currentRating) { + const fivePointRating = convertToFivePoint(currentRating); let stars = ''; + for (let i = 1; i <= 5; i++) { - const filled = i <= currentRating; - const starClass = filled ? 'text-yellow-400' : 'text-gray-400'; - stars += `${filled ? '★' : '☆'}`; + const filled = i <= Math.floor(fivePointRating); + const halfFilled = !filled && (i - 0.5) <= fivePointRating; + let starHtml = ''; + + if (filled) { + starHtml = '★'; + } else if (halfFilled) { + starHtml = '⭐'; // Using different symbol for half-star + } else { + starHtml = '☆'; + } + + const starClass = filled ? 'text-yellow-400' : + halfFilled ? 'text-yellow-200' : 'text-gray-400'; + + stars += `${starHtml}`; } + + // Add rating text + stars += `${currentRating > 0 ? fivePointRating.toFixed(1) + '/5' : 'Not rated'}`; + return stars; } + function openRatingDialog(ebookId, currentRating) { + const fivePointRating = convertToFivePoint(currentRating); + const newRating = prompt(`Rate this ebook (1-5, supports 0.5 increments):`, currentRating > 0 ? fivePointRating.toFixed(1) : ''); + + if (newRating === null) return; // User cancelled + + const rating = parseFloat(newRating); + if (isNaN(rating) || rating < 0.5 || rating > 5) { + alert('Please enter a valid rating between 0.5 and 5'); + return; + } + + // Round to nearest 0.5 + const roundedRating = Math.round(rating * 2) / 2; + setRating(ebookId, convertToTenPoint(roundedRating)); + } + async function setRating(ebookId, rating) { try { const response = await fetch(`/api/ebooks/${ebookId}/rating`, { @@ -198,10 +271,13 @@ templ Dashboard(user User) { ratingContainer.innerHTML = renderRatingStars(ebookId, data.rating); } } else { - console.error('Error setting rating:', await response.text()); + const errorText = await response.text(); + console.error('Error setting rating:', errorText); + alert('Failed to set rating: ' + errorText); } } catch (error) { console.error('Error setting rating:', error); + alert('Failed to set rating due to network error'); } }