2 lines
9.1 KiB
JavaScript
2 lines
9.1 KiB
JavaScript
export const plannerActionsSource = " const PLANNER_API_BASE = '/api/download-planner';\n\n async function plannerApiJson(url, options={}){\n const response = await fetch(url, {cache:'no-store', ...options});\n const json = await response.json().catch(() => ({}));\n if(!response.ok || json.ok === false){\n throw new Error(json.error || `Planner API failed (${response.status})`);\n }\n return json;\n }\n\n function renderPlannerPreview(preview={}){\n updatePlannerCurrentSummary(preview);\n const box=$('plannerPreview');\n if(!box) return;\n const down=plannerLimitText(preview.down||0), up=plannerLimitText(preview.up||0);\n box.innerHTML=`Matched <b>${esc(preview.matched_rule||'-')}</b> \u00b7 next change ${esc(plannerDateText(preview.next_change_at))} \u00b7 DL ${esc(down)} / UL ${esc(up)}${preview.pause_downloads?' \u00b7 pauses downloads':''}${preview.manual_override_until?' \u00b7 override active':''}`;\n updatePlannerFooter(!!$('plannerEnabled')?.checked,preview);\n const ov=$('plannerOverrideStatus');\n if(ov) ov.textContent=preview.manual_override_until?`Active until ${plannerDateText(preview.manual_override_until)}`:'No active override.';\n }\n\n function plannerHistoryDetails(row={}){ return row && typeof row==='object' ? row : {}; }\n function plannerHistoryLimitText(value){ return plannerLimitText(Number(value||0)); }\n\n function renderPlannerHistory(items=[], total=items.length){\n const box=$('plannerHistory');\n if(!box) return;\n const body=items.length\n ? responsiveTable(['Time','Event','Rule','DL','UL','Paused','Resumed','Dry run','Reason'],items.map(x=>{\n // Note: Planner history uses the same table pattern as Smart Queue, with compact decision columns first.\n const d=plannerHistoryDetails(x);\n const event=d.event||'-';\n const rule=d.rule||d.matched_rule||d.profile_name||'-';\n const down=d.down!==undefined?plannerHistoryLimitText(d.down):'-';\n const up=d.up!==undefined?plannerHistoryLimitText(d.up):'-';\n const paused=d.paused ?? d.count ?? 0;\n const resumed=d.resumed ?? 0;\n const dry=d.dry_run?'yes':'-';\n const reason=d.pause_reason||d.reason||d.manual_override_reason||'-';\n return [dateCell(d.at),esc(event),esc(rule),esc(down),esc(up),esc(paused),esc(resumed),esc(dry),esc(reason)];\n }),'planner-history-table')\n : '<div class=\"empty-mini\">No Planner actions yet.</div>';\n const canToggle=Number(total||0)>10;\n const toggle=canToggle?`<button id=\"plannerHistoryToggle\" class=\"btn btn-xs btn-outline-secondary mt-2\"><i class=\"fa-solid fa-list\"></i> ${plannerHistoryExpanded?'Show last 10':'Show more'} (${esc(total)})</button>`:'';\n const clear=Number(total||0)?`<button id=\"plannerHistoryClear\" class=\"btn btn-xs btn-outline-danger mt-2 ms-2\"><i class=\"fa-solid fa-trash-can\"></i> Clear history</button>`:'';\n box.innerHTML=`${body}${toggle}${clear}`;\n }\n\n function fillPlanner(st){\n if(!st) return;\n $('plannerEnabled')&&($('plannerEnabled').checked=!!st.enabled);\n $('plannerProfileName')&&($('plannerProfileName').value=st.profile_name||'night mode');\n $('plannerDryRun')&&($('plannerDryRun').checked=!!st.dry_run);\n updatePlannerFooter(!!st.enabled,st);\n $('plannerHourlyEnabled')&&($('plannerHourlyEnabled').checked=!!st.hourly_schedule_enabled);\n const hourly=Array.isArray(st.hourly_schedule)?st.hourly_schedule:[];\n for(let hour=0;hour<24;hour++){\n const item=hourly.find(x=>Number(x.hour)===hour)||{};\n const d=$(`plannerHour${hour}Down`), u=$(`plannerHour${hour}Up`);\n if(d) d.value=Number(item.down||0);\n if(u) u.value=Number(item.up||0);\n updatePlannerHourSummary(hour);\n }\n $('plannerNightOnly')&&($('plannerNightOnly').checked=!!st.night_only_enabled);\n $('plannerNightStart')&&($('plannerNightStart').value=st.night_start||'23:00');\n $('plannerNightEnd')&&($('plannerNightEnd').value=st.night_end||'07:00');\n $('plannerQuietEnabled')&&($('plannerQuietEnabled').checked=!!st.quiet_hours_enabled);\n $('plannerQuietStart')&&($('plannerQuietStart').value=st.quiet_start||'22:00');\n $('plannerQuietEnd')&&($('plannerQuietEnd').value=st.quiet_end||'06:00');\n $('plannerWeekdayDown')&&($('plannerWeekdayDown').value=st.weekday_down||0);\n $('plannerWeekdayUp')&&($('plannerWeekdayUp').value=st.weekday_up||0);\n $('plannerWeekendDown')&&($('plannerWeekendDown').value=st.weekend_down||0);\n $('plannerWeekendUp')&&($('plannerWeekendUp').value=st.weekend_up||0);\n updatePlannerSpeedControls('plannerWeekday');\n updatePlannerSpeedControls('plannerWeekend');\n $('plannerCpuEnabled')&&($('plannerCpuEnabled').checked=!!st.auto_pause_cpu_enabled);\n $('plannerCpuPercent')&&($('plannerCpuPercent').value=st.auto_pause_cpu_percent||90);\n $('plannerDiskEnabled')&&($('plannerDiskEnabled').checked=!!st.auto_pause_disk_enabled);\n $('plannerDiskPercent')&&($('plannerDiskPercent').value=st.auto_pause_disk_percent||95);\n $('plannerNetworkEnabled')&&($('plannerNetworkEnabled').checked=!!st.network_protection_enabled);\n $('plannerNetworkDown')&&($('plannerNetworkDown').value=st.network_max_down||0);\n $('plannerNetworkUp')&&($('plannerNetworkUp').value=st.network_max_up||0);\n $('plannerLoadEnabled')&&($('plannerLoadEnabled').checked=!!st.load_protection_enabled);\n $('plannerLoadCpu')&&($('plannerLoadCpu').value=st.load_cpu_percent||95);\n $('plannerAutoResume')&&($('plannerAutoResume').checked=st.auto_resume!==false);\n $('plannerResumeGrace')&&($('plannerResumeGrace').value=st.auto_resume_grace_seconds||0);\n if(st.manual_override_until) renderPlannerPreview(st);\n updatePlannerCurrentSummary(st);\n }\n\n function applyPlannerPreset(){\n const name=$('plannerProfileName')?.value||'';\n if(name==='night mode'){\n $('plannerNightOnly').checked=true;\n $('plannerQuietEnabled').checked=false;\n setPlannerSpeed('plannerWeekday',100);\n setPlannerSpeed('plannerWeekend',250);\n }\n if(name==='weekend mode'){\n $('plannerNightOnly').checked=false;\n setPlannerSpeed('plannerWeekday',50);\n setPlannerSpeed('plannerWeekend',0);\n }\n if(name==='low power mode'){\n $('plannerLoadEnabled').checked=true;\n $('plannerCpuEnabled').checked=true;\n $('plannerCpuPercent').value=70;\n setPlannerSpeed('plannerWeekday',50);\n setPlannerSpeed('plannerWeekend',50);\n }\n if(name==='unlimited mode'){\n $('plannerNightOnly').checked=false;\n $('plannerQuietEnabled').checked=false;\n setPlannerSpeed('plannerWeekday',0);\n setPlannerSpeed('plannerWeekend',0);\n }\n updatePlannerCurrentSummary();\n }\n\n async function loadPlannerPreview(){\n try{\n const limit=plannerHistoryExpanded?100:10;\n const j=await plannerApiJson(`${PLANNER_API_BASE}/preview?history_limit=${limit}`);\n renderPlannerPreview(j.preview||{});\n renderPlannerHistory(j.history||[], Number(j.history_total ?? (j.history||[]).length));\n }catch(e){\n const box=$('plannerPreview');\n if(box) box.innerHTML=`<span class=\"text-danger\">${esc(e.message||'Planner preview failed')}</span>`;\n }\n }\n\n async function loadDownloadPlanner(){\n ensurePlannerToolsUI();\n try{\n const j=await plannerApiJson(PLANNER_API_BASE);\n fillPlanner(j.settings||{});\n await loadPlannerPreview();\n }catch(e){\n const box=$('plannerPreview');\n if(box) box.innerHTML=`<span class=\"text-danger\">${esc(e.message||'Planner settings failed')}</span>`;\n }\n }\n\n async function saveDownloadPlanner(){\n setBusy(true);\n try{\n // Note: Save uses the canonical Planner endpoint registered on the shared API blueprint.\n const payload=plannerPayload();\n const j=await post(PLANNER_API_BASE,payload);\n fillPlanner(j.settings||payload);\n await loadPlannerPreview();\n toast('Download planner saved','success');\n }catch(e){\n toast(e.message,'danger');\n }finally{\n setBusy(false);\n }\n }\n\n async function applyDownloadPlannerNow(dryRun=false){\n setBusy(true);\n try{\n const j=await post(`${PLANNER_API_BASE}/check`,{dry_run:!!dryRun});\n const r=j.result||{};\n if(r.settings) fillPlanner(r.settings);\n renderPlannerPreview(r.preview||r);\n if(r.history) renderPlannerHistory(r.history, r.history_total ?? r.history.length);\n else await loadPlannerPreview();\n toastMessage('toast.plannerApplied','success',{dryRun,paused:r.paused,resumed:r.resumed,limitsChanged:r.limits_changed});\n }catch(e){\n toast(e.message,'danger');\n }finally{\n setBusy(false);\n }\n }\n\n async function setPlannerOverride(){\n setBusy(true);\n try{\n const seconds=Number($('plannerOverrideSeconds')?.value||0);\n await post(`${PLANNER_API_BASE}/override`,{seconds});\n toast(seconds?'Planner override set':'Planner override cleared','success');\n await loadDownloadPlanner();\n }catch(e){\n toast(e.message,'danger');\n }finally{\n setBusy(false);\n }\n }\n";
|