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
|
# 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)
|
||||||
|
if get_app_setting('standup_enabled', '1') == '1':
|
||||||
trigger_daily_standup()
|
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():
|
||||||
|
|
|
||||||
|
|
@ -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 %}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue