librelad 1b0040dbf1 refactor(tasks): decompose tasks-manager god-file into 8 responsibility files
Faithful brace-aware split of tasks-manager.js (2664->491 line base) into
list-render, data-load, log-stream, row-expand, actions, modals, logs-modal,
format — augmenting TasksManager.prototype. First removed 4 provably-dead
duplicate defs (the earlier init/setupAutoRefresh/startLogStreaming/loadTaskLogs
that the later defs already overrode — behavior-preserving). Methods relocated
verbatim via a brace-aware Node extractor (handles strings/templates/comments/
regex, fixing the line-heuristic over-capture). Verified: all 60 (deduped)
methods present exactly once, no dups, all 9 files node --check clean. Wired
after the base in the task-system loader (async=false-ordered).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: librelad <librelad@digitalangels.vip>
2026-05-30 14:53:19 +01:00

178 lines
8.2 KiB
JavaScript

// Auto-extracted from tasks-manager.js (verbatim) — augments TasksManager.prototype. Loaded after the base.
Object.assign(TasksManager.prototype, {
// Confirmation modal for deleteTask. Uses the shared openEoModal so it
// visually matches every other destructive confirmation on the app
// (Uninstall, Apply Configuration, etc). Resolves true if the user
// confirms Delete, false on Cancel / backdrop click / close.
_showDeleteTaskModal(task, isActive) {
return new Promise((resolve) => {
const escHtml = (s) => String(s == null ? '' : s)
.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
// Friendly title + app icon — mirrors the completion-toast format so
// the modal's identity matches every other surface.
const { isSystemTask, actionTitle, displayName, icon: taskIcon } = this._taskNotificationDescriptor(task);
// For the modal we omit the "LibrePortal" subject (the eyebrow already
// says "Delete Task") and just lead with the action: "Update Config".
const modalSubject = isSystemTask ? '' : displayName;
const taskLabel = modalSubject
? `${actionTitle} ${modalSubject}`
: (actionTitle || (task && task.id) || 'Unknown task');
const taskStatus = (task && task.status) || 'unknown';
const warningTitle = isActive ? 'Active task' : 'This cannot be undone';
const warningText = isActive
? 'This task is still running or queued. It will be cancelled first, then deleted.'
: 'The task and its logs will be permanently removed.';
const bodyHtml = `
<div class="eo-empty-state danger" role="status">
<div class="eo-empty-state-icon">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
<line x1="12" y1="9" x2="12" y2="13"></line>
<line x1="12" y1="17" x2="12.01" y2="17"></line>
</svg>
</div>
<div class="eo-empty-state-body">
<p class="eo-empty-state-title">${escHtml(warningTitle)}</p>
<p class="eo-empty-state-text">${escHtml(warningText)}</p>
</div>
</div>
${window.eoBadgeRow ? window.eoBadgeRow([
{ label: `Status: ${taskStatus}`, variant: isActive ? 'warning' : 'info' }
]) : ''}
`;
let decided = false;
const finish = (val, modal) => {
if (decided) return;
decided = true;
if (modal) modal.close();
resolve(val);
};
window.openEoModal({
id: 'delete-task-modal',
size: 'sm',
icon: taskIcon || undefined,
iconAlt: displayName || 'Task',
eyebrow: 'Delete Task',
title: taskLabel,
desc: 'Confirm to delete this task.',
body: bodyHtml,
actions: [
{ label: 'Delete Task', variant: 'danger', onClick: (m) => finish(true, m) },
{ label: 'Cancel', variant: 'secondary', onClick: (m) => finish(false, m) }
],
onClose: () => finish(false, null)
});
});
},
// Confirmation modal for clearAllTasks. Same openEoModal shape as
// _showDeleteTaskModal so visual identity matches every other destructive
// confirmation (Uninstall, Delete Task, …). Adds a "Cancel running tasks
// too" toggle (off by default) — when off, running/queued tasks are
// skipped; when on, they're cancelled first then deleted.
// Resolves {confirmed, cancelRunning}. Cancel/backdrop/close → confirmed=false.
// mode: 'all' (everything) or 'selected' (user-picked subset) — just
// shapes the title/copy.
_showClearAllModal(tasks, mode) {
return new Promise((resolve) => {
const escHtml = (s) => String(s == null ? '' : s)
.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
const isActive = (t) => t && (t.status === 'running' || t.status === 'queued' || t.status === 'pending');
const total = (tasks || []).length;
const runningCount = (tasks || []).filter(isActive).length;
const terminalCount = total - runningCount;
const bodyHtml = `
<div class="eo-empty-state danger" role="status">
<div class="eo-empty-state-icon">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"></path>
<line x1="12" y1="9" x2="12" y2="13"></line>
<line x1="12" y1="17" x2="12.01" y2="17"></line>
</svg>
</div>
<div class="eo-empty-state-body">
<p class="eo-empty-state-title">This cannot be undone</p>
<p class="eo-empty-state-text">All selected tasks and their logs will be permanently removed.</p>
</div>
</div>
${window.eoBadgeRow ? window.eoBadgeRow([
{ label: `Total: ${total}`, variant: 'info' },
...(runningCount > 0 ? [{ label: `Running: ${runningCount}`, variant: 'warning' }] : []),
...(terminalCount > 0 ? [{ label: `Terminal: ${terminalCount}`, variant: 'success' }] : []),
]) : ''}
${runningCount > 0 ? `
<label class="eo-toggle eo-toggle-card" data-eo-toggle-row>
<input type="checkbox" id="clear-all-cancel-running">
<span class="eo-toggle-track"></span>
<span class="eo-toggle-text">
<span class="eo-toggle-text-title">Cancel running tasks too</span>
<span class="eo-toggle-text-help">Off: skip the ${runningCount} active task${runningCount === 1 ? '' : 's'}. On: cancel them first, then delete.</span>
</span>
</label>
` : ''}
`;
let decided = false;
const finish = (val, modal) => {
if (decided) return;
decided = true;
if (modal) modal.close();
resolve(val);
};
// Title shape depends on which path got us here:
// selected → "Delete N selected task(s)?" (multi-select chip)
// all → "Delete all N tasks?" (legacy Clear All)
const isSelectedMode = mode === 'selected';
const titleText = isSelectedMode
? (total === 1 ? 'Delete 1 selected task?' : `Delete ${total} selected tasks?`)
: (total === 1 ? 'Delete 1 task?' : `Delete all ${total} tasks?`);
const m = window.openEoModal({
id: 'clear-all-tasks-modal',
size: 'sm',
eyebrow: isSelectedMode ? 'Delete Selected' : 'Delete Tasks',
title: titleText,
desc: isSelectedMode
? 'Confirm to delete the ticked tasks.'
: 'Confirm to delete every task on the list.',
body: bodyHtml,
actions: [
{
label: 'Delete',
variant: 'danger',
onClick: (modal) => {
const cb = modal.bodyEl.querySelector('#clear-all-cancel-running');
finish({ confirmed: true, cancelRunning: !!(cb && cb.checked) }, modal);
}
},
{ label: 'Cancel', variant: 'secondary', onClick: (modal) => finish({ confirmed: false }, modal) }
],
onClose: () => finish({ confirmed: false }, null)
});
// Live-update the danger button's label so the user knows exactly
// what will happen as they flip the toggle. No-op when no running
// tasks (no toggle in the body in that case).
const cb = m.bodyEl.querySelector('#clear-all-cancel-running');
const deleteBtn = m.contentEl.querySelector('.btn-danger');
const updateLabel = () => {
if (!deleteBtn) return;
const cancelRunning = !!(cb && cb.checked);
if (runningCount > 0 && !cancelRunning) {
deleteBtn.textContent = `Delete ${terminalCount} (skip ${runningCount} running)`;
} else {
deleteBtn.textContent = total === 1 ? 'Delete Task' : `Delete ${total} Tasks`;
}
};
if (cb) cb.addEventListener('change', updateLabel);
updateLabel();
});
},
});