| 1 |
/* global jQuery, ajaxurl */ |
| 2 |
/** |
| 3 |
* MetaSync report issue form. |
| 4 |
* |
| 5 |
* Extracted from views/metasync-report-issue.php (Phase 5, #887). |
| 6 |
*/ |
| 7 |
jQuery(document).ready(function($) { |
| 8 |
const CHAR_LIMIT = 1000; |
| 9 |
const $messageField = $('#metasync_issue_message'); |
| 10 |
const $charCounter = $('#metasync_char_counter'); |
| 11 |
|
| 12 |
// Update character counter and show feedback |
| 13 |
function updateCharCounter() { |
| 14 |
const len = $messageField.val() ? $messageField.val().length : 0; |
| 15 |
$charCounter.text(len + '/' + CHAR_LIMIT); |
| 16 |
$charCounter.removeClass('metasync-char-counter-warning metasync-char-counter-limit'); |
| 17 |
if (len >= CHAR_LIMIT) { |
| 18 |
$charCounter.addClass('metasync-char-counter-limit'); |
| 19 |
} else if (len >= CHAR_LIMIT - 50) { |
| 20 |
$charCounter.addClass('metasync-char-counter-warning'); |
| 21 |
} |
| 22 |
} |
| 23 |
|
| 24 |
$messageField.on('input keyup paste', updateCharCounter); |
| 25 |
updateCharCounter(); // Initial state |
| 26 |
|
| 27 |
$('#metasync-report-issue-form').on('submit', function(e) { |
| 28 |
e.preventDefault(); |
| 29 |
|
| 30 |
const $form = $(this); |
| 31 |
const $submitBtn = $('#metasync-submit-report-btn'); |
| 32 |
const $btnText = $submitBtn.find('.metasync-btn-text'); |
| 33 |
const $btnLoading = $submitBtn.find('.metasync-btn-loading'); |
| 34 |
const $responseMsg = $('#metasync-report-response-message'); |
| 35 |
const $messageField = $('#metasync_issue_message'); |
| 36 |
|
| 37 |
// Validate message length |
| 38 |
const message = $messageField.val() ? $messageField.val().trim() : ''; |
| 39 |
if (message.length < 10) { |
| 40 |
$responseMsg |
| 41 |
.removeClass('success') |
| 42 |
.addClass('error') |
| 43 |
.html('❌ Please provide a more detailed description (at least 10 characters).') |
| 44 |
.css('display', 'block'); |
| 45 |
return; |
| 46 |
} |
| 47 |
|
| 48 |
// Validate message length (max 1000 characters) |
| 49 |
if (message.length > 1000) { |
| 50 |
$responseMsg |
| 51 |
.removeClass('success') |
| 52 |
.addClass('error') |
| 53 |
.html('❌ Message is too long. Please limit to 1000 characters.') |
| 54 |
.css('display', 'block'); |
| 55 |
return; |
| 56 |
} |
| 57 |
|
| 58 |
// Validate file size (max 5MB) |
| 59 |
const $fileInput = $('#metasync_issue_attachment')[0]; |
| 60 |
if ($fileInput && $fileInput.files && $fileInput.files[0]) { |
| 61 |
const fileSize = $fileInput.files[0].size; |
| 62 |
const maxSize = 5 * 1024 * 1024; // 5MB |
| 63 |
if (fileSize > maxSize) { |
| 64 |
$responseMsg |
| 65 |
.removeClass('success') |
| 66 |
.addClass('error') |
| 67 |
.html('❌ File size exceeds 5MB. Please choose a smaller file.') |
| 68 |
.css('display', 'block'); |
| 69 |
return; |
| 70 |
} |
| 71 |
} |
| 72 |
|
| 73 |
// Disable submit button and show loading |
| 74 |
$submitBtn.prop('disabled', true); |
| 75 |
$btnText.css('display', 'none'); |
| 76 |
$btnLoading.css('display', 'flex'); |
| 77 |
$responseMsg.css('display', 'none'); |
| 78 |
|
| 79 |
// Prepare form data using FormData to support file uploads |
| 80 |
const formData = new FormData(); |
| 81 |
formData.append('action', 'metasync_submit_issue_report'); |
| 82 |
formData.append('nonce', $form.find('#metasync_report_issue_nonce').val()); |
| 83 |
formData.append('issue_message', message); |
| 84 |
formData.append('issue_severity', $('#metasync_issue_severity').val()); |
| 85 |
formData.append('include_user_info', $('#metasync_include_user_info').is(':checked')); |
| 86 |
|
| 87 |
// Add file if selected |
| 88 |
if ($fileInput && $fileInput.files && $fileInput.files[0]) { |
| 89 |
formData.append('issue_attachment', $fileInput.files[0]); |
| 90 |
} |
| 91 |
|
| 92 |
// Submit via AJAX |
| 93 |
$.ajax({ |
| 94 |
url: ajaxurl, |
| 95 |
type: 'POST', |
| 96 |
data: formData, |
| 97 |
dataType: 'json', |
| 98 |
processData: false, |
| 99 |
contentType: false, |
| 100 |
success: function(response) { |
| 101 |
// Ensure response is valid |
| 102 |
if (!response || typeof response !== 'object') { |
| 103 |
$responseMsg |
| 104 |
.removeClass('success') |
| 105 |
.addClass('error') |
| 106 |
.html('❌ Invalid response from server. Please try again.') |
| 107 |
.css('display', 'block'); |
| 108 |
return; |
| 109 |
} |
| 110 |
|
| 111 |
if (response.success) { |
| 112 |
const message = (response.data && response.data.message) ? response.data.message : 'Report submitted successfully!'; |
| 113 |
$responseMsg |
| 114 |
.removeClass('error') |
| 115 |
.addClass('success') |
| 116 |
.text('� |
| 117 |
' + message) // SECURITY: Use .text() to prevent XSS |
| 118 |
.css('display', 'block'); |
| 119 |
|
| 120 |
// Reset form |
| 121 |
$form[0].reset(); |
| 122 |
updateCharCounter(); |
| 123 |
|
| 124 |
// Scroll to success message |
| 125 |
if ($responseMsg.offset()) { |
| 126 |
$('html, body').animate({ |
| 127 |
scrollTop: $responseMsg.offset().top - 100 |
| 128 |
}, 500); |
| 129 |
} |
| 130 |
} else { |
| 131 |
const errorMessage = (response.data && response.data.message) ? response.data.message : 'Failed to submit report. Please try again.'; |
| 132 |
$responseMsg |
| 133 |
.removeClass('success') |
| 134 |
.addClass('error') |
| 135 |
.text('❌ ' + errorMessage) // SECURITY: Use .text() to prevent XSS |
| 136 |
.css('display', 'block'); |
| 137 |
} |
| 138 |
}, |
| 139 |
error: function(xhr, status, error) { |
| 140 |
$responseMsg |
| 141 |
.removeClass('success') |
| 142 |
.addClass('error') |
| 143 |
.html('❌ An error occurred while submitting your report. Please try again later.') |
| 144 |
.css('display', 'block'); |
| 145 |
|
| 146 |
// Log error only in debug mode |
| 147 |
if (typeof console !== 'undefined' && console.error && window.metasyncDebug) { |
| 148 |
console.error('MetaSync report submission error:', error); |
| 149 |
} |
| 150 |
}, |
| 151 |
complete: function() { |
| 152 |
// Re-enable submit button |
| 153 |
$submitBtn.prop('disabled', false); |
| 154 |
$btnText.css('display', 'inline'); |
| 155 |
$btnLoading.css('display', 'none'); |
| 156 |
} |
| 157 |
}); |
| 158 |
}); |
| 159 |
}); |
| 160 |
|