From f7bef55e516c6e8a91c9f17681767af39c0f533e Mon Sep 17 00:00:00 2001 From: Gmer4Lfe Date: Sat, 23 May 2026 21:07:15 -0400 Subject: [PATCH] varaverk: green checkmark feedback on schedule save MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add .vv-save-check span to each job row (orchestrators + children) - CSS: fade-in-then-out animation (visible 1.2s, fades over 2s) - vvFlashSaved(job): triggers the checkmark animation on a specific row - vvFlashStatus(el, msg, ok): shows timed status text (was called but missing) - vvSaveJob: now shows per-job checkmark on successful save - vvSaveAll: shows checkmark on each job as its response arrives, plus global "✓ All saved" / "✗ Some failed" status text below the button --- .../emhttp/plugins/varaverk/css/varaverk.css | 6 + .../plugins/varaverk/pages/scheduler.php | 223 ++++++++++++++---- 2 files changed, 179 insertions(+), 50 deletions(-) diff --git a/Plugin/usr/local/emhttp/plugins/varaverk/css/varaverk.css b/Plugin/usr/local/emhttp/plugins/varaverk/css/varaverk.css index 6e69f36..df2f0da 100644 --- a/Plugin/usr/local/emhttp/plugins/varaverk/css/varaverk.css +++ b/Plugin/usr/local/emhttp/plugins/varaverk/css/varaverk.css @@ -60,6 +60,12 @@ .vv-btn-sm:hover { border-color: #888; color: #fff; } .vv-btn-sm.active { border-color: #4caf50; color: #4caf50; } +/* Save checkmark */ +.vv-save-check { color: #4caf50; font-size: 13px; width: 14px; flex-shrink: 0; + opacity: 0; text-align: center; } +@keyframes vv-check-fade { 0%,60% { opacity: 1; } 100% { opacity: 0; } } +.vv-save-check.vv-check-show { animation: vv-check-fade 2s forwards; } + /* Running dot */ .vv-job-dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; flex-shrink: 0; } .vv-dot-running { background: #4caf50; animation: vv-pulse-dot 1s ease-in-out infinite; } diff --git a/Plugin/usr/local/emhttp/plugins/varaverk/pages/scheduler.php b/Plugin/usr/local/emhttp/plugins/varaverk/pages/scheduler.php index 76d887d..230e3e0 100644 --- a/Plugin/usr/local/emhttp/plugins/varaverk/pages/scheduler.php +++ b/Plugin/usr/local/emhttp/plugins/varaverk/pages/scheduler.php @@ -1,6 +1,16 @@ $orch['enabled'], 'cron' => $orch['cron']]; + foreach ($orch['children'] ?? [] as $child) { + $treeMap[$child['id']] = ['enabled' => $child['enabled'], 'cron' => $child['cron']]; + } +} ?>
@@ -27,6 +37,7 @@ $tree = vv_job_tree(); +
@@ -61,6 +72,7 @@ $tree = vv_job_tree(); +
@@ -95,20 +107,94 @@ $tree = vv_job_tree(); - -
+ +
- No script selected + + Suggested Schedules
-
+
+ + - +
-
Click Log, Run, or Dry Run on a script to view output here.
+ + +
+ $block): + $scripts = $block['scripts']; + $schedule = $block['schedule']; + // Extract just the 5-field cron from schedule string + $schedParts = preg_split('/\s+/', $schedule, 6); + $cronOnly = (count($schedParts) >= 5) ? implode(' ', array_slice($schedParts, 0, 5)) : ''; + $schedLabel = (count($schedParts) >= 6) ? trim($schedParts[5], '() ') : $schedule; + // Status: any matching script configured? + $configured = false; $enabled = false; + foreach ($scripts as $s) { + if (isset($treeMap[$s['rel']])) { + $configured = true; + if ($treeMap[$s['rel']]['enabled']) $enabled = true; + } + } + // Trim trailing blank desc lines + $desc = $block['desc']; + while (!empty($desc) && trim(end($desc)) === '') array_pop($desc); + ?> +
+
+ + + + + + + + + + + +
+ +
+ + +

