forked from Rockachopa/Timmy-time-dashboard
Task Queue system: - New /tasks page with three-column layout (Pending/Active/Completed) - Full CRUD API at /api/tasks with approve/veto/modify/pause/cancel/retry - SQLite persistence in task_queue table - WebSocket live updates via ws_manager - Create task modal with agent assignment and priority - Auto-approve rules for low-risk tasks - HTMX polling for real-time column updates - HOME TASK buttons now link to task queue with agent pre-selected - MARKET HIRE buttons link to task queue with agent pre-selected Work Order system: - External submission API for agents/users (POST /work-orders/submit) - Risk scoring and configurable auto-execution thresholds - Dashboard at /work-orders/queue with approve/reject/execute flow - Integration with swarm task system for execution UI & Dashboard bug fixes: - EVENTS: add startup event so page is never empty - LEDGER: fix empty filter params in URL - MISSION CONTROL: LLM backend and model now read from /health - MISSION CONTROL: agent count fallback to /swarm/agents - SWARM: HTMX fallback loads initial data if WebSocket is slow - MEMORY: add edit/delete buttons for personal facts - UPGRADES: add empty state guidance with links - BRIEFING: add regenerate button and POST /briefing/regenerate endpoint Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
106 lines
4.5 KiB
HTML
106 lines
4.5 KiB
HTML
<div id="task-{{ task.id }}" class="task-card priority-{{ task.priority.value }}">
|
|
<div class="task-card-title">{{ task.title | e }}</div>
|
|
|
|
{% if task.description %}
|
|
<div class="task-card-desc">{{ task.description | e }}</div>
|
|
{% endif %}
|
|
|
|
<div class="task-card-meta">
|
|
<span class="task-badge task-badge-{{ task.priority.value }}">{{ task.priority.value | upper }}</span>
|
|
<span class="task-badge task-badge-{{ task.status.value }}">{{ task.status.value | replace("_", " ") | upper }}</span>
|
|
<span class="task-badge">{{ task.assigned_to | e }}</span>
|
|
<span class="task-badge">by {{ task.created_by | e }}</span>
|
|
</div>
|
|
|
|
{% if task.steps %}
|
|
<div class="task-steps">
|
|
{% for step in task.steps %}
|
|
<div class="task-step {{ step.status if step.status else 'pending' }}">
|
|
{% if step.status == 'completed' %}✓{% elif step.status == 'running' %}▶{% else %}○{% endif %}
|
|
{{ step.description if step.description else step }}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
{% if task.result %}
|
|
<div class="task-result" title="Click to expand">{{ task.result | e }}</div>
|
|
{% endif %}
|
|
|
|
<!-- Action buttons based on status -->
|
|
{% if task.status.value == 'pending_approval' %}
|
|
<div class="task-actions">
|
|
<button class="task-btn task-btn-approve"
|
|
hx-post="/tasks/{{ task.id }}/approve"
|
|
hx-target="#task-{{ task.id }}"
|
|
hx-swap="outerHTML">APPROVE</button>
|
|
<button class="task-btn task-btn-modify"
|
|
onclick="toggleModify('{{ task.id }}')">MODIFY</button>
|
|
<button class="task-btn task-btn-veto"
|
|
hx-post="/tasks/{{ task.id }}/veto"
|
|
hx-target="#task-{{ task.id }}"
|
|
hx-swap="outerHTML">VETO</button>
|
|
</div>
|
|
<!-- Inline modify form (hidden by default) -->
|
|
<form id="modify-{{ task.id }}" style="display:none; margin-top:8px;"
|
|
hx-post="/tasks/{{ task.id }}/modify"
|
|
hx-target="#task-{{ task.id }}"
|
|
hx-swap="outerHTML">
|
|
<input type="text" name="title" value="{{ task.title | e }}" style="width:100%; padding:4px; margin-bottom:4px; background:var(--bg-tertiary); color:var(--text); border:1px solid var(--border); border-radius:4px; font-size:0.8rem;">
|
|
<textarea name="description" style="width:100%; padding:4px; margin-bottom:4px; background:var(--bg-tertiary); color:var(--text); border:1px solid var(--border); border-radius:4px; font-size:0.8rem; min-height:40px;">{{ task.description | e }}</textarea>
|
|
<button type="submit" class="task-btn task-btn-approve" style="font-size:0.65rem;">SAVE</button>
|
|
<button type="button" class="task-btn task-btn-cancel" style="font-size:0.65rem;" onclick="toggleModify('{{ task.id }}')">CANCEL</button>
|
|
</form>
|
|
|
|
{% elif task.status.value == 'approved' %}
|
|
<div class="task-actions">
|
|
<span style="font-size:0.7rem; color:var(--green);">Approved — waiting to run</span>
|
|
<button class="task-btn task-btn-veto"
|
|
hx-post="/tasks/{{ task.id }}/veto"
|
|
hx-target="#task-{{ task.id }}"
|
|
hx-swap="outerHTML">VETO</button>
|
|
</div>
|
|
|
|
{% elif task.status.value == 'running' %}
|
|
<div class="task-actions">
|
|
<button class="task-btn task-btn-pause"
|
|
hx-post="/tasks/{{ task.id }}/pause"
|
|
hx-target="#task-{{ task.id }}"
|
|
hx-swap="outerHTML">PAUSE</button>
|
|
<button class="task-btn task-btn-cancel"
|
|
hx-post="/tasks/{{ task.id }}/cancel"
|
|
hx-target="#task-{{ task.id }}"
|
|
hx-swap="outerHTML">CANCEL</button>
|
|
</div>
|
|
|
|
{% elif task.status.value == 'paused' %}
|
|
<div class="task-actions">
|
|
<button class="task-btn task-btn-approve"
|
|
hx-post="/tasks/{{ task.id }}/approve"
|
|
hx-target="#task-{{ task.id }}"
|
|
hx-swap="outerHTML">RESUME</button>
|
|
<button class="task-btn task-btn-cancel"
|
|
hx-post="/tasks/{{ task.id }}/cancel"
|
|
hx-target="#task-{{ task.id }}"
|
|
hx-swap="outerHTML">CANCEL</button>
|
|
</div>
|
|
|
|
{% elif task.status.value == 'failed' %}
|
|
<div class="task-actions">
|
|
<button class="task-btn task-btn-retry"
|
|
hx-post="/tasks/{{ task.id }}/retry"
|
|
hx-target="#task-{{ task.id }}"
|
|
hx-swap="outerHTML">RETRY</button>
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div class="task-time">{{ task.created_at[:16].replace("T", " ") }}</div>
|
|
</div>
|
|
|
|
<script>
|
|
function toggleModify(id) {
|
|
var form = document.getElementById('modify-' + id);
|
|
form.style.display = form.style.display === 'none' ? 'block' : 'none';
|
|
}
|
|
</script>
|