diff --git a/updoot.js b/updoot.js new file mode 100644 index 0000000..474d872 --- /dev/null +++ b/updoot.js @@ -0,0 +1,1242 @@ +(function() { + try { + console.log('Jellyfin Updoot: Initializing'); + + if (!document.querySelector('link[href*="material-icons"]')) { + console.log('Loading Material Icons'); + const link = document.createElement('link'); + link.rel = 'stylesheet'; + link.href = 'https://fonts.googleapis.com/icon?family=Material+Icons'; + document.head.appendChild(link); + } + + const jellyfinCredentials = JSON.parse(localStorage.getItem('jellyfin_credentials') || '{}'); + const server = jellyfinCredentials.Servers && jellyfinCredentials.Servers[0]; + const apiKey = server ? server.AccessToken : ''; + const serverUrl = server ? server.ManualAddress || server.LocalAddress : window.location.origin; + const userId = server ? server.UserId : ''; + const backendUrl = `${window.location.origin}/updoot`; + const adminUserIds = ['ee8996be37aa4da0912a08b410940d3e']; + + console.log('Credentials:', { serverUrl, apiKey, userId, backendUrl, isAdmin: adminUserIds.includes(userId) }); + + let recommendButton = null; + let recommendationsButton = null; + let adminButton = null; + let overlay = null; + let adminOverlay = null; + + async function fetchItemDetails(itemId) { + console.log('Fetching item details for itemId:', itemId); + try { + const url = `${serverUrl}/Items/${itemId}?api_key=${apiKey}`; // Fixed typo from original: 'melalui' to 'apiKey' + console.log('Requesting:', url); + const response = await fetch(url, { + method: 'GET', + headers: { 'X-Emby-Token': apiKey } + }); + if (!response.ok) { + console.error('Fetch item details failed:', `HTTP ${response.status}`); + throw new Error(`HTTP ${response.status}: ${await response.text()}`); + } + const data = await response.json(); + console.log('Item details received:', data); + return data; + } catch (error) { + console.error('Error fetching item details:', error.message); + return null; + } + } + + function createRecommendButton(playButton) { + console.log('Attempting to create Recommend button'); + if (!playButton || !playButton.parentNode) { + console.log('Play button or its parent not found'); + return; + } + if (document.querySelector('.itemDetailPage:not(.hide) .btnRecommend')) { + console.log('Recommend button already exists'); + return; + } + + console.log('Creating Recommend button'); + recommendButton = document.createElement('button'); + recommendButton.setAttribute('is', 'paper-icon-button-light'); + recommendButton.className = 'btnRecommend detailButton emby-button paper-icon-button-light'; + recommendButton.title = 'Recommend'; + recommendButton.innerHTML = ''; + recommendButton.style.width = '43.64px'; + recommendButton.style.height = '43.64px'; + + ApiClient.getCurrentUser().then((user) => { + const usersRecommended = document.querySelector('.flyout-user-list'); + + if (usersRecommended && usersRecommended.innerText.includes(user.name)) { + recommendButton.dataset.active = 'true'; + } else { + recommendButton.dataset.active = 'false'; + } + }); + + try { + playButton.parentNode.insertBefore(recommendButton, playButton.nextSibling || null); + console.log('Recommend button inserted'); + } catch (error) { + console.error('Error inserting Recommend button:', error.message); + const targetContainer = document.querySelector('.detailPagePrimaryContainer, .detailButton-container'); + if (targetContainer) { + targetContainer.appendChild(recommendButton); + console.log('Recommend button appended to targetContainer'); + } + } + + const displayArea = document.createElement('div'); + displayArea.className = 'recommendationArea'; + displayArea.style.padding = '0 10px'; + const targetContainer = playButton.closest('.mainDetailButtons, .detailButton-container, .detailPagePrimaryContainer'); + if (targetContainer) { + targetContainer.appendChild(displayArea); + console.log('Recommendation display area added'); + } else { + console.log('Target container for display area not found'); + } + + createCommentsSection(targetContainer); + + recommendButton.addEventListener('click', () => { + console.log('Recommend button clicked at ' + new Date().toISOString()); + toggleRecommendation(); + recommendButton.dataset.active = recommendButton.dataset.active === 'true' ? 'false' : 'true'; + }); + + updateRecommendationDisplay(); + } + + function createCommentsSection(targetContainer) { + console.log('Attempting to create comments section'); + if (!targetContainer) { + console.log('Target container for comments section not found'); + return; + } + if (document.querySelector('.commentsSection')) { + console.log('Comments section already exists'); + return; + } + + console.log('Creating comments section'); + const commentsSection = document.createElement('div'); + commentsSection.className = 'commentsSection'; + commentsSection.style.cssText = ` + margin-top: 20px; + padding: 10px; + background: transparent; + border-radius: 8px; + position: relative; + z-index: 100; + height: auto; + `; + + const addCommentButton = document.createElement('button'); + addCommentButton.textContent = '+ Add Comment'; + addCommentButton.style.cssText = ` + background: #4CAF50; + color: white; + border: none; + padding: 8px 16px; + border-radius: 4px; + cursor: pointer; + margin-bottom: 10px; + `; + addCommentButton.addEventListener('click', () => { + console.log('Add Comment button clicked'); + if (!userId) { + console.log('No userId, cannot show comment form'); + alert('Please log in to add a comment'); + return; + } + commentForm.style.display = commentForm.style.display === 'none' ? 'block' : 'none'; + }); + + const commentForm = document.createElement('div'); + commentForm.style.display = 'none'; + commentForm.innerHTML = ` + + + `; + const sendButton = commentForm.querySelector('button'); + sendButton.addEventListener('click', () => { + console.log('Send comment button clicked'); + const textarea = commentForm.querySelector('textarea'); + const comment = textarea.value.trim(); + if (comment) { + submitComment(comment).then(() => { + textarea.value = ''; + commentForm.style.display = 'none'; + updateCommentsDisplay(); + }); + } else { + console.log('Empty comment, not submitting'); + alert('Comment cannot be empty'); + } + }); + + const commentsDisplay = document.createElement('div'); + commentsDisplay.className = 'commentsDisplay'; + commentsDisplay.style.cssText = ` + margin-top: 10px; + height: auto; + overflow-y: visible; + `; + + commentsSection.appendChild(addCommentButton); + commentsSection.appendChild(commentForm); + commentsSection.appendChild(commentsDisplay); + + const primaryContent = document.querySelector('.detailPagePrimaryContent.padded-right'); + if (primaryContent && primaryContent.parentNode) { + primaryContent.parentNode.insertBefore(commentsSection, primaryContent.nextSibling); + console.log('Comments section inserted below .detailPagePrimaryContent'); + } else { + console.log('Primary content not found, appending to targetContainer'); + targetContainer.appendChild(commentsSection); + } + + updateCommentsDisplay(); + } + + async function submitComment(comment) { + console.log('Submitting comment for userId:', userId); + const itemId = getItemId(); + if (!itemId) { + console.log('No itemId found for comment'); + alert('Cannot add comment: Item not found'); + return; + } + if (!userId) { + console.log('No userId found for comment'); + alert('Please log in to add a comment'); + return; + } + + try { + const url = `${backendUrl}/comments`; + console.log('Submitting comment to:', url, { userId, itemId, comment }); + const response = await fetch(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ userId, itemId, comment }) + }); + if (!response.ok) { + console.error('Comment submission failed:', `HTTP ${response.status}`); + throw new Error(`HTTP ${response.status}: ${await response.text()}`); + } + console.log('Comment submitted successfully'); + } catch (error) { + console.error('Error submitting comment:', error.message); + alert('Failed to submit comment: ' + error.message); + } + } + + async function editComment(commentId, newComment) { + console.log('Editing comment:', commentId); + if (!userId) { + console.log('No userId found for editing comment'); + alert('Please log in to edit comment'); + return; + } + + try { + const url = `${backendUrl}/comments/${commentId}`; + console.log('Editing comment at:', url, { userId, comment: newComment }); + const response = await fetch(url, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ userId, comment: newComment }) + }); + if (!response.ok) { + console.error('Comment edit failed:', `HTTP ${response.status}`); + throw new Error(`HTTP ${response.status}: ${await response.text()}`); + } + console.log('Comment edited successfully'); + updateCommentsDisplay(); + } catch (error) { + console.error('Error editing comment:', error.message); + alert('Failed to edit comment: ' + error.message); + } + } + + async function deleteComment(commentId) { + console.log('Deleting comment:', commentId); + if (!userId) { + console.log('No userId found for deleting comment'); + alert('Please log in to delete comment'); + return; + } + + try { + const url = `${backendUrl}/comments/${commentId}`; + console.log('Deleting comment at:', url, { userId }); + const response = await fetch(url, { + method: 'DELETE', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ userId }) + }); + if (!response.ok) { + console.error('Comment deletion failed:', `HTTP ${response.status}`); + throw new Error(`HTTP ${response.status}: ${await response.text()}`); + } + console.log('Comment deleted successfully'); + updateCommentsDisplay(); + } catch (error) { + console.error('Error deleting comment:', error.message); + alert('Failed to delete comment: ' + error.message); + } + } + + async function updateCommentsDisplay() { + console.log('Updating comments display'); + const itemId = getItemId(); + const commentsDisplay = document.querySelector('.commentsDisplay'); + if (!itemId || !commentsDisplay) { + console.log('Missing itemId or commentsDisplay:', { itemId, commentsDisplay }); + return; + } + + try { + const url = `${backendUrl}/comments/${itemId}`; + console.log('Fetching comments from:', url); + const response = await fetch(url); + if (!response.ok) { + console.error('Fetch comments failed:', `HTTP ${response.status}`); + throw new Error(`HTTP ${response.status}: ${await response.text()}`); + } + const comments = await response.json(); + console.log('Comments received:', comments); + commentsDisplay.innerHTML = ''; + if (comments.length > 0) { + comments.forEach(comment => { + const commentDiv = document.createElement('div'); + commentDiv.style.cssText = 'margin-bottom: 8px; display: flex; align-items: flex-start;'; + commentDiv.innerHTML = ` +
${comment.comment}
+No comments yet.
'; + } + } catch (error) { + console.error('Error fetching comments:', error.message); + commentsDisplay.innerHTML = 'Failed to load comments: ' + error.message + '
'; + } + } + + async function toggleRecommendation() { + console.log('Toggling recommendation for userId:', userId); + const itemId = getItemId(); + if (!itemId) { + console.log('No itemId found'); + alert('Cannot recommend: Item not found'); + return; + } + if (!userId) { + console.log('No userId found in credentials'); + alert('Please log in to recommend'); + return; + } + + try { + const url = `${backendUrl}/recommend`; + console.log('Sending recommendation toggle:', url, { userId, itemId }); + const response = await fetch(url, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ userId, itemId }) + }); + if (!response.ok) { + console.error('Recommendation toggle failed:', `HTTP ${response.status}`); + throw new Error(`HTTP ${response.status}: ${await response.text()}`); + } + const result = await response.json(); + console.log('Toggle response:', result); + updateRecommendationDisplay(); + } catch (error) { + console.error('Error toggling recommendation:', error.message); + alert('Failed to toggle recommendation: ' + error.message); + } + } + + async function updateRecommendationDisplay() { + console.log('Updating recommendation display'); + const itemId = getItemId(); + const displayArea = document.querySelector('.recommendationArea'); + if (!itemId || !displayArea) { + console.log('Missing itemId or displayArea:', { itemId, displayArea }); + return; + } + + try { + const url = `${backendUrl}/recommendations/${itemId}`; + console.log('Fetching recommendations from:', url); + const response = await fetch(url); + if (!response.ok) { + console.error('Fetch recommendations failed:', `HTTP ${response.status}`); + throw new Error(`HTTP ${response.status}: ${await response.text()}`); + } + const recommendations = await response.json(); + console.log('Recommendations received:', recommendations); + + displayArea.innerHTML = ''; + + displayArea.innerHTML = ''; + + if (recommendations.length > 0) { + // Create the "Recommendations" tag element + const recommendationTag = document.createElement('button'); + recommendationTag.className = 'recommendation-tag'; + recommendationTag.innerHTML = ` + + ${recommendations.length} ${recommendations.length > 1 ? 'Recommendations' : 'Recommendation'} + `; + + // Create the flyout element + const flyout = document.createElement('div'); + flyout.className = 'recommendation-flyout hidden'; + + // Add the "Recommended by:" heading to the flyout + const flyoutHeading = document.createElement('h4'); + flyoutHeading.textContent = 'Recommended by:'; + flyout.appendChild(flyoutHeading); + + // Create the user list for the flyout + const flyoutUserList = document.createElement('ul'); + flyoutUserList.className = 'flyout-user-list'; + + // Populate the list with usernames from the array + recommendations.forEach(r => { + const userItem = document.createElement('li'); + userItem.textContent = r.username; + flyoutUserList.appendChild(userItem); + }); + + flyout.appendChild(flyoutUserList); + + // Append both the tag and the flyout to the display area + displayArea.appendChild(recommendationTag); + displayArea.appendChild(flyout); + + // Add a click event listener to the tag to toggle the flyout + recommendationTag.addEventListener('click', (e) => { + e.stopPropagation(); // Prevents the document click from immediately closing it + flyout.classList.toggle('hidden'); + }); + + // Add a global click listener to close the flyout if the user clicks anywhere else + document.addEventListener('click', (e) => { + if (!flyout.contains(e.target) && !recommendationTag.contains(e.target)) { + flyout.classList.add('hidden'); + } + }); + } + } catch (error) { + console.error('Error fetching recommendations:', error.message); + displayArea.textContent = 'Failed to load recommendations: ' + error.message; + } + } + + function getItemId() { + console.log('Attempting to extract itemId'); + let itemId = null; + + const detailLogo = document.querySelector('.detailLogo.lazy.lazy-image-fadein-fast'); + if (detailLogo) { + const style = window.getComputedStyle(detailLogo); + const bgImage = style.backgroundImage; + console.log('DetailLogo background-image:', bgImage); + if (bgImage && bgImage.includes('/Items/')) { + const match = bgImage.match(/\/Items\/([0-9a-f]{32})\//); + itemId = match ? match[1] : null; + console.log('DetailLogo itemId:', itemId); + } + } + + if (!itemId) { + const backdropImage = document.querySelector('.backdropImage.displayingBackdropImage.backdropImageFadeIn'); + if (backdropImage) { + const style = window.getComputedStyle(backdropImage); + let bgImage = style.backgroundImage; + console.log('BackdropImage background-image:', bgImage); + if (bgImage && bgImage.includes('/Items/')) { + const match = bgImage.match(/\/Items\/([0-9a-f]{32})\//); + itemId = match ? match[1] : null; + console.log('BackdropImage itemId:', itemId); + } + if (!itemId && backdropImage.dataset.url) { + const dataUrl = backdropImage.dataset.url; + console.log('BackdropImage data-url:', dataUrl); + const match = dataUrl.match(/\/Items\/([0-9a-f]{32})\//); + itemId = match ? match[1] : null; + console.log('BackdropImage data-url itemId:', itemId); + } + } + } + + if (!itemId) { + const urlParams = new URLSearchParams(window.location.search); + itemId = urlParams.get('id'); + console.log('URLSearchParams itemId:', itemId); + } + + if (!itemId) { + const hash = window.location.hash; + if (hash.includes('details?id=')) { + const match = hash.match(/id=([^&]+)/); + itemId = match ? match[1] : null; + console.log('Hash-based itemId:', itemId); + } + } + + if (!itemId) { + const pathParts = window.location.pathname.split('/'); + itemId = pathParts[pathParts.length - 1]; + console.log('Pathname itemId:', itemId); + } + + if (!itemId || !/^[0-9a-f]{32}$/.test(itemId)) { + console.log('Invalid or missing itemId'); + return null; + } + + console.log('Final extracted itemId:', itemId); + return itemId; + } + + function createRecommendationsButton(castButton) { + if (document.querySelector('.btnRecommendations')) { + return; + } + + console.log('Attempting to create Recommendations button'); + if (!castButton || !castButton.parentNode) { + console.log('Cast button or its parent not found'); + } + + console.log('Creating Recommendations button'); + recommendationsButton = document.createElement('button'); + recommendationsButton.setAttribute('is', 'paper-icon-button-light'); + recommendationsButton.className = 'headerButton btnRecommendations emby-button paper-icon-button-light'; + recommendationsButton.title = 'Recommendations'; + recommendationsButton.innerHTML = ''; // Fixed HTML syntax + recommendationsButton.style.backgroundColor = '#00ff0000'; + try { + const castWidth = parseFloat(getComputedStyle(castButton).width) || 40; + const castHeight = parseFloat(getComputedStyle(castButton).height) || 40; + recommendationsButton.style.width = `${castWidth * 1.2}px`; + recommendationsButton.style.height = `${castHeight * 1.2}px`; + } catch (error) { + console.error('Error setting Recommendations button size:', error.message); + recommendationsButton.style.width = '48px'; + recommendationsButton.style.height = '48px'; + } + + try { + castButton.parentNode.insertBefore(recommendationsButton, castButton); + console.log('Recommendations button inserted'); + } catch (error) { + console.error('Error inserting Recommendations button:', error.message); + const topBar = document.querySelector('.headerRight, .headerTabs, .mainDrawer-scrollContainer, .header'); + if (topBar) { + topBar.prepend(recommendationsButton); + console.log('Recommendations button appended to topBar'); + } + } + + recommendationsButton.addEventListener('click', () => { + console.log('Recommendations button clicked at ' + new Date().toISOString()); + showRecommendationsOverlay(); + }); + } + + function createAdminButton(castButton) { + if (document.querySelector('.btnAdmin')) { + return; + } + + console.log('Attempting to create Admin button for userId:', userId); + if (!adminUserIds.includes(userId)) { + console.log('User is not an admin, skipping admin button'); + return; + } + + console.log('Creating Admin button'); + adminButton = document.createElement('button'); + adminButton.setAttribute('is', 'paper-icon-button-light'); + adminButton.className = 'headerButton btnAdmin emby-button paper-icon-button-light'; + adminButton.title = 'Admin Settings'; + adminButton.innerHTML = ''; + adminButton.style.backgroundColor = '#00ff0000'; + try { + if (castButton && castButton.parentNode) { + const castWidth = parseFloat(getComputedStyle(castButton).width) || 40; + const castHeight = parseFloat(getComputedStyle(castButton).height) || 40; + adminButton.style.width = `${castWidth * 1.2}px`; + adminButton.style.height = `${castHeight * 1.2}px`; + castButton.parentNode.insertBefore(adminButton, castButton); + console.log('Admin button inserted next to castButton'); + } else { + throw new Error('Cast button or its parent not found'); + } + } catch (error) { + console.error('Error setting or inserting Admin button:', error.message); + adminButton.style.width = '48px'; + adminButton.style.height = '48px'; + const topBar = document.querySelector('.headerRight, .headerTabs, .mainDrawer-scrollContainer, .header'); + if (topBar) { + console.log('Appending Admin button to topBar as fallback'); + topBar.prepend(adminButton); + } else { + console.error('No topBar found for Admin button'); + return; + } + } + + adminButton.addEventListener('click', () => { + console.log('Admin button clicked at ' + new Date().toISOString()); + try { + showAdminOverlay(); + } catch (error) { + console.error('Error triggering showAdminOverlay:', error.message); + alert('Failed to open admin settings: ' + error.message); + } + }); + } + + async function showRecommendationsOverlay() { + console.log('Opening recommendations overlay for userId:', userId); + if (!overlay) { + console.log('Creating recommendations overlay'); + overlay = document.createElement('div'); + overlay.style.cssText = ` + display: none; + position: fixed; + top: 5px; + left: 5px; + width: calc(100% - 10px); + height: calc(100% - 10px); + background: rgba(0,0,0,0.8); + z-index: 1000; + color: white; + padding: 20px; + overflow-y: auto; + border: 5px solid #333; + box-sizing: border-box; + display: flex; + flex-wrap: wrap; + gap: 10px; + align-items: flex-start; + justify-content: center; + `; + document.body.appendChild(overlay); + + const closeButton = document.createElement('button'); + closeButton.innerHTML = ''; + closeButton.style.cssText = ` + position: absolute; + top: 10px; + right: 10px; + background: #ff4444; + color: white; + border: none; + border-radius: 50%; + width: 30px; + height: 30px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + `; + closeButton.addEventListener('click', () => { + console.log('Closing recommendations overlay'); + overlay.style.display = 'none'; + }); + overlay.appendChild(closeButton); + + overlay.addEventListener('click', (e) => { + if (e.target === overlay) { + console.log('Recommendations overlay clicked outside, closing'); + overlay.style.display = 'none'; + } + }); + } + + const closeButton = overlay.querySelector('button'); + overlay.innerHTML = ''; + if (closeButton) { + overlay.appendChild(closeButton); + console.log('Close button reattached to recommendations overlay'); + } + + try { + const url = `${backendUrl}/recommendations`; + console.log('Fetching all recommendations from:', url, { userId }); + const response = await fetch(url, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'X-Emby-Token': apiKey // Added for potential backend auth + } + }); + if (!response.ok) { + console.error('Fetch recommendations failed:', `HTTP ${response.status}`, await response.text()); + throw new Error(`HTTP ${response.status}: ${await response.text()}`); + } + const recommendations = await response.json(); + console.log('Recommendations received:', recommendations); + + if (!Array.isArray(recommendations) || recommendations.length === 0) { + console.log('No recommendations available'); + const noRecsMessage = document.createElement('p'); + noRecsMessage.textContent = 'No recommendations available yet.'; + noRecsMessage.style.cssText = 'width: 100%; text-align: center;'; + overlay.appendChild(noRecsMessage); + overlay.style.display = 'flex'; + console.log('Recommendations overlay displayed with no recommendations message'); + return; + } + + const groupedByItem = {}; + recommendations.forEach(rec => { + if (!groupedByItem[rec.itemId]) { + groupedByItem[rec.itemId] = []; + } + groupedByItem[rec.itemId].push(rec.username); + }); + + for (const itemId in groupedByItem) { + const usernames = groupedByItem[itemId]; + console.log('Fetching item details for itemId:', itemId); + const itemDetails = await fetchItemDetails(itemId); + if (!itemDetails) { + console.log('Skipping itemId due to missing details:', itemId); + continue; + } + + const card = document.createElement('div'); + card.className = 'recommendationCard'; + card.style.cssText = ` + flex: 0 0 200px; + margin: 10px; + background: #333; + border-radius: 8px; + padding: 10px; + color: white; + cursor: pointer; + box-sizing: border-box; + `; + card.addEventListener('click', () => { + console.log('Navigating to item:', itemId); + window.location.href = `/web/index.html#!/details?id=${itemId}`; + overlay.style.display = 'none'; + }); + + const imageUrl = itemDetails.ImageTags?.Primary + ? `${serverUrl}/Items/${itemId}/Images/Primary?api_key=${apiKey}` + : ''; + const logoUrl = itemDetails.ImageTags?.Logo + ? `${serverUrl}/Items/${itemId}/Images/Logo?api_key=${apiKey}` + : ''; + + card.innerHTML = ` + ${imageUrl ? `Recommended by: ${usernames.join(', ')}
+ `; + + overlay.appendChild(card); + console.log('Recommendation card added for itemId:', itemId); + } + + overlay.style.display = 'flex'; + console.log('Recommendations overlay displayed with', Object.keys(groupedByItem).length, 'items'); + } catch (error) { + console.error('Error fetching recommendations:', error.message); + const errorMessage = document.createElement('p'); + errorMessage.textContent = 'Failed to load recommendations: ' + error.message; + errorMessage.style.cssText = 'width: 100%; text-align: center;'; + overlay.appendChild(errorMessage); + overlay.style.display = 'flex'; + console.log('Recommendations overlay displayed with error message'); + } + } + + async function showAdminOverlay() { + console.log('Attempting to open admin overlay for userId:', userId); + if (!adminUserIds.includes(userId)) { + console.log('Access denied: User is not an admin'); + alert('Access denied: Admin privileges required'); + return; + } + + if (!adminOverlay) { + console.log('Creating admin overlay'); + adminOverlay = document.createElement('div'); + adminOverlay.style.cssText = ` + display: none; + position: fixed; + top: 5px; + left: 5px; + width: calc(100% - 10px); + height: calc(100% - 10px); + background: rgba(0,0,0,0.8); + z-index: 1000; + color: white; + padding: 20px; + overflow-y: auto; + border: 5px solid #333; + box-sizing: border-box; + `; + try { + document.body.appendChild(adminOverlay); + console.log('Admin overlay appended to document.body'); + } catch (error) { + console.error('Error appending admin overlay:', error.message); + alert('Failed to create admin overlay: ' + error.message); + return; + } + + const closeButton = document.createElement('button'); + closeButton.innerHTML = ''; + closeButton.style.cssText = ` + position: absolute; + top: 10px; + right: 10px; + background: #ff4444; + color: white; + border: none; + border-radius: 50%; + width: 30px; + height: 30px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + `; + closeButton.addEventListener('click', () => { + console.log('Closing admin overlay'); + adminOverlay.style.display = 'none'; + }); + adminOverlay.appendChild(closeButton); + console.log('Close button added to admin overlay'); + + adminOverlay.addEventListener('click', (e) => { + if (e.target === adminOverlay) { + console.log('Admin overlay clicked outside, closing'); + adminOverlay.style.display = 'none'; + } + }); + } + + adminOverlay.innerHTML = ''; + const closeButton = document.createElement('button'); + closeButton.innerHTML = ''; + closeButton.style.cssText = ` + position: absolute; + top: 10px; + right: 10px; + background: #ff4444; + color: white; + border: none; + border-radius: 50%; + width: 30px; + height: 30px; + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + `; + closeButton.addEventListener('click', () => { + console.log('Closing admin overlay'); + adminOverlay.style.display = 'none'; + }); + adminOverlay.appendChild(closeButton); + console.log('Close button reattached to admin overlay'); + + try { + console.log('Fetching admin settings from:', `${backendUrl}/admin/settings`); + const settingsResponse = await fetch(`${backendUrl}/admin/settings`); + if (!settingsResponse.ok) { + console.error('Fetch settings failed:', `HTTP ${settingsResponse.status}`); + throw new Error(`HTTP ${settingsResponse.status}: ${await settingsResponse.text()}`); + } + const settings = await settingsResponse.json(); + console.log('Settings received:', settings); + + const settingsForm = document.createElement('div'); + settingsForm.style.cssText = 'margin-bottom: 20px;'; + settingsForm.innerHTML = ` +${comment.username} on Item ${comment.itemId}: ${comment.comment}
+ + `; + const deleteButton = commentDiv.querySelector('button'); + deleteButton.addEventListener('click', async () => { + console.log('Delete button clicked for comment:', comment.id); + if (!confirm('Are you sure you want to delete this comment?')) { + console.log('Delete cancelled for comment:', comment.id); + return; + } + try { + const response = await fetch(`${backendUrl}/admin/comments/${comment.id}`, { + method: 'DELETE' + }); + if (!response.ok) { + console.error('Delete comment failed:', `HTTP ${response.status}`); + throw new Error(`HTTP ${response.status}: ${await response.text()}`); + } + console.log('Comment deleted:', comment.id); + commentDiv.remove(); + } catch (error) { + console.error('Error deleting comment:', error.message); + alert('Failed to delete comment: ' + error.message); + } + }); + commentSection.appendChild(commentDiv); + }); + + adminOverlay.appendChild(commentSection); + console.log('Comment section added to admin overlay'); + + const bulkDeleteForm = document.createElement('div'); + bulkDeleteForm.style.cssText = 'margin-top: 20px;'; + bulkDeleteForm.innerHTML = ` +