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>
178 lines
8.2 KiB
JavaScript
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, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
|
|
// 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, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
|
|
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();
|
|
});
|
|
},
|
|
});
|