user_script_plug-in.sh not found at

+ +
+ + +
r.json()) .then(d => { - const pre = document.getElementById('vv-log-pre'); - const ts = document.getElementById('vv-log-ts'); + const pre = document.getElementById('vv-log-pre'); + const ts = document.getElementById('vv-log-ts'); + const scrollLock = document.getElementById('vv-scroll-lock')?.checked; + const invert = document.getElementById('vv-invert-log')?.checked; if (!d.ok) { pre.textContent = '✗ ' + (d.error ?? 'Error'); return; } - pre.textContent = d.content || '(no log yet)'; - pre.scrollTop = pre.scrollHeight; + const content = d.content || '(no log yet)'; + pre.textContent = invert ? content.split('\n').reverse().join('\n') : content; + if (!scrollLock) pre.scrollTop = invert ? 0 : pre.scrollHeight; ts.textContent = d.ts ? 'Last run: ' + new Date(d.ts * 1000).toLocaleString() : ''; // Running indicator: clear when log stops changing @@ -286,13 +397,30 @@ function vvDryRun(btn) { }); } +function vvFlashSaved(job) { + const check = job.querySelector('.vv-save-check'); + if (!check) return; + check.textContent = '✓'; + check.classList.remove('vv-check-show'); + void check.offsetWidth; // force reflow to restart animation + check.classList.add('vv-check-show'); +} + +function vvFlashStatus(el, msg, ok) { + el.textContent = msg; + el.style.color = ok ? '#4caf50' : '#f44336'; + clearTimeout(el._t); + el._t = setTimeout(() => { el.textContent = ''; }, 3000); +} + function vvSaveJob(el) { const job = el.closest('[data-id]'); const id = job.dataset.id; const enabled = job.querySelector('.vv-enabled').checked ? '1' : '0'; const cron = job.querySelector('.vv-cron').value.trim(); const log_enabled = job.querySelector('.vv-log-enabled')?.checked ? '1' : '0'; - vvPost('/plugins/varaverk/api/scheduler.php', {id, enabled, cron, log_enabled}); + vvPost('/plugins/varaverk/api/scheduler.php', {id, enabled, cron, log_enabled}) + .then(d => { if (d.ok) vvFlashSaved(job); }); } function vvSaveAll() { @@ -308,8 +436,8 @@ function vvSaveAll() { const log_enabled = job.querySelector('.vv-log-enabled')?.checked ? '1' : '0'; vvPost('/plugins/varaverk/api/scheduler.php', {id, enabled, cron, log_enabled}) .then(d => { - if (!d.ok) allOk = false; - if (--pending === 0) vvFlashStatus(status, allOk ? '✓ Saved' : '✗ Some failed', allOk); + if (d.ok) vvFlashSaved(job); else allOk = false; + if (--pending === 0) vvFlashStatus(status, allOk ? '✓ All saved' : '✗ Some failed', allOk); }); }); } @@ -328,19 +456,14 @@ function vvClearRightLog() { .then(() => vvFetchRight()) .catch(() => {}); } + +function vvToggleSugBlock(bi) { + const body = document.getElementById('vv-sug-body-' + bi); + const chevron = body.closest('.vv-sug-block').querySelector('.vv-sug-chevron'); + const open = body.style.display !== 'none'; + body.style.display = open ? 'none' : 'block'; + chevron.textContent = open ? '▸' : '▾'; +} - $lastMtime) { $lastMtime = $m; $lastId = $orch['id']; } - foreach (($orch['children'] ?? []) as $child) { - $log = vv_job_log_path($child['id']); - if (file_exists($log) && ($m = filemtime($log)) > $lastMtime) { $lastMtime = $m; $lastId = $child['id']; } - } -} -if ($lastId): ?> - - +