Files
Aissa 5d60d33db1 Platform bundle: marketing publish, IT ops, packaging, agents mesh.
Volledige Foodlinkk Command Center uitbreiding met social automatisering,
reclamefolder filters, Proxmox monitoring en documentatie.
2026-06-09 00:41:27 +00:00

78 lines
3.8 KiB
HTML

{% extends "base.html" %}
{% block content %}
<div x-data="monitorPage()">
<header class="page-header">
<h1>Site Monitor</h1>
<p class="subtitle page-tip">Websites volgen — alles in PostgreSQL. <a href="/browser">Browser research ↗</a></p>
</header>
<div class="kpi-grid">
<div class="kpi-card clickable-kpi"><div class="kpi-label">Active sites</div><div class="kpi-value">{{ stats.active_sites }}</div></div>
<div class="kpi-card amber clickable-kpi"><div class="kpi-label">Changes 24h</div><div class="kpi-value">{{ stats.changes_24h }}</div></div>
</div>
<section class="panel">
<h2>Website toevoegen</h2>
<form class="form-grid" style="display:flex;flex-wrap:wrap;gap:0.5rem;align-items:center" @submit.prevent="addSite">
<input class="form-input" style="flex:2;min-width:200px" placeholder="https://..." x-model="form.url" required />
<input class="form-input" style="flex:1;min-width:120px" placeholder="Naam" x-model="form.name" />
<button class="btn btn-primary" type="submit">Toevoegen</button>
<button type="button" class="btn btn-secondary" @click="crawlAll()">Alles crawlen</button>
</form>
</section>
<section class="panel">
<h2>Gemonitorde websites</h2>
<table class="data-table"><thead><tr><th>Naam</th><th>URL</th><th>Laatste titel</th><th>Laatst gecheckt</th><th>Acties</th></tr></thead>
<tbody>{% for s in sites %}
<tr>
<td>{{ s.name }}</td>
<td style="max-width:220px;overflow:hidden;text-overflow:ellipsis" title="{{ s.url }}">{{ s.url }}</td>
<td>{{ s.last_title or '—' }}</td>
<td>{{ s.last_crawled or '—' }}</td>
<td class="btn-group">
<button class="btn btn-sm" @click="crawlOne({{ s.id }})">Crawl</button>
<button class="btn btn-sm btn-secondary" @click="toggle({{ s.id }})">{{ 'Pauze' if s.is_active else 'Activeer' }}</button>
<button class="btn btn-sm btn-reject" @click="remove({{ s.id }}, false)">Verwijder</button>
</td></tr>{% else %}
<tr><td colspan="5" class="empty-state">Geen sites.</td></tr>
{% endfor %}</tbody></table>
<p class="page-tip" style="margin-top:0.75rem">Verwijder = permanent uit database. Pauze = tijdelijk uitgeschakeld.</p>
</section>
<div class="grid-2">
<section class="panel"><h2>Wijzigingen</h2>
<ul>{% for c in page_changes %}<li><strong>{{ c.name or c.url }}</strong> · {{ c.changed_at }}</li>{% else %}<li class="empty-state">Geen wijzigingen</li>{% endfor %}</ul>
</section>
<section class="panel"><h2>Crawl logs</h2>
<ul>{% for l in crawl_logs %}<li><span class="badge">{{ l.status }}</span> {{ l.message }} <small>{{ l.logged_at }}</small></li>{% endfor %}</ul>
</section>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
function monitorPage() {
return {
form: { url: '', name: '' },
async addSite() {
try {
await Cockpit.api('/monitor/sites', { method: 'POST', body: JSON.stringify(this.form) });
Cockpit.toast('Site toegevoegd', 'success'); location.reload();
} catch (e) { Cockpit.toast(e.message, 'error'); }
},
async crawlAll() {
await Cockpit.api('/monitor/trigger-crawl', { method: 'POST', body: '{}' });
Cockpit.toast('Crawl gestart', 'success'); setTimeout(() => location.reload(), 2000);
},
async crawlOne(id) {
await Cockpit.api('/monitor/trigger-crawl', { method: 'POST', body: JSON.stringify({ site_id: id }) });
Cockpit.toast('Crawl gestart', 'success'); setTimeout(() => location.reload(), 2000);
},
async toggle(id) { await Cockpit.api('/monitor/sites/' + id + '/toggle', { method: 'PATCH' }); location.reload(); },
async remove(id) {
if (!confirm('Website permanent verwijderen uit monitoring?')) return;
await Cockpit.api('/monitor/sites/' + id + '?hard=true', { method: 'DELETE' });
Cockpit.toast('Verwijderd', 'success'); location.reload();
}
};
}
</script>
{% endblock %}