Raiju/admin/dashboard/scripts/page-actions.php
2026-10-25 06:05:42 +03:00

340 lines
13 KiB
PHP

<?php if (!defined('RAIJU_DASHBOARD')) { http_response_code(404); exit; } ?>
// Save content
async function saveContent() {
collectAllContent();
currentContent.page = currentPage;
currentContent.lang = document.getElementById('langSelect').value;
try {
const formData = new FormData();
formData.append('action', 'save');
formData.append('page', currentPage);
formData.append('content', JSON.stringify(currentContent));
formData.append('csrf_token', CSRF_TOKEN);
const response = await fetch('api.php', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.success) {
showToast(t('toast.saved'), 'success');
isDirty = false;
currentContent.lastModified = result.data.lastModified;
if (result.data.seoHealth) {
const lang = document.getElementById('langSelect').value;
const page = document.getElementById('pageSelect').value;
currentSeoHealth = result.data.seoHealth;
setPageSeoHealth(lang, page, result.data.seoHealth);
updateEditorSeoHealth(result.data.seoHealth);
}
document.getElementById('lastModified').textContent =
t('editor.last_saved', {date: formatDateShort(result.data.lastModified)});
loadBackups();
} else {
showToast(result.message, 'error');
}
} catch (error) {
showToast(t('toast.error_saving', {message: error.message}), 'error');
}
}
// Load backups
async function loadBackups() {
try {
const response = await fetch(`api.php?action=backups&page=${currentPage}`);
const result = await response.json();
if (result.success) {
renderBackups(result.data);
}
} catch (error) {
console.error('Error loading backups:', error);
}
}
function renderBackups(backups) {
const container = document.getElementById('backupList');
if (backups.length === 0) {
container.innerHTML = '<p style="color: #666;">' + t('backups.no_backups') + '</p>';
return;
}
container.innerHTML = backups.map(backup => `
<div class="backup-item">
<div class="backup-info">
<span class="backup-date">${backup.date}</span>
<span class="backup-time">${backup.time}</span>
</div>
<div class="backup-actions">
<button class="btn btn-sm btn-secondary" onclick="previewBackup('${backup.filename}')">${t('backups.view')}</button>
<button class="btn btn-sm btn-primary" onclick="restoreBackup('${backup.filename}')">${t('backups.restore')}</button>
<button class="btn btn-sm btn-danger" onclick="deleteBackup('${backup.filename}')">${t('backups.delete')}</button>
</div>
</div>
`).join('');
}
async function previewBackup(filename) {
try {
const response = await fetch(`api.php?action=preview-backup&backup=${filename}`);
const result = await response.json();
if (result.success) {
showModal(
t('backups.view'),
renderBackupPreview(result.data),
closeModal,
{
html: true,
modalClass: 'modal-backup-preview',
confirmText: t('btn.close'),
confirmClass: 'btn btn-primary'
}
);
} else {
showToast(result.message, 'error');
}
} catch (error) {
showToast(t('toast.error_generic', {message: error.message}), 'error');
}
}
function flattenBackupPreviewFields(value, prefix, rows) {
if (Array.isArray(value)) {
if (value.length === 0) {
rows.push({ name: prefix || '[]', value: '[]' });
return;
}
var allScalar = value.every(function(item) {
return item === null || typeof item !== 'object';
});
if (allScalar) {
rows.push({
name: prefix || '[]',
value: value.map(formatBackupPreviewValue).join(', ')
});
return;
}
value.forEach(function(item, index) {
flattenBackupPreviewFields(item, (prefix ? prefix : '') + '[' + index + ']', rows);
});
return;
}
if (value && typeof value === 'object') {
var keys = Object.keys(value);
if (keys.length === 0) {
rows.push({ name: prefix || '{}', value: '{}' });
return;
}
keys.forEach(function(key) {
flattenBackupPreviewFields(value[key], prefix ? prefix + '.' + key : key, rows);
});
return;
}
rows.push({ name: prefix || 'value', value: formatBackupPreviewValue(value) });
}
function formatBackupPreviewValue(value) {
if (value === null) return 'null';
if (value === undefined) return '';
if (typeof value === 'boolean') return value ? 'true' : 'false';
if (typeof value === 'number') return String(value);
var text = String(value);
return text === '' ? '—' : text;
}
function renderBackupPreview(data) {
var rows = [];
flattenBackupPreviewFields(data, '', rows);
if (!rows.length) {
return '<div class="backup-preview-modal"><p class="backup-preview-empty">Keine Inhalte im Backup gefunden.</p></div>';
}
return '<div class="backup-preview-modal">' + rows.map(function(row) {
return '<article class="backup-preview-field">' +
'<div class="backup-preview-field__name" title="' + escapeHtml(row.name) + '">' + escapeHtml(row.name) + '</div>' +
'<div class="backup-preview-field__value">' + escapeHtml(row.value) + '</div>' +
'</article>';
}).join('') + '</div>';
}
function restoreBackup(filename) {
showModal(t('modal.restore_backup'),
t('modal.restore_backup_confirm'),
async () => {
try {
const formData = new FormData();
formData.append('action', 'restore');
formData.append('backup', filename);
formData.append('csrf_token', CSRF_TOKEN);
const response = await fetch('api.php', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.success) {
showToast(t('toast.backup_restored'), 'success');
closeModal();
loadContent();
} else {
showToast(result.message, 'error');
}
} catch (error) {
showToast(t('toast.error_generic', {message: error.message}), 'error');
}
}
);
}
function deleteBackup(filename) {
showModal(t('modal.delete_backup'),
t('modal.delete_backup_confirm'),
async () => {
try {
const formData = new FormData();
formData.append('action', 'delete-backup');
formData.append('backup', filename);
formData.append('csrf_token', CSRF_TOKEN);
const response = await fetch('api.php', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.success) {
showToast(t('toast.backup_deleted'), 'success');
closeModal();
loadBackups();
} else {
showToast(result.message, 'error');
}
} catch (error) {
showToast(t('toast.error_generic', {message: error.message}), 'error');
}
}
);
}
// Modal
function closeAllComboboxes() {
document.querySelectorAll('.rj-combobox__list:not([hidden])').forEach(function(list) {
list.hidden = true;
var root = list.closest('.rj-combobox');
if (root) {
var input = root.querySelector('input[role="combobox"]');
if (input) input.setAttribute('aria-expanded', 'false');
}
});
}
function showModal(title, text, onConfirm, options) {
options = options || {};
closeAllComboboxes();
var modalEl = document.querySelector('#modalOverlay .modal');
if (modalEl) {
modalEl.className = 'modal' + (options.modalClass ? ' ' + options.modalClass : '');
}
document.getElementById('modalTitle').textContent = title;
var modalText = document.getElementById('modalText');
if (options.html) {
modalText.innerHTML = text;
} else {
modalText.textContent = text;
}
document.getElementById('modalOverlay').style.display = 'flex';
document.getElementById('modalOverlay').setAttribute('aria-hidden', 'false');
var confirmBtn = document.getElementById('modalConfirm');
confirmBtn.onclick = onConfirm || closeModal;
if (options.confirmText) confirmBtn.textContent = options.confirmText;
if (options.confirmClass) confirmBtn.className = options.confirmClass;
if (options.hideConfirm) confirmBtn.style.display = 'none';
setTimeout(() => {
var focusTarget = options.focusSelector ? document.querySelector(options.focusSelector) : confirmBtn;
(focusTarget || confirmBtn).focus();
}, 0);
}
function closeModal() {
const overlay = document.getElementById('modalOverlay');
overlay.style.display = 'none';
overlay.setAttribute('aria-hidden', 'true');
const modalEl = overlay.querySelector('.modal');
if (modalEl) modalEl.className = 'modal';
// Reset confirm button to default state
const btn = document.getElementById('modalConfirm');
btn.textContent = t('btn.confirm');
btn.className = 'btn btn-danger';
btn.disabled = false;
btn.style.display = '';
}
// Format date without seconds
function formatDateShort(dateStr) {
const d = new Date(dateStr);
return rjFormatDate(d, 'datetime');
}
// Toast
function showToast(message, type = 'success') {
const existing = document.querySelector('.toast');
if (existing) existing.remove();
const toast = document.createElement('div');
toast.className = `toast ${type}`;
toast.setAttribute('role', type === 'error' ? 'alert' : 'status');
toast.setAttribute('aria-live', type === 'error' ? 'assertive' : 'polite');
toast.setAttribute('aria-atomic', 'true');
toast.textContent = message;
document.body.appendChild(toast);
const duration = type === 'error' ? 6000 : 4000;
setTimeout(() => {
toast.classList.add('toast-fade-out');
toast.addEventListener('animationend', () => toast.remove());
}, duration);
}
// HTML escape
function escapeHtml(str) {
if (!str) return '';
const div = document.createElement('div');
div.textContent = str;
return div.innerHTML;
}
// Init
document.getElementById('langSelect').addEventListener('change', function() {
onTopbarLanguageChanged();
const m = document.getElementById('langSelectMobile');
if (m) m.value = this.value;
});
const langMobile = document.getElementById('langSelectMobile');
if (langMobile) {
langMobile.addEventListener('change', function() {
document.getElementById('langSelect').value = this.value;
onTopbarLanguageChanged();
});
}
const pageMobile = document.getElementById('pageSelectMobile');
if (pageMobile) {
pageMobile.addEventListener('change', function() {
document.getElementById('pageSelect').value = this.value;
});
}
document.getElementById('pageSelect').addEventListener('change', function() {
if (pageMobile) pageMobile.value = this.value;
});
const DASHBOARD_PATH = window.location.pathname.replace(/\/dashboard\.php$/, '/dashboard');