Files
backPRO/templates/topics/index.php
Jacek Pyziak b653cea252 Add installer functionality for WordPress with FTP and database configuration
- Create SQL migration for prompt templates used in article and image generation.
- Add migration to change publish interval from days to hours in the sites table.
- Implement InstallerController to handle installation requests and validation.
- Develop FtpService for FTP connections and file uploads.
- Create InstallerService to manage the WordPress installation process, including downloading, extracting, and configuring WordPress.
- Add index view for the installer with form inputs for FTP, database, and WordPress admin settings.
- Implement progress tracking for the installation process with AJAX polling.
2026-02-16 21:55:24 +01:00

202 lines
11 KiB
PHP

<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h2>Tematy: <?= htmlspecialchars($site['name']) ?></h2>
<a href="/sites" class="text-muted small">&larr; Powrót do stron</a>
</div>
</div>
<div class="row g-4">
<div class="col-md-8">
<div class="card">
<div class="card-body p-0">
<table class="table table-hover mb-0">
<thead>
<tr>
<th>Temat</th>
<th>Kategoria</th>
<th>Kat. WP</th>
<th>Artykuły</th>
<th>Status</th>
<th>Akcje</th>
</tr>
</thead>
<tbody>
<?php if (empty($topics)): ?>
<tr><td colspan="6" class="text-center text-muted py-4">Brak tematów. Dodaj tematy z biblioteki lub utwórz własny.</td></tr>
<?php else: ?>
<?php
// Group topics by parent category
$grouped = [];
foreach ($topics as $topic) {
$group = $topic['global_category_name']
?? ($topic['global_topic_name'] && !$topic['global_category_name'] ? $topic['global_topic_name'] : null)
?? '__custom__';
$grouped[$group][] = $topic;
}
?>
<?php foreach ($grouped as $groupName => $groupTopics): ?>
<tr class="table-light">
<td colspan="6" class="py-1 px-3">
<strong class="small text-uppercase text-muted">
<?= $groupName === '__custom__' ? 'Własne tematy' : htmlspecialchars($groupName) ?>
</strong>
<span class="badge bg-secondary ms-1"><?= count($groupTopics) ?></span>
</td>
</tr>
<?php foreach ($groupTopics as $topic): ?>
<tr>
<td class="ps-4"><strong><?= htmlspecialchars($topic['name']) ?></strong></td>
<td class="small text-muted">
<?php if ($topic['global_category_name']): ?>
<?= htmlspecialchars($topic['global_category_name']) ?>
<?php elseif ($topic['global_topic_name']): ?>
<?= htmlspecialchars($topic['global_topic_name']) ?>
<?php else: ?>
<em>własny</em>
<?php endif; ?>
</td>
<td><?= $topic['wp_category_id'] ?: '-' ?></td>
<td><span class="badge bg-primary"><?= $topic['article_count'] ?></span></td>
<td>
<?php if ($topic['is_active']): ?>
<span class="badge bg-success">Aktywny</span>
<?php else: ?>
<span class="badge bg-secondary">Nieaktywny</span>
<?php endif; ?>
</td>
<td>
<div class="btn-group btn-group-sm">
<button class="btn btn-outline-primary btn-edit-topic"
data-id="<?= $topic['id'] ?>"
data-name="<?= htmlspecialchars($topic['name']) ?>"
data-description="<?= htmlspecialchars($topic['description'] ?? '') ?>"
data-wp-category="<?= $topic['wp_category_id'] ?>"
data-global-topic="<?= $topic['global_topic_id'] ?>"
data-active="<?= $topic['is_active'] ?>">
<i class="bi bi-pencil"></i>
</button>
<button class="btn btn-outline-danger btn-delete-topic" data-id="<?= $topic['id'] ?>" data-name="<?= htmlspecialchars($topic['name']) ?>">
<i class="bi bi-trash"></i>
</button>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</div>
</div>
</div>
<div class="col-md-4">
<div class="card">
<div class="card-header">
<h5 class="mb-0" id="topicFormTitle">Dodaj temat</h5>
</div>
<div class="card-body">
<form method="post" action="/sites/<?= $site['id'] ?>/topics" id="topicForm">
<div class="mb-3">
<label class="form-label">Wybierz z biblioteki</label>
<select class="form-select" id="topic_global_id" name="global_topic_id" onchange="fillFromLibrary(this)">
<option value="">-- własny temat --</option>
<?php foreach ($globalTopics as $cat): ?>
<optgroup label="<?= htmlspecialchars($cat['name']) ?>">
<?php foreach ($cat['children'] as $child): ?>
<option value="<?= $child['id'] ?>"
data-name="<?= htmlspecialchars($child['name']) ?>"
data-desc="<?= htmlspecialchars($child['description'] ?? '') ?>">
<?= htmlspecialchars($child['name']) ?>
</option>
<?php endforeach; ?>
</optgroup>
<?php endforeach; ?>
</select>
<div class="form-text">Wybierz temat z <a href="/global-topics">biblioteki</a> lub wpisz własny poniżej</div>
</div>
<div class="mb-3">
<label for="topic_name" class="form-label">Nazwa tematu</label>
<input type="text" class="form-control" id="topic_name" name="name" required placeholder="np. Ogrodnictwo">
</div>
<div class="mb-3">
<label for="topic_description" class="form-label">Opis / wytyczne dla AI</label>
<textarea class="form-control" id="topic_description" name="description" rows="3" placeholder="Opisz czego powinny dotyczyć artykuły..."></textarea>
<div class="form-text">Wskazówki dla AI, jak pisać artykuły z tego tematu</div>
</div>
<div class="mb-3">
<label for="topic_wp_category" class="form-label">ID kategorii WordPress</label>
<input type="number" class="form-control" id="topic_wp_category" name="wp_category_id" placeholder="np. 5">
<div class="form-text">
<a href="/sites/<?= $site['id'] ?>/categories">Sprawdź kategorie na stronie</a>
</div>
</div>
<div class="mb-3">
<div class="form-check">
<input type="checkbox" class="form-check-input" id="topic_is_active" name="is_active" value="1" checked>
<label class="form-check-label" for="topic_is_active">Aktywny</label>
</div>
</div>
<button type="submit" class="btn btn-primary w-100" id="topicFormSubmit">Dodaj temat</button>
</form>
</div>
</div>
</div>
</div>
<script>
function fillFromLibrary(select) {
var opt = select.options[select.selectedIndex];
if (opt.value) {
document.getElementById('topic_name').value = opt.dataset.name || '';
document.getElementById('topic_description').value = opt.dataset.desc || '';
}
}
document.querySelectorAll('.btn-delete-topic').forEach(function(btn) {
btn.addEventListener('click', function() {
var id = this.dataset.id;
var name = this.dataset.name;
if (!confirm('Na pewno usunąć temat "' + name + '"?')) return;
var row = this.closest('tr');
btn.disabled = true;
btn.innerHTML = '<span class="spinner-border spinner-border-sm"></span>';
fetch('/topics/' + id + '/delete', {
method: 'POST',
headers: { 'X-Requested-With': 'XMLHttpRequest' }
})
.then(function(r) { return r.json(); })
.then(function(data) {
if (data.success) {
row.style.transition = 'opacity .3s';
row.style.opacity = '0';
setTimeout(function() {
row.remove();
var tbody = document.querySelector('table tbody');
if (!tbody.querySelector('tr')) {
tbody.innerHTML = '<tr><td colspan="6" class="text-center text-muted py-4">Brak tematów. Dodaj tematy z biblioteki lub utwórz własny.</td></tr>';
}
}, 300);
} else {
alert(data.message || 'Błąd usuwania');
btn.disabled = false;
btn.innerHTML = '<i class="bi bi-trash"></i>';
}
})
.catch(function() {
alert('Błąd połączenia');
btn.disabled = false;
btn.innerHTML = '<i class="bi bi-trash"></i>';
});
});
});
</script>