Enhance jobs page layout with job IDs, worker details, and improved actions

- Add Job ID and Queue ID columns for better job tracking
- Separate worker assignment details from action buttons
- Reorganize action buttons side by side (restart + delete)
- Add confirmation dialog for delete actions
- Update CSS grid layout to accommodate new columns
- Real-time updates for worker details column
parent e04521ef
......@@ -8,7 +8,7 @@
.jobs-table { background: white; border-radius: 12px; box-shadow: 0 2px 10px rgba(0,0,0,0.05); overflow: hidden; }
.table-header { padding: 2rem; border-bottom: 1px solid #e5e7eb; }
.table-header h2 { margin: 0; color: #1e293b; }
.job-row { display: grid; grid-template-columns: 1fr 2fr 1fr 100px 100px 120px; gap: 1rem; padding: 1rem 2rem; border-bottom: 1px solid #f1f5f9; align-items: center; }
.job-row { display: grid; grid-template-columns: 80px 80px 1fr 2fr 1fr 1fr 100px 100px 120px; gap: 1rem; padding: 1rem 2rem; border-bottom: 1px solid #f1f5f9; align-items: center; }
.job-row:last-child { border-bottom: none; }
.job-type { font-weight: 600; color: #1e293b; }
.job-data { color: #64748b; font-size: 0.9rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
......@@ -119,6 +119,12 @@
}
}
// Update worker details
const workerDetailsElement = jobRow.querySelector('.worker-details');
if (workerDetailsElement && jobUpdate.result) {
workerDetailsElement.textContent = jobUpdate.result.status || '-';
}
// Add progress info for processing jobs
if (jobUpdate.status === 'processing' && jobUpdate.result) {
let progressElement = jobRow.querySelector('.job-progress');
......@@ -167,6 +173,10 @@
actionsElement.appendChild(cancelBtn);
} else if (status === 'cancelled') {
const buttonContainer = document.createElement('div');
buttonContainer.style.display = 'flex';
buttonContainer.style.gap = '0.5rem';
const restartBtn = document.createElement('button');
restartBtn.className = 'restart-btn';
restartBtn.style.background = '#10b981';
......@@ -176,12 +186,9 @@
restartBtn.style.borderRadius = '4px';
restartBtn.style.fontSize = '0.8rem';
restartBtn.style.cursor = 'pointer';
restartBtn.style.marginRight = '0.5rem';
restartBtn.textContent = 'Restart';
restartBtn.onclick = () => performJobAction(jobId, 'restart', restartBtn);
actionsElement.appendChild(restartBtn);
const deleteBtn = document.createElement('button');
deleteBtn.className = 'delete-btn';
deleteBtn.style.background = '#dc2626';
......@@ -192,9 +199,15 @@
deleteBtn.style.fontSize = '0.8rem';
deleteBtn.style.cursor = 'pointer';
deleteBtn.textContent = 'Delete';
deleteBtn.onclick = () => performJobAction(jobId, 'delete', deleteBtn);
deleteBtn.onclick = () => {
if (confirm('Are you sure you want to delete this job? This action cannot be undone.')) {
performJobAction(jobId, 'delete', deleteBtn);
}
};
actionsElement.appendChild(deleteBtn);
buttonContainer.appendChild(restartBtn);
buttonContainer.appendChild(deleteBtn);
actionsElement.appendChild(buttonContainer);
} else if (status === 'queued') {
const deleteBtn = document.createElement('button');
deleteBtn.className = 'delete-btn';
......@@ -206,7 +219,11 @@
deleteBtn.style.fontSize = '0.8rem';
deleteBtn.style.cursor = 'pointer';
deleteBtn.textContent = 'Delete';
deleteBtn.onclick = () => performJobAction(jobId, 'delete', deleteBtn);
deleteBtn.onclick = () => {
if (confirm('Are you sure you want to delete this job? This action cannot be undone.')) {
performJobAction(jobId, 'delete', deleteBtn);
}
};
actionsElement.appendChild(deleteBtn);
} else if (status === 'failed') {
......@@ -220,7 +237,11 @@
deleteBtn.style.fontSize = '0.8rem';
deleteBtn.style.cursor = 'pointer';
deleteBtn.textContent = 'Delete';
deleteBtn.onclick = () => performJobAction(jobId, 'delete', deleteBtn);
deleteBtn.onclick = () => {
if (confirm('Are you sure you want to delete this job? This action cannot be undone.')) {
performJobAction(jobId, 'delete', deleteBtn);
}
};
actionsElement.appendChild(deleteBtn);
}
......@@ -424,11 +445,20 @@
</div>
{% for job in queue_items %}
<div class="job-row" data-job-id="{{ job.id }}">
<div class="job-id" style="font-family: monospace; font-size: 0.8rem; color: #6b7280;">{{ job.job_id or 'N/A' }}</div>
<div class="queue-id" style="font-family: monospace; font-size: 0.8rem; color: #6b7280;">{{ job.id }}</div>
<div class="job-type">{{ job.request_type.title() }}</div>
<div class="job-data" title="{{ job.data.get('prompt', job.data.get('description', 'N/A')) }}">
{{ job.data.get('prompt', job.data.get('description', 'N/A'))[:50] }}{% if job.data.get('prompt', job.data.get('description', 'N/A'))|length > 50 %}...{% endif %}
</div>
<div class="job-time">{{ job.created_at[:19] }}</div>
<div class="worker-details" style="font-size: 0.8rem; color: #6b7280;">
{% if job.result and job.result.get('status') %}
{{ job.result.status }}
{% else %}
-
{% endif %}
</div>
<span class="job-status status-{{ job.status }}">
{% if job.status == 'processing' %}<div class="spinner"></div>{% endif %}
{{ job.status.title() }}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment