Add dashboard for managing autonomous Hands:
Routes (src/dashboard/routes/hands.py):
- GET /api/hands - List all Hands with status
- GET /api/hands/{name} - Get Hand details
- POST /api/hands/{name}/trigger - Manual trigger
- POST /api/hands/{name}/pause - Pause scheduled Hand
- POST /api/hands/{name}/resume - Resume paused Hand
- GET /api/approvals - List pending approvals
- POST /api/approvals/{id}/approve - Approve request
- POST /api/approvals/{id}/reject - Reject request
- GET /api/executions - List execution history
Templates:
- hands.html - Main dashboard page
- partials/hands_list.html - Active Hands list
- partials/approvals_list.html - Pending approvals
- partials/hand_executions.html - Execution history
Integration:
- Wired up in app.py
- Navigation links in base.html
78 lines
2.9 KiB
HTML
78 lines
2.9 KiB
HTML
{# Hands list partial #}
|
|
{% if hands %}
|
|
<div class="list-group list-group-flush">
|
|
{% for item in hands %}
|
|
{% set hand = item.config %}
|
|
{% set state = item.state %}
|
|
<div class="list-group-item hand-card {{ state.status.value }} p-3">
|
|
<div class="d-flex justify-content-between align-items-start mb-2">
|
|
<div class="d-flex align-items-center gap-2">
|
|
<span class="status-dot {{ state.status.value }}"></span>
|
|
<h6 class="mb-0">{{ hand.name }}</h6>
|
|
{% if not hand.enabled %}
|
|
<span class="badge bg-secondary">Disabled</span>
|
|
{% endif %}
|
|
</div>
|
|
<div class="btn-group btn-group-sm">
|
|
{% if state.status.value == 'running' %}
|
|
<button class="btn btn-outline-info" disabled>Running...</button>
|
|
{% else %}
|
|
<button class="btn btn-outline-success"
|
|
hx-post="/hands/api/hands/{{ hand.name }}/trigger"
|
|
hx-swap="none"
|
|
hx-confirm="Trigger {{ hand.name }} now?">
|
|
Run
|
|
</button>
|
|
{% endif %}
|
|
|
|
{% if state.is_paused %}
|
|
<button class="btn btn-outline-warning"
|
|
hx-post="/hands/api/hands/{{ hand.name }}/resume"
|
|
hx-target="#hands-container">
|
|
Resume
|
|
</button>
|
|
{% else %}
|
|
<button class="btn btn-outline-secondary"
|
|
hx-post="/hands/api/hands/{{ hand.name }}/pause"
|
|
hx-target="#hands-container">
|
|
Pause
|
|
</button>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<p class="small text-muted mb-2">{{ hand.description }}</p>
|
|
|
|
<div class="d-flex justify-content-between align-items-center small">
|
|
<div class="text-muted">
|
|
{% if hand.schedule %}
|
|
<span class="me-2">🕐 {{ hand.schedule.cron }}</span>
|
|
{% endif %}
|
|
<span class="me-2">▶️ {{ state.run_count }} runs</span>
|
|
{% if state.last_run %}
|
|
<span title="Last run: {{ state.last_run }}">Last: {{ state.last_run.strftime('%H:%M') }}</span>
|
|
{% endif %}
|
|
</div>
|
|
<span class="badge bg-{{ 'success' if state.status.value == 'scheduled' else 'warning' if state.status.value == 'paused' else 'danger' if state.status.value == 'error' else 'info' }}">
|
|
{{ state.status.value.upper() }}
|
|
</span>
|
|
</div>
|
|
|
|
{% if hand.tools_required %}
|
|
<div class="mt-2">
|
|
<small class="text-muted">Tools:</small>
|
|
{% for tool in hand.tools_required %}
|
|
<span class="badge bg-dark me-1">{{ tool }}</span>
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-5 text-muted">
|
|
<p class="mb-0">No Hands configured.</p>
|
|
<small>Create Hand packages in the hands/ directory.</small>
|
|
</div>
|
|
{% endif %}
|