Add relative time to post cards; add Dispatch now button for manual testing

- View cards now show "2026-06-04 14:30 (in 2h)" next to the scheduled time
- New "Dispatch now" button runs dispatch-social.php immediately and shows
  output inline (useful for diagnosing why cron isn't sending posts)
- dispatch action added to /api/social

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Bashy 2026-06-04 20:49:56 +03:00
parent e52f90445b
commit 629f2515e9
3 changed files with 31 additions and 1 deletions

View file

@ -192,5 +192,15 @@ if ($action === 'retry') {
exit;
}
// ── DISPATCH NOW ──────────────────────────────────────────────────────────────
if ($action === 'dispatch') {
$script = ROOT . '/bin/dispatch-social.php';
$output = [];
$code = 0;
exec('/usr/bin/php ' . escapeshellarg($script) . ' 2>&1', $output, $code);
echo json_encode(['ok' => true, 'output' => implode("\n", $output), 'exit' => $code]);
exit;
}
http_response_code(400);
echo json_encode(['error' => 'Unknown action']);

View file

@ -3994,7 +3994,7 @@ window.addEventListener('DOMContentLoaded', () => {
return `<div class="sc-view-pane p-3 d-flex flex-column h-100">
<div class="d-flex align-items-start gap-2 mb-2">
<div class="flex-grow-1 min-w-0">
<div class="small text-muted">${esc(fmtDT(post.scheduled_at))}</div>
<div class="small text-muted">${esc(fmtDT(post.scheduled_at))} <span class="opacity-75">(${relTime(post.scheduled_at)})</span></div>
<div>${STATUS_BADGE(post.status)}</div>
</div>
<div class="d-flex align-items-center gap-1 flex-shrink-0">
@ -4308,6 +4308,21 @@ window.addEventListener('DOMContentLoaded', () => {
});
document.getElementById('socialRefreshBtn').addEventListener('click', loadPosts);
document.getElementById('socialDispatchBtn').addEventListener('click', async function () {
this.disabled = true;
this.innerHTML = '<i class="bi bi-hourglass-split me-1"></i>Running…';
const r = await fetch('/api/social', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ action: 'dispatch' }) });
const d = await r.json();
this.disabled = false;
this.innerHTML = '<i class="bi bi-send me-1"></i>Dispatch now';
const out = d.output?.trim();
const msg = out || 'No pending posts were due (or all sent silently).';
const box = document.getElementById('socialDispatchOut');
box.textContent = msg;
box.classList.remove('d-none');
loadPosts();
});
// ── Per-card error helpers ────────────────────────────────────────────────────
function showCardError(card, msg) {
const el = card.querySelector('.sc-error');