From 629f2515e9701065c99bdc8ff69d3c3f076966b7 Mon Sep 17 00:00:00 2001 From: Bashy Date: Thu, 4 Jun 2026 20:49:56 +0300 Subject: [PATCH] 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 --- views/social.php | 5 +++++ web/api/social.php | 10 ++++++++++ web/assets/js/app.js | 17 ++++++++++++++++- 3 files changed, 31 insertions(+), 1 deletion(-) diff --git a/views/social.php b/views/social.php index 642f97b..49002a9 100644 --- a/views/social.php +++ b/views/social.php @@ -12,8 +12,13 @@ include ROOT . '/views/_header.php'; + +

+
 
Loading…
diff --git a/web/api/social.php b/web/api/social.php index 0210a0a..1b08762 100644 --- a/web/api/social.php +++ b/web/api/social.php @@ -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']); diff --git a/web/assets/js/app.js b/web/assets/js/app.js index 59a6981..242151f 100644 --- a/web/assets/js/app.js +++ b/web/assets/js/app.js @@ -3994,7 +3994,7 @@ window.addEventListener('DOMContentLoaded', () => { return `
-
${esc(fmtDT(post.scheduled_at))}
+
${esc(fmtDT(post.scheduled_at))} (${relTime(post.scheduled_at)})
${STATUS_BADGE(post.status)}
@@ -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 = '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 = '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');