render_meta_box_body_html( $order_id, $selected_template, $is_premium, $status_label, $nonce, $ajax_url, $order );
$this->render_meta_box_inline_script();
}
/**
* Renders the meta box main body HTML (template info + action buttons).
*
* @param int $order_id WooCommerce order ID.
* @param array|null $selected_template Template data array, or null if none assigned.
* @param bool $is_premium True if the current license is premium.
* @param string $status_label Human-readable order status label.
* @param string $nonce WordPress nonce for AJAX security.
* @param string $ajax_url Admin AJAX URL.
* @param object $order WooCommerce WC_Order instance.
*/
private function render_meta_box_body_html( int $order_id, ?array $selected_template, bool $is_premium, string $status_label, string $nonce, string $ajax_url, object $order ): void {
?>
render_meta_box_pdf_mail_buttons( $order_id, $selected_template, $is_premium, $nonce, $ajax_url, $order ); ?>
render_meta_box_mail_modal(); ?>
render_meta_box_queue_modal(); ?>
(function($) {
render_meta_box_js_utils();
$this->render_meta_box_js_action_handlers();
$this->render_meta_box_js_mail_handlers();
?>
})(jQuery);
render_meta_box_js_generate_pdf();
$this->render_meta_box_js_queue_functions();
}
/**
* Outputs the doGeneratePdf() JavaScript function that fetches and opens the PDF blob.
*/
private function render_meta_box_js_generate_pdf(): void {
?>
/** Fetch le PDF et l'ouvre en blob */
function doGeneratePdf(ajaxUrl, orderId, templateId, nonce, btn, orig, callback) {
_generateAbortController = new AbortController();
var fd = new FormData();
fd.append('action', 'pdfib_stream_pdf');
fd.append('order_id', orderId);
fd.append('template_id', templateId);
fd.append('nonce', nonce);
fetch(ajaxUrl, {
method: 'POST',
body: fd,
signal: _generateAbortController.signal
})
.then(function(res) {
var ct = res.headers.get('Content-Type') || '';
if (!res.ok || ct.indexOf('application/pdf') === -1) {
return res.text().then(function(t) {
throw new Error(t || 'Erreur ' + res.status);
});
}
return res.blob();
})
.then(function(blob) {
btn.prop('disabled', false).html(orig);
openPdfBlob(blob, orderId);
if (callback) callback(null);
})
.catch(function(err) {
if (err.name === 'AbortError') return; // Annulé par l'utilisateur
btn.prop('disabled', false).html(orig);
alert('Erreur génération PDF :\n' + err.message);
if (callback) callback(err);
});
}
function pollQueue(ajaxUrl, nonce, onReady) {
if (_queueCancelled) return;
var fd = new FormData();
fd.append('action', 'pdfib_pdf_queue_poll');
fd.append('nonce', nonce);
fetch(ajaxUrl, {
method: 'POST',
body: fd
})
.then(function(r) {
return r.json();
})
.then(function(data) {
if (_queueCancelled) return;
if (data.success) {
var pos = data.data.position;
var size = data.data.queue_size;
updateQueueModal(pos, size);
if (pos === 0) {
onReady();
} else {
_queuePollTimer = setTimeout(function() {
pollQueue(ajaxUrl, nonce, onReady);
}, 3000);
}
} else {
_queuePollTimer = setTimeout(function() {
pollQueue(ajaxUrl, nonce, onReady);
}, 3000);
}
})
.catch(function() {
if (!_queueCancelled) {
_queuePollTimer = setTimeout(function() {
pollQueue(ajaxUrl, nonce, onReady);
}, 5000);
}
});
}
function leaveQueue(ajaxUrl, nonce) {
var fd = new FormData();
fd.append('action', 'pdfib_pdf_queue_leave');
fd.append('nonce', nonce);
fetch(ajaxUrl, {
method: 'POST',
body: fd
});
}
function startCountdown(onDone) {
var remaining = 10;
$('#pdfb-queue-section').hide();
$('#pdfb-countdown-section').show();
$('#pdfb-countdown-num').text(remaining);
$('#pdfb-queue-status-text').text('');
_countdownTimer = setInterval(function() {
if (_queueCancelled) {
clearInterval(_countdownTimer);
_countdownTimer = null;
return;
}
remaining--;
$('#pdfb-countdown-num').text(remaining);
if (remaining <= 0) {
clearInterval(_countdownTimer);
_countdownTimer = null;
onDone();
}
}, 1000);
}
// Bouton Annuler du modal
$('#pdfb-queue-cancel').on('click', function() {
_queueCancelled = true;
if (_queueBtn) { _queueBtn.prop('disabled', false).html(_queueBtnOrig); }
if (_queueAjaxUrl) { leaveQueue(_queueAjaxUrl, _queueNonce); }
hideQueueModal();
});
$('.pdf-builder-action-btn').on('click', function() {
var btn = $(this);
var type = btn.data('action-type'); // pdf | png | jpg
var orderId = btn.data('order-id');
var templateId = btn.data('template-id');
var nonce = btn.data('nonce');
var ajaxUrl = btn.data('ajax');
var isPremium = btn.data('is-premium') === 1 || btn.data('is-premium') === '1';
var orig = btn.html();
_queueBtn = btn;
_queueBtnOrig = orig;
_queueAjaxUrl = ajaxUrl;
_queueNonce = nonce;
btn.prop('disabled', true).html('⏳');
if (type === 'pdf') {
if (isPremium) {
doGeneratePdf(ajaxUrl, orderId, templateId, nonce, btn, orig);
} else {
render_meta_box_js_free_queue_flow(); ?>
}
} else {
render_meta_box_js_image_handler(); ?>
}
});
var fd = new FormData();
fd.append('action', 'pdfib_pdf_queue_join');
fd.append('nonce', nonce);
fetch(ajaxUrl, {
method: 'POST',
body: fd
})
.then(function(r) {
return r.json();
})
.then(function(data) {
if (!data.success) {
btn.prop('disabled', false).html(orig);
alert('' + (data.data && data.data.message ? data.data.message : ''));
return;
}
var pos = data.data.position;
if (pos === 0) {
showQueueModal(0);
updateQueueModal(0, 1);
// Laisser 1 s pour que l'utilisateur voie sa position (#1) avant le compte Ă rebours
setTimeout(function() {
if (_queueCancelled) return;
startCountdown(function() {
doGeneratePdf(ajaxUrl, orderId, templateId, nonce, btn, orig, function() {
leaveQueue(ajaxUrl, nonce);
hideQueueModal();
});
});
}, 1000);
} else {
showQueueModal(pos);
pollQueue(ajaxUrl, nonce, function() {
startCountdown(function() {
doGeneratePdf(ajaxUrl, orderId, templateId, nonce, btn, orig, function() {
leaveQueue(ajaxUrl, nonce);
hideQueueModal();
});
});
});
}
})
.catch(function(err) {
btn.prop('disabled', false).html(orig);
alert('' + err.message);
});
var formData = new FormData();
formData.append('action', 'pdfib_generate_image');
formData.append('template_id', templateId);
formData.append('order_id', orderId);
formData.append('format', type);
formData.append('nonce', nonce);
fetch(ajaxUrl, {
method: 'POST',
body: formData
})
.then(function(response) {
if (!response.ok) {
return response.text().then(function(text) {
var message = text || ('Erreur ' + response.status);
try {
var err = JSON.parse(text);
if (err && err.data) {
message = err.data.details || err.data.message || err.message || message;
}
} catch (parseError) {
message = text.replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim() || message;
}
throw new Error(message);
});
}
return response.blob();
})
.then(function(blob) {
btn.prop('disabled', false).html(orig);
openImageInTab(blob, type, orderId);
})
.catch(function(err) {
btn.prop('disabled', false).html(orig);
alert('Erreur lors de la génération ' + type.toUpperCase() + '\n\n' + err.message);
});
// --- Modal Mail ---
var $modal = $('#pdf-builder-mail-modal');
var $mstatus = $('#pdf-builder-mail-status');
$('#pdf-builder-mail-btn').on('click', function() {
var btn = $(this);
var num = btn.data('order-number');
var email = btn.data('order-email');
$('#pdf-builder-mail-to').val(email);
$('#pdf-builder-mail-subject').val(' #' + num);
$('#pdf-builder-mail-message').val('' + num + '.\n\n');
$mstatus.hide();
$modal.fadeIn(200);
});
function closeModal() {
$modal.fadeOut(150);
}
$('#pdf-builder-mail-close, #pdf-builder-mail-cancel').on('click', closeModal);
$modal.on('click', function(e) {
if ($(e.target).is($modal)) closeModal();
});
$(document).on('keydown.pdfBuilderMail', function(e) {
if (e.key === 'Escape') closeModal();
});
render_meta_box_js_mail_send_handler(); ?>
$('#pdf-builder-mail-send').on('click', function() {
var sendBtn = $(this);
var mailBtn = $('#pdf-builder-mail-btn');
var to = $('#pdf-builder-mail-to').val().trim();
var subject = $('#pdf-builder-mail-subject').val().trim();
var message = $('#pdf-builder-mail-message').val().trim();
var orderId = mailBtn.data('order-id');
var templateId = mailBtn.data('template-id');
var nonce = mailBtn.data('nonce');
var ajaxUrl = mailBtn.data('ajax');
if (!to || !subject) {
$mstatus.css({
display: 'block',
background: '#f8d7da',
color: '#721c24',
border: '1px solid #f5c6cb'
})
.text('');
return;
}
$mstatus.css({
display: 'block',
background: '#d1ecf1',
color: '#0c5460',
border: '1px solid #bee5eb'
})
.text('⏳ ');
sendBtn.prop('disabled', true).text('⏳');
$.post(ajaxUrl, {
action: 'pdfib_send_order_email',
order_id: orderId,
template_id: templateId,
nonce: nonce,
to: to,
subject: subject,
message: message
}, function(response) {
sendBtn.prop('disabled', false).html('✉️ ');
if (response.success) {
$mstatus.css({
background: '#d4edda',
color: '#155724',
border: '1px solid #c3e6cb'
})
.text('âś… ' + (response.data.message || ''));
setTimeout(closeModal, 2500);
} else {
var msg = (response.data && response.data.message) ? response.data.message : '';
$mstatus.css({
background: '#f8d7da',
color: '#721c24',
border: '1px solid #f5c6cb'
}).text('❌ ' + msg);
}
}).fail(function() {
sendBtn.prop('disabled', false).html('✉️ ');
$mstatus.css({
display: 'block',
background: '#f8d7da',
color: '#721c24',
border: '1px solid #f5c6cb'
})
.text('❌ ');
});
});
// ----- File d'attente (utilisateurs gratuits) -----
var _queuePollTimer = null;
var _queueCancelled = false;
var _generateAbortController = null;
var _countdownTimer = null;
var _queueBtn = null;
var _queueBtnOrig = '';
var _queueAjaxUrl = '';
var _queueNonce = '';
function showQueueModal(position) {
_queueCancelled = false;
$('#pdfb-queue-section').show();
$('#pdfb-countdown-section').hide();
$('#pdfb-queue-pos-num').text(position + 1);
$('#pdfb-queue-progress-bar').css('width', '0%');
$('#pdfb-queue-status-text').text('');
$('#pdfb-queue-modal').fadeIn(200);
}
var updateQueueModal = function(position, queueSize) {
$('#pdfb-queue-pos-num').text(position + 1);
var pct = queueSize > 1 ? Math.max(5, Math.round((1 - position / queueSize) * 100)) : 80;
$('#pdfb-queue-progress-bar').css('width', pct + '%');
if (position === 0) {
$('#pdfb-queue-status-text').text('');
}
}
function hideQueueModal() {
$('#pdfb-queue-modal').fadeOut(150);
if (_queuePollTimer) {
clearTimeout(_queuePollTimer);
_queuePollTimer = null;
}
if (_countdownTimer) {
clearInterval(_countdownTimer);
_countdownTimer = null;
}
if (_generateAbortController) {
_generateAbortController.abort();
_generateAbortController = null;
}
}
render_meta_box_js_queue_poll_leave(); ?>