fix in planner

This commit is contained in:
Mateusz Gruszczyński
2026-06-16 22:57:29 +02:00
parent 03ce088d24
commit 48f68cf125
5 changed files with 5 additions and 4 deletions
+1
View File
@@ -33,6 +33,7 @@ def download_planner_get():
@bp.post("/download-planner") @bp.post("/download-planner")
def download_planner_save(): def download_planner_save():
# Note: Planner settings are saved through one canonical endpoint to avoid hidden frontend/backend fallbacks.
profile, error = _profile_or_error() profile, error = _profile_or_error()
if error: if error:
return error return error
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1 +1 @@
export const profileListSource = " function markActiveProfileRow(id){\n // Note: Keeps the active rTorrent profile frame in sync immediately after switching, before diagnostics refresh finishes.\n const activeId=String(id||'');\n document.querySelectorAll('#profileList .profile-row').forEach(row=>{\n const isActive=String(row.dataset.profileId||'')===activeId;\n row.classList.toggle('active', isActive);\n row.setAttribute('aria-current', isActive ? 'true' : 'false');\n const badge=row.querySelector('[data-active-profile-badge]');\n if(badge) badge.classList.toggle('d-none', !isActive);\n });\n }\n function profileDiagnosticStatusClass(status){\n // Note: rTorrent profile badges reuse Bootstrap colors and the same normal/slow/error idea as the poller panel.\n const value=String(status||'unknown').toLowerCase();\n if(value==='normal' || value==='online') return 'success';\n if(value==='slow' || value==='slowdown') return 'warning';\n if(value==='error' || value==='recovery') return 'danger';\n return 'secondary';\n }\n function profileDiagnosticStatusLabel(status){\n const value=String(status||'unknown').toLowerCase();\n return value==='online' ? 'normal' : value;\n }\n async function refreshProfiles(){\n const j=await (await fetch('/api/profiles')).json();\n profileCache=new Map((j.profiles||[]).map(p=>[String(p.id),p]));\n const active=String(j.active?.id ?? activeProfileId ?? '');\n const rows=j.profiles||[];\n const statusMap=new Map();\n try{ const d=await (await fetch('/api/profiles/diagnostics')).json(); (d.diagnostics||[]).forEach(x=>statusMap.set(String(x.profile_id), x)); }catch(e){}\n $('profileList').innerHTML=rows.map(p=>{\n const d=statusMap.get(String(p.id))||{};\n const st=profileDiagnosticStatusLabel(d.status || 'unknown');\n const cls=profileDiagnosticStatusClass(st);\n const response=d.response_time_ms?` \u00b7 ${esc(d.response_time_ms)} ms`:'';\n const threshold=d.slow_threshold_ms?` \u00b7 slow > ${esc(d.slow_threshold_ms)} ms`:'';\n const isActive=String(p.id)===active;\n const backupIcon=p.profile_backup_enabled?`<span class=\"profile-backup-icon\" title=\"Automatic profile backup enabled\" aria-label=\"Automatic profile backup enabled\"><i class=\"fa-solid fa-floppy-disk\"></i></span>`:'';\n return `<div class=\"profile-row ${isActive?'active':''}\" data-profile-id=\"${esc(p.id)}\" aria-current=\"${isActive?'true':'false'}\"><b><span class=\"profile-id-badge\">#${esc(p.id)}</span> ${esc(p.name)} <span data-active-profile-badge class='badge text-bg-primary ms-1 ${isActive?'':'d-none'}'>active</span> ${p.is_remote?\"<span class='badge text-bg-secondary ms-1'>remote</span>\":''} <span class=\"badge text-bg-${cls}\">${esc(st)}</span></b><span>ID ${esc(p.id)} \u00b7 ${esc(p.scgi_url)} \u00b7 heavy ${esc(p.max_parallel_jobs||5)} \u00b7 light ${esc(p.light_parallel_jobs||4)} \u00b7 poll ${esc(p.polling_min_interval_seconds||'-')}s${response}${threshold}</span><div class=\"profile-actions\"><button class=\"btn btn-xs btn-outline-primary\" data-use-profile=\"${p.id}\"><i class=\"fa-solid fa-plug-circle-check\"></i> use</button>${backupIcon}<button class=\"btn btn-xs btn-outline-info\" data-test-saved-profile=\"${p.id}\" title=\"Diagnostics\"><i class=\"fa-solid fa-stethoscope\"></i></button><button class=\"btn btn-xs btn-outline-secondary\" data-edit-profile=\"${p.id}\" title=\"Edit\"><i class=\"fa-solid fa-pen-to-square\"></i></button><button class=\"btn btn-xs btn-outline-danger\" data-del-profile=\"${p.id}\" title=\"Delete\"><i class=\"fa-solid fa-trash-can\"></i> Remove</button></div></div>`;\n }).join('')||'No profiles.';\n }\n"; export const profileListSource = " function markActiveProfileRow(id){\n // Note: Keeps the active rTorrent profile frame in sync immediately after switching, before diagnostics refresh finishes.\n const activeId=String(id||'');\n document.querySelectorAll('#profileList .profile-row').forEach(row=>{\n const isActive=String(row.dataset.profileId||'')===activeId;\n row.classList.toggle('active', isActive);\n row.setAttribute('aria-current', isActive ? 'true' : 'false');\n const badge=row.querySelector('[data-active-profile-badge]');\n if(badge) badge.classList.toggle('d-none', !isActive);\n });\n }\n function profileDiagnosticStatusClass(status){\n // Note: rTorrent profile badges reuse Bootstrap colors and the same normal/slow/error idea as the poller panel.\n const value=String(status||'unknown').toLowerCase();\n if(value==='normal' || value==='online') return 'success';\n if(value==='slow' || value==='slowdown') return 'warning';\n if(value==='error' || value==='recovery') return 'danger';\n return 'secondary';\n }\n function profileDiagnosticStatusLabel(status){\n const value=String(status||'unknown').toLowerCase();\n return value==='online' ? 'normal' : value;\n }\n async function refreshProfiles(){\n const j=await (await fetch('/api/profiles')).json();\n profileCache=new Map((j.profiles||[]).map(p=>[String(p.id),p]));\n const active=String(j.active?.id ?? activeProfileId ?? '');\n const rows=j.profiles||[];\n const statusMap=new Map();\n try{ const d=await (await fetch('/api/profiles/diagnostics')).json(); (d.diagnostics||[]).forEach(x=>statusMap.set(String(x.profile_id), x)); }catch(e){}\n $('profileList').innerHTML=rows.map(p=>{\n const d=statusMap.get(String(p.id))||{};\n const st=profileDiagnosticStatusLabel(d.status || 'unknown');\n const cls=profileDiagnosticStatusClass(st);\n const response=d.response_time_ms?` \u00b7 ${esc(d.response_time_ms)} ms`:'';\n const threshold=d.slow_threshold_ms?` \u00b7 slow > ${esc(d.slow_threshold_ms)} ms`:'';\n const isActive=String(p.id)===active;\n const backupIcon=p.profile_backup_enabled?`<span class=\"profile-backup-icon\" title=\"Automatic profile backup enabled\" aria-label=\"Automatic profile backup enabled\"><i class=\"fa-solid fa-floppy-disk\"></i></span>`:'';\n return `<div class=\"profile-row ${isActive?'active':''}\" data-profile-id=\"${esc(p.id)}\" aria-current=\"${isActive?'true':'false'}\"><b><span class=\"profile-id-badge\">#${esc(p.id)}</span> ${esc(p.name)} <span data-active-profile-badge class='badge text-bg-primary ms-1 ${isActive?'':'d-none'}'>active</span> ${p.is_remote?\"<span class='badge text-bg-secondary ms-1'>remote</span>\":''} <span class=\"badge text-bg-${cls}\">${esc(st)}</span></b><span>ID ${esc(p.id)} \u00b7 ${esc(p.scgi_url)} \u00b7 heavy ${esc(p.max_parallel_jobs||5)} \u00b7 light ${esc(p.light_parallel_jobs||4)} \u00b7 poll ${esc(p.polling_min_interval_seconds||'-')}s${response}${threshold}</span><div class=\"profile-actions\">${backupIcon}<button class=\"btn btn-xs btn-outline-primary\" data-use-profile=\"${p.id}\"><i class=\"fa-solid fa-plug-circle-check\"></i> use</button><button class=\"btn btn-xs btn-outline-info\" data-test-saved-profile=\"${p.id}\" title=\"Diagnostics\"><i class=\"fa-solid fa-stethoscope\"></i></button><button class=\"btn btn-xs btn-outline-secondary\" data-edit-profile=\"${p.id}\" title=\"Edit\"><i class=\"fa-solid fa-pen-to-square\"></i></button><button class=\"btn btn-xs btn-outline-danger\" data-del-profile=\"${p.id}\" title=\"Delete\"><i class=\"fa-solid fa-trash-can\"></i> Remove</button></div></div>`;\n }).join('')||'No profiles.';\n }\n";