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:
pdyde 2026-02-21 16:59:18 +01:00
parent f43bf1646d
commit 2a9941f35f
14 changed files with 461 additions and 582 deletions

View file

@ -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

View file

@ -1,44 +1,44 @@
# Erinnerungen - Negotiator
## NOTFALL-AKTIVIERUNG — 21. Februar 2026
> **Das Event findet am 1. März 2026 statt — in 8 Tagen.**
> Du wirst sofort benötigt. Alle unten stehenden Tasks sind KRITISCH.
---
## Aktuelle Tasks (Priorität: SOFORT)
### TASK 1 — Rathaus Wien: Verfügbarkeit bestätigen [DEADLINE: 28.02.2026]
- Kontakt: Wiener Rathaus Eventmanagement (eventmanagement@rathaus.wien.gv.at oder +43 1 4000-0)
- Ziel: Schriftliche Bestätigung der Verfügbarkeit des Festsaals am **Sonntag, 1. März 2026, 18:0002:00 Uhr**
- Fallback: Hofburg Wien (als Option im Hinterkopf behalten)
- Verhandlungsmandat: Nutzungsgebühr, Auf-/Abbauzeiten, Exklusivitätszeitraum
### TASK 2 — Rathauskeller Wien: Catering-Vertrag [DEADLINE: 24.02.2026]
- Fixierter Partner: Rathauskeller Wien ist bereits als Catering-Partner vorgesehen
- Ziel: Vertrag final unterzeichnen (oder zumindest LOI = Letter of Intent)
- Grundlage: Cateringkonzept des catering_manager (320.800€ Kostenrahmen)
- Verhandlungsmandat: Mengenrabatte, Stornobedingungen, Zahlungsplan
### TASK 3 — MA 36 Genehmigung: Vorbereitung [DEADLINE: 23.02.2026]
- Kontakt: Magistratsabteilung 36, Wien (ma36@wien.gv.at)
- Ziel: Genehmigungsantrag für Großveranstaltung einreichen
- Unterlagen: Sicherheitskonzept, Fluchtwegplan, Kapazitätsnachweis
- HINWEIS: Ohne MA 36 Genehmigung kann das Event nicht stattfinden
### TASK 4 — Getränke-Sponsor akquirieren [DEADLINE: 26.02.2026]
- Ziel: Getränke-Sponsor für 50.000€ Gegenwert (Budget-Entlastung)
- Kandidaten aus Recherche: Ottakringer, Viennese Spirits, Schweppes Österreich
- Gegenleistungen: Logorechte, exkl. Ausschank, 20 VIP-Tickets, Nennungen
---
## Notizen
- **Korrektes Event-Datum: 1. März 2026** (nicht 5. September — war ein Fehler in alten Dokumenten)
- Budget gesamt: 750.000€ | Ticketpreis: 214€ (Basis) | Gäste: 3.500
- Alle Verhandlungsergebnisse schriftlich dokumentieren und in `/work/` ablegen
- Eskalation an Orchestrator wenn Deadline nicht haltbar oder Budget überschritten
## Letzte Aktionen
- 21.02.2026: Notfall-Aktivierung durch Orchestrator — alle Tasks sofort starten
# Erinnerungen - Negotiator
## NOTFALL-AKTIVIERUNG — 21. Februar 2026
> **Das Event findet am 1. März 2026 statt — in 8 Tagen.**
> Du wirst sofort benötigt. Alle unten stehenden Tasks sind KRITISCH.
---
## Aktuelle Tasks (Priorität: SOFORT)
### TASK 1 — Rathaus Wien: Verfügbarkeit bestätigen [DEADLINE: 28.02.2026]
- Kontakt: Wiener Rathaus Eventmanagement (eventmanagement@rathaus.wien.gv.at oder +43 1 4000-0)
- Ziel: Schriftliche Bestätigung der Verfügbarkeit des Festsaals am **Sonntag, 1. März 2026, 18:0002:00 Uhr**
- Fallback: Hofburg Wien (als Option im Hinterkopf behalten)
- Verhandlungsmandat: Nutzungsgebühr, Auf-/Abbauzeiten, Exklusivitätszeitraum
### TASK 2 — Rathauskeller Wien: Catering-Vertrag [DEADLINE: 24.02.2026]
- Fixierter Partner: Rathauskeller Wien ist bereits als Catering-Partner vorgesehen
- Ziel: Vertrag final unterzeichnen (oder zumindest LOI = Letter of Intent)
- Grundlage: Cateringkonzept des catering_manager (320.800€ Kostenrahmen)
- Verhandlungsmandat: Mengenrabatte, Stornobedingungen, Zahlungsplan
### TASK 3 — MA 36 Genehmigung: Vorbereitung [DEADLINE: 23.02.2026]
- Kontakt: Magistratsabteilung 36, Wien (ma36@wien.gv.at)
- Ziel: Genehmigungsantrag für Großveranstaltung einreichen
- Unterlagen: Sicherheitskonzept, Fluchtwegplan, Kapazitätsnachweis
- HINWEIS: Ohne MA 36 Genehmigung kann das Event nicht stattfinden
### TASK 4 — Getränke-Sponsor akquirieren [DEADLINE: 26.02.2026]
- Ziel: Getränke-Sponsor für 50.000€ Gegenwert (Budget-Entlastung)
- Kandidaten aus Recherche: Ottakringer, Viennese Spirits, Schweppes Österreich
- Gegenleistungen: Logorechte, exkl. Ausschank, 20 VIP-Tickets, Nennungen
---
## Notizen
- **Korrektes Event-Datum: 1. März 2026** (nicht 5. September — war ein Fehler in alten Dokumenten)
- Budget gesamt: 750.000€ | Ticketpreis: 214€ (Basis) | Gäste: 3.500
- Alle Verhandlungsergebnisse schriftlich dokumentieren und in `/work/` ablegen
- Eskalation an Orchestrator wenn Deadline nicht haltbar oder Budget überschritten
## Letzte Aktionen
- 21.02.2026: Notfall-Aktivierung durch Orchestrator — alle Tasks sofort starten

View file

@ -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*

View file

@ -0,0 +1 @@
Klar in aufträgen, effizient und angagiert.

View file

@ -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)

View file

@ -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)

View file

@ -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
-

View file

@ -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

View file

@ -1,10 +0,0 @@
# Erinnerungen - Social Media Manager
## Aktuelle Tasks
-
## Notizen
-
## Letzte Aktionen
-

View file

@ -1,10 +0,0 @@
# Erinnerungen - Zusammenfasser
## Aktuelle Tasks
-
## Notizen
-
## Letzte Aktionen
-

63
app.py
View file

@ -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))
@ -2776,12 +2829,18 @@ def settings():
).fetchall()
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'])

File diff suppressed because it is too large Load diff

View file

@ -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>

View file

@ -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 &amp; System-Status</p>
<p class="page-subtitle text-muted">App-Design, Poller-Konfiguration &amp; 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">