From the feng-shui audit naming findings: - admin/overview/js/admin-overview.js -> overview-page.js (class AdminOverview -> OverviewPage, window globals + the 'admin-overview' task-refresh id -> overview-page, lazy-load path + typeof/new in config-manager.js). - admin/system/js/admin-system.js -> system-page.js (class AdminSystem -> SystemPage; now sits beside its -page sub-views system-metric-page.js / system-storage-page.js). - tasks/js/tasks-logs-modal.js -> tasks-log-modal.js (singular 'log' to match its sibling tasks-log-stream.js; single path ref in system-loader.js). These were the only page controllers breaking the dominant <thing>-page.js / <Thing>Page convention (ssh-page/peers-page/backup-page/updater-page/ system-metric-page/system-storage-page). Pure renames; node --check clean. Signed-off-by: librelad <librelad@digitalangels.vip>
105 lines
4.0 KiB
JavaScript
105 lines
4.0 KiB
JavaScript
// Auto-extracted from tasks-manager.js (verbatim) — augments TasksManager.prototype. Loaded after the base.
|
||
Object.assign(TasksManager.prototype, {
|
||
async viewTaskLogs(taskId) {
|
||
// Open logs in a modal or new view
|
||
const task = this.tasks.find(t => t.id === taskId);
|
||
if (!task) return;
|
||
|
||
// Load full logs for modal
|
||
const fullLogs = await this.taskManager.readFullTaskLog(taskId);
|
||
|
||
// Create modal with streaming logs
|
||
const modal = document.createElement('div');
|
||
modal.className = 'task-logs-modal';
|
||
modal.innerHTML = `
|
||
<div class="modal-overlay" onclick="closeTaskLogsModal()"></div>
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h3>📋 Task Logs - ${task.id}</h3>
|
||
<div class="log-status">
|
||
<span class="status-indicator" id="log-status-${taskId}">Loading...</span>
|
||
<button class="log-toggle" id="log-toggle-${taskId}" onclick="toggleLogStreaming('${taskId}')">⏸️ Pause</button>
|
||
</div>
|
||
<button class="modal-close" onclick="closeTaskLogsModal()">×</button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<div class="task-info-summary">
|
||
<div class="info-row">
|
||
<strong>Command:</strong> <code>${task.command}</code>
|
||
</div>
|
||
<div class="info-row">
|
||
<strong>Status:</strong> <span class="status-${task.status || 'unknown'}">${this.getStatusIcon(task.status)} ${task.status ? task.status.toUpperCase() : 'UNKNOWN'}</span>
|
||
</div>
|
||
<div class="info-row">
|
||
<strong>Created:</strong> ${new Date(task.createdAt).toLocaleString()}
|
||
</div>
|
||
</div>
|
||
|
||
<div class="logs-section">
|
||
<h4>Execution Logs <span class="log-line-count">(0 lines)</span></h4>
|
||
<div class="log-viewer terminal-style" id="log-viewer-${taskId}">
|
||
${fullLogs.split('\n').map(log => `<div class="log-line">${this.taskManager.parseAnsiColors(log)}</div>`).join('')}
|
||
</div>
|
||
</div>
|
||
|
||
${task.output ? `
|
||
<div class="output-section">
|
||
<h4>Command Output</h4>
|
||
<pre class="output-viewer">${this.taskManager.parseAnsiColors(task.output)}</pre>
|
||
</div>
|
||
` : ''}
|
||
|
||
${task.error ? `
|
||
<div class="error-section">
|
||
<h4>Error Details</h4>
|
||
<pre class="error-viewer">${this.escapeHtml(task.error)}</pre>
|
||
</div>
|
||
` : ''}
|
||
</div>
|
||
</div>
|
||
`;
|
||
|
||
document.body.appendChild(modal);
|
||
|
||
// Update line count
|
||
const lineCount = fullLogs.split('\n').length;
|
||
const lineCountElement = modal.querySelector('.log-line-count');
|
||
if (lineCountElement) {
|
||
lineCountElement.textContent = `(${lineCount} lines)`;
|
||
}
|
||
|
||
// Start streaming if task is running
|
||
if (task.status === 'running' || task.status === 'queued') {
|
||
this.startLogStreaming(taskId, modal);
|
||
}
|
||
|
||
// Store streaming controller
|
||
this.activeLogStreams = this.activeLogStreams || new Map();
|
||
},
|
||
toggleLogStreaming(taskId) {
|
||
const streamData = this.activeLogStreams?.get(taskId);
|
||
if (!streamData) return;
|
||
|
||
const toggleButton = document.getElementById(`log-toggle-${taskId}`);
|
||
const statusIndicator = document.getElementById(`log-status-${taskId}`);
|
||
|
||
if (streamData.isPaused) {
|
||
// Resume streaming
|
||
streamData.isPaused = false;
|
||
if (toggleButton) toggleButton.textContent = '⏸️ Pause';
|
||
if (statusIndicator) {
|
||
statusIndicator.textContent = '🔴 Live';
|
||
statusIndicator.className = 'status-indicator live';
|
||
}
|
||
} else {
|
||
// Pause streaming
|
||
streamData.isPaused = true;
|
||
if (toggleButton) toggleButton.textContent = '▶️ Resume';
|
||
if (statusIndicator) {
|
||
statusIndicator.textContent = '⏸️ Paused';
|
||
statusIndicator.className = 'status-indicator paused';
|
||
}
|
||
}
|
||
},
|
||
});
|