Add daily standup toggle slider and clear-all-tasks button to dashboard

This commit is contained in:
eric 2026-02-22 09:56:12 +00:00
parent c82ecdd5f1
commit 86dc556c4e
2 changed files with 127 additions and 4 deletions

40
app.py
View file

@ -316,8 +316,8 @@ def init_journal():
# Default-Werte setzen falls nicht vorhanden # Default-Werte setzen falls nicht vorhanden
con.execute(""" con.execute("""
INSERT OR IGNORE INTO app_settings (key, value, updated_at) INSERT OR IGNORE INTO app_settings (key, value, updated_at)
VALUES ('app_name', 'Frankenbot', ?), ('theme', 'dark', ?) VALUES ('app_name', 'Frankenbot', ?), ('theme', 'dark', ?), ('standup_enabled', '1', ?)
""", (datetime.now().isoformat(), datetime.now().isoformat())) """, (datetime.now().isoformat(), datetime.now().isoformat(), datetime.now().isoformat()))
con.commit() con.commit()
con.close() con.close()
@ -2611,7 +2611,10 @@ def daily_standup_beat():
sleep_secs = (target - now).total_seconds() 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) 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) time.sleep(sleep_secs)
trigger_daily_standup() if get_app_setting('standup_enabled', '1') == '1':
trigger_daily_standup()
else:
logger.info("[DailyStandup] Standup ist deaktiviert übersprungen.")
except Exception as e: except Exception as e:
logger.error("[DailyStandup] Fehler: %s", e) logger.error("[DailyStandup] Fehler: %s", e)
time.sleep(60) time.sleep(60)
@ -2655,7 +2658,8 @@ def index():
# Hole die 5 neuesten Tasks aus DB # Hole die 5 neuesten Tasks aus DB
all_tasks = get_tasks(order='desc') all_tasks = get_tasks(order='desc')
recent_tasks = all_tasks[:5] if all_tasks else [] 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']) @app.route('/chat', methods=['GET', 'POST'])
@login_required @login_required
@ -3820,6 +3824,34 @@ def api_trigger_standup():
return jsonify({'success': True, 'message': 'Daily Standup wurde gestartet.'}) 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']) @app.route('/api/broadcast', methods=['POST'])
@login_required @login_required
def api_broadcast(): def api_broadcast():

View file

@ -76,6 +76,34 @@
{% endfor %} {% endfor %}
</div> </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 --> <!-- Recent Tasks -->
{% if recent_tasks %} {% if recent_tasks %}
<div class="d-flex align-items-center justify-content-between mb-3"> <div class="d-flex align-items-center justify-content-between mb-3">
@ -123,4 +151,67 @@
</div> </div>
</div> </div>
{% endif %} {% 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 %} {% endblock %}