Files
Jacek Pyziak 4d5e220b3c Add BackPRO News theme and update database schema for article tracking
- 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.
2026-02-17 20:08:02 +01:00

232 lines
12 KiB
PHP

<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Biblioteka tematów</h2>
<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addCategoryModal">
<i class="bi bi-plus-lg me-1"></i>Dodaj kategorię
</button>
</div>
<?php if (empty($categories)): ?>
<div class="alert alert-info">Brak tematów. Dodaj pierwszą kategorię.</div>
<?php endif; ?>
<div class="accordion" id="topicsAccordion">
<?php foreach ($categories as $cat): ?>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#cat-<?= $cat['id'] ?>">
<strong><?= htmlspecialchars($cat['name']) ?></strong>
<span class="badge bg-secondary ms-2"><?= count($cat['children']) ?></span>
<?php if ($cat['description']): ?>
<small class="text-muted ms-3"><?= htmlspecialchars(mb_strimwidth($cat['description'], 0, 60, '...')) ?></small>
<?php endif; ?>
</button>
</h2>
<div id="cat-<?= $cat['id'] ?>" class="accordion-collapse collapse" data-bs-parent="#topicsAccordion">
<div class="accordion-body p-0">
<div class="p-3 bg-light border-bottom d-flex justify-content-between align-items-center">
<div class="d-flex gap-2">
<button class="btn btn-sm btn-outline-primary btn-edit-global"
data-id="<?= $cat['id'] ?>"
data-name="<?= htmlspecialchars($cat['name']) ?>"
data-description="<?= htmlspecialchars($cat['description'] ?? '') ?>">
<i class="bi bi-pencil me-1"></i>Edytuj kategorię
</button>
<button class="btn btn-sm btn-outline-danger btn-delete-global" data-id="<?= $cat['id'] ?>" data-type="category">
<i class="bi bi-trash me-1"></i>Usuń
</button>
</div>
<button class="btn btn-sm btn-success btn-add-subtopic" data-parent-id="<?= $cat['id'] ?>" data-parent-name="<?= htmlspecialchars($cat['name']) ?>">
<i class="bi bi-plus-lg me-1"></i>Dodaj temat
</button>
</div>
<?php if (!empty($cat['children'])): ?>
<table class="table table-hover mb-0">
<thead>
<tr>
<th>Temat</th>
<th>Opis</th>
<th style="width:120px">Akcje</th>
</tr>
</thead>
<tbody>
<?php foreach ($cat['children'] as $child): ?>
<tr>
<td><?= htmlspecialchars($child['name']) ?></td>
<td class="text-muted small"><?= htmlspecialchars(mb_strimwidth($child['description'] ?? '', 0, 80, '...')) ?></td>
<td>
<div class="btn-group btn-group-sm">
<button class="btn btn-outline-primary btn-edit-global"
data-id="<?= $child['id'] ?>"
data-name="<?= htmlspecialchars($child['name']) ?>"
data-description="<?= htmlspecialchars($child['description'] ?? '') ?>">
<i class="bi bi-pencil"></i>
</button>
<button class="btn btn-outline-danger btn-delete-global" data-id="<?= $child['id'] ?>" data-type="topic">
<i class="bi bi-trash"></i>
</button>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php else: ?>
<div class="p-3 text-muted text-center">Brak tematów w tej kategorii</div>
<?php endif; ?>
</div>
</div>
</div>
<?php endforeach; ?>
</div>
<!-- Modal: Dodaj kategorię -->
<div class="modal fade" id="addCategoryModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<form method="post" action="/global-topics/categories">
<div class="modal-header">
<h5 class="modal-title">Dodaj kategorię</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">Nazwa kategorii</label>
<input type="text" class="form-control" name="name" required placeholder="np. Sport">
</div>
<div class="mb-3">
<label class="form-label">Opis</label>
<textarea class="form-control" name="description" rows="2" placeholder="Krótki opis kategorii..."></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Anuluj</button>
<button type="submit" class="btn btn-primary">Dodaj</button>
</div>
</form>
</div>
</div>
</div>
<!-- Modal: Dodaj temat (subtopic) -->
<div class="modal fade" id="addSubtopicModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<form method="post" id="subtopicForm" action="">
<div class="modal-header">
<h5 class="modal-title">Dodaj temat do: <span id="subtopicParentName"></span></h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">Nazwa tematu</label>
<input type="text" class="form-control" name="name" required placeholder="np. Piłka nożna">
</div>
<div class="mb-3">
<label class="form-label">Opis / wytyczne dla AI</label>
<textarea class="form-control" name="description" rows="3" placeholder="Opisz zakres tematyczny, styl pisania..."></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Anuluj</button>
<button type="submit" class="btn btn-success">Dodaj temat</button>
</div>
</form>
</div>
</div>
</div>
<!-- Modal: Edytuj -->
<div class="modal fade" id="editGlobalModal" tabindex="-1">
<div class="modal-dialog">
<div class="modal-content">
<form method="post" id="editGlobalForm" action="">
<div class="modal-header">
<h5 class="modal-title">Edytuj</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<label class="form-label">Nazwa</label>
<input type="text" class="form-control" name="name" id="editGlobalName" required>
</div>
<div class="mb-3">
<label class="form-label">Opis</label>
<textarea class="form-control" name="description" id="editGlobalDesc" rows="3"></textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-outline-secondary" data-bs-dismiss="modal">Anuluj</button>
<button type="submit" class="btn btn-primary">Zapisz</button>
</div>
</form>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('.btn-add-subtopic').forEach(function (btn) {
btn.addEventListener('click', function () {
document.getElementById('subtopicForm').action = '/global-topics/' + this.dataset.parentId + '/subtopics';
document.getElementById('subtopicParentName').textContent = this.dataset.parentName;
new bootstrap.Modal(document.getElementById('addSubtopicModal')).show();
});
});
document.querySelectorAll('.btn-edit-global').forEach(function (btn) {
btn.addEventListener('click', function () {
document.getElementById('editGlobalForm').action = '/global-topics/' + this.dataset.id + '/update';
document.getElementById('editGlobalName').value = this.dataset.name;
document.getElementById('editGlobalDesc').value = this.dataset.description;
new bootstrap.Modal(document.getElementById('editGlobalModal')).show();
});
});
document.querySelectorAll('.btn-delete-global').forEach(function (btn) {
btn.addEventListener('click', async function () {
var id = this.dataset.id;
var isCategory = this.dataset.type === 'category';
var msg = isCategory ? 'Usunac kategorie i wszystkie jej tematy?' : 'Usunac ten temat?';
var ok = await backproConfirm(msg, {
title: 'Potwierdzenie usuniecia',
confirmText: 'Usun',
cancelText: 'Anuluj',
confirmClass: 'btn-danger'
});
if (!ok) return;
var el = isCategory ? this.closest('.accordion-item') : this.closest('tr');
var origHtml = btn.innerHTML;
btn.disabled = true;
btn.innerHTML = '<span class="spinner-border spinner-border-sm"></span>';
fetch('/global-topics/' + id + '/delete', {
method: 'POST',
headers: { 'X-Requested-With': 'XMLHttpRequest' }
})
.then(function (r) { return r.json(); })
.then(function (data) {
if (data.success) {
el.style.transition = 'opacity .3s';
el.style.opacity = '0';
setTimeout(function () {
el.remove();
}, 300);
backproNotify('Element zostal usuniety.', 'success', { delay: 2500 });
} else {
backproNotify(data.message || 'Blad usuwania', 'danger');
btn.disabled = false;
btn.innerHTML = origHtml;
}
})
.catch(function () {
backproNotify('Blad polaczenia', 'danger');
btn.disabled = false;
btn.innerHTML = origHtml;
});
});
});
});
</script>