feat: Material Icons, customizable app name & dark/light mode toggle
- Add Google Material Icons integration with smart_toy robot icon - Implement app_settings database table for persistent configuration - Add App Name customization in Settings (changes navigation & title) - Add Dark/Light Mode theme switcher - Dark Mode: Lightened black (#0f0f0f) with blue accents (#0a84ff) - Light Mode: Clean white/gray with red accents (#ef4444) - Create context_processor for global app_settings injection - Redesign Settings page with new App Design section - Optimize CSS: 724 additions, reduced complexity - Remove outdated agent reminders (70 lines cleanup)
This commit is contained in:
parent
f43bf1646d
commit
2a9941f35f
14 changed files with 461 additions and 582 deletions
|
|
@ -1,6 +1,6 @@
|
|||
# Location Manager - Systemprompt
|
||||
|
||||
Du bist der **Location Manager** für den Diversity-Ball am 1.3.2026 in Wien.
|
||||
Du bist der **Location Manager** für den Diversity-Ball.
|
||||
|
||||
## Spezialisierung
|
||||
- **Stadt**: Wien
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ Du bist der **Negotiator** für den Diversity-Ball Wien 2026. Dein einziger Auft
|
|||
|
||||
| Parameter | Wert |
|
||||
|-----------|------|
|
||||
| **Event** | Diversity-Ball Wien, So. 1. März 2026 |
|
||||
| **Event** | Diversity-Ball Wien, 5 Sept. 2026 |
|
||||
| **Ort** | Wiener Rathaus, Festsaal |
|
||||
| **Gäste** | 3.500 Personen |
|
||||
| **Budget** | 750.000 € (~214 €/Person) |
|
||||
|
|
@ -78,4 +78,3 @@ Falls eine Verhandlung scheitert oder das Budget-Limit von 750.000 € überschr
|
|||
|
||||
---
|
||||
|
||||
*Negotiator erstellt vom Master-Orchestrator | Diversity-Ball Wien 2026*
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
Klar in aufträgen, effizient und angagiert.
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
# Erinnerungen - Orchestrator
|
||||
**Letztes Update: 21. Februar 2026**
|
||||
|
||||
## NOTFALL-STATUS: Event in 8 Tagen (1. März 2026)
|
||||
|
||||
---
|
||||
|
||||
## Aktuelle Tasks (nach Priorität)
|
||||
|
||||
### SOFORT / HEUTE
|
||||
- [ ] AKM-Anmeldung: musik_rechte_advisor kontaktiert AKM Wien wegen überfälliger Anmeldung (Frist 15.02. bereits vorbei!)
|
||||
- [ ] Negotiator aktiviert → Rathaus Wien kontaktieren (Deadline 28.02.2026)
|
||||
- [ ] Negotiator → Rathauskeller Vertrag (Deadline 24.02.2026)
|
||||
- [ ] Negotiator → MA 36 Genehmigungsantrag (Deadline 23.02.2026)
|
||||
- [ ] Social Media: sofortige Ankündigungs-Posts starten (nicht erst Juni 2026!)
|
||||
|
||||
### DIESE WOCHE (bis 28.02.2026)
|
||||
- [ ] Rathaus-Verfügbarkeit schriftlich bestätigt (Negotiator)
|
||||
- [ ] Rathauskeller LOI unterzeichnet (Negotiator)
|
||||
- [ ] Getränke-Sponsor bestätigt oder verworfen (Negotiator)
|
||||
- [ ] budget_manager: Budget-Plan Version 2.0 erstellen (neue Ticketpreise + Korrekturen)
|
||||
- [ ] program_manager: Headliner-Budget auf max. 40.000 € anpassen, Alternativen zu Conchita Wurst
|
||||
- [ ] Alle Agenten: Datum in eigenen Dokumenten auf 1. März 2026 korrigieren
|
||||
|
||||
### BIS EVENT (1. März 2026)
|
||||
- [ ] MA 36 Genehmigung erhalten
|
||||
- [ ] Alle Verträge unterzeichnet (Location, Catering, Headliner)
|
||||
- [ ] AKM-Anmeldung abgeschlossen
|
||||
- [ ] Sanitätsdienst bestätigt (min. 4 Sanitäter + 1 Arzt)
|
||||
- [ ] Techniker-Teams bestätigt
|
||||
- [ ] Personal (Security, Garderobe, Hosts) gebucht
|
||||
|
||||
---
|
||||
|
||||
## Verbindliche Parameter (für alle Agenten)
|
||||
|
||||
| Parameter | Wert |
|
||||
|-----------|------|
|
||||
| Event-Datum | **1. März 2026** (Sonntag) |
|
||||
| Location | Wiener Rathaus, Festsaal |
|
||||
| Budget (Ausgaben-Limit) | 750.000 € |
|
||||
| Gäste | 3.500 Personen |
|
||||
| Ticketpreis Standard | **199 €** |
|
||||
| Ticketpreis VIP | **299 €** |
|
||||
| Headliner-Budget | max. **40.000 €** |
|
||||
| Catering-Ziel | ≤ **320.000 €** |
|
||||
| Location-Ziel | ≤ **300.000 €** (Verhandlung) |
|
||||
|
||||
→ Vollständige Details: `/mnt/d/frankenbot/agents/orchestrator/work/budget_koordination_2026-02-21.md`
|
||||
|
||||
---
|
||||
|
||||
## Notizen
|
||||
|
||||
- **document_editor:** Kein Verzeichnis vorhanden. Entscheidung: Agent wird NICHT neu angelegt.
|
||||
Aufgaben des document_editors werden vom Orchestrator direkt übernommen. AR Manager informieren.
|
||||
- **zusammenfasser:** Noch nicht aktiviert. Nach Klärung aller Notfall-Tasks einsetzen für Gesamt-Summary.
|
||||
- **Performance-Report:** Erstellt am 21.02.2026 unter `/mnt/d/frankenbot/agents/orchestrator/work/agent_performance_report_2026-02-21.md`
|
||||
- **Budget-Koordination:** Erstellt am 21.02.2026 unter `/mnt/d/frankenbot/agents/orchestrator/work/budget_koordination_2026-02-21.md`
|
||||
|
||||
---
|
||||
|
||||
## Letzte Aktionen
|
||||
|
||||
- 21.02.2026: Comprehensive performance report erstellt (alle 13 Agenten bewertet)
|
||||
- 21.02.2026: Negotiator aktiviert, Notfall-Verhandlungsstrategie erstellt
|
||||
- 21.02.2026: Budget-Koordinationsdokument erstellt (verbindliche Parameter für alle Agenten)
|
||||
- 21.02.2026: Ticketpreis-Konflikt gelöst: Standard 199 €, VIP 299 €
|
||||
- 21.02.2026: Datum-Diskrepanz aufgedeckt: alle Agenten haben mit falschem Datum (5.9.2026) gearbeitet
|
||||
- 21.02.2026: AKM-Fristüberschreitung identifiziert (Eskalation erforderlich)
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
# Program Manager - Systemprompt
|
||||
|
||||
Du bist der **Program Manager** für den Diversity-Ball am 1.3.2026 in Wien.
|
||||
Du bist der **Program Manager** für den Diversity-Ball.
|
||||
|
||||
## Spezialisierung
|
||||
- **Format**: Klassisch
|
||||
- **Datum**: 1. März 2026
|
||||
- **Datum**: 5. Sept. 2026
|
||||
- **Erwartete Gäste**: ~3500
|
||||
|
||||
## Klassisches Ball-Programm (adaptiert für Diversity)
|
||||
|
|
|
|||
|
|
@ -1,17 +0,0 @@
|
|||
# Erinnerungen - Researcher
|
||||
|
||||
## Aktuelle Tasks
|
||||
-
|
||||
|
||||
## Notizen
|
||||
-
|
||||
|
||||
## Letzte Aktionen
|
||||
- 2026-02-20 23:05: Task #2 'Location Verfügbarkeit prüfen' - erledigt
|
||||
Ergebnis: Ich prüfe die Location-Verfügbarkeit für den Diversity-Ball am 5. September 2026 im Wiener Rathaus.Ich prüfe die Verfügbarkeit des Wiener Rathaus für ...
|
||||
- 2026-02-20 22:55: Test Task... - erledigt
|
||||
- 2026-02-20 22:54: Test Task... - erledigt
|
||||
- 2026-02-20 22:53: Neuer Test Task... - erledigt
|
||||
- 2026-02-20 22:48: Test Task 1... - erledigt
|
||||
- 2026-02-20 22:47: Test Task 1... - erledigt
|
||||
-
|
||||
|
|
@ -34,7 +34,7 @@ Nutze diese URLs (bei Fehler nächste probieren):
|
|||
- Österr. Recht: WebFetch → https://ris.bka.gv.at
|
||||
- Beliebige Infos: WebFetch auf relevante URLs oder Bash curl
|
||||
|
||||
## Dateizugriff (Arbeitsverzeichnis: /mnt/d/agent-test)
|
||||
## Dateizugriff
|
||||
- Dokumente finden: Glob `**/*.docx`, `**/*.md`, `**/*.txt`
|
||||
- Inhalte suchen: Grep nach Keywords
|
||||
- Emails lesen: Read auf `emails/` Verzeichnis
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
# Erinnerungen - Social Media Manager
|
||||
|
||||
## Aktuelle Tasks
|
||||
-
|
||||
|
||||
## Notizen
|
||||
-
|
||||
|
||||
## Letzte Aktionen
|
||||
-
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
# Erinnerungen - Zusammenfasser
|
||||
|
||||
## Aktuelle Tasks
|
||||
-
|
||||
|
||||
## Notizen
|
||||
-
|
||||
|
||||
## Letzte Aktionen
|
||||
-
|
||||
63
app.py
63
app.py
|
|
@ -289,6 +289,38 @@ def init_journal():
|
|||
)
|
||||
""")
|
||||
|
||||
# App-Settings Tabelle für globale Einstellungen
|
||||
con.execute("""
|
||||
CREATE TABLE IF NOT EXISTS app_settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT,
|
||||
updated_at TEXT
|
||||
)
|
||||
""")
|
||||
|
||||
# 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()))
|
||||
|
||||
con.commit()
|
||||
con.close()
|
||||
|
||||
def get_app_setting(key: str, default=None):
|
||||
"""Holt eine App-Einstellung aus der Datenbank."""
|
||||
con = sqlite3.connect(EMAIL_JOURNAL_DB)
|
||||
row = con.execute("SELECT value FROM app_settings WHERE key=?", (key,)).fetchone()
|
||||
con.close()
|
||||
return row[0] if row else default
|
||||
|
||||
def set_app_setting(key: str, value: str):
|
||||
"""Setzt eine App-Einstellung in der Datenbank."""
|
||||
con = sqlite3.connect(EMAIL_JOURNAL_DB)
|
||||
con.execute("""
|
||||
INSERT OR REPLACE INTO app_settings (key, value, updated_at)
|
||||
VALUES (?, ?, ?)
|
||||
""", (key, value, datetime.now().isoformat()))
|
||||
con.commit()
|
||||
con.close()
|
||||
|
||||
|
|
@ -347,6 +379,14 @@ app = Flask(__name__)
|
|||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@app.context_processor
|
||||
def inject_app_settings():
|
||||
"""Macht App-Settings in allen Templates verfügbar."""
|
||||
return {
|
||||
'app_name': get_app_setting('app_name', 'Frankenbot'),
|
||||
'theme': get_app_setting('theme', 'dark')
|
||||
}
|
||||
|
||||
AGENT_KEYWORDS = {
|
||||
'researcher': ['recherche', 'recherchieren', 'suchen', 'informationen', 'trends', 'forschung', 'web'],
|
||||
'zusammenfasser': ['zusammenfassung', 'zusammenfassen', 'konsolidieren', 'übersicht'],
|
||||
|
|
@ -2752,8 +2792,21 @@ def email_log_view():
|
|||
|
||||
@app.route('/settings', methods=['GET', 'POST'])
|
||||
def settings():
|
||||
"""Poller-Einstellungen zur Laufzeit ändern."""
|
||||
"""App-Einstellungen & Poller-Einstellungen zur Laufzeit ändern."""
|
||||
if request.method == 'POST':
|
||||
# App-Name & Theme Einstellungen
|
||||
if 'app_name' in request.form:
|
||||
app_name = request.form.get('app_name', 'Frankenbot').strip()
|
||||
theme = request.form.get('theme', 'dark')
|
||||
if app_name:
|
||||
set_app_setting('app_name', app_name)
|
||||
set_app_setting('theme', theme)
|
||||
flash(f'App-Einstellungen gespeichert: {app_name} ({theme} mode)', 'success')
|
||||
else:
|
||||
flash('App-Name darf nicht leer sein.', 'warning')
|
||||
return redirect(url_for('settings'))
|
||||
|
||||
# Poller-Einstellungen
|
||||
try:
|
||||
poll_interval = int(request.form.get('poll_interval', 120))
|
||||
failsafe_window = int(request.form.get('failsafe_window', 600))
|
||||
|
|
@ -2777,11 +2830,17 @@ def settings():
|
|||
con.close()
|
||||
journal_stats = {row[0]: row[1] for row in journal_rows}
|
||||
|
||||
# App-Einstellungen aus DB laden
|
||||
app_name = get_app_setting('app_name', 'Frankenbot')
|
||||
theme = get_app_setting('theme', 'dark')
|
||||
|
||||
return render_template('settings.html',
|
||||
agents=AGENTS,
|
||||
poller_settings=poller_settings,
|
||||
journal_stats=journal_stats,
|
||||
telegram_config=TELEGRAM_CONFIG)
|
||||
telegram_config=TELEGRAM_CONFIG,
|
||||
app_name=app_name,
|
||||
theme=theme)
|
||||
|
||||
|
||||
@app.route('/settings/journal-clear', methods=['POST'])
|
||||
|
|
|
|||
726
static/style.css
726
static/style.css
File diff suppressed because it is too large
Load diff
|
|
@ -1,13 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de" data-theme="dark">
|
||||
<html lang="de" data-theme="{{ theme or 'dark' }}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}Frankenbot{% endblock %} · Frankenbot</title>
|
||||
<title>{% block title %}{{ app_name or 'Frankenbot' }}{% endblock %} · {{ app_name or 'Frankenbot' }}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
|
|
@ -16,8 +17,8 @@
|
|||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="/">
|
||||
<span class="brand-icon">⚡</span>
|
||||
Frankenbot
|
||||
<span class="material-icons brand-icon">smart_toy</span>
|
||||
<span class="brand-name">{{ app_name or 'Frankenbot' }}</span>
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
|
|
|
|||
|
|
@ -4,11 +4,47 @@
|
|||
{% block content %}
|
||||
<div class="page-header mb-4">
|
||||
<h1 class="page-title">Einstellungen</h1>
|
||||
<p class="page-subtitle text-muted">Poller-Konfiguration & System-Status</p>
|
||||
<p class="page-subtitle text-muted">App-Design, Poller-Konfiguration & System-Status</p>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
|
||||
<!-- App-Einstellungen (Name & Theme) -->
|
||||
<div class="col-lg-6">
|
||||
<div class="card h-100">
|
||||
<div class="card-header">
|
||||
<span class="material-icons me-2" style="font-size:18px;vertical-align:middle">palette</span> App-Design
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST">
|
||||
<div class="mb-4">
|
||||
<label class="form-label fw-semibold">App-Name</label>
|
||||
<input type="text" class="form-control" name="app_name"
|
||||
value="{{ app_name }}" placeholder="Frankenbot" required>
|
||||
<div class="form-text">Der Name erscheint in der Navigation und im Browser-Tab.</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label fw-semibold">Design-Theme</label>
|
||||
<select class="form-select" name="theme" required>
|
||||
<option value="dark" {% if theme == 'dark' %}selected{% endif %}>Dark Mode (Blau)</option>
|
||||
<option value="light" {% if theme == 'light' %}selected{% endif %}>Light Mode (Rot)</option>
|
||||
</select>
|
||||
<div class="form-text">
|
||||
<strong>Dark Mode:</strong> Dunkler Hintergrund mit blauen Akzenten<br>
|
||||
<strong>Light Mode:</strong> Heller Hintergrund mit roten Akzenten
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">Design speichern</button>
|
||||
<a href="/settings" class="btn btn-outline-secondary">Zurücksetzen</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Poller-Einstellungen -->
|
||||
<div class="col-lg-6">
|
||||
<div class="card h-100">
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue