Real-time ticket chat updates (auto-refresh every 15s)#1296
Real-time ticket chat updates (auto-refresh every 15s)#1296KroZen-Dev wants to merge 14 commits intoCtrlpanel-gg:developmentfrom
Conversation
Removed unnecessary whitespace and improved code formatting.
|
why was the alert for ticket deletion removed or am mi missing something here? |
Yes, my fault, it got mixed up with my local test versions. I'll fix it |
Add confirmation dialog for ticket deletion.
|
Returned confirmation. Next time I'll be more careful with local versions 😅 |
There was a problem hiding this comment.
Pull request overview
This PR implements automatic real-time updates for ticket chat messages by introducing AJAX polling every 15 seconds. Previously, users had to manually refresh the page to see new replies, but now the chat history synchronizes automatically in the background without requiring full page reloads.
Changes:
- Added auto-refresh functionality to ticket chat views with 15-second polling intervals
- Introduced new API endpoints (
getComments) in user and admin ticket controllers to return comment data as JSON - Added automatic table refresh for the admin ticket index page
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 9 comments.
| File | Description |
|---|---|
| themes/default/views/ticket/show.blade.php | Added JavaScript polling logic to fetch and render ticket comments every 15 seconds for user-facing ticket view |
| themes/default/views/admin/ticket/show.blade.php | Added JavaScript polling logic to fetch and render ticket comments every 15 seconds for admin ticket view |
| themes/default/views/admin/ticket/index.blade.php | Added DataTable auto-reload every 15 seconds and refactored variable assignment |
| routes/web.php | Added new routes for comment retrieval endpoints and refactored server resource routes |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| <span class="badge badge-primary">${comment.created_at}</span> | ||
| </div> | ||
| </div> | ||
| <div class="card-body" style="white-space:pre-wrap">${comment.ticketcomment}</div> |
There was a problem hiding this comment.
User-generated content is being rendered without HTML escaping. The ${comment.ticketcomment} should be escaped to prevent XSS attacks. Consider using a function to escape HTML entities before inserting into the DOM, or use textContent instead of innerHTML for this field.
| data.forEach(comment => { | ||
| let rolesHtml = ''; | ||
| comment.user.roles.forEach(role => { | ||
| rolesHtml += `<span style='background-color: ${role.color}' class='badge'>${role.name}</span> `; |
There was a problem hiding this comment.
The role.color and role.name values are inserted directly into HTML without sanitization. This could allow XSS attacks if malicious values are present in the database. Ensure these values are properly escaped before rendering.
| setInterval(function() { | ||
| fetch("{{ route('admin.ticket.comments', $ticket->ticket_id) }}") | ||
| .then(response => response.json()) | ||
| .then(data => { | ||
| let html = ''; | ||
| data.forEach(comment => { | ||
| let rolesHtml = ''; | ||
| comment.user.roles.forEach(role => { | ||
| rolesHtml += `<span style='background-color: ${role.color}' class='badge'>${role.name}</span> `; | ||
| }); | ||
|
|
||
| html += ` | ||
| <div class="card"> | ||
| <div class="card-header"> | ||
| <div class="d-flex justify-content-between"> | ||
| <h5 class="card-title"><img | ||
| src="${comment.user.avatar}" | ||
| class="user-image" alt="User Image"> | ||
| <a href="/admin/users/${comment.user.id}">${comment.user.name}</a> | ||
| ${rolesHtml} | ||
| </h5> | ||
| <span class="badge badge-primary">${comment.created_at}</span> | ||
| </div> | ||
| </div> | ||
| <div class="card-body" style="white-space:pre-wrap">${comment.ticketcomment}</div> | ||
| </div>`; | ||
| }); | ||
| document.getElementById('ticket-comments').innerHTML = html; | ||
| }); |
There was a problem hiding this comment.
User-generated content is being rendered without HTML escaping. The ${comment.ticketcomment} should be escaped to prevent XSS attacks. Consider using a function to escape HTML entities before inserting into the DOM, or use textContent instead of innerHTML for this field.
| setInterval(function() { | |
| fetch("{{ route('admin.ticket.comments', $ticket->ticket_id) }}") | |
| .then(response => response.json()) | |
| .then(data => { | |
| let html = ''; | |
| data.forEach(comment => { | |
| let rolesHtml = ''; | |
| comment.user.roles.forEach(role => { | |
| rolesHtml += `<span style='background-color: ${role.color}' class='badge'>${role.name}</span> `; | |
| }); | |
| html += ` | |
| <div class="card"> | |
| <div class="card-header"> | |
| <div class="d-flex justify-content-between"> | |
| <h5 class="card-title"><img | |
| src="${comment.user.avatar}" | |
| class="user-image" alt="User Image"> | |
| <a href="/admin/users/${comment.user.id}">${comment.user.name}</a> | |
| ${rolesHtml} | |
| </h5> | |
| <span class="badge badge-primary">${comment.created_at}</span> | |
| </div> | |
| </div> | |
| <div class="card-body" style="white-space:pre-wrap">${comment.ticketcomment}</div> | |
| </div>`; | |
| }); | |
| document.getElementById('ticket-comments').innerHTML = html; | |
| }); | |
| function escapeHtml(value) { | |
| if (value === null || value === undefined) { | |
| return ''; | |
| } | |
| return String(value) | |
| .replace(/&/g, '&') | |
| .replace(/</g, '<') | |
| .replace(/>/g, '>') | |
| .replace(/"/g, '"') | |
| .replace(/'/g, '''); | |
| } | |
| setInterval(function() { | |
| fetch("{{ route('admin.ticket.comments', $ticket->ticket_id) }}") | |
| .then(response => response.json()) | |
| .then(data => { | |
| let html = ''; | |
| data.forEach(comment => { | |
| let rolesHtml = ''; | |
| comment.user.roles.forEach(role => { | |
| rolesHtml += `<span style='background-color: ${escapeHtml(role.color)}' class='badge'>${escapeHtml(role.name)}</span> `; | |
| }); | |
| html += ` | |
| <div class="card"> | |
| <div class="card-header"> | |
| <div class="d-flex justify-content-between"> | |
| <h5 class="card-title"><img | |
| src="${escapeHtml(comment.user.avatar)}" | |
| class="user-image" alt="User Image"> | |
| <a href="/admin/users/${escapeHtml(comment.user.id)}">${escapeHtml(comment.user.name)}</a> | |
| ${rolesHtml} | |
| </h5> | |
| <span class="badge badge-primary">${escapeHtml(comment.created_at)}</span> | |
| </div> | |
| </div> | |
| <div class="card-body" style="white-space:pre-wrap">${escapeHtml(comment.ticketcomment)}</div> | |
| </div>`; | |
| }); | |
| document.getElementById('ticket-comments').innerHTML = html; | |
| }); |
| data.forEach(comment => { | ||
| let rolesHtml = ''; | ||
| comment.user.roles.forEach(role => { | ||
| rolesHtml += `<span style='background-color: ${role.color}' class='badge'>${role.name}</span> `; |
There was a problem hiding this comment.
The role.color and role.name values are inserted directly into HTML without sanitization. This could allow XSS attacks if malicious values are present in the database. Ensure these values are properly escaped before rendering.
| setInterval(function() { | ||
| fetch("{{ route('ticket.comments', $ticket->ticket_id) }}") | ||
| .then(response => response.json()) | ||
| .then(data => { |
There was a problem hiding this comment.
The fetch request lacks error handling. If the request fails (network error, server error, etc.), the user won't receive any feedback. Add a .catch() block to handle errors gracefully, or check response.ok before parsing JSON.
| setInterval(function() { | ||
| fetch("{{ route('admin.ticket.comments', $ticket->ticket_id) }}") | ||
| .then(response => response.json()) | ||
| .then(data => { |
There was a problem hiding this comment.
The fetch request lacks error handling. If the request fails (network error, server error, etc.), the user won't receive any feedback. Add a .catch() block to handle errors gracefully, or check response.ok before parsing JSON.
| <div class="card-header"> | ||
| <div class="d-flex justify-content-between"> | ||
| <h5 class="card-title"><img | ||
| src="${comment.user.avatar}" |
There was a problem hiding this comment.
The avatar URL is inserted without validation. If this value contains javascript: or data: URLs, it could lead to XSS attacks. Validate that avatar URLs are safe before rendering them in img src attributes.
| <div class="card-header"> | ||
| <div class="d-flex justify-content-between"> | ||
| <h5 class="card-title"><img | ||
| src="${comment.user.avatar}" |
There was a problem hiding this comment.
The avatar URL is inserted without validation. If this value contains javascript: or data: URLs, it could lead to XSS attacks. Validate that avatar URLs are safe before rendering them in img src attributes.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
🚀 What does this PR do?
This Pull Request introduces automatic synchronization for ticket chat messages. Previously, users and administrators had to manually refresh the entire page to check for new replies. Now, the chat history updates automatically in the background every 15 seconds.
✨ Key Features:
Auto-Refresh: The ticket chat history is fetched and updated every 15 seconds via AJAX.
Improved UX: No more full page reloads required to see new messages.
Status Updates: Ticket status (e.g., Open, Answered, Closed) also updates dynamically without refreshing.
🛠 Technical Changes:
Added getComments method to TicketsController (User & Admin) to return JSON data.
Updated ticket/show.blade.php and admin/ticket/show.blade.php with JavaScript for polling.
Updated routes/web.php to include new API endpoints.
🎯 Benefits:
This improvement significantly speeds up communication between support staff and users, making the support process more fluid and responsive.