front update

This commit is contained in:
Mateusz Gruszczyński
2026-05-03 13:44:56 +02:00
parent 4fac1716a6
commit 4696d92dd8
3 changed files with 66 additions and 11 deletions

View File

@@ -514,9 +514,9 @@
['limitDown','limitUp'].forEach(id=>$(id)?.addEventListener('input',updateLimitSliders));
$('saveSpeedBtn')?.addEventListener('click',async()=>{const btn=$('saveSpeedBtn');buttonBusy(btn,true);setBusy(true);try{await post('/api/speed/limits',{down:Math.round(Number($('limitDown').value||0)*1024),up:Math.round(Number($('limitUp').value||0)*1024)});toast('Speed limits queued','success');bootstrap.Modal.getInstance($('speedModal'))?.hide();}catch(e){toast(e.message,'danger');}finally{buttonBusy(btn,false);setBusy(false);}}); $('speedModal')?.addEventListener('show.bs.modal',()=>{setLimitValue('limitDown',lastLimits.down?Math.round(lastLimits.down/1024):0);setLimitValue('limitUp',lastLimits.up?Math.round(lastLimits.up/1024):0);updateLimitSliders();});
async function refreshProfiles(){ $('profileList').innerHTML='<span class="spinner-border spinner-border-sm me-2"></span>Loading profiles...'; const j=await (await fetch('/api/profiles')).json(); const active=j.active?.id; profileCache=new Map((j.profiles||[]).map(p=>[String(p.id),p])); $('profileList').innerHTML=(j.profiles||[]).map(p=>`<div class="profile-row ${p.id===active?'active':''}"><b>${esc(p.name)} ${p.id===active?"<span class='badge text-bg-primary ms-1'>active</span>":''} ${p.is_remote?"<span class='badge text-bg-secondary ms-1'>remote</span>":''}</b><span>${esc(p.scgi_url)} · jobs ${esc(p.max_parallel_jobs||5)}${p.is_remote?' · remote CPU/RAM/IP':''}</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><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"></i></button></div></div>`).join('')||'No profiles.'; }
function resetProfileForm(){ if($('profileId')) $('profileId').value=''; if($('profileName')) $('profileName').value=''; if($('profileUrl')) $('profileUrl').value=''; if($('profileTimeout')) $('profileTimeout').value='5'; if($('profileParallel')) $('profileParallel').value='5'; if($('profileRemote')) $('profileRemote').checked=false; if($('profileFormTitle')) $('profileFormTitle').textContent='Add one rTorrent'; if($('saveProfileBtn')) $('saveProfileBtn').innerHTML='<i class="fa-solid fa-plus"></i> Add profile'; $('cancelProfileEditBtn')?.classList.add('d-none'); }
function editProfileForm(profile){ if(!profile) return; if($('profileId')) $('profileId').value=profile.id; if($('profileName')) $('profileName').value=profile.name||''; if($('profileUrl')) $('profileUrl').value=profile.scgi_url||''; if($('profileTimeout')) $('profileTimeout').value=profile.timeout_seconds||5; if($('profileParallel')) $('profileParallel').value=profile.max_parallel_jobs||5; if($('profileRemote')) $('profileRemote').checked=!!profile.is_remote; if($('profileFormTitle')) $('profileFormTitle').textContent='Edit rTorrent'; if($('saveProfileBtn')) $('saveProfileBtn').innerHTML='<i class="fa-solid fa-floppy-disk"></i> Save profile'; $('cancelProfileEditBtn')?.classList.remove('d-none'); $('profileName')?.focus(); }
$('profileModal')?.addEventListener('show.bs.modal',refreshProfiles); $('profileList')?.addEventListener('click',async e=>{const btn=e.target.closest('[data-del-profile],[data-use-profile],[data-edit-profile]'); const del=btn?.dataset.delProfile,use=btn?.dataset.useProfile,edit=btn?.dataset.editProfile;if(edit){editProfileForm(profileCache.get(String(edit)));return;} if(del){setBusy(true);await fetch(`/api/profiles/${del}`,{method:'DELETE'});setBusy(false);refreshProfiles();location.reload();} if(use){setBusy(true);await post(`/api/profiles/${use}/activate`,{});setBusy(false);location.reload();}}); $('cancelProfileEditBtn')?.addEventListener('click',resetProfileForm); $('saveProfileBtn')?.addEventListener('click',async()=>{setBusy(true);const id=$('profileId')?.value;const payload={name:$('profileName').value,scgi_url:$('profileUrl').value,timeout_seconds:$('profileTimeout').value,max_parallel_jobs:$('profileParallel').value,is_remote:$('profileRemote')?.checked};const j=await post(id?`/api/profiles/${id}`:'/api/profiles',payload,id?'PUT':'POST').catch(e=>toast(e.message,'danger'));setBusy(false);if(j?.profile)location.reload();}); $('saveBulkProfilesBtn')?.addEventListener('click',async()=>{const lines=($('bulkProfiles').value||'').split(/\n+/).map(x=>x.trim()).filter(Boolean);setBusy(true);try{for(const line of lines){const [name,scgi_url]=line.split('|').map(x=>x.trim());if(name&&scgi_url)await post('/api/profiles',{name,scgi_url,timeout_seconds:$('profileTimeout').value,max_parallel_jobs:$('profileParallel').value,is_remote:$('profileRemote')?.checked});}location.reload();}catch(e){toast(e.message,'danger');}finally{setBusy(false);}}); $('profileSelect')?.addEventListener('change',async e=>{await post(`/api/profiles/${e.target.value}/activate`,{});const opt=e.target.selectedOptions?.[0];if($('activeProfileName') && opt) $('activeProfileName').textContent=opt.textContent || 'rTorrent';bootstrap.Modal.getInstance($('profilePickerModal'))?.hide();defaultDownloadPath=null;applyDefaultDownloadPath(true).catch(()=>{});socket.emit('select_profile',{profile_id:e.target.value});hasTorrentSnapshot=false;torrents.clear();selected.clear();scheduleRender(true);}); $('themeToggle')?.addEventListener('click',async()=>{const cur=document.documentElement.dataset.bsTheme==='dark'?'light':'dark';document.documentElement.dataset.bsTheme=cur;await post('/api/preferences',{theme:cur}).catch(()=>{});}); $('mobileToggle')?.addEventListener('click',()=>{document.body.classList.toggle('mobile-mode-manual');syncMobileMode();}); window.addEventListener('resize',()=>syncMobileMode(),{passive:true}); syncMobileMode();
function resetProfileForm(){ if($('profileId')) $('profileId').value=''; if($('profileName')) $('profileName').value=''; if($('profileUrl')) $('profileUrl').value=''; if($('profileTimeout')) $('profileTimeout').value='5'; if($('profileParallel')) $('profileParallel').value='5'; if($('profileRemote')) $('profileRemote').checked=false; if($('profileFormTitle')) $('profileFormTitle').textContent='Add rTorrent profile'; if($('saveProfileBtn')) $('saveProfileBtn').innerHTML='<i class="fa-solid fa-plus"></i> Add profile'; $('cancelProfileEditBtn')?.classList.add('d-none'); }
function editProfileForm(profile){ if(!profile) return; if($('profileId')) $('profileId').value=profile.id; if($('profileName')) $('profileName').value=profile.name||''; if($('profileUrl')) $('profileUrl').value=profile.scgi_url||''; if($('profileTimeout')) $('profileTimeout').value=profile.timeout_seconds||5; if($('profileParallel')) $('profileParallel').value=profile.max_parallel_jobs||5; if($('profileRemote')) $('profileRemote').checked=!!profile.is_remote; if($('profileFormTitle')) $('profileFormTitle').textContent='Edit rTorrent profile'; if($('saveProfileBtn')) $('saveProfileBtn').innerHTML='<i class="fa-solid fa-floppy-disk"></i> Save profile'; $('cancelProfileEditBtn')?.classList.remove('d-none'); $('profileName')?.focus(); }
$('profileModal')?.addEventListener('show.bs.modal',refreshProfiles); $('profileList')?.addEventListener('click',async e=>{const btn=e.target.closest('[data-del-profile],[data-use-profile],[data-edit-profile]'); const del=btn?.dataset.delProfile,use=btn?.dataset.useProfile,edit=btn?.dataset.editProfile;if(edit){editProfileForm(profileCache.get(String(edit)));return;} if(del){setBusy(true);await fetch(`/api/profiles/${del}`,{method:'DELETE'});setBusy(false);refreshProfiles();location.reload();} if(use){setBusy(true);await post(`/api/profiles/${use}/activate`,{});setBusy(false);location.reload();}}); $('cancelProfileEditBtn')?.addEventListener('click',resetProfileForm); $('saveProfileBtn')?.addEventListener('click',async()=>{setBusy(true);const id=$('profileId')?.value;const payload={name:$('profileName').value,scgi_url:$('profileUrl').value,timeout_seconds:$('profileTimeout').value,max_parallel_jobs:$('profileParallel').value,is_remote:$('profileRemote')?.checked};const j=await post(id?`/api/profiles/${id}`:'/api/profiles',payload,id?'PUT':'POST').catch(e=>toast(e.message,'danger'));setBusy(false);if(j?.profile)location.reload();}); $('profileSelect')?.addEventListener('change',async e=>{await post(`/api/profiles/${e.target.value}/activate`,{});const opt=e.target.selectedOptions?.[0];if($('activeProfileName') && opt) $('activeProfileName').textContent=opt.textContent || 'rTorrent';bootstrap.Modal.getInstance($('profilePickerModal'))?.hide();defaultDownloadPath=null;applyDefaultDownloadPath(true).catch(()=>{});socket.emit('select_profile',{profile_id:e.target.value});hasTorrentSnapshot=false;torrents.clear();selected.clear();scheduleRender(true);}); $('themeToggle')?.addEventListener('click',async()=>{const cur=document.documentElement.dataset.bsTheme==='dark'?'light':'dark';document.documentElement.dataset.bsTheme=cur;await post('/api/preferences',{theme:cur}).catch(()=>{});}); $('mobileToggle')?.addEventListener('click',()=>{document.body.classList.toggle('mobile-mode-manual');syncMobileMode();}); window.addEventListener('resize',()=>syncMobileMode(),{passive:true}); syncMobileMode();
function drawTraffic(down,up){ traffic.push({down:Number(down||0),up:Number(up||0)}); if(traffic.length>60)traffic.shift(); const c=$('trafficChart'); if(!c)return; const ctx=c.getContext('2d'),w=c.width,h=c.height; ctx.clearRect(0,0,w,h); const max=Math.max(1,...traffic.map(p=>Math.max(p.down,p.up))); ctx.beginPath(); traffic.forEach((p,i)=>{const x=i*(w/59),y=h-(p.down/max)*h; i?ctx.lineTo(x,y):ctx.moveTo(x,y);}); ctx.strokeStyle='#38bdf8'; ctx.stroke(); ctx.beginPath(); traffic.forEach((p,i)=>{const x=i*(w/59),y=h-(p.up/max)*h; i?ctx.lineTo(x,y):ctx.moveTo(x,y);}); ctx.strokeStyle='#f59e0b'; ctx.stroke(); }
function drawSystemUsage(cpu,ram){
const c=$('systemChart'); if(!c) return;

View File

@@ -213,11 +213,60 @@ body {
.ctx-menu button:hover { background: var(--bs-secondary-bg); }
.ctx-menu .danger { color: var(--bs-danger); }
.ctx-menu hr { margin: .25rem 0; border-color: var(--bs-border-color); }
.profile-row { display: grid; grid-template-columns: 1fr auto; gap: .25rem .5rem; align-items: center; padding: .45rem; border: 1px solid var(--bs-border-color); border-radius: .6rem; margin-bottom: .45rem; background: rgba(var(--bs-secondary-bg-rgb), .58); }
.profile-row span { grid-column: 1 / 2; color: var(--bs-secondary-color); overflow-wrap: anywhere; }
.profile-form-actions { display: inline-flex; gap: .35rem; flex-wrap: wrap; }
.profile-actions { display: inline-flex; gap: .35rem; }
.profile-row.active { border-color: var(--bs-primary); background: var(--bs-primary-bg-subtle); }
.profile-row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
gap: .25rem .5rem;
align-items: center;
margin-bottom: .45rem;
padding: .45rem;
border: 1px solid var(--bs-border-color);
border-radius: .6rem;
background: rgba(var(--bs-secondary-bg-rgb), .58);
}
.profile-row.active {
border-color: var(--bs-primary);
background: var(--bs-primary-bg-subtle);
}
.profile-row span {
grid-column: 1 / 2;
color: var(--bs-secondary-color);
overflow-wrap: anywhere;
}
.profile-actions,
.profile-form-actions {
display: inline-flex;
gap: .35rem;
flex-wrap: wrap;
}
.profile-form-grid {
display: grid;
grid-template-columns: minmax(150px, 1.1fr) minmax(260px, 2.1fr) minmax(90px, .55fr) minmax(120px, .75fr) minmax(145px, auto) auto;
gap: .65rem;
align-items: start;
}
.profile-form-field {
display: grid;
gap: .25rem;
min-width: 0;
}
.profile-form-field > span:first-child {
color: var(--bs-secondary-color);
font-size: .72rem;
font-weight: 700;
line-height: 1.1;
text-transform: uppercase;
}
.profile-form-field small {
color: var(--bs-secondary-color);
line-height: 1.25;
}
.profile-check-field .form-check {
min-height: 31px;
display: flex;
align-items: center;
gap: .45rem;
}
.flag-icon { border-radius: 2px; box-shadow: 0 0 0 1px rgba(255,255,255,.12); }
.flag-code { color: var(--bs-secondary-color); margin-left: .25rem; }
.peer-actions { display: flex; align-items: center; gap: .25rem; flex-wrap: nowrap; }
@@ -349,11 +398,17 @@ body.mobile-mode .torrent-table { display: none; }
body.mobile-mode #mobileList{min-height:0;height:100%;overflow:auto;display:block!important}
body.mobile-mode .mobile-card{display:block}.mobile-card .mobile-actions button{min-width:34px}
#toolSmart .form-label{font-size:.75rem;color:var(--bs-secondary-color);margin-bottom:.2rem}
.profile-form-grid{display:grid;grid-template-columns:1.1fr 2.1fr .55fr .75fr auto auto;gap:.5rem;align-items:center}
#toolSmart .btn{padding:.25rem .55rem;border-radius:.5rem;white-space:nowrap}
#toolSmart .row .d-flex{align-items:end;justify-content:flex-start}
#trafficHistoryChart{width:100%;height:420px;border:1px solid var(--bs-border-color);border-radius:.75rem;background:var(--bs-body-bg)}
@media (max-width: 992px){.profile-form-grid{grid-template-columns:1fr}.profile-form-grid .btn{width:100%}}
@media (max-width: 992px) {
.profile-form-grid {
grid-template-columns: 1fr;
}
.profile-form-grid .btn {
width: 100%;
}
}
/* Requested fixes: stable charts, Smart Queue exceptions, label actions, mobile readability */
.history-grid{display:grid;grid-template-columns:1fr;gap:1rem}

File diff suppressed because one or more lines are too long