feat(tasks): refresh button in cron panel + auto-refresh on job creation (closes #835)
* feat(tasks): refresh button in cron panel + hermes:cron_created event Add a ↺ refresh button to the Scheduled Jobs header so the job list can be reloaded without a full page refresh. Closes #835. - static/index.html: ↺ button with cronRefreshBtn id, calls loadCrons(true) - static/panels.js: loadCrons(animate) dims+disables the button while fetching, restores it in finally; hermes:cron_created window event auto-refreshes list when the agent creates a job from chat * test: add regression tests for cron refresh button + event listener The PR shipped without automated coverage (pure UI wiring). Filling that gap with 8 source-level tests: - Refresh button element exists with aria-label + title (icon-only a11y) - Button wires onclick to loadCrons(true) for the dim animation - Button sits in the same header row as "New job" - loadCrons() now accepts an animate parameter - loadCrons() restores the button's opacity/disabled in finally (so a throwing fetch doesn't leave the button stuck) - hermes:cron_created window listener is registered at module scope - Listener calls loadCrons() when dispatched Also rebased onto master (CHANGELOG conflict resolved — v0.50.143 → v0.50.142 since master's top is currently v0.50.141). Full suite: 1750 passed, 0 new failures. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: nesquena-hermes <nesquena-hermes@users.noreply.github.com> Co-authored-by: Nathan Esquenazi <nesquena@gmail.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -48,7 +48,10 @@
|
||||
<div class="panel-view" id="panelTasks">
|
||||
<div class="sidebar-section" style="padding-bottom:4px;display:flex;align-items:center;justify-content:space-between">
|
||||
<div style="font-size:11px;color:var(--muted)" data-i18n="scheduled_jobs">Scheduled jobs</div>
|
||||
<button class="cron-btn run" style="padding:3px 8px;font-size:10px" onclick="toggleCronForm()">+ <span data-i18n="new_job">New job</span></button>
|
||||
<div style="display:flex;gap:4px;align-items:center">
|
||||
<button class="cron-btn" id="cronRefreshBtn" style="padding:3px 7px;font-size:10px;line-height:1" onclick="loadCrons(true)" title="Refresh job list" aria-label="Refresh job list"><svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/></svg></button>
|
||||
<button class="cron-btn run" style="padding:3px 8px;font-size:10px" onclick="toggleCronForm()">+ <span data-i18n="new_job">New job</span></button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Create job form (hidden by default) -->
|
||||
<div id="cronCreateForm" style="display:none;padding:8px 12px;border-bottom:1px solid var(--border);flex-shrink:0">
|
||||
|
||||
@@ -19,8 +19,13 @@ async function switchPanel(name) {
|
||||
}
|
||||
|
||||
// ── Cron panel ──
|
||||
async function loadCrons() {
|
||||
async function loadCrons(animate) {
|
||||
const box = $('cronList');
|
||||
const refreshBtn = $('cronRefreshBtn');
|
||||
if (animate && refreshBtn) {
|
||||
refreshBtn.style.opacity = '0.5';
|
||||
refreshBtn.disabled = true;
|
||||
}
|
||||
try {
|
||||
const data = await api('/api/crons');
|
||||
if (!data.jobs || !data.jobs.length) {
|
||||
@@ -77,6 +82,12 @@ async function loadCrons() {
|
||||
loadCronOutput(job.id);
|
||||
}
|
||||
} catch(e) { box.innerHTML = `<div style="padding:12px;color:var(--accent);font-size:12px">${esc(t('error_prefix'))}${esc(e.message)}</div>`; }
|
||||
finally {
|
||||
if (animate && refreshBtn) {
|
||||
refreshBtn.style.opacity = '';
|
||||
refreshBtn.disabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _cronSelectedSkills=[];
|
||||
@@ -1472,6 +1483,12 @@ let _cronPollSince=Date.now()/1000; // track from page load
|
||||
let _cronPollTimer=null;
|
||||
let _cronUnreadCount=0;
|
||||
|
||||
// Auto-refresh the cron list when a job is created from chat or any external source.
|
||||
// The chat path dispatches this event when the agent response mentions cron creation.
|
||||
window.addEventListener('hermes:cron_created', () => {
|
||||
if ($('cronList')) loadCrons();
|
||||
});
|
||||
|
||||
function startCronPolling(){
|
||||
if(_cronPollTimer) return;
|
||||
_cronPollTimer=setInterval(async()=>{
|
||||
|
||||
Reference in New Issue
Block a user