Add daily standup toggle slider and clear-all-tasks button to dashboard
This commit is contained in:
parent
c82ecdd5f1
commit
86dc556c4e
2 changed files with 127 additions and 4 deletions
38
app.py
38
app.py
|
|
@ -316,8 +316,8 @@ def init_journal():
|
|||
# Default-Werte setzen falls nicht vorhanden
|
||||
con.execute("""
|
||||
INSERT OR IGNORE INTO app_settings (key, value, updated_at)
|
||||
VALUES ('app_name', 'Frankenbot', ?), ('theme', 'dark', ?)
|
||||
""", (datetime.now().isoformat(), datetime.now().isoformat()))
|
||||
VALUES ('app_name', 'Frankenbot', ?), ('theme', 'dark', ?), ('standup_enabled', '1', ?)
|
||||
""", (datetime.now().isoformat(), datetime.now().isoformat(), datetime.now().isoformat()))
|
||||
|
||||
con.commit()
|
||||
con.close()
|
||||
|
|
@ -2611,7 +2611,10 @@ def daily_standup_beat():
|
|||
sleep_secs = (target - now).total_seconds()
|
||||
logger.info("[DailyStandup] Nächstes Standup um %s (in %.0f Minuten)", target.strftime('%d.%m.%Y %H:%M'), sleep_secs / 60)
|
||||
time.sleep(sleep_secs)
|
||||
if get_app_setting('standup_enabled', '1') == '1':
|
||||
trigger_daily_standup()
|
||||
else:
|
||||
logger.info("[DailyStandup] Standup ist deaktiviert – übersprungen.")
|
||||
except Exception as e:
|
||||
logger.error("[DailyStandup] Fehler: %s", e)
|
||||
time.sleep(60)
|
||||
|
|
@ -2655,7 +2658,8 @@ def index():
|
|||
# Hole die 5 neuesten Tasks aus DB
|
||||
all_tasks = get_tasks(order='desc')
|
||||
recent_tasks = all_tasks[:5] if all_tasks else []
|
||||
return render_template('index.html', agents=AGENTS, recent_tasks=recent_tasks)
|
||||
standup_enabled = get_app_setting('standup_enabled', '1') == '1'
|
||||
return render_template('index.html', agents=AGENTS, recent_tasks=recent_tasks, standup_enabled=standup_enabled)
|
||||
|
||||
@app.route('/chat', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
|
|
@ -3820,6 +3824,34 @@ def api_trigger_standup():
|
|||
return jsonify({'success': True, 'message': 'Daily Standup wurde gestartet.'})
|
||||
|
||||
|
||||
@app.route('/api/standup/toggle', methods=['POST'])
|
||||
@login_required
|
||||
def api_standup_toggle():
|
||||
"""Aktiviert oder deaktiviert den täglichen Standup."""
|
||||
data = request.get_json() or {}
|
||||
enabled = data.get('enabled', True)
|
||||
set_app_setting('standup_enabled', '1' if enabled else '0')
|
||||
state = 'aktiviert' if enabled else 'deaktiviert'
|
||||
logger.info("[DailyStandup] Standup %s.", state)
|
||||
return jsonify({'success': True, 'enabled': enabled, 'message': f'Daily Standup wurde {state}.'})
|
||||
|
||||
|
||||
@app.route('/api/tasks/clear', methods=['POST'])
|
||||
@login_required
|
||||
def api_tasks_clear():
|
||||
"""Löscht alle Tasks aus der Datenbank."""
|
||||
try:
|
||||
con = sqlite3.connect(EMAIL_JOURNAL_DB)
|
||||
con.execute("DELETE FROM tasks")
|
||||
con.commit()
|
||||
con.close()
|
||||
logger.info("[Tasks] Alle Tasks wurden gelöscht.")
|
||||
return jsonify({'success': True, 'message': 'Alle Tasks wurden gelöscht.'})
|
||||
except Exception as e:
|
||||
logger.error("[Tasks] Fehler beim Löschen aller Tasks: %s", e)
|
||||
return jsonify({'success': False, 'message': str(e)}), 500
|
||||
|
||||
|
||||
@app.route('/api/broadcast', methods=['POST'])
|
||||
@login_required
|
||||
def api_broadcast():
|
||||
|
|
|
|||
|
|
@ -76,6 +76,34 @@
|
|||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- System-Steuerung -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-body">
|
||||
<h5 style="margin-bottom:1rem;font-weight:600;">System-Steuerung</h5>
|
||||
<div class="d-flex align-items-center justify-content-between flex-wrap gap-3">
|
||||
<!-- Daily Standup Toggle -->
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div>
|
||||
<div style="font-weight:500;font-size:.9rem;">Daily Standup</div>
|
||||
<div style="font-size:.78rem;color:var(--text-muted);">Täglich 09:00 – automatische Statusabfrage</div>
|
||||
</div>
|
||||
<div class="form-check form-switch mb-0">
|
||||
<input class="form-check-input" type="checkbox" role="switch" id="standupToggle"
|
||||
{% if standup_enabled %}checked{% endif %}
|
||||
style="width:2.5em;height:1.4em;cursor:pointer;">
|
||||
</div>
|
||||
<span id="standupLabel" class="badge {% if standup_enabled %}bg-success{% else %}bg-secondary{% endif %}">
|
||||
{% if standup_enabled %}Aktiv{% else %}Inaktiv{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
<!-- Tasks leeren -->
|
||||
<button id="clearTasksBtn" class="btn btn-outline-danger btn-sm" onclick="clearAllTasks()">
|
||||
Alle Tasks leeren
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Recent Tasks -->
|
||||
{% if recent_tasks %}
|
||||
<div class="d-flex align-items-center justify-content-between mb-3">
|
||||
|
|
@ -123,4 +151,67 @@
|
|||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<script>
|
||||
// Daily Standup Toggle
|
||||
document.getElementById('standupToggle').addEventListener('change', function() {
|
||||
const enabled = this.checked;
|
||||
const label = document.getElementById('standupLabel');
|
||||
fetch('/api/standup/toggle', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({enabled: enabled})
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
label.textContent = enabled ? 'Aktiv' : 'Inaktiv';
|
||||
label.className = 'badge ' + (enabled ? 'bg-success' : 'bg-secondary');
|
||||
} else {
|
||||
// Revert on error
|
||||
this.checked = !enabled;
|
||||
alert('Fehler: ' + data.message);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
this.checked = !enabled;
|
||||
alert('Netzwerkfehler beim Speichern.');
|
||||
});
|
||||
});
|
||||
|
||||
// Alle Tasks leeren
|
||||
function clearAllTasks() {
|
||||
if (!confirm('Alle Tasks wirklich löschen? Dies kann nicht rückgängig gemacht werden.')) return;
|
||||
const btn = document.getElementById('clearTasksBtn');
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Löschen...';
|
||||
fetch('/api/tasks/clear', {method: 'POST'})
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
// Task-Tabelle aus DOM entfernen falls vorhanden
|
||||
const taskSection = document.querySelector('.table-responsive');
|
||||
if (taskSection) taskSection.closest('.card').remove();
|
||||
const taskHeader = document.querySelectorAll('h3');
|
||||
taskHeader.forEach(h => { if (h.textContent.includes('Letzte Tasks')) h.closest('.d-flex').remove(); });
|
||||
btn.textContent = 'Geleert';
|
||||
btn.className = 'btn btn-success btn-sm';
|
||||
setTimeout(() => {
|
||||
btn.textContent = 'Alle Tasks leeren';
|
||||
btn.className = 'btn btn-outline-danger btn-sm';
|
||||
btn.disabled = false;
|
||||
}, 2000);
|
||||
} else {
|
||||
alert('Fehler: ' + data.message);
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Alle Tasks leeren';
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
alert('Netzwerkfehler.');
|
||||
btn.disabled = false;
|
||||
btn.textContent = 'Alle Tasks leeren';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue