ux fix
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
let hiddenColumns = new Set((window.PYTORRENT?.tableColumns?.hidden || []));
|
||||
let knownLabels = [];
|
||||
let jobsPage = 0, jobsLimit = 25, jobsTotal = 0, smartHistoryExpanded = false;
|
||||
let automationSmartQueueStats = null;
|
||||
let peersRefreshTimer = null;
|
||||
let peersRefreshSeconds = Number(window.PYTORRENT?.peersRefreshSeconds || 0);
|
||||
let portCheckEnabled = !!Number(window.PYTORRENT?.portCheckEnabled || 0);
|
||||
@@ -421,6 +422,41 @@
|
||||
|
||||
function smartHistoryDetails(row){ try{ return typeof row.details_json==='string'?JSON.parse(row.details_json||'{}'):(row.details_json||{}); }catch(e){ return {}; } }
|
||||
function smartQueueToastMessage(r){ const noEffect=r.start_no_effect?.length||0; const requested=r.start_requested?.length||0; const stopFailed=r.stop_failed?.length||0; const limit=r.max_active_downloads||r.settings?.max_active_downloads||''; const activeBefore=r.active_before; const activeAfter=r.active_after_stop ?? r.active_after_expected; const activeTail=activeBefore!==undefined?`, active ${esc(activeBefore)}->${esc(activeAfter ?? '?')}${limit?`/${esc(limit)}`:''}`:''; const cap=r.rtorrent_cap?.updated?`, cap ${r.rtorrent_cap.current}->${r.rtorrent_cap.new}`:''; const waiting=r.waiting_labeled||0; const stalled=r.stalled_labeled?.length||0; const tail=noEffect?`, no effect ${noEffect}`:requested?`, requested ${requested}`:''; const waitTail=waiting?`, waiting labeled ${waiting}`:''; const stalledTail=stalled?`, stalled ${stalled}`:''; const failTail=stopFailed?`, stop failed ${stopFailed}`:''; return `Smart Queue: stopped ${r.stopped?.length||r.paused?.length||0}, started ${r.started?.length||r.resumed?.length||0}${activeTail}${tail}${waitTail}${stalledTail}${failTail}${cap}`; }
|
||||
function buildSmartQueueNerdStats(hist=[], totalHistory=0){
|
||||
// Note: Small Smart Queue telemetry for automation nerds; it reads history only and does not affect queue behavior.
|
||||
const stats=hist.reduce((acc,h)=>{
|
||||
const details=smartHistoryDetails(h);
|
||||
const stopped=Number(h.paused_count||0);
|
||||
const started=Number(h.resumed_count||0);
|
||||
const checked=Number(h.checked_count||0);
|
||||
const over=Number(details.over_limit||0);
|
||||
const stopFailed=Array.isArray(details.stop_failed)?details.stop_failed.length:0;
|
||||
acc.checked += checked;
|
||||
acc.stopped += stopped;
|
||||
acc.started += started;
|
||||
acc.overLimit += over;
|
||||
acc.stopFailed += stopFailed;
|
||||
if(over>0) acc.overEvents += 1;
|
||||
return acc;
|
||||
},{checked:0,stopped:0,started:0,overLimit:0,overEvents:0,stopFailed:0});
|
||||
const latest=hist[0]||null;
|
||||
return {...stats,total:Number(totalHistory||hist.length||0),sample:hist.length,latestEvent:latest?.event||'-',latestAt:latest?.created_at||''};
|
||||
}
|
||||
|
||||
function renderSmartQueueNerdStats(stats){
|
||||
// Note: Compact cards keep the extra diagnostics readable above Automation history without changing the history table.
|
||||
if(!stats) return '<div class="automation-smart-stats empty-mini">No Smart Queue stats yet.</div>';
|
||||
const cards=[
|
||||
['Runs',stats.total,`${stats.sample} loaded`],
|
||||
['Checked',stats.checked,'torrent scans'],
|
||||
['Stopped',stats.stopped,'queue trims'],
|
||||
['Started',stats.started,'queue fills'],
|
||||
['Over limit',stats.overEvents,`${stats.overLimit} total over`],
|
||||
['Stop failed',stats.stopFailed,'rTorrent rejects'],
|
||||
['Latest',stats.latestEvent,stats.latestAt?dateCell(stats.latestAt):'no timestamp'],
|
||||
];
|
||||
return `<div class="automation-smart-stats" aria-label="Smart Queue nerd stats">${cards.map(([label,value,hint])=>`<div class="automation-smart-stat"><span>${esc(label)}</span><b>${esc(value)}</b><small>${hint}</small></div>`).join('')}</div>`;
|
||||
}
|
||||
async function loadSmartQueue(){
|
||||
if($('smartManager')) $('smartManager').innerHTML=loadingMarkup('Loading Smart Queue...');
|
||||
if($('smartHistory')) $('smartHistory').innerHTML=loadingMarkup('Loading Smart Queue history...');
|
||||
@@ -702,13 +738,14 @@
|
||||
return `<details class="automation-history-details"><summary>${summary||'No actions'}</summary><pre>${details}</pre></details>`;
|
||||
}
|
||||
|
||||
function renderAutomationHistory(hist=[]){
|
||||
function renderAutomationHistory(hist=[], smartStats=automationSmartQueueStats){
|
||||
if(!$('automationHistory')) return;
|
||||
const stats=renderSmartQueueNerdStats(smartStats);
|
||||
const toolbar='<div class="automation-history-toolbar"><button id="automationClearHistoryBtn" class="btn btn-xs btn-outline-danger" type="button"><i class="fa-solid fa-trash"></i> Clear history</button></div>';
|
||||
const rows=hist.map(h=>[humanDateCell(h.created_at),esc(h.rule_name||''),esc(h.torrent_name||h.torrent_hash||''),automationHistoryActions(h.actions_json||'')]);
|
||||
// Note: Automation history uses the shared responsive table wrapper so it stays inside narrow mobile modals.
|
||||
const body=hist.length?responsiveTable(['Time','Rule','Torrent / batch','Actions'],rows,'automation-history-table'):'<div class="empty-mini">No automation history yet.</div>';
|
||||
$('automationHistory').innerHTML=toolbar+body;
|
||||
$('automationHistory').innerHTML=stats+toolbar+body;
|
||||
}
|
||||
|
||||
async function clearAutomationHistory(){
|
||||
@@ -732,8 +769,13 @@
|
||||
}
|
||||
|
||||
async function loadAutomations(){
|
||||
const j=await (await fetch('/api/automations')).json();
|
||||
const [j,smart]=await Promise.all([
|
||||
fetch('/api/automations').then(r=>r.json()),
|
||||
fetch('/api/smart-queue?history_limit=100').then(r=>r.json()).catch(()=>({}))
|
||||
]);
|
||||
const rules=j.rules||[], hist=j.history||[];
|
||||
// Note: Automations only display Smart Queue diagnostics here; saving/checking rules remains unchanged.
|
||||
automationSmartQueueStats=smart?.ok?buildSmartQueueNerdStats(smart.history||[], Number(smart.history_total||0)):null;
|
||||
automationRulesCache=rules;
|
||||
if($('automationManager')) $('automationManager').innerHTML=rules.length?rules.map(r=>{
|
||||
const enabled=!!r.enabled;
|
||||
@@ -742,7 +784,7 @@
|
||||
const toggleClass=enabled?'btn-outline-warning':'btn-outline-success';
|
||||
return `<div class="automation-row"><div class="automation-row-main"><div><b>${esc(r.name)}</b> ${enabled?'<span class="badge text-bg-success">on</span>':'<span class="badge text-bg-secondary">off</span>'}</div><div class="small text-muted automation-rule-summary">${esc(ruleSummary(r))} · cooldown ${esc(r.cooldown_minutes||0)} min</div></div><div class="automation-row-actions"><button class="btn btn-xs ${toggleClass} automation-toggle" data-id="${esc(r.id)}" type="button" title="${toggleTitle}"><i class="fa-solid ${toggleIcon}"></i></button><button class="btn btn-xs btn-outline-secondary automation-edit" data-id="${esc(r.id)}" type="button" title="Edit automation"><i class="fa-solid fa-pen"></i></button><button class="btn btn-xs btn-outline-danger automation-delete" data-id="${esc(r.id)}" type="button" title="Delete automation"><i class="fa-solid fa-trash"></i></button></div></div>`;
|
||||
}).join(''):'<div class="empty-mini">No automation rules.</div>';
|
||||
renderAutomationHistory(hist);
|
||||
renderAutomationHistory(hist, automationSmartQueueStats);
|
||||
}
|
||||
|
||||
async function toggleAutomationRule(rule){
|
||||
|
||||
Reference in New Issue
Block a user