Fix [[ ]] visual bug and add missing log type handlers
Root cause: CSS button::before/after adds [ ] universally, but many buttons had hardcoded [ text ] content, producing [[ text ]]. - Strip manual [ ] wrappers from all button text in HTML and JS - Fix JS textContent assignments for compare mode buttons - Fix dynamic button HTML strings in execution details panel - Add formatLogEntry handlers for previously unhandled action types: dry_run_skipped, execution_timeout, goto_error, step_error, workflow_result, params, server_restart_recovery - Unknown log actions now show action name instead of raw JSON - Add cron schedule type display in schedules list Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -875,15 +875,15 @@
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 10px; align-items: center;">
|
||||
<button onclick="clearFilters()" class="small">[ Clear Filters ]</button>
|
||||
<button onclick="clearFilters()" class="small">Clear Filters</button>
|
||||
<span id="filterStats" style="color: var(--terminal-green); font-size: 0.9em; font-family: var(--font-mono);"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button onclick="refreshData()">[ 🔄 Refresh ]</button>
|
||||
<button onclick="clearCompletedExecutions()" style="margin-left: 10px;">[ 🗑️ Clear Completed ]</button>
|
||||
<button onclick="toggleCompareMode()" id="compareModeBtn" style="margin-left: 10px;">[ 📊 Compare Mode ]</button>
|
||||
<button onclick="compareSelectedExecutions()" id="compareBtn" style="margin-left: 10px; display: none;">[ ⚖️ Compare Selected ]</button>
|
||||
<button onclick="refreshData()">🔄 Refresh</button>
|
||||
<button onclick="clearCompletedExecutions()" style="margin-left: 10px;">🗑️ Clear Completed</button>
|
||||
<button onclick="toggleCompareMode()" id="compareModeBtn" style="margin-left: 10px;">📊 Compare Mode</button>
|
||||
<button onclick="compareSelectedExecutions()" id="compareBtn" style="margin-left: 10px; display: none;">⚖️ Compare Selected</button>
|
||||
|
||||
<div id="compareInstructions" style="display: none; background: rgba(255, 176, 0, 0.1); border: 2px solid var(--terminal-amber); padding: 12px; margin: 15px 0; color: var(--terminal-amber);">
|
||||
Select 2-5 executions to compare their outputs. Click executions to toggle selection.
|
||||
@@ -900,8 +900,8 @@
|
||||
<p style="color: var(--terminal-green); margin-bottom: 20px;">Execute a command on selected workers instantly</p>
|
||||
|
||||
<div style="display: flex; gap: 10px; margin-bottom: 15px;">
|
||||
<button onclick="showCommandTemplates()" style="flex: 0;">[ 📋 Templates ]</button>
|
||||
<button onclick="showCommandHistory()" style="flex: 0;">[ 🕐 History ]</button>
|
||||
<button onclick="showCommandTemplates()" style="flex: 0;">📋 Templates</button>
|
||||
<button onclick="showCommandHistory()" style="flex: 0;">🕐 History</button>
|
||||
</div>
|
||||
|
||||
<label style="display: block; margin-bottom: 10px; font-weight: 600;">Execution Mode:</label>
|
||||
@@ -929,16 +929,16 @@
|
||||
<div class="loading">Loading workers...</div>
|
||||
</div>
|
||||
<div style="margin-bottom: 15px;">
|
||||
<button onclick="selectAllWorkers()" class="small">[ Select All ]</button>
|
||||
<button onclick="selectOnlineWorkers()" class="small">[ Online Only ]</button>
|
||||
<button onclick="deselectAllWorkers()" class="small">[ Clear All ]</button>
|
||||
<button onclick="selectAllWorkers()" class="small">Select All</button>
|
||||
<button onclick="selectOnlineWorkers()" class="small">Online Only</button>
|
||||
<button onclick="deselectAllWorkers()" class="small">Clear All</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label style="display: block; margin-bottom: 10px; margin-top: 20px; font-weight: 600;">Command:</label>
|
||||
<textarea id="quickCommand" placeholder="Enter command to execute (e.g., 'uptime' or 'df -h')"></textarea>
|
||||
|
||||
<button onclick="executeQuickCommand()">[ ▶️ Execute Command ]</button>
|
||||
<button onclick="executeQuickCommand()">▶️ Execute Command</button>
|
||||
|
||||
<div id="quickCommandResult" style="margin-top: 20px;"></div>
|
||||
</div>
|
||||
@@ -950,8 +950,8 @@
|
||||
<h3>⏰ Scheduled Commands</h3>
|
||||
<p style="color: var(--terminal-green); margin-bottom: 20px;">Automate command execution with flexible scheduling</p>
|
||||
|
||||
<button onclick="showCreateSchedule()">[ ➕ Create Schedule ]</button>
|
||||
<button onclick="refreshData()" style="margin-left: 10px;">[ 🔄 Refresh ]</button>
|
||||
<button onclick="showCreateSchedule()">➕ Create Schedule</button>
|
||||
<button onclick="refreshData()" style="margin-left: 10px;">🔄 Refresh</button>
|
||||
|
||||
<div id="scheduleList" style="margin-top: 20px;"><div class="loading">Loading...</div></div>
|
||||
</div>
|
||||
@@ -987,7 +987,7 @@
|
||||
<div class="modal-content">
|
||||
<h2>Execution Details</h2>
|
||||
<div id="executionDetails"></div>
|
||||
<button onclick="closeModal('viewExecutionModal')">[ Close ]</button>
|
||||
<button onclick="closeModal('viewExecutionModal')">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -996,7 +996,7 @@
|
||||
<div class="modal-content">
|
||||
<h2>Command Templates</h2>
|
||||
<div id="templateList" style="max-height: 400px; overflow-y: auto;"></div>
|
||||
<button onclick="closeModal('commandTemplatesModal')">[ Close ]</button>
|
||||
<button onclick="closeModal('commandTemplatesModal')">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1005,7 +1005,7 @@
|
||||
<div class="modal-content">
|
||||
<h2>Command History</h2>
|
||||
<div id="historyList" style="max-height: 400px; overflow-y: auto;"></div>
|
||||
<button onclick="closeModal('commandHistoryModal')">[ Close ]</button>
|
||||
<button onclick="closeModal('commandHistoryModal')">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1014,7 +1014,7 @@
|
||||
<div class="modal-content" style="max-width: 90%; max-height: 90vh;">
|
||||
<h2>⚖️ Execution Comparison</h2>
|
||||
<div id="compareContent" style="max-height: 70vh; overflow-y: auto; padding: 20px;"></div>
|
||||
<button onclick="closeModal('compareExecutionsModal')">[ Close ]</button>
|
||||
<button onclick="closeModal('compareExecutionsModal')">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1031,7 +1031,7 @@
|
||||
<div style="display:flex;gap:10px;margin-top:20px;">
|
||||
<button onclick="submitParamForm()"
|
||||
style="flex:1;padding:8px;background:rgba(0,255,65,.1);border:1px solid var(--terminal-green);color:var(--terminal-green);font-family:var(--font-mono);cursor:pointer;font-size:.9em;">
|
||||
[ ▶ Run ]
|
||||
▶ Run
|
||||
</button>
|
||||
<button onclick="closeParamModal()"
|
||||
style="padding:8px 16px;background:transparent;border:1px solid #555;color:#888;font-family:var(--font-mono);cursor:pointer;font-size:.9em;">
|
||||
@@ -1056,7 +1056,7 @@
|
||||
<input type="url" id="editWorkflowWebhookUrl" placeholder="https://example.com/webhook">
|
||||
<div id="editWorkflowError" style="color:var(--terminal-red);font-size:0.85em;margin-top:8px;display:none;"></div>
|
||||
<div style="margin-top:16px;display:flex;gap:10px;">
|
||||
<button onclick="saveWorkflow()">[ 💾 Save ]</button>
|
||||
<button onclick="saveWorkflow()">💾 Save</button>
|
||||
<button onclick="closeModal('editWorkflowModal')">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1091,8 +1091,8 @@
|
||||
</div>
|
||||
|
||||
<div style="margin-top: 20px;">
|
||||
<button onclick="createSchedule()">[ Create Schedule ]</button>
|
||||
<button onclick="closeModal('createScheduleModal')" style="margin-left: 10px;">[ Cancel ]</button>
|
||||
<button onclick="createSchedule()">Create Schedule</button>
|
||||
<button onclick="closeModal('createScheduleModal')" style="margin-left: 10px;">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1215,7 +1215,7 @@
|
||||
` : ''}
|
||||
</div>
|
||||
${currentUser && currentUser.isAdmin ? `
|
||||
<button class="danger small" onclick="deleteWorker('${w.id}', '${w.name}')">[ 🗑️ Delete ]</button>
|
||||
<button class="danger small" onclick="deleteWorker('${w.id}', '${w.name}')">🗑️ Delete</button>
|
||||
` : ''}
|
||||
</div>
|
||||
</div>
|
||||
@@ -1292,6 +1292,8 @@
|
||||
scheduleDesc = `Every ${s.schedule_value} hour(s)`;
|
||||
} else if (s.schedule_type === 'daily') {
|
||||
scheduleDesc = `Daily at ${s.schedule_value}`;
|
||||
} else if (s.schedule_type === 'cron') {
|
||||
scheduleDesc = `Cron: ${s.schedule_value}`;
|
||||
}
|
||||
|
||||
const nextRun = s.next_run ? new Date(s.next_run).toLocaleString() : 'Not scheduled';
|
||||
@@ -1498,7 +1500,7 @@
|
||||
|
||||
// Add "Load More" button if there are more executions
|
||||
if (data.hasMore) {
|
||||
const loadMoreBtn = `<button onclick="loadMoreExecutions()" style="width: 100%; margin-top: 15px;">[ Load More Executions ]</button>`;
|
||||
const loadMoreBtn = `<button onclick="loadMoreExecutions()" style="width: 100%; margin-top: 15px;">Load More Executions</button>`;
|
||||
document.getElementById('executionList').innerHTML += loadMoreBtn;
|
||||
}
|
||||
|
||||
@@ -1640,13 +1642,13 @@
|
||||
const instructions = document.getElementById('compareInstructions');
|
||||
|
||||
if (compareMode) {
|
||||
btn.textContent = '[ ✗ Exit Compare Mode ]';
|
||||
btn.textContent = '✗ Exit Compare Mode';
|
||||
btn.style.borderColor = 'var(--terminal-amber)';
|
||||
btn.style.color = 'var(--terminal-amber)';
|
||||
compareBtn.style.display = 'inline-block';
|
||||
instructions.style.display = 'block';
|
||||
} else {
|
||||
btn.textContent = '[ 📊 Compare Mode ]';
|
||||
btn.textContent = '📊 Compare Mode';
|
||||
btn.style.borderColor = '';
|
||||
btn.style.color = '';
|
||||
compareBtn.style.display = 'none';
|
||||
@@ -1672,9 +1674,9 @@
|
||||
// Update compare button text
|
||||
const compareBtn = document.getElementById('compareBtn');
|
||||
if (selectedExecutions.size >= 2) {
|
||||
compareBtn.textContent = `[ ⚖️ Compare Selected (${selectedExecutions.size}) ]`;
|
||||
compareBtn.textContent = `⚖️ Compare Selected (${selectedExecutions.size})`;
|
||||
} else {
|
||||
compareBtn.textContent = '[ ⚖️ Compare Selected ]';
|
||||
compareBtn.textContent = '⚖️ Compare Selected';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1999,17 +2001,17 @@
|
||||
|
||||
// Abort button (only for running executions)
|
||||
if (execution.status === 'running') {
|
||||
html += `<button onclick="abortExecution('${executionId}')" style="background-color: var(--terminal-red); border-color: var(--terminal-red);">[ ⛔ Abort Execution ]</button>`;
|
||||
html += `<button onclick="abortExecution('${executionId}')" style="background-color: var(--terminal-red); border-color: var(--terminal-red);">⛔ Abort Execution</button>`;
|
||||
}
|
||||
|
||||
// Re-run button (only for quick commands with command in logs)
|
||||
const commandLog = execution.logs?.find(l => l.action === 'command_sent');
|
||||
if (commandLog && commandLog.command) {
|
||||
html += `<button onclick="rerunCommand('${escapeHtml(commandLog.command)}', '${commandLog.worker_id}')">[ 🔄 Re-run Command ]</button>`;
|
||||
html += `<button onclick="rerunCommand('${escapeHtml(commandLog.command)}', '${commandLog.worker_id}')">🔄 Re-run Command</button>`;
|
||||
}
|
||||
|
||||
// Download logs button
|
||||
html += `<button onclick="downloadExecutionLogs('${executionId}')">[ 💾 Download Logs ]</button>`;
|
||||
html += `<button onclick="downloadExecutionLogs('${executionId}')">💾 Download Logs</button>`;
|
||||
|
||||
html += '</div>';
|
||||
|
||||
@@ -2170,8 +2172,87 @@
|
||||
`;
|
||||
}
|
||||
|
||||
if (log.action === 'dry_run_skipped') {
|
||||
return `
|
||||
<div class="log-entry" style="border-left-color: var(--terminal-amber);">
|
||||
<div class="log-timestamp">[${timestamp}]</div>
|
||||
<div class="log-title" style="color: var(--terminal-amber);">🔍 [DRY RUN] Step ${log.step} Skipped: ${escapeHtml(log.step_name || '')}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
if (log.action === 'execution_timeout') {
|
||||
return `
|
||||
<div class="log-entry failed">
|
||||
<div class="log-timestamp">[${timestamp}]</div>
|
||||
<div class="log-title">⏱️ Execution Timeout</div>
|
||||
<div class="log-details">
|
||||
<div class="log-field">${escapeHtml(log.message || 'Execution exceeded maximum allowed time')}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
if (log.action === 'goto_error') {
|
||||
return `
|
||||
<div class="log-entry failed">
|
||||
<div class="log-timestamp">[${timestamp}]</div>
|
||||
<div class="log-title">✗ Goto Error</div>
|
||||
<div class="log-details">
|
||||
<div class="log-field"><span class="log-label">Target:</span> ${escapeHtml(String(log.target || ''))}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
if (log.action === 'step_error') {
|
||||
return `
|
||||
<div class="log-entry failed">
|
||||
<div class="log-timestamp">[${timestamp}]</div>
|
||||
<div class="log-title">✗ Step ${log.step} Error: ${escapeHtml(log.step_name || '')}</div>
|
||||
<div class="log-details">
|
||||
<div class="log-field"><span class="log-label">Error:</span> ${escapeHtml(log.error || '')}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
if (log.action === 'workflow_result') {
|
||||
const statusIcon = log.success ? '✓' : '✗';
|
||||
const statusClass = log.success ? 'success' : 'failed';
|
||||
return `
|
||||
<div class="log-entry ${statusClass}">
|
||||
<div class="log-timestamp">[${timestamp}]</div>
|
||||
<div class="log-title">${statusIcon} Workflow Result: ${log.success ? 'Success' : 'Failed'}</div>
|
||||
${log.message ? `<div class="log-details"><div class="log-field">${escapeHtml(log.message)}</div></div>` : ''}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
if (log.action === 'params') {
|
||||
const paramStr = Object.entries(log.params || {}).map(([k, v]) => `${escapeHtml(k)}=${escapeHtml(String(v))}`).join(', ');
|
||||
return `
|
||||
<div class="log-entry" style="border-left-color: #555;">
|
||||
<div class="log-timestamp">[${timestamp}]</div>
|
||||
<div class="log-title" style="color: #888;">⚙ Parameters: ${paramStr || '(none)'}</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
if (log.action === 'server_restart_recovery') {
|
||||
return `
|
||||
<div class="log-entry failed">
|
||||
<div class="log-timestamp">[${timestamp}]</div>
|
||||
<div class="log-title" style="color: var(--terminal-red);">⚠️ Server Restart Recovery</div>
|
||||
<div class="log-details">
|
||||
<div class="log-field">${escapeHtml(log.message || 'Execution interrupted by server restart')}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// Fallback for unknown log types
|
||||
return `<div class="log-entry"><pre>${JSON.stringify(log, null, 2)}</pre></div>`;
|
||||
return `<div class="log-entry" style="border-left-color:#555;"><div class="log-timestamp">[${timestamp}]</div><div class="log-title" style="color:#666;">${escapeHtml(log.action || 'unknown')}</div></div>`;
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
|
||||
Reference in New Issue
Block a user