- Wire up Telegram bot with token, allowed users and username from .env - Fix TaskBeat to handle direct tasks (Telegram/email) without sub_tasks - Fix send_telegram_message to use run_coroutine_threadsafe (avoid event loop clash) - Add TaskBeat watchdog thread for auto-restart on crash - Reset stuck in_progress tasks on startup - Add task detail page (/tasks/<id>) with full response/log view and auto-refresh - Add task delete route (/tasks/delete/<id>) with confirmation - Include agent sender info in Telegram task prompts - Orchestrator self-updated knowledge base with Telegram contact info
117 lines
5.1 KiB
HTML
117 lines
5.1 KiB
HTML
{% extends "base.html" %}
|
|
{% block title %}Task #{{ task.id }}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="page-header d-flex justify-content-between align-items-start flex-wrap gap-2">
|
|
<div>
|
|
<a href="{{ url_for('task_list') }}" style="color:var(--text-muted);font-size:.85rem;">← Zurück zu Tasks</a>
|
|
<h1 class="mt-1">Task #{{ task.id }}</h1>
|
|
<p style="color:var(--text-muted);">{{ task.title }}</p>
|
|
</div>
|
|
<form method="POST" action="{{ url_for('delete_task', task_id=task.id) }}" onsubmit="return confirm('Task #{{ task.id }} wirklich löschen?')">
|
|
<button type="submit" class="btn btn-danger">Löschen</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="row g-4">
|
|
|
|
<!-- Meta-Infos -->
|
|
<div class="col-12 col-md-4">
|
|
<div class="card h-100">
|
|
<div class="card-header bg-primary">
|
|
<h5 class="mb-0">Details</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<table class="table table-sm mb-0" style="font-size:.85rem;">
|
|
<tbody>
|
|
<tr><th style="color:var(--text-muted);white-space:nowrap;">Status</th><td>
|
|
{% if task.status == 'pending' %}
|
|
<span class="badge bg-warning">Pending</span>
|
|
{% elif task.status == 'in_progress' %}
|
|
<span class="badge bg-primary">In Progress</span>
|
|
{% elif task.status == 'completed' %}
|
|
<span class="badge bg-success">Done</span>
|
|
{% elif task.status == 'error' %}
|
|
<span class="badge bg-danger">Fehler</span>
|
|
{% else %}
|
|
<span class="badge bg-secondary">{{ task.status }}</span>
|
|
{% endif %}
|
|
</td></tr>
|
|
<tr><th style="color:var(--text-muted);">Typ</th><td><code>{{ task.type or '—' }}</code></td></tr>
|
|
<tr><th style="color:var(--text-muted);">Agent</th><td>{{ task.assigned_agent or task.agent_key or '—' }}</td></tr>
|
|
<tr><th style="color:var(--text-muted);">Erstellt von</th><td><code>{{ task.created_by or '—' }}</code></td></tr>
|
|
<tr><th style="color:var(--text-muted);">Erstellt</th><td>{{ task.created_at or '—' }}</td></tr>
|
|
<tr><th style="color:var(--text-muted);">Abgeschlossen</th><td>{{ task.completed_at or '—' }}</td></tr>
|
|
{% if task.telegram_chat_id %}
|
|
<tr><th style="color:var(--text-muted);">Telegram</th><td>{{ task.telegram_user or '' }} <code>{{ task.telegram_chat_id }}</code></td></tr>
|
|
{% endif %}
|
|
{% if task.parent_task_id %}
|
|
<tr><th style="color:var(--text-muted);">Parent Task</th><td><a href="{{ url_for('task_detail', task_id=task.parent_task_id) }}">#{{ task.parent_task_id }}</a></td></tr>
|
|
{% endif %}
|
|
{% if task.reply_to %}
|
|
<tr><th style="color:var(--text-muted);">Reply-To</th><td><code>{{ task.reply_to }}</code></td></tr>
|
|
{% endif %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Beschreibung + Antwort -->
|
|
<div class="col-12 col-md-8">
|
|
<div class="card mb-4">
|
|
<div class="card-header" style="background:var(--surface-2);">
|
|
<h5 class="mb-0">Anfrage / Beschreibung</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if task.description %}
|
|
<pre style="white-space:pre-wrap;font-family:inherit;font-size:.9rem;color:var(--text-primary);margin:0;">{{ task.description }}</pre>
|
|
{% else %}
|
|
<span style="color:var(--text-muted);">Keine Beschreibung.</span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-header d-flex justify-content-between align-items-center" style="background:var(--surface-2);">
|
|
<h5 class="mb-0">Agent-Antwort / Log</h5>
|
|
{% if task.response %}
|
|
<button class="btn btn-sm btn-outline-secondary" onclick="copyResponse()">Kopieren</button>
|
|
{% endif %}
|
|
</div>
|
|
<div class="card-body p-0">
|
|
{% if task.status == 'in_progress' %}
|
|
<div class="p-3" style="color:var(--info);">
|
|
<span class="spinner-border spinner-border-sm me-2"></span>
|
|
Task läuft gerade... Seite wird alle 5s aktualisiert.
|
|
</div>
|
|
{% endif %}
|
|
{% if task.response %}
|
|
<pre id="responseBlock" style="white-space:pre-wrap;font-family:'JetBrains Mono',monospace;font-size:.82rem;color:var(--text-primary);margin:0;padding:1.25rem;max-height:600px;overflow-y:auto;">{{ task.response }}</pre>
|
|
{% elif task.status != 'in_progress' %}
|
|
<div class="p-3" style="color:var(--text-muted);">Noch keine Antwort vorhanden.</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block scripts %}
|
|
<script>
|
|
// Auto-Refresh wenn Task noch läuft
|
|
{% if task.status in ['pending', 'in_progress'] %}
|
|
setTimeout(() => location.reload(), 5000);
|
|
{% endif %}
|
|
|
|
function copyResponse() {
|
|
const text = document.getElementById('responseBlock').innerText;
|
|
navigator.clipboard.writeText(text).then(() => {
|
|
const btn = event.target;
|
|
btn.textContent = 'Kopiert!';
|
|
setTimeout(() => btn.textContent = 'Kopieren', 2000);
|
|
});
|
|
}
|
|
</script>
|
|
{% endblock %}
|