- Introduced a new WordPress theme "BackPRO News" with a lightweight magazine-style design. - Added columns for tracking retry attempts and timestamps for unpublished/generated articles in the articles table. - Included remote service metadata fields in the sites table for better management. - Created log files for image replacements, installer actions, OpenAI article generation, and publishing processes. - Implemented a dashboard template for site management, including permalink settings and theme installation options.
295 lines
12 KiB
JavaScript
295 lines
12 KiB
JavaScript
// BackPRO - Frontend Scripts
|
|
(function () {
|
|
'use strict';
|
|
|
|
var confirmQueue = Promise.resolve();
|
|
var confirmUi = null;
|
|
|
|
function getToastClass(type) {
|
|
if (type === 'success') return 'text-bg-success';
|
|
if (type === 'danger' || type === 'error') return 'text-bg-danger';
|
|
if (type === 'warning') return 'text-bg-warning';
|
|
return 'text-bg-primary';
|
|
}
|
|
|
|
function getToastTitle(type) {
|
|
if (type === 'success') return 'Sukces';
|
|
if (type === 'danger' || type === 'error') return 'Blad';
|
|
if (type === 'warning') return 'Uwaga';
|
|
return 'Informacja';
|
|
}
|
|
|
|
function ensureToastContainer() {
|
|
var container = document.getElementById('bpToastContainer');
|
|
if (container) return container;
|
|
|
|
container = document.createElement('div');
|
|
container.id = 'bpToastContainer';
|
|
container.className = 'toast-container position-fixed top-0 end-0 p-3 bp-toast-container';
|
|
document.body.appendChild(container);
|
|
return container;
|
|
}
|
|
|
|
function showToast(message, type, options) {
|
|
if (!message) return;
|
|
|
|
if (!window.bootstrap || !window.bootstrap.Toast) {
|
|
var fallbackContainer = ensureToastContainer();
|
|
var fallbackToast = document.createElement('div');
|
|
fallbackToast.className = 'bp-toast-fallback';
|
|
fallbackToast.textContent = message;
|
|
fallbackContainer.appendChild(fallbackToast);
|
|
setTimeout(function () {
|
|
fallbackToast.remove();
|
|
}, 4000);
|
|
return;
|
|
}
|
|
|
|
var opts = options || {};
|
|
var container = ensureToastContainer();
|
|
var toast = document.createElement('div');
|
|
var toastClass = getToastClass(type || 'info');
|
|
var title = opts.title || getToastTitle(type || 'info');
|
|
var delay = typeof opts.delay === 'number' ? opts.delay : 5000;
|
|
|
|
toast.className = 'toast border-0 shadow-sm bp-toast ' + toastClass;
|
|
toast.setAttribute('role', 'alert');
|
|
toast.setAttribute('aria-live', 'assertive');
|
|
toast.setAttribute('aria-atomic', 'true');
|
|
toast.innerHTML =
|
|
'<div class="d-flex">' +
|
|
'<div class="toast-body">' +
|
|
'<strong class="me-1">' + title + ':</strong> ' + message +
|
|
'</div>' +
|
|
'<button type="button" class="btn-close btn-close-white me-2 m-auto" data-bs-dismiss="toast" aria-label="Zamknij"></button>' +
|
|
'</div>';
|
|
|
|
container.appendChild(toast);
|
|
var bsToast = new bootstrap.Toast(toast, { delay: delay });
|
|
toast.addEventListener('hidden.bs.toast', function () {
|
|
toast.remove();
|
|
});
|
|
bsToast.show();
|
|
}
|
|
|
|
function ensureConfirmUi() {
|
|
if (confirmUi) return confirmUi;
|
|
|
|
var existing = document.getElementById('bpConfirmModal');
|
|
if (!existing) {
|
|
var modal = document.createElement('div');
|
|
modal.className = 'modal fade bp-confirm-modal';
|
|
modal.id = 'bpConfirmModal';
|
|
modal.tabIndex = -1;
|
|
modal.setAttribute('aria-hidden', 'true');
|
|
modal.innerHTML =
|
|
'<div class="modal-dialog modal-dialog-centered">' +
|
|
'<div class="modal-content">' +
|
|
'<div class="modal-header border-0 pb-1">' +
|
|
'<h5 class="modal-title" id="bpConfirmTitle">Potwierdzenie</h5>' +
|
|
'<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Zamknij"></button>' +
|
|
'</div>' +
|
|
'<div class="modal-body pt-2">' +
|
|
'<p class="mb-0" id="bpConfirmMessage"></p>' +
|
|
'</div>' +
|
|
'<div class="modal-footer border-0 pt-2">' +
|
|
'<button type="button" class="btn btn-outline-secondary" data-role="cancel">Anuluj</button>' +
|
|
'<button type="button" class="btn btn-danger" data-role="confirm">Potwierdz</button>' +
|
|
'</div>' +
|
|
'</div>' +
|
|
'</div>';
|
|
document.body.appendChild(modal);
|
|
existing = modal;
|
|
}
|
|
|
|
confirmUi = {
|
|
el: existing,
|
|
modal: window.bootstrap && window.bootstrap.Modal
|
|
? new bootstrap.Modal(existing, { backdrop: 'static', keyboard: false })
|
|
: null,
|
|
titleEl: existing.querySelector('#bpConfirmTitle'),
|
|
messageEl: existing.querySelector('#bpConfirmMessage'),
|
|
cancelBtn: existing.querySelector('[data-role="cancel"]'),
|
|
confirmBtn: existing.querySelector('[data-role="confirm"]')
|
|
};
|
|
return confirmUi;
|
|
}
|
|
|
|
function showConfirmDialog(message, options) {
|
|
var opts = options || {};
|
|
|
|
if (!window.bootstrap || !window.bootstrap.Modal) {
|
|
showToast('Brak komponentu potwierdzenia. Operacja zostala wstrzymana.', 'warning');
|
|
return Promise.resolve(false);
|
|
}
|
|
|
|
var ui = ensureConfirmUi();
|
|
ui.titleEl.textContent = opts.title || 'Potwierdzenie';
|
|
ui.messageEl.textContent = message || 'Czy na pewno?';
|
|
ui.cancelBtn.textContent = opts.cancelText || 'Anuluj';
|
|
ui.confirmBtn.textContent = opts.confirmText || 'Potwierdz';
|
|
ui.confirmBtn.className = 'btn ' + (opts.confirmClass || 'btn-danger');
|
|
|
|
return new Promise(function (resolve) {
|
|
var confirmed = false;
|
|
|
|
function cleanup() {
|
|
ui.confirmBtn.removeEventListener('click', onConfirm);
|
|
ui.cancelBtn.removeEventListener('click', onCancel);
|
|
ui.el.removeEventListener('hidden.bs.modal', onHidden);
|
|
}
|
|
|
|
function onConfirm() {
|
|
confirmed = true;
|
|
ui.modal.hide();
|
|
}
|
|
|
|
function onCancel() {
|
|
ui.modal.hide();
|
|
}
|
|
|
|
function onHidden() {
|
|
cleanup();
|
|
resolve(confirmed);
|
|
}
|
|
|
|
ui.confirmBtn.addEventListener('click', onConfirm);
|
|
ui.cancelBtn.addEventListener('click', onCancel);
|
|
ui.el.addEventListener('hidden.bs.modal', onHidden);
|
|
ui.modal.show();
|
|
});
|
|
}
|
|
|
|
function queueConfirm(message, options) {
|
|
var run = function () {
|
|
return showConfirmDialog(message, options);
|
|
};
|
|
var pending = confirmQueue.then(run, run);
|
|
confirmQueue = pending.catch(function () { return false; });
|
|
return pending;
|
|
}
|
|
|
|
function installConfirmForForms() {
|
|
document.addEventListener('submit', function (event) {
|
|
var form = event.target;
|
|
if (!(form instanceof HTMLFormElement)) return;
|
|
if (!form.matches('form[data-confirm]')) return;
|
|
|
|
if (form.dataset.confirmBypass === '1') {
|
|
form.dataset.confirmBypass = '';
|
|
return;
|
|
}
|
|
|
|
event.preventDefault();
|
|
queueConfirm(form.getAttribute('data-confirm'), {
|
|
title: form.dataset.confirmTitle || 'Potwierdzenie',
|
|
confirmText: form.dataset.confirmOk || 'Potwierdz',
|
|
cancelText: form.dataset.confirmCancel || 'Anuluj',
|
|
confirmClass: form.dataset.confirmClass || 'btn-danger'
|
|
}).then(function (ok) {
|
|
if (!ok) return;
|
|
form.dataset.confirmBypass = '1';
|
|
form.submit();
|
|
});
|
|
}, true);
|
|
}
|
|
|
|
function initTestConnectionButtons() {
|
|
document.querySelectorAll('.btn-test-connection').forEach(function (btn) {
|
|
btn.addEventListener('click', function () {
|
|
var siteId = this.dataset.siteId;
|
|
var button = this;
|
|
var originalHtml = button.innerHTML;
|
|
|
|
button.innerHTML = '<span class="spinner-border spinner-border-sm"></span>';
|
|
button.disabled = true;
|
|
|
|
fetch('/sites/' + siteId + '/test', { method: 'POST' })
|
|
.then(function (r) { return r.json(); })
|
|
.then(function (data) {
|
|
if (data.success) {
|
|
button.innerHTML = '<i class="bi bi-check-lg"></i>';
|
|
button.classList.remove('btn-outline-success');
|
|
button.classList.add('btn-success');
|
|
showToast(data.message || 'Polaczenie poprawne.', 'success', { delay: 3500 });
|
|
} else {
|
|
button.innerHTML = '<i class="bi bi-x-lg"></i>';
|
|
button.classList.remove('btn-outline-success');
|
|
button.classList.add('btn-danger');
|
|
showToast('Blad polaczenia: ' + (data.message || 'Nieznany blad'), 'danger');
|
|
}
|
|
})
|
|
.catch(function () {
|
|
button.innerHTML = '<i class="bi bi-x-lg"></i>';
|
|
button.classList.add('btn-danger');
|
|
showToast('Blad sieci podczas testu polaczenia.', 'danger');
|
|
})
|
|
.finally(function () {
|
|
button.disabled = false;
|
|
setTimeout(function () {
|
|
button.innerHTML = originalHtml;
|
|
button.className = button.className.replace('btn-success', 'btn-outline-success').replace('btn-danger', 'btn-outline-success');
|
|
}, 3000);
|
|
});
|
|
});
|
|
});
|
|
}
|
|
|
|
function initTopicEditButtons() {
|
|
document.querySelectorAll('.btn-edit-topic').forEach(function (btn) {
|
|
btn.addEventListener('click', function () {
|
|
var id = this.dataset.id;
|
|
var form = document.getElementById('topicForm');
|
|
var title = document.getElementById('topicFormTitle');
|
|
var submit = document.getElementById('topicFormSubmit');
|
|
|
|
if (!form || !title || !submit) return;
|
|
|
|
form.action = '/topics/' + id + '/update';
|
|
title.textContent = 'Edytuj temat';
|
|
submit.textContent = 'Zapisz zmiany';
|
|
|
|
document.getElementById('topic_name').value = this.dataset.name;
|
|
document.getElementById('topic_description').value = this.dataset.description;
|
|
document.getElementById('topic_wp_category').value = this.dataset.wpCategory || '';
|
|
document.getElementById('topic_is_active').checked = this.dataset.active === '1';
|
|
|
|
var globalSelect = document.getElementById('topic_global_id');
|
|
if (globalSelect) {
|
|
globalSelect.value = this.dataset.globalTopic || '';
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function highlightActiveSidebarLink() {
|
|
var currentPath = window.location.pathname;
|
|
document.querySelectorAll('.sidebar .nav-link').forEach(function (link) {
|
|
var href = link.getAttribute('href');
|
|
if (currentPath === href || (href !== '/' && currentPath.startsWith(href))) {
|
|
link.classList.add('active');
|
|
}
|
|
});
|
|
}
|
|
|
|
window.BackProUI = {
|
|
toast: showToast,
|
|
confirm: queueConfirm
|
|
};
|
|
|
|
window.backproNotify = function (message, type, options) {
|
|
showToast(message, type || 'info', options);
|
|
};
|
|
|
|
window.backproConfirm = function (message, options) {
|
|
return queueConfirm(message, options);
|
|
};
|
|
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
installConfirmForForms();
|
|
initTestConnectionButtons();
|
|
initTopicEditButtons();
|
|
highlightActiveSidebarLink();
|
|
});
|
|
})();
|