File manager - Edit - /var/www/ratemypay_dev/storage/framework/views/76d83ceefb8d3cc094aa8e37b5410a8d.php
Back
<div id="postModal" class="hidden fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4"> <div class="bg-white rounded-lg shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto"> <div class="sticky top-0 bg-white border-b border-gray-200 px-6 py-4 flex items-center justify-between"> <h2 class="text-lg font-bold text-gray-900" id="modal-post-title">Community Post</h2> <button onclick="closePostModal()" class="text-gray-400 hover:text-gray-600"> <svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" /> </svg> </button> </div> <div class="p-6"> <div class="flex items-center gap-3 mb-6"> <img id="modal-author-avatar" src="https://ui-avatars.com/api/?name=User&background=FFA500&color=fff" alt="Author" class="w-10 h-10 rounded-full"> <div class="flex items-center gap-2"> <span id="modal-author-name" class="font-semibold text-gray-900 text-sm">Loading...</span> </div> </div> <div id="modal-payslip-wrapper" class="mb-6 rounded-lg overflow-hidden border border-gray-200 hidden"> <img id="modal-payslip-image" src="" alt="Payslip" class="w-full h-auto object-cover"> </div> <p id="modal-post-content" class="text-gray-700 mb-6 font-extralight leading-relaxed"> Loading content... </p> <div class="bg-gray-50 rounded-lg p-4 mb-6"> <h3 class="font-semibold text-gray-900 mb-3">Salary Summary:</h3> <ul class="space-y-2 text-sm" id="modal-salary-summary"> <li class="flex items-center gap-2"> <span class="text-lg">💼</span> <span class="text-gray-700">Base Pay: <strong id="modal-base-pay">...</strong></span> </li> <li class="flex items-center gap-2"> <span class="text-lg">🎁</span> <span class="text-gray-700">Bonus: <strong id="modal-bonus-commission">...</strong></span> </li> <li class="flex items-center gap-2"> <span class="text-lg">📊</span> <span class="text-gray-700">Equity: <strong id="modal-equity">...</strong></span> </li> <li class="flex items-center gap-2"> <span class="text-lg">🏥</span> <span class="text-gray-700">Benefits: <strong id="modal-benefits">...</strong></span> </li> </ul> </div> <div class="flex items-center gap-6 mb-6 pb-6 border-b border-gray-200"> <div class="flex items-center gap-2"> <form method="POST" action="" class="modal-like-form" data-post-id=""> <?php echo csrf_field(); ?> <button type="submit" class="flex items-center space-x-2 text-gray-600 hover:text-red-600 transition"> <svg class="w-5 h-5 text-red-600 heart-icon-modal" id="modal-like-icon" fill="currentColor" viewBox="0 0 20 20"> <path fill-rule="evenodd" d="M3.172 5.172a4 4 0 015.656 0L10 6.343l1.172-1.171a4 4 0 115.656 5.656L10 17.657l-6.828-6.829a4 4 0 010-5.656z" /> </svg> <span class="text-sm font-medium text-gray-700 modal-like-count" id="modal-likes-count">0</span> </button> </form> </div> <div class="flex items-center gap-2"> <svg class="w-5 h-5 text-blue-600" fill="currentColor" viewBox="0 0 20 20"> <path fill-rule="evenodd" d="M18 10c0 3.866-3.582 7-8 7a8.841 8.841 0 01-4.083-.98L2 17l1.338-3.123C2.493 12.767 2 11.434 2 10c0-3.866 3.582-7 8-7s8 3.134 8 7zM7 9H5v2h2V9zm8 0h-2v2h2V9zM9 9h2v2H9V9z" /> </svg> <span class="text-sm font-medium text-gray-700" id="modal-comments-count">0</span> </div> <button class="flex items-center gap-2 text-gray-600 hover:text-[#2F3D7E] transition"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.684 13.342C9.922 10.938 11.539 9 13.94 9c1.821 0 2.586.605 3.44 1.933a6.001 6.001 0 01-9.519 8.592" /> </svg> <span class="text-sm font-medium">Share</span> </button> <div class="flex gap-1 ml-auto" id="modal-rating-stars"> </div> <span class="text-sm font-medium text-gray-700" id="modal-average-rating">0.0</span> </div> <div class="mb-6"> <h3 class="font-semibold text-gray-900 mb-4">Rate Chart</h3> <div class="bg-gray-50 rounded-lg p-4" style="height: 300px;"> <canvas id="rateChart"></canvas> </div> </div> <div class="mb-6"> <form id="comment-form" method="POST" action=""> <?php echo csrf_field(); ?> <div class="border border-gray-300 rounded-lg p-4 flex items-end gap-3"> <div class="flex-1"> <textarea name="content" id="comment-content-input" placeholder="Type a new message here" class="w-full focus:outline-none resize-none" rows="2" required></textarea> <input type="hidden" name="rate_my_pay_id" id="comment-post-id-input"> <input type="hidden" name="parent_id" id="comment-parent-id-input"> </div> <div class="flex items-center gap-2"> <button type="submit" class="px-3 py-2 bg-[#2F3D7E] text-white rounded-lg hover:bg-[#1E2B5A] transition flex items-center gap-1"> <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24"> <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 19l9 2-9-18-9 18 9-2zm0 0v-8" /> </svg> Send </button> </div> </div> </form> </div> <div> <h3 class="font-semibold text-red-600 mb-4" id="comments-section-title">Comments:</h3> <div class="space-y-4" id="modal-comments-list"> </div> </div> </div> </div> </div> <script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.9.1/chart.min.js"></script> <script> let rateChartInstance = null; // A. Chart Initialization function initRateChart() { const ctx = document.getElementById('rateChart'); if (ctx && !rateChartInstance) { rateChartInstance = new Chart(ctx, { type: 'bar', data: { labels: ['name', 'name', 'name', 'name', 'name', 'name', 'name'], datasets: [{ label: 'Salary', data: [589.12, 840.20, 898.00, 203.00, 400.01, 920.12, 398.00], backgroundColor: '#2F3D7E', borderRadius: 4, }] }, options: { responsive: true, maintainAspectRatio: false, plugins: { legend: { display: true, position: 'top', } }, scales: { y: { beginAtZero: true, max: 1000, ticks: { color: '#6B7280', }, grid: { color: 'rgba(0,0,0,0.05)', } }, x: { ticks: { color: '#6B7280', }, grid: { display: false } } } } }); } } // B. Static Rendering Helpers (renderStars, renderComment) function renderStars(averageRating) { let starsHtml = ''; const roundedRating = Math.round(averageRating); for (let i = 1; i <= 5; i++) { if (i <= roundedRating) { starsHtml += '<span class="text-yellow-400">★</span>'; } else { starsHtml += '<span class="text-gray-300">★</span>'; } } return starsHtml; } function buildCommentTree(flatComments) { const commentsById = {}; const topLevelComments = []; // 1. Map all comments by their ID and initialize replies array flatComments.forEach(comment => { commentsById[comment.id] = { ...comment, replies: [] }; }); // 2. Assign replies to their parents flatComments.forEach(comment => { const commentWithReplies = commentsById[comment.id]; if (comment.parent_id) { // This is a reply. Find its parent. const parent = commentsById[comment.parent_id]; if (parent) { parent.replies.push(commentWithReplies); } else { // Orphan reply, treat as top level topLevelComments.push(commentWithReplies); } } else { // Top level comment topLevelComments.push(commentWithReplies); } }); return topLevelComments; } function renderCommentWithReplies(comment) { // Determine indentation class for replies const indentClass = comment.parent_id ? 'ml-8 mt-4' : ''; const isReply = comment.parent_id !== null; // Use your existing renderComment logic, but make it recursive const avatarUrl = `https://ui-avatars.com/api/?name=${encodeURIComponent(comment.user.name)}&background=2F3D7E&color=fff`; const isLiked = comment.is_liked_by_user || false; const csrfToken = document.querySelector('#comment-form input[name="_token"]').value; // Start rendering the current comment/reply let html = ` <div id="comment-${comment.id}" class="flex gap-3 comment-item ${indentClass}"> <img src="${avatarUrl}" alt="${comment.user.name}" class="w-8 h-8 rounded-full"> <div class="flex-1"> <p class="font-semibold text-gray-900 text-sm">${comment.user.name} ${isReply ? '<span class="text-xs text-gray-500">(Reply)</span>' : ''}</p> <p class="text-sm text-gray-700 mt-1">${comment.content}</p> <div class="flex items-center gap-4 mt-2 text-xs text-gray-600"> <span class="comment-like-count-${comment.id}">${comment.likes_count || 0} Likes</span> <form method="POST" action="/rate-my-pay/comments/${comment.id}/like" class="comment-like-form"> <input type="hidden" name="_token" value="${csrfToken}"> <button type="submit" class="comment-like-btn flex items-center gap-1 hover:text-[#2F3D7E] transition" data-comment-id="${comment.id}"> <span class="like-icon-comment-${comment.id}" style="color: ${isLiked ? '#dc2626' : 'inherit'};">👍</span> Like </button> </form> <button class="comment-reply-btn hover:text-[#2F3D7E] transition" data-comment-id="${comment.id}" data-comment-author="${comment.user.name}"> 💬 Reply </button> </div> </div> </div> `; // Recursively render replies within a nested container if (comment.replies.length > 0) { comment.replies.forEach(reply => { html += renderCommentWithReplies(reply); }); } // Wrap the top-level comment and its replies in a single container for better spacing return `<div class="comment-thread">${html}</div>`; } // C. Reply Handler window.handleCommentReply = function (commentId, authorName) { document.getElementById('comment-parent-id-input').value = commentId; const contentInput = document.getElementById('comment-content-input'); contentInput.placeholder = `Replying to @${authorName}...`; contentInput.focus(); const modalBody = document.querySelector('#postModal > div'); const commentForm = document.getElementById('comment-form'); modalBody.scrollTop = commentForm.offsetTop; }; // A. openPostModal window.openPostModal = function(post) { if (post) { populateModalWithPost(post); } document.getElementById('postModal').classList.remove('hidden'); document.body.style.overflow = 'hidden'; // Attach dynamic listeners and initialize chart setTimeout(attachCommentListeners, 50); setTimeout(initRateChart, 100); }; // B. closePostModal window.closePostModal = function() { document.getElementById('postModal').classList.add('hidden'); document.body.style.overflow = 'auto'; if (rateChartInstance) { rateChartInstance.destroy(); rateChartInstance = null; } }; // C. populateModalWithPost function populateModalWithPost(post) { // 1. Author and Content const authorName = post.is_anonymous ? 'Anonymous' : post.user.name; document.getElementById('modal-author-name').textContent = authorName; document.getElementById('modal-post-content').textContent = post.content; const avatarSrc = post.is_anonymous ? 'https://ui-avatars.com/api/?name=Anonymous&background=FFA500&color=fff' : `https://ui-avatars.com/api/?name=${encodeURIComponent(authorName)}&background=2F3D7E&color=fff`; document.getElementById('modal-author-avatar').src = avatarSrc; // 2. Payslip Image const payslipWrapper = document.getElementById('modal-payslip-wrapper'); const payslipImage = document.getElementById('modal-payslip-image'); if (post.payslip_url) { payslipImage.src = post.payslip_url; payslipWrapper.classList.remove('hidden'); } else { payslipWrapper.classList.add('hidden'); payslipImage.src = ''; } // 3. Salary Summary document.getElementById('modal-base-pay').textContent = post.formatted_salary; document.getElementById('modal-bonus-commission').textContent = post.bonus ? `${(post.currency || '')} ${Number(post.bonus).toLocaleString('en-US', { minimumFractionDigits: 0, maximumFractionDigits: 0 })}` : 'Not disclosed'; document.getElementById('modal-equity').textContent = post.equity ? `${(post.currency || '')} ${Number(post.equity).toLocaleString('en-US', { minimumFractionDigits: 0, maximumFractionDigits: 0 })}/year` : 'Not disclosed'; document.getElementById('modal-benefits').textContent = post.benefits || 'None provided'; // 4. Engagement Stats document.getElementById('modal-likes-count').textContent = post.likes_count; document.getElementById('modal-comments-count').textContent = post.comments_count; document.getElementById('modal-average-rating').textContent = Number(post.average_rating).toFixed(1); document.getElementById('modal-rating-stars').innerHTML = renderStars(post.average_rating); // Update Like Form Action and Icon const likeForm = document.querySelector('.modal-like-form'); likeForm.action = `/rate-my-pay/${post.id}/like`; likeForm.dataset.postId = post.id; const heartIcon = document.getElementById('modal-like-icon'); if (post.is_liked_by_user) { heartIcon.setAttribute('fill', 'currentColor'); heartIcon.classList.add('text-red-600'); } else { heartIcon.setAttribute('fill', 'none'); heartIcon.classList.remove('text-red-600'); } // 5. Comments Section and Form const commentsList = document.getElementById('modal-comments-list'); commentsList.innerHTML = ''; if (post.all_comments && post.all_comments.length > 0) { // 1. Build the tree structure from the flat array const structuredComments = buildCommentTree(post.all_comments); // 2. Render the tree structure structuredComments.forEach(comment => { commentsList.innerHTML += renderCommentWithReplies(comment); }); } const commentForm = document.getElementById('comment-form'); commentForm.action = `/rate-my-pay/${post.id}/comments`; document.getElementById('comment-post-id-input').value = post.id; // Reset Reply Context when populating a new post document.getElementById('comment-parent-id-input').value = ''; document.getElementById('comment-content-input').placeholder = 'Type a new message here'; } // EVENT LISTENERS & ATTACHERS // A. Comment Listeners (For dynamic content) function attachCommentListeners() { // --- 1. Comment Like Listener --- document.querySelectorAll('.comment-like-form').forEach(form => { form.addEventListener('submit', async function(e) { e.preventDefault(); const csrf = this.querySelector('input[name="_token"]').value; const url = this.action; // Get comment ID for updating counts const commentId = url.split('/comments/')[1].split('/like')[0]; try { const response = await fetch(url, { method: 'POST', headers: { 'X-CSRF-TOKEN': csrf, 'Accept': 'application/json' }, }); const data = await response.json(); if (data.status) { // Update like count and icon document.querySelector(`.comment-like-count-${commentId}`).textContent = `${data.likes_count} Likes`; const iconSpan = document.querySelector(`.like-icon-comment-${commentId}`); if (data.liked) { iconSpan.style.color = '#dc2626'; } else { iconSpan.style.color = 'inherit'; } } else { console.error('Comment like failed:', data.message); } } catch (error) { console.error('AJAX Error:', error); } }); }); // --- 2. Comment Reply Listener --- document.querySelectorAll('.comment-reply-btn').forEach(button => { button.addEventListener('click', function() { const commentId = this.dataset.commentId; const authorName = this.dataset.commentAuthor; handleCommentReply(commentId, authorName); }); }); } // 1. Post Like Listener (For the main post itself) document.querySelector('.modal-like-form')?.addEventListener('submit', async function(e) { e.preventDefault(); const url = this.action; const csrf = this.querySelector('input[name="_token"]').value; try { const response = await fetch(url, { method: 'POST', headers: { 'X-CSRF-TOKEN': csrf, 'Accept': 'application/json' }, }); const data = await response.json(); if (data.status) { document.getElementById('modal-likes-count').textContent = data.likes_count; const heartIcon = document.getElementById('modal-like-icon'); if (data.liked) { heartIcon.setAttribute('fill', 'currentColor'); } else { heartIcon.setAttribute('fill', 'none'); } } else { console.error('Like failed:', data.message); } } catch (error) { console.error('AJAX Error:', error); } }); // 2. AJAX Comment Submission (Primary form action) document.getElementById('comment-form')?.addEventListener('submit', async function(e) { e.preventDefault(); const form = this; const url = form.action; const contentInput = document.getElementById('comment-content-input'); const csrf = form.querySelector('input[name="_token"]').value; const commentContent = contentInput.value; if (!commentContent.trim()) return; const submitButton = form.querySelector('button[type="submit"]'); submitButton.disabled = true; try { const response = await fetch(url, { method: 'POST', headers: { 'X-CSRF-TOKEN': csrf, 'Content-Type': 'application/json', 'Accept': 'application/json' }, body: JSON.stringify({ content: commentContent, parent_id: document.getElementById('comment-parent-id-input').value, }) }); const data = await response.json(); if (response.ok && data.status) { if (!data.comment.replies) { data.comment.replies = []; } const commentsList = document.getElementById('modal-comments-list'); const newCommentHtml = renderCommentWithReplies(data.comment); if (data.comment.parent_id) { const parentThread = document.getElementById(`comment-${data.comment.parent_id}`).closest('.comment-thread'); if (parentThread) { // Append the new reply HTML block inside the parent thread parentThread.insertAdjacentHTML('beforeend', newCommentHtml); } else { // Fallback for an orphaned reply (treat as top-level) commentsList.innerHTML += newCommentHtml; } } else { // Top-level comment: append directly to the main list commentsList.innerHTML += newCommentHtml; } // UPDATE COUNTS AND UI document.getElementById('modal-comments-count').textContent = data.comments_count; contentInput.value = ''; document.getElementById('comment-parent-id-input').value = ''; contentInput.placeholder = 'Type a new message here'; const modalBody = document.querySelector('#postModal > div'); modalBody.scrollTop = modalBody.scrollHeight; // Re-attach listeners for the new comment form (if any elements needed listeners) attachCommentListeners(); } else { console.error('Comment failed:', data.message || 'Unknown error'); alert('Failed to add comment: ' + (data.message || 'Please try again.')); } } catch (error) { console.error('AJAX Error:', error); alert('An network error occurred while submitting your comment.'); } finally { submitButton.disabled = false; } }); // C. Simple Modal Control (Clicking outside/Escape key) document.getElementById('postModal')?.addEventListener('click', function(e) { if (e.target === this) { closePostModal(); } }); document.addEventListener('keydown', function(e) { if (e.key === 'Escape') { closePostModal(); } }); </script><?php /**PATH /var/www/ratemypay_dev/resources/views/dashboard/community/modals/post-detail-modal.blade.php ENDPATH**/ ?>
| ver. 1.4 |
Github
|
.
| PHP 8.3.30 | Generation time: 0 |
proxy
|
phpinfo
|
Settings