Add interactive rating display and controls to dashboard
This commit is contained in:
@@ -129,21 +129,80 @@ templ Dashboard(user User) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function renderEbooks(ebooks) {
|
async function renderEbooks(ebooks) {
|
||||||
const container = document.getElementById('ebooks-container');
|
const container = document.getElementById('ebooks-container');
|
||||||
container.innerHTML = '';
|
container.innerHTML = '';
|
||||||
|
|
||||||
ebooks.forEach(ebook => {
|
for (const ebook of ebooks) {
|
||||||
const card = document.createElement('div');
|
const card = document.createElement('div');
|
||||||
card.className = 'card p-4 rounded-lg border hover:shadow-lg transition-shadow';
|
card.className = 'card p-4 rounded-lg border hover:shadow-lg transition-shadow';
|
||||||
card.style.cssText = 'background-color: var(--bg-secondary); border-color: var(--border);';
|
card.style.cssText = 'background-color: var(--bg-secondary); border-color: var(--border);';
|
||||||
|
|
||||||
const coverUrl = ebook.cover_image_path ? ebook.cover_image_path : '/static/placeholder-book.svg';
|
const coverUrl = ebook.cover_image_path ? ebook.cover_image_path : '/static/placeholder-book.svg';
|
||||||
|
|
||||||
card.innerHTML = '<div class="flex flex-col h-full"><div class="flex-1"><div class="aspect-w-3 aspect-h-4 mb-3 overflow-hidden rounded"><img src="' + coverUrl + '" alt="Cover" class="w-full h-48 object-cover rounded" onerror="this.src=\'/static/placeholder-book.svg\'"></div><h3 class="font-semibold text-lg mb-1 line-clamp-2" style="color: var(--text-primary)">' + ebook.title + '</h3>' + (ebook.author ? '<p class="text-sm mb-2" style="color: var(--text-secondary)">by ' + ebook.author + '</p>' : '') + '<div class="flex justify-between items-center mt-4"><div class="flex space-x-2"><button onclick="viewEbook(\'' + ebook.id + '\')" class="px-3 py-1 text-sm border rounded hover:opacity-80" style="border-color: var(--border); color: var(--text-secondary)">View</button></div></div></div>';
|
// Get user's rating for this ebook
|
||||||
|
const rating = await getEbookRating(ebook.id);
|
||||||
|
const ratingStars = renderRatingStars(ebook.id, rating);
|
||||||
|
|
||||||
|
card.innerHTML = '<div class="flex flex-col h-full"><div class="flex-1"><div class="aspect-w-3 aspect-h-4 mb-3 overflow-hidden rounded"><img src="' + coverUrl + '" alt="Cover" class="w-full h-48 object-cover rounded" onerror="this.src=\'/static/placeholder-book.svg\'"></div><h3 class="font-semibold text-lg mb-1 line-clamp-2" style="color: var(--text-primary)">' + ebook.title + '</h3>' + (ebook.author ? '<p class="text-sm mb-2" style="color: var(--text-secondary)">by ' + ebook.author + '</p>' : '') + '<div class="mb-2" id="rating-' + ebook.id + '">' + ratingStars + '</div><div class="flex justify-between items-center mt-4"><div class="flex space-x-2"><button onclick="viewEbook(\'' + ebook.id + '\')" class="px-3 py-1 text-sm border rounded hover:opacity-80" style="border-color: var(--border); color: var(--text-secondary)">View</button></div></div></div>';
|
||||||
|
|
||||||
container.appendChild(card);
|
container.appendChild(card);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getEbookRating(ebookId) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/ebooks/${ebookId}/rating`, {
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ' + localStorage.getItem('token'),
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
return data.rating || 0;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error fetching rating:', error);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function renderRatingStars(ebookId, currentRating) {
|
||||||
|
let stars = '';
|
||||||
|
for (let i = 1; i <= 5; i++) {
|
||||||
|
const filled = i <= currentRating;
|
||||||
|
const starClass = filled ? 'text-yellow-400' : 'text-gray-400';
|
||||||
|
stars += `<span onclick="setRating('${ebookId}', ${i})" class="cursor-pointer text-xl ${starClass} hover:text-yellow-300 transition-colors">${filled ? '★' : '☆'}</span>`;
|
||||||
|
}
|
||||||
|
return stars;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function setRating(ebookId, rating) {
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/ebooks/${ebookId}/rating`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Authorization': 'Bearer ' + localStorage.getItem('token'),
|
||||||
|
'Content-Type': 'application/json'
|
||||||
|
},
|
||||||
|
body: JSON.stringify({ rating: rating })
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
const data = await response.json();
|
||||||
|
const ratingContainer = document.getElementById(`rating-${ebookId}`);
|
||||||
|
if (ratingContainer) {
|
||||||
|
ratingContainer.innerHTML = renderRatingStars(ebookId, data.rating);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error('Error setting rating:', await response.text());
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error setting rating:', error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function logout() {
|
function logout() {
|
||||||
|
|||||||
Reference in New Issue
Block a user