From 11f75fd823b4484170fac8167a131d01dd1131a4 Mon Sep 17 00:00:00 2001 From: Jared Vititoe Date: Fri, 20 Mar 2026 11:27:46 -0400 Subject: [PATCH] Migrate all raw fetch() calls to lt.api, fix CSS fallback values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Replace all 23 raw fetch() calls in dashboard.js and ticket.js with lt.api.get/post/delete — removes manual CSRF header injection, manual JSON parsing boilerplate, and response.ok checks throughout - dashboard.js: 10 calls (inline save x2, template GET, 5x bulk ops, quick-status, quick-assign) - ticket.js: 13 calls (main save, add/update/delete comment x3, reply, assign, metadata update, status change, deps GET/POST/DELETE, attachments GET, delete attachment) - Remove stale csrf_token from deleteAttachment body (lt.api sends the X-CSRF-Token header automatically) - Fix CSS variable fallbacks in ticket.css: replace var(--text-primary, #f7fafc) and var(--bg-secondary, #1a202c) with plain var(--text-primary) and var(--bg-secondary) Co-Authored-By: Claude Sonnet 4.6 --- assets/css/ticket.css | 8 +- assets/js/dashboard.js | 152 +++++------------------------ assets/js/ticket.js | 211 ++++++----------------------------------- 3 files changed, 55 insertions(+), 316 deletions(-) diff --git a/assets/css/ticket.css b/assets/css/ticket.css index 37c8550..84179ed 100644 --- a/assets/css/ticket.css +++ b/assets/css/ticket.css @@ -1410,16 +1410,16 @@ body.dark-mode .status-select option { /* Dark mode for Activity tab and general improvements */ body.dark-mode .tab-content { - color: var(--text-primary, #f7fafc); + color: var(--text-primary); } body.dark-mode #activity-tab { - background: var(--bg-secondary, #1a202c); - color: var(--text-primary, #f7fafc); + background: var(--bg-secondary); + color: var(--text-primary); } body.dark-mode #activity-tab p { - color: var(--text-primary, #f7fafc); + color: var(--text-primary); } /* Comprehensive Dark Mode Fix - terminal CSS variables apply throughout */ diff --git a/assets/js/dashboard.js b/assets/js/dashboard.js index a7acea3..c8280f6 100644 --- a/assets/js/dashboard.js +++ b/assets/js/dashboard.js @@ -522,24 +522,7 @@ function quickSave() { priority: parseInt(prioritySelect.value) }; - fetch('/api/update_ticket.php', { - method: 'POST', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-Token': window.CSRF_TOKEN - }, - body: JSON.stringify(data) - }) - .then(response => { - return response.text().then(text => { - try { - return JSON.parse(text); - } catch (e) { - throw new Error('Invalid JSON response: ' + text); - } - }); - }) + lt.api.post('/api/update_ticket.php', data) .then(result => { if (result.success) { // Update the hamburger menu display @@ -589,19 +572,7 @@ function saveTicket() { } }); - fetch('/api/update_ticket.php', { - method: 'POST', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-Token': window.CSRF_TOKEN - }, - body: JSON.stringify({ - ticket_id: ticketId, - ...data - }) - }) - .then(response => response.json()) + lt.api.post('/api/update_ticket.php', { ticket_id: ticketId, ...data }) .then(data => { if(data.success) { const statusDisplay = document.getElementById('statusDisplay'); @@ -640,15 +611,7 @@ function loadTemplate() { } // Fetch template data - fetch(`/api/get_template.php?template_id=${templateId}`, { - credentials: 'same-origin' - }) - .then(response => { - if (!response.ok) { - throw new Error('Failed to fetch template'); - } - return response.json(); - }) + lt.api.get(`/api/get_template.php?template_id=${templateId}`) .then(data => { if (data.success && data.template) { const template = data.template; @@ -770,19 +733,10 @@ function bulkClose() { function performBulkCloseAction(ticketIds) { - fetch('/api/bulk_operation.php', { - method: 'POST', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-Token': window.CSRF_TOKEN - }, - body: JSON.stringify({ - operation_type: 'bulk_close', - ticket_ids: ticketIds - }) + lt.api.post('/api/bulk_operation.php', { + operation_type: 'bulk_close', + ticket_ids: ticketIds }) - .then(response => response.json()) .then(data => { if (data.success) { if (data.failed > 0) { @@ -866,20 +820,11 @@ function performBulkAssign() { return; } - fetch('/api/bulk_operation.php', { - method: 'POST', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-Token': window.CSRF_TOKEN - }, - body: JSON.stringify({ - operation_type: 'bulk_assign', - ticket_ids: ticketIds, - parameters: { assigned_to: parseInt(userId) } - }) + lt.api.post('/api/bulk_operation.php', { + operation_type: 'bulk_assign', + ticket_ids: ticketIds, + parameters: { assigned_to: parseInt(userId) } }) - .then(response => response.json()) .then(data => { if (data.success) { closeBulkAssignModal(); @@ -951,20 +896,11 @@ function performBulkPriority() { return; } - fetch('/api/bulk_operation.php', { - method: 'POST', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-Token': window.CSRF_TOKEN - }, - body: JSON.stringify({ - operation_type: 'bulk_priority', - ticket_ids: ticketIds, - parameters: { priority: parseInt(priority) } - }) + lt.api.post('/api/bulk_operation.php', { + operation_type: 'bulk_priority', + ticket_ids: ticketIds, + parameters: { priority: parseInt(priority) } }) - .then(response => response.json()) .then(data => { if (data.success) { closeBulkPriorityModal(); @@ -1067,20 +1003,11 @@ function performBulkStatusChange() { return; } - fetch('/api/bulk_operation.php', { - method: 'POST', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-Token': window.CSRF_TOKEN - }, - body: JSON.stringify({ - operation_type: 'bulk_status', - ticket_ids: ticketIds, - parameters: { status: status } - }) + lt.api.post('/api/bulk_operation.php', { + operation_type: 'bulk_status', + ticket_ids: ticketIds, + parameters: { status: status } }) - .then(response => response.json()) .then(data => { closeBulkStatusModal(); if (data.success) { @@ -1140,19 +1067,10 @@ function closeBulkDeleteModal() { function performBulkDelete() { const ticketIds = getSelectedTicketIds(); - fetch('/api/bulk_operation.php', { - method: 'POST', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-Token': window.CSRF_TOKEN - }, - body: JSON.stringify({ - operation_type: 'bulk_delete', - ticket_ids: ticketIds - }) + lt.api.post('/api/bulk_operation.php', { + operation_type: 'bulk_delete', + ticket_ids: ticketIds }) - .then(response => response.json()) .then(data => { closeBulkDeleteModal(); if (data.success) { @@ -1341,19 +1259,7 @@ function closeQuickStatusModal() { function performQuickStatusChange(ticketId) { const newStatus = document.getElementById('quickStatusSelect').value; - fetch('/api/update_ticket.php', { - method: 'POST', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-Token': window.CSRF_TOKEN - }, - body: JSON.stringify({ - ticket_id: ticketId, - status: newStatus - }) - }) - .then(response => response.json()) + lt.api.post('/api/update_ticket.php', { ticket_id: ticketId, status: newStatus }) .then(data => { closeQuickStatusModal(); if (data.success) { @@ -1423,19 +1329,7 @@ function closeQuickAssignModal() { function performQuickAssign(ticketId) { const assignedTo = document.getElementById('quickAssignSelect').value || null; - fetch('/api/assign_ticket.php', { - method: 'POST', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-Token': window.CSRF_TOKEN - }, - body: JSON.stringify({ - ticket_id: ticketId, - assigned_to: assignedTo - }) - }) - .then(response => response.json()) + lt.api.post('/api/assign_ticket.php', { ticket_id: ticketId, assigned_to: assignedTo }) .then(data => { closeQuickAssignModal(); if (data.success) { diff --git a/assets/js/ticket.js b/assets/js/ticket.js index 938137d..978ca57 100644 --- a/assets/js/ticket.js +++ b/assets/js/ticket.js @@ -48,28 +48,7 @@ function saveTicket() { } // Use the correct API path - const apiUrl = '/api/update_ticket.php'; - - fetch(apiUrl, { - method: 'POST', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-Token': window.CSRF_TOKEN - }, - body: JSON.stringify({ - ticket_id: ticketId, - ...data - }) - }) - .then(response => { - if (!response.ok) { - return response.text().then(text => { - throw new Error('Network response was not ok'); - }); - } - return response.json(); - }) + lt.api.post('/api/update_ticket.php', { ticket_id: ticketId, ...data }) .then(data => { if(data.success) { const statusDisplay = document.getElementById('statusDisplay'); @@ -150,26 +129,10 @@ function addComment() { const markdownMaster = document.getElementById('markdownMaster'); const isMarkdownEnabled = markdownMaster ? markdownMaster.checked : false; - fetch('/api/add_comment.php', { - method: 'POST', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-Token': window.CSRF_TOKEN - }, - body: JSON.stringify({ - ticket_id: ticketId, - comment_text: commentText, - markdown_enabled: isMarkdownEnabled - }) - }) - .then(response => { - if (!response.ok) { - return response.text().then(text => { - throw new Error('Network response was not ok'); - }); - } - return response.json(); + lt.api.post('/api/add_comment.php', { + ticket_id: ticketId, + comment_text: commentText, + markdown_enabled: isMarkdownEnabled }) .then(data => { if(data.success) { @@ -317,16 +280,7 @@ function handleAssignmentChange() { const ticketId = window.ticketData.id; const assignedTo = this.value || null; - fetch('/api/assign_ticket.php', { - method: 'POST', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-Token': window.CSRF_TOKEN - }, - body: JSON.stringify({ ticket_id: ticketId, assigned_to: assignedTo }) - }) - .then(response => response.json()) + lt.api.post('/api/assign_ticket.php', { ticket_id: ticketId, assigned_to: assignedTo }) .then(data => { if (!data.success) { lt.toast.error('Error updating assignment'); @@ -350,19 +304,10 @@ function handleMetadataChanges() { function updateTicketField(fieldName, newValue) { const ticketId = window.ticketData.id; - fetch('/api/update_ticket.php', { - method: 'POST', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-Token': window.CSRF_TOKEN - }, - body: JSON.stringify({ - ticket_id: ticketId, - [fieldName]: fieldName === 'priority' ? parseInt(newValue) : newValue - }) + lt.api.post('/api/update_ticket.php', { + ticket_id: ticketId, + [fieldName]: fieldName === 'priority' ? parseInt(newValue) : newValue }) - .then(response => response.json()) .then(data => { if (!data.success) { lt.toast.error(`Error updating ${fieldName}`); @@ -456,35 +401,7 @@ function performStatusChange(statusSelect, selectedOption, newStatus) { } // Update status via API - fetch('/api/update_ticket.php', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-Token': window.CSRF_TOKEN - }, - body: JSON.stringify({ - ticket_id: ticketId, - status: newStatus - }) - }) - .then(async response => { - const text = await response.text(); - - if (!response.ok) { - try { - const data = JSON.parse(text); - throw new Error(data.error || 'Server returned an error'); - } catch (parseError) { - throw new Error(text || 'Network response was not ok'); - } - } - - try { - return JSON.parse(text); - } catch (parseError) { - throw new Error('Invalid JSON response from server'); - } - }) + lt.api.post('/api/update_ticket.php', { ticket_id: ticketId, status: newStatus }) .then(data => { if (data.success) { // Update the dropdown to show new status as current @@ -575,15 +492,7 @@ function showTab(tabName) { function loadDependencies() { const ticketId = window.ticketData.id; - fetch(`/api/ticket_dependencies.php?ticket_id=${ticketId}`, { - credentials: 'same-origin' - }) - .then(response => { - if (!response.ok) { - throw new Error(`HTTP ${response.status}`); - } - return response.json(); - }) + lt.api.get(`/api/ticket_dependencies.php?ticket_id=${ticketId}`) .then(data => { if (data.success) { renderDependencies(data.dependencies); @@ -694,20 +603,11 @@ function addDependency() { return; } - fetch('/api/ticket_dependencies.php', { - method: 'POST', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-Token': window.CSRF_TOKEN - }, - body: JSON.stringify({ - ticket_id: ticketId, - depends_on_id: dependsOnId, - dependency_type: dependencyType - }) + lt.api.post('/api/ticket_dependencies.php', { + ticket_id: ticketId, + depends_on_id: dependsOnId, + dependency_type: dependencyType }) - .then(response => response.json()) .then(data => { if (data.success) { lt.toast.success('Dependency added', 3000); @@ -727,18 +627,7 @@ function removeDependency(dependencyId) { return; } - fetch('/api/ticket_dependencies.php', { - method: 'DELETE', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-Token': window.CSRF_TOKEN - }, - body: JSON.stringify({ - dependency_id: dependencyId - }) - }) - .then(response => response.json()) + lt.api.delete('/api/ticket_dependencies.php', { dependency_id: dependencyId }) .then(data => { if (data.success) { lt.toast.success('Dependency removed', 3000); @@ -895,10 +784,7 @@ function loadAttachments() { if (!container) return; - fetch(`/api/upload_attachment.php?ticket_id=${ticketId}`, { - credentials: 'same-origin' - }) - .then(response => response.json()) + lt.api.get(`/api/upload_attachment.php?ticket_id=${ticketId}`) .then(data => { if (data.success) { renderAttachments(data.attachments || []); @@ -973,19 +859,7 @@ function deleteAttachment(attachmentId) { return; } - fetch('/api/delete_attachment.php', { - method: 'POST', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-Token': window.CSRF_TOKEN - }, - body: JSON.stringify({ - attachment_id: attachmentId, - csrf_token: window.CSRF_TOKEN - }) - }) - .then(response => response.json()) + lt.api.post('/api/delete_attachment.php', { attachment_id: attachmentId }) .then(data => { if (data.success) { lt.toast.success('Attachment deleted', 3000); @@ -1312,20 +1186,11 @@ function saveEditComment(commentId) { const markdownEnabled = markdownCheckbox ? markdownCheckbox.checked : false; // Send update request - fetch('/api/update_comment.php', { - method: 'POST', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-Token': window.CSRF_TOKEN - }, - body: JSON.stringify({ - comment_id: commentId, - comment_text: newText, - markdown_enabled: markdownEnabled - }) + lt.api.post('/api/update_comment.php', { + comment_id: commentId, + comment_text: newText, + markdown_enabled: markdownEnabled }) - .then(response => response.json()) .then(data => { if (data.success) { // Update the comment display @@ -1396,18 +1261,7 @@ function deleteComment(commentId) { return; } - fetch('/api/delete_comment.php', { - method: 'POST', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-Token': window.CSRF_TOKEN - }, - body: JSON.stringify({ - comment_id: commentId - }) - }) - .then(response => response.json()) + lt.api.post('/api/delete_comment.php', { comment_id: commentId }) .then(data => { if (data.success) { // Remove the comment from the DOM @@ -1498,21 +1352,12 @@ function submitReply(parentCommentId) { const commentText = replyText.value.trim(); const isMarkdownEnabled = replyMarkdown ? replyMarkdown.checked : false; - fetch('/api/add_comment.php', { - method: 'POST', - credentials: 'same-origin', - headers: { - 'Content-Type': 'application/json', - 'X-CSRF-Token': window.CSRF_TOKEN - }, - body: JSON.stringify({ - ticket_id: ticketId, - comment_text: commentText, - markdown_enabled: isMarkdownEnabled, - parent_comment_id: parentCommentId - }) + lt.api.post('/api/add_comment.php', { + ticket_id: ticketId, + comment_text: commentText, + markdown_enabled: isMarkdownEnabled, + parent_comment_id: parentCommentId }) - .then(response => response.json()) .then(data => { if (data.success) { // Close the reply form