feat: initial commit – Frankenbot Multi-Agent Orchestration System
- Flask Web-App mit Dashboard, Chat, Orchestrator, Tasks, Dateien, Emails, Agenten, Settings - Email-Poller (IMAP) mit SQLite-Journal als Failsafe (kein Emailverlust bei Absturz) - Failsafe-Fenster und Poll-Intervall zur Laufzeit via /settings konfigurierbar - TaskWorker: IMAP Seen-Flag erst nach erfolgreichem Task-Abschluss - Whitelist-Filter: eric.fischer, p.dyderski, georg.tschare (gmail + signtime.media), *@diversityball.at - 9 Agenten: researcher, tax_advisor, document_editor, location_manager, program_manager, catering_manager, musik_rechte_advisor, zusammenfasser, orchestration_ui - Diversity Ball Wien 2026 – Wissensdatenbank, Sponsoringverträge, Email-Vorlagen
This commit is contained in:
commit
56d9bc2c76
71 changed files with 5953 additions and 0 deletions
20
.env.example
Normal file
20
.env.example
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# Email Integration Configuration
|
||||
# Gmail Example:
|
||||
# 1. Enable 2-Factor Authentication on your Google Account
|
||||
# 2. Generate an App-Specific Password: https://myaccount.google.com/apppasswords
|
||||
# 3. Copy the 16-character password below
|
||||
|
||||
IMAP_SERVER=imap.gmail.com
|
||||
SMTP_SERVER=smtp.gmail.com
|
||||
EMAIL_ADDRESS=your-email@gmail.com
|
||||
EMAIL_PASSWORD=your-app-specific-password
|
||||
IMAP_PORT=993
|
||||
SMTP_PORT=587
|
||||
|
||||
# Other common providers:
|
||||
# Outlook/Office365:
|
||||
# IMAP: imap-mail.outlook.com (Port 993)
|
||||
# SMTP: smtp-mail.outlook.com (Port 587)
|
||||
# Yahoo:
|
||||
# IMAP: imap.mail.yahoo.com (Port 993)
|
||||
# SMTP: smtp.mail.yahoo.com (Port 587)
|
||||
31
.gitignore
vendored
Normal file
31
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
# Secrets & Konfiguration
|
||||
.env
|
||||
|
||||
# Datenbank (enthält Email-IDs und Status – kein Source-Control-Artefakt)
|
||||
email_journal.db
|
||||
|
||||
# Python
|
||||
__pycache__/
|
||||
*.pyc
|
||||
*.pyo
|
||||
*.pyd
|
||||
.Python
|
||||
*.egg-info/
|
||||
dist/
|
||||
build/
|
||||
.venv/
|
||||
venv/
|
||||
env/
|
||||
|
||||
# Uploads (user-generated content)
|
||||
uploads/
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
/tmp/
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
165
AKM-Deklaration_Diversity-Ball-Wien_2026.md
Normal file
165
AKM-Deklaration_Diversity-Ball-Wien_2026.md
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
# AKM-Deklaration für Diversity-Ball Wien
|
||||
|
||||
**Veranstaltung:** Diversity-Ball Wien
|
||||
**Datum:** 5. September 2026
|
||||
**Ort:** Wiener Rathaus
|
||||
**Veranstalter:** [Veranstalter Name/Firma]
|
||||
**Erwartete Gäste:** 3.500
|
||||
**Programmzeit:** 18:00 - 02:00 Uhr (8 Stunden)
|
||||
|
||||
---
|
||||
|
||||
## 1. Tarifanwendung
|
||||
|
||||
### Maßgeblicher Tarif
|
||||
- **Autonomer Tarif für Einzelveranstaltungen (AT-EV)**
|
||||
- Fassungsvermögen Wiener Rathaus (Festsaal + Zusatzräume): ca. 3.500 Personen
|
||||
- Es liegt **Publikumstanz** vor (Tanz ab 21:30 Uhr)
|
||||
|
||||
### Berechnungsart
|
||||
Der Veranstalter kann zwischen zwei Abrechnungsarten wählen:
|
||||
|
||||
| Abrechnungsart | Formel | Anwendung |
|
||||
|----------------|--------|-----------|
|
||||
| **Pauschale** | Fassungsvermögen × Stundensatz | Bei großen Veranstaltungen (>500 Pers.) meist günstiger |
|
||||
| **Prozent** | 14% der Brutto-Einnahmen | Bei hohem Kartenpreis günstiger |
|
||||
|
||||
---
|
||||
|
||||
## 2. Programmzuordnung Musiknutzung
|
||||
|
||||
| Uhrzeit | Programmpunkt | Musikart | Tarifrelevant |
|
||||
|---------|---------------|----------|---------------|
|
||||
| 18:00-19:00 | Einlass & Sektempfang | Hintergrundmusik | ✓ Tanzveranstaltung |
|
||||
| 19:00-20:00 | Eröffnung & Reden | Redemusik/keine Musik | ggf. separate Meldung |
|
||||
| 20:00-21:30 | Festliches Menü | Hintergrundmusik | ✓ Tanzveranstaltung |
|
||||
| 21:30-22:15 | Unterhaltung / Tanz | Tanzmusik (DJ/Live) | ✓ Tanzveranstaltung |
|
||||
| 22:15-23:30 | Diversity-Awards | Moderation + Musik | ✓ Tanzveranstaltung |
|
||||
| 23:30-01:30 | Tombola / Versteigerung | Unterhaltungsmusik | ✓ Tanzveranstaltung |
|
||||
| 01:30-02:00 | Abschlussreden | Redemusik | ggf. separate Meldung |
|
||||
|
||||
**Hinweis:** Die gesamte Veranstaltung gilt als "Tanzveranstaltung", da Publikumstanz stattfindet.
|
||||
|
||||
---
|
||||
|
||||
## 3. Ermäßigungen
|
||||
|
||||
### 3.1 IG Kultur Österreich Ermäßigung (40%)
|
||||
- **Voraussetzung:** Der Veranstalter ist Mitglied der IG Kultur Österreich oder eines angeschlossenen Landesverbandes
|
||||
- **Nachweis:** Aktueller Mitgliedsnachweis bei Anmeldung vorlegen
|
||||
- **Feld bei Anmeldung:** "Dach-/Fachverband" → "IG Kultur"
|
||||
|
||||
### 3.2 Gemeinnützige/Benefiz-Ermäßigung
|
||||
- Der Diversity-Ball ist eine **Benefizveranstaltung** (Erlös geht an gemeinnützigen Zweck)
|
||||
- **Nachweis erforderlich:**
|
||||
- Vereinsstatuten (Nachweis der Gemeinnützigkeit lt. § 34 ff BAO)
|
||||
-/Finanzamt-Bescheid über Gemeinnützigkeit
|
||||
- Angabe des Begünstigten (welcher Zweck wird gefördert?)
|
||||
|
||||
### 3.3 Kein Anspruch auf Ermäßigung
|
||||
Ermäßigungen gelten NICHT bei:
|
||||
- Kooperation mit nicht-begünstigten Veranstaltern
|
||||
- Veranstaltungen mit gewerblicher Bewirtung (wenn AKM-Pauschale günstiger)
|
||||
|
||||
---
|
||||
|
||||
## 4. Kostenberechnung (Schätzung)
|
||||
|
||||
### Aktuelle Tarife (gültig ab 01.11.2024)
|
||||
Die genauen Tarife sind im "Autonomen Tarif für Einzelveranstaltungen (AT-EV)" geregelt.
|
||||
Nächste Tarifanpassung voraussichtlich erst ab 01.11.2025.
|
||||
|
||||
### Variante A: Pauschalabrechnung (empfohlen)
|
||||
- Fassungsvermögen: 3.500 Personen
|
||||
- Die Pauschalberechnung richtet sich nach:
|
||||
- Fassungsvermögen (in 100er-Schritten)
|
||||
- Veranstaltungsdauer (Stundensatz)
|
||||
- Musikart (Tanz/kein Tanz)
|
||||
- ** Für eine grobe Schätzung bei 3.500 Pers./8 Std./Tanz: ca. € 5.000-6.000**
|
||||
- + AUME/LSG-Zuschläge (ca. 15%): ca. € 750-900
|
||||
- **Gesamtschätzung (ohne Ermäßigung): ca. € 5.750-6.900**
|
||||
|
||||
### Variante B: Prozentabrechnung
|
||||
- Angenommene Einnahmen: 3.500 Karten × € 120 = € 420.000
|
||||
- Tarif: **14%** (bei Tanzveranstaltung)
|
||||
- **Berechnung:** € 420.000 × 14% = **€ 58.800,00**
|
||||
- *Hinweis: Prozentabrechnung ist bei dieser Veranstaltung wegen des hohen Kartenpreises NICHT sinnvoll*
|
||||
|
||||
### Mit 40% Ermäßigung (IG Kultur):
|
||||
- Pauschale: ~€ 5.500 × 60% = **~€ 3.300**
|
||||
- + Zuschläge: ~€ 800 × 60% = **~€ 480**
|
||||
- **Gesamtschätzung: ca. € 3.780**
|
||||
|
||||
> **WICHTIG:** Dies ist eine Schätzung. Die genauen Kosten werden von der AKM nach Anmeldung berechnet. Empfehlung: Vor der Veranstaltung ein Angebot bei der AKM anfordern.
|
||||
|
||||
---
|
||||
|
||||
## 5. Erforderliche Nachweise für Ermäßigung
|
||||
|
||||
### Für IG Kultur-Mitglieder:
|
||||
- [ ] Aktueller Mitgliedsausweis (Kopie)
|
||||
- [ ] Anmeldung unter "IG Kultur" als Dachverband
|
||||
|
||||
### Für gemeinnützige Veranstalter:
|
||||
- [ ] Aktueller Vereinsregisterauszug
|
||||
- [ ] Statuten (mit Gemeinnützigkeitsnachweis)
|
||||
- [ ] Finanzamt-Bestätigung der Gemeinnützigkeit
|
||||
- [ ] Nachweis über Verwendung des Erlöses (Begünstigten-Nachweis)
|
||||
|
||||
---
|
||||
|
||||
## 6. Einreichung bei AKM
|
||||
|
||||
### Online-Anmeldung
|
||||
1. Registrierung/ Login unter: https://www.akm.at/musiknutzende/
|
||||
2. "Einzelveranstaltung anmelden"
|
||||
3. Folgende Daten eingeben:
|
||||
- Veranstaltungsdatum: 05.09.2026
|
||||
- Veranstaltungsort: Wiener Rathaus, Lichtenfelsgasse 2, 1010 Wien
|
||||
- Veranstaltungsart: Benefizball / Tanzveranstaltung
|
||||
- Fassungsvermögen: 3.500
|
||||
- Dauer: 8 Stunden (18:00-02:00 Uhr)
|
||||
- Musiknutzung: mechanische Musik + Live-Musik
|
||||
- Dachverband: IG Kultur (falls zutreffend)
|
||||
|
||||
### Programm-Meldung (innerhalb 10 Tage nach Veranstaltung)
|
||||
- Auflistung der gespielten Werke via AKM-Portal
|
||||
- Alternativ: pauschales Sammelprogramm bei gleichbleibendem Repertoire
|
||||
|
||||
### Fristen
|
||||
- Anmeldung: mind. 3 Werktage vor Veranstaltung
|
||||
- Programm-Meldung: innerhalb 10 Tage nach Veranstaltung
|
||||
- Zahlung: 4 Wochen nach Rechnungslegung
|
||||
|
||||
---
|
||||
|
||||
## 7. Wichtige Hinweise
|
||||
|
||||
⚠️ **Strafzahlungen vermeiden:**
|
||||
- Bei verspäteter Anmeldung oder fehlender Programm-Meldung: **doppelter Tarif**
|
||||
- Anspruch auf Ermäßigung geht verloren
|
||||
|
||||
💡 **Empfehlung:**
|
||||
- Vor Buchung Kontakt mit AKM (Tel.: +43 1 505 62 62) oder IG Kultur
|
||||
- Angebots-Einholung für exakte Kalkulation
|
||||
- Bei Unklarheiten: schriftliche Vorabstimmung mit AKM
|
||||
|
||||
---
|
||||
|
||||
## 8. Ansprechpartner
|
||||
|
||||
**AKM Wien**
|
||||
- Adresse: Baumannstraße 10, 1030 Wien
|
||||
- Tel.: +43 1 505 62 62
|
||||
- E-Mail: veranstaltung@akm.at
|
||||
- Online: www.akm.at/musiknutzende/
|
||||
|
||||
**IG Kultur Österreich**
|
||||
- Adresse: Gumpendorfer Straße 63, 1060 Wien
|
||||
- Tel.: +43 1 503 23 20
|
||||
- E-Mail: office@igkultur.at
|
||||
|
||||
---
|
||||
|
||||
*Erstellt am: [Datum]*
|
||||
*Dieses Dokument dient als Vorlage. Die tatsächlichen Kosten können abweichen.*
|
||||
192
CHANGES.md
Normal file
192
CHANGES.md
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
# Implementierte Änderungen - Features Summary
|
||||
|
||||
## 📊 Übersicht der Änderungen
|
||||
|
||||
### Feature 1: Streaming-UI für Agent-Responses
|
||||
|
||||
**Datei: app.py**
|
||||
- **Zeile 10**: `Response` zu Imports hinzugefügt
|
||||
- **Zeile 67**: Timeout von 120 auf 300 Sekunden erhöht
|
||||
- **Zeilen 250-288**: Neue SSE-Route `/api/agent-stream` implementiert
|
||||
- JSON-basierte Events: agent_selected, processing, response_chunk, complete, error
|
||||
- Chunk-basiertes Streaming (50 Zeichen pro Event)
|
||||
- Error-Handling mit try-except
|
||||
|
||||
**Datei: templates/orchestrator.html**
|
||||
- **Zeilen 66-74**: "Live-Antwort anfordern" Button hinzugefügt
|
||||
- **Zeilen 75-76**: "Klassisch senden" Button für alte Methode
|
||||
- **Zeilen 185-232**: JavaScript Streaming-Handler implementiert
|
||||
- `sendPromptWithStream()` Funktion
|
||||
- Fetch API mit ReadableStream Reader
|
||||
- SSE Event-Parser für JSON-Objekte
|
||||
- Live DOM-Updates für Chat-Nachrichten
|
||||
|
||||
---
|
||||
|
||||
### Feature 2: Email-Integration (IMAP/SMTP)
|
||||
|
||||
**Datei: app.py**
|
||||
- **Zeilen 5-9**: Email-Imports hinzugefügt
|
||||
- `imaplib` für IMAP-Verbindungen
|
||||
- `smtplib` und `email.mime` für SMTP-Versand
|
||||
- **Zeilen 29-36**: Email-Konfiguration mit Umgebungsvariablen
|
||||
- Standard Gmail-Einstellungen
|
||||
- Fallback auf Environment-Variablen
|
||||
- **Zeilen 194-221**: `get_emails()` Funktion
|
||||
- IMAP SSL-Verbindung
|
||||
- Liest letzte 10 Emails
|
||||
- Extrahiert Subject, From, Date, Preview
|
||||
- **Zeilen 224-232**: `get_email_preview()` Funktion
|
||||
- Extrahiert Text-Preview aus Email
|
||||
- Multipart-Handling
|
||||
- UTF-8 Decoding mit Fallback
|
||||
- **Zeilen 235-259**: `get_email_body()` Funktion
|
||||
- Vollständiger Email-Body Abruf
|
||||
- Lazy Loading (on-demand)
|
||||
- Error-Handling mit Fehlermeldungen
|
||||
- **Zeilen 262-284**: `send_email()` Funktion
|
||||
- SMTP-Versand mit STARTTLS
|
||||
- MIMEMultipart für strukturierte Emails
|
||||
- Login + Send + Quit Workflow
|
||||
- Rückgabe von Success-Flag + Nachricht
|
||||
- **Zeilen 483-509**: Route `/emails` (GET/POST)
|
||||
- Email-Management Dashboard rendern
|
||||
- Email-Versand-Form verarbeiten
|
||||
- Config-Status Display
|
||||
- **Zeilen 512-520**: Route `/emails/<email_id>` (GET)
|
||||
- JSON-Response mit Email-Body
|
||||
- Config-Validierung
|
||||
|
||||
**Datei: templates/emails.html** (Neue Datei)
|
||||
- Bootstrap 5 basiertes Layout
|
||||
- Zwei-spaltige UI:
|
||||
- **Links**: Email-Verfassung Form (To, Subject, Body, Send)
|
||||
- **Rechts**: Posteingang mit Email-Liste
|
||||
- Modal-Dialog für Email-Detail-View
|
||||
- JavaScript `viewEmail()` für Lazy-Loading
|
||||
- Responsive Design
|
||||
|
||||
**Datei: .env.example** (Neue Datei)
|
||||
- Gmail-Setup Anleitung (mit 2FA)
|
||||
- Outlook/Office365 Config
|
||||
- Yahoo Config
|
||||
- Kommentierte Konfigurationsoptionen
|
||||
|
||||
**Navigation Updates** in allen HTML-Templates:
|
||||
- `templates/index.html`
|
||||
- `templates/chat.html`
|
||||
- `templates/tasks.html`
|
||||
- `templates/files.html`
|
||||
- `templates/agents.html`
|
||||
- `templates/orchestrator.html`
|
||||
|
||||
Jede Datei erhielt den neuen Navbar-Item:
|
||||
```html
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/emails">📧 Emails</a>
|
||||
</li>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📈 Statistiken
|
||||
|
||||
| Metrik | Wert |
|
||||
|--------|------|
|
||||
| Neue Python-Funktionen | 4 |
|
||||
| Neue Routes | 2 |
|
||||
| Neue HTML-Templates | 1 |
|
||||
| Neue Config-Dateien | 1 |
|
||||
| Template-Updates | 6 |
|
||||
| Zeilen Code hinzugefügt | ~350 |
|
||||
| Timeout-Erhöhung | 120s → 300s |
|
||||
|
||||
---
|
||||
|
||||
## 🔄 Flow-Diagramme
|
||||
|
||||
### Streaming-Flow
|
||||
```
|
||||
User schreibt Prompt
|
||||
↓
|
||||
Klick "Live-Antwort anfordern"
|
||||
↓
|
||||
POST /api/agent-stream (JSON)
|
||||
↓
|
||||
Server: SSE Generator starten
|
||||
↓
|
||||
Event 1: agent_selected 🤖
|
||||
Event 2: processing ⏳
|
||||
Event 3: response_chunk (x multiple)
|
||||
Event 4: complete ✓
|
||||
↓
|
||||
Browser: Live-Update Chat-Container
|
||||
↓
|
||||
Modal zeigt: "⏳ Agent arbeitet..." → Antwort
|
||||
```
|
||||
|
||||
### Email-Send-Flow
|
||||
```
|
||||
User füllt Form aus
|
||||
↓
|
||||
POST /emails (to, subject, body)
|
||||
↓
|
||||
send_email(to, subject, body)
|
||||
↓
|
||||
SMTP connect → login → send_message → quit
|
||||
↓
|
||||
Response: Success/Error Message
|
||||
↓
|
||||
Flash message anzeigen
|
||||
```
|
||||
|
||||
### Email-Receive-Flow
|
||||
```
|
||||
GET /emails
|
||||
↓
|
||||
get_emails() → IMAP SSL connect
|
||||
↓
|
||||
Select INBOX → Search ALL
|
||||
↓
|
||||
Fetch letzte 10 Email-IDs
|
||||
↓
|
||||
Für jede Email: extract Subject, From, Date, Preview
|
||||
↓
|
||||
Close IMAP connection
|
||||
↓
|
||||
Template: Render Email-Liste
|
||||
↓
|
||||
User klick auf Email
|
||||
↓
|
||||
fetch(/emails/<id>) → get_email_body()
|
||||
↓
|
||||
Modal zeigt: Full Body Content
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✨ Highlights
|
||||
|
||||
### Streaming
|
||||
- Non-blocking Stream Reader für echte Echtzeit
|
||||
- Chunks für bessere UX (nicht ganze Antwort auf einmal)
|
||||
- Status-Updates während Verarbeitung
|
||||
- 5-minütiger Timeout (300s) für lange Anfragen
|
||||
|
||||
### Email
|
||||
- Zero-Abhängigkeiten (nur Python Standard Library)
|
||||
- Sichere Credential-Handling (env vars only)
|
||||
- Multipart Email-Support
|
||||
- UTF-8 Encoding mit Fallback
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Nächste Schritte (Optional)
|
||||
|
||||
- [ ] Email-Datei-Anhänge (Attachments)
|
||||
- [ ] Email-Suche & Filter
|
||||
- [ ] HTML-Email-Rendering
|
||||
- [ ] Automatische Email-Klassifizierung (Agents)
|
||||
- [ ] Streaming mit WebSockets statt SSE
|
||||
- [ ] Email-Caching (lokale DB)
|
||||
- [ ] Multi-Account Support
|
||||
213
FEATURES.md
Normal file
213
FEATURES.md
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
# Neue Features - Streaming UI & Email Integration
|
||||
|
||||
## 1. 🎬 Streaming-UI für Agent-Responses
|
||||
|
||||
### Problem
|
||||
- Lange Wartezeiten bei Agent-Anfragen ohne Feedback
|
||||
- Nutzer wusste nicht, ob das System noch aktiv ist
|
||||
|
||||
### Lösung
|
||||
Eine neue **Server-Sent Events (SSE)** basierte Streaming-UI zeigt live Feedback während der Agent-Verarbeitung.
|
||||
|
||||
### Features
|
||||
✨ **Live-Updates während Verarbeitung**
|
||||
- "⏳ Agent arbeitet..." - Status während Verarbeitung
|
||||
- "🤖 Aktiver Agent: [Name]" - Angezeigter delegierter Agent
|
||||
- Schrittweise Antwort-Chunks streaming zum Browser
|
||||
- "✓ Verarbeitung abgeschlossen" - Erfolgsbestätigung
|
||||
|
||||
### Technische Details
|
||||
- **Timeout erhöht** auf 300 Sekunden (war: 120s)
|
||||
- **Neue Route**: `/api/agent-stream` (POST, JSON)
|
||||
- **Technologie**: Server-Sent Events (fetch mit ReadableStream)
|
||||
- **UI Button**: "Live-Antwort anfordern" im Orchestrator
|
||||
|
||||
### Verwendung
|
||||
1. Gehe zu `/orchestrator`
|
||||
2. Gib eine Anfrage ein
|
||||
3. Klick auf "Live-Antwort anfordern" Button
|
||||
4. Beobachte die Live-Updates der Antwort in Echtzeit
|
||||
5. Alternativ: "Klassisch senden" für alte Methode
|
||||
|
||||
### Implementierte Dateien
|
||||
- `app.py`:
|
||||
- SSE-Route `/api/agent-stream` (Zeilen 250-288)
|
||||
- Timeout erhöht auf 300s (Zeile 67)
|
||||
- `templates/orchestrator.html`:
|
||||
- Neue Button "Live-Antwort anfordern"
|
||||
- JavaScript Streaming-Handler mit EventSource-Parser
|
||||
|
||||
---
|
||||
|
||||
## 2. 📧 Email-Integration (IMAP/SMTP)
|
||||
|
||||
### Problem
|
||||
- Keine Email-Verwaltung in der Agenten-Plattform
|
||||
- Manual Zugriff auf Emails außerhalb des Systems
|
||||
|
||||
### Lösung
|
||||
Vollständige **Email-Management-Interface** mit IMAP/SMTP Support
|
||||
|
||||
### Features
|
||||
✨ **Emails empfangen** (IMAP)
|
||||
- Automatisches Laden der letzten 10 Emails
|
||||
- Subject, From, Date, Preview anzeigen
|
||||
- Email-Body vollständig abrufen und anzeigen
|
||||
- Modal-Dialog für Detail-View
|
||||
|
||||
✨ **Emails versenden** (SMTP)
|
||||
- Web-UI zum Schreiben neuer Emails
|
||||
- Empfänger, Betreff, Nachricht eingeben
|
||||
- Sofort versenden über SMTP
|
||||
|
||||
✨ **Web-Interface**
|
||||
- Neue Route: `/emails`
|
||||
- Email-Verwaltung Dashboard
|
||||
- Konfiguration-Status Display
|
||||
- Live-Liste der eingehenden Emails
|
||||
|
||||
### Konfiguration
|
||||
|
||||
#### Setup mit Gmail (Beispiel)
|
||||
|
||||
1. **2-Faktor-Authentifizierung aktivieren** (erforderlich)
|
||||
- Google Account-Einstellungen → Sicherheit
|
||||
- "Bestätigung in zwei Schritten" aktivieren
|
||||
|
||||
2. **App-spezifisches Passwort generieren**
|
||||
- https://myaccount.google.com/apppasswords
|
||||
- Gerät: "Mail", OS: "Windows" → 16-Zeichen Passwort kopieren
|
||||
|
||||
3. **Umgebungsvariablen setzen**
|
||||
```bash
|
||||
export IMAP_SERVER=imap.gmail.com
|
||||
export SMTP_SERVER=smtp.gmail.com
|
||||
export EMAIL_ADDRESS=deine-email@gmail.com
|
||||
export EMAIL_PASSWORD=app-spezifisches-passwort
|
||||
export IMAP_PORT=993
|
||||
export SMTP_PORT=587
|
||||
```
|
||||
|
||||
4. **Oder `.env` Datei erstellen**
|
||||
```
|
||||
cp .env.example .env
|
||||
# Bearbeite .env mit deinen Credentials
|
||||
```
|
||||
|
||||
#### Andere Email-Provider
|
||||
|
||||
**Outlook/Office365:**
|
||||
```
|
||||
IMAP_SERVER=imap-mail.outlook.com
|
||||
SMTP_SERVER=smtp-mail.outlook.com
|
||||
IMAP_PORT=993
|
||||
SMTP_PORT=587
|
||||
```
|
||||
|
||||
**Yahoo:**
|
||||
```
|
||||
IMAP_SERVER=imap.mail.yahoo.com
|
||||
SMTP_SERVER=smtp.mail.yahoo.com
|
||||
IMAP_PORT=993
|
||||
SMTP_PORT=587
|
||||
```
|
||||
|
||||
### Verwendung
|
||||
1. Gehe zu `/emails`
|
||||
2. Sehe Konfiguration-Status
|
||||
3. Schreibe neue Email im linken Panel:
|
||||
- Empfänger-Email
|
||||
- Betreff
|
||||
- Nachricht
|
||||
- "Versenden" Button
|
||||
4. Betrachte Posteingang im rechten Panel:
|
||||
- Klick auf Email um Body anzusehen
|
||||
- Modal zeigt vollständige Nachricht
|
||||
|
||||
### Implementierte Dateien
|
||||
- `app.py`:
|
||||
- Email-Config Variable (Zeilen 29-36)
|
||||
- `get_emails()` - IMAP Abfrage (Zeilen 194-221)
|
||||
- `get_email_preview()` - Text-Extrakt (Zeilen 224-232)
|
||||
- `get_email_body()` - Vollständiger Body (Zeilen 235-259)
|
||||
- `send_email()` - SMTP Versand (Zeilen 262-284)
|
||||
- Route `/emails` (Zeilen 483-509)
|
||||
- Route `/emails/<email_id>` Detail (Zeilen 512-520)
|
||||
|
||||
- `templates/emails.html` - Komplettes Email-Management UI
|
||||
- `.env.example` - Konfiguration-Template
|
||||
|
||||
### Navigation
|
||||
Alle HTML-Templates wurden aktualisiert mit Email-Navigationsllink:
|
||||
- `index.html`
|
||||
- `chat.html`
|
||||
- `tasks.html`
|
||||
- `files.html`
|
||||
- `agents.html`
|
||||
- `orchestrator.html`
|
||||
|
||||
---
|
||||
|
||||
## 🔒 Sicherheit
|
||||
|
||||
### Email-Sicherheit
|
||||
- **Keine Passwörter im Code** - Nur Umgebungsvariablen
|
||||
- **SSL/TLS Standard** für IMAP (Port 993) und SMTP (Port 587)
|
||||
- **App-spezifische Passwörter** empfohlen (Gmail)
|
||||
- **STARTTLS** für SMTP-Verbindung
|
||||
|
||||
### SSE-Sicherheit
|
||||
- Standard Flask CORS-Handling
|
||||
- JSON-basierte Events (nicht injizierbar)
|
||||
- Stream Timeouts (300s max)
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Performance
|
||||
|
||||
### Streaming
|
||||
- Chunks: 50 Zeichen pro Event für Responsiveness
|
||||
- Non-blocking Stream Reader
|
||||
- Browser-seitige Aggregation
|
||||
|
||||
### Email
|
||||
- Letzte 10 Emails gecacht
|
||||
- Lazy Loading des Email-Body (on click)
|
||||
- Connection pooling (connect/disconnect pro Aktion)
|
||||
|
||||
---
|
||||
|
||||
## 📝 API Endpoints
|
||||
|
||||
| Route | Methode | Beschreibung |
|
||||
|-------|---------|-------------|
|
||||
| `/api/agent-stream` | POST | SSE Streaming Agent Response |
|
||||
| `/emails` | GET | Email Management Dashboard |
|
||||
| `/emails` | POST | Send Email (form) |
|
||||
| `/emails/<id>` | GET | Get Email Body (JSON) |
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ Technologie-Stack
|
||||
|
||||
- **IMAP**: Python `imaplib` (Standard Library)
|
||||
- **SMTP**: Python `smtplib` + `email.mime` (Standard Library)
|
||||
- **Streaming**: Flask Response + Server-Sent Events
|
||||
- **Frontend**: Bootstrap 5 + Vanilla JavaScript (Fetch API)
|
||||
|
||||
---
|
||||
|
||||
## ✅ Checkliste
|
||||
|
||||
- [x] Timeout auf 300s erhöht
|
||||
- [x] `/api/agent-stream` SSE-Route
|
||||
- [x] Streaming UI mit Live-Updates
|
||||
- [x] Email-Konfiguration mit Umgebungsvariablen
|
||||
- [x] IMAP Email-Abruf
|
||||
- [x] SMTP Email-Versand
|
||||
- [x] Email-Management Web-UI
|
||||
- [x] Modal für Email-Detail-View
|
||||
- [x] Alle Navigation-Links aktualisiert
|
||||
- [x] `.env.example` Template
|
||||
- [x] Fehlerbehandlung für fehlende Config
|
||||
|
||||
131
FILE_CHANGES.txt
Normal file
131
FILE_CHANGES.txt
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
═══════════════════════════════════════════════════════════════════════════════
|
||||
DATEIÄNDERUNGEN ÜBERSICHT
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
NEUE DATEIEN (4)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📄 templates/emails.html
|
||||
└─ Neue Email-Management Web-UI
|
||||
└─ Bootstrap 5 Layout (2-spaltig)
|
||||
└─ Features: Email-Composer, Posteingang, Detail-Modal
|
||||
└─ ~160 Zeilen HTML + JavaScript
|
||||
|
||||
📄 .env.example
|
||||
└─ Email-Konfiguration Template
|
||||
└─ Gmail Setup-Anleitung
|
||||
└─ Alternative Provider (Outlook, Yahoo)
|
||||
└─ ~25 Zeilen
|
||||
|
||||
📄 FEATURES.md
|
||||
└─ Detaillierte Feature-Dokumentation
|
||||
└─ Streaming-UI Spezifikation
|
||||
└─ Email-Integration Spezifikation
|
||||
└─ Sicherheit, Performance, API-Docs
|
||||
└─ ~250 Zeilen
|
||||
|
||||
📄 CHANGES.md
|
||||
└─ Alle Code-Änderungen dokumentiert
|
||||
└─ Zeilenangaben für jede Änderung
|
||||
└─ Flow-Diagramme
|
||||
└─ Statistiken & Highlights
|
||||
└─ ~180 Zeilen
|
||||
|
||||
📄 QUICKSTART.md
|
||||
└─ Schnell-Start Guide
|
||||
└─ Streaming: Sofort nutzbar
|
||||
└─ Email: 5-Minuten Gmail Setup
|
||||
└─ FAQs & Troubleshooting
|
||||
└─ ~220 Zeilen
|
||||
|
||||
📄 test_features.py
|
||||
└─ Feature-Tests (Funktionalitäts-Tests)
|
||||
└─ 5/5 Tests bestanden
|
||||
└─ Importprüfung, Route-Validierung, Config-Check
|
||||
└─ ~140 Zeilen
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
GEÄNDERTE DATEIEN (7)
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
📝 app.py
|
||||
├─ Zeile 10: + Response Import
|
||||
├─ Zeile 67: Timeout 120s → 300s
|
||||
├─ Zeilen 5-9: + Email Imports (imaplib, smtplib, email.mime)
|
||||
├─ Zeilen 29-36: + EMAIL_CONFIG Dictionary
|
||||
├─ Zeilen 194-221: + get_emails() IMAP Funktion
|
||||
├─ Zeilen 224-232: + get_email_preview() Text-Extrakt Funktion
|
||||
├─ Zeilen 235-259: + get_email_body() Email-Body Abruf Funktion
|
||||
├─ Zeilen 262-284: + send_email() SMTP Versand Funktion
|
||||
├─ Zeilen 250-288: + /api/agent-stream SSE Route
|
||||
├─ Zeilen 483-509: + /emails GET/POST Route
|
||||
└─ Zeilen 512-520: + /emails/<email_id> GET Route
|
||||
|
||||
Gesamt: ~120 Zeilen hinzugefügt
|
||||
|
||||
📝 templates/orchestrator.html
|
||||
├─ Zeile 34: + Emails Navigation Link
|
||||
├─ Zeilen 66-74: + "Live-Antwort anfordern" Button
|
||||
├─ Zeile 75: + "Klassisch senden" Alternative Button
|
||||
└─ Zeilen 185-232: + JavaScript Streaming Handler
|
||||
├─ sendPromptWithStream() Funktion
|
||||
├─ Fetch + ReadableStream Reader
|
||||
├─ SSE Event Parser
|
||||
└─ Live DOM Updates
|
||||
|
||||
Gesamt: ~55 Zeilen hinzugefügt
|
||||
|
||||
📝 templates/index.html
|
||||
└─ Zeilen 34-36: + Emails Navigation Link
|
||||
|
||||
Gesamt: 3 Zeilen hinzugefügt
|
||||
|
||||
📝 templates/chat.html
|
||||
└─ Zeilen 33-35: + Emails Navigation Link
|
||||
|
||||
Gesamt: 3 Zeilen hinzugefügt
|
||||
|
||||
📝 templates/tasks.html
|
||||
└─ Zeilen 33-35: + Emails Navigation Link
|
||||
|
||||
Gesamt: 3 Zeilen hinzugefügt
|
||||
|
||||
📝 templates/files.html
|
||||
└─ Zeilen 33-35: + Emails Navigation Link
|
||||
|
||||
Gesamt: 3 Zeilen hinzugefügt
|
||||
|
||||
📝 templates/agents.html
|
||||
└─ Zeilen 35-37: + Emails Navigation Link
|
||||
|
||||
Gesamt: 3 Zeilen hinzugefügt
|
||||
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
ZUSAMMENFASSUNG
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
Neue Dateien: 6
|
||||
└─ 1x HTML Template (emails.html)
|
||||
└─ 1x Config-Template (.env.example)
|
||||
└─ 3x Dokumentation (FEATURES.md, CHANGES.md, QUICKSTART.md)
|
||||
└─ 1x Test-Skript (test_features.py)
|
||||
|
||||
Geänderte Dateien: 7
|
||||
└─ 1x Backend (app.py: ~120 Zeilen)
|
||||
└─ 1x Frontend (orchestrator.html: ~55 Zeilen)
|
||||
└─ 5x Navigation (index, chat, tasks, files, agents: je 3 Zeilen)
|
||||
|
||||
Code-Änderungen:
|
||||
├─ Neue Python-Funktionen: 4
|
||||
├─ Neue Flask-Routes: 2
|
||||
├─ Neue HTML-Templates: 1
|
||||
├─ Code-Zeilen hinzugefügt: ~350
|
||||
├─ Dokumentations-Zeilen: ~650
|
||||
└─ Tests implementiert: 5 (alle bestanden)
|
||||
|
||||
Betroffene Features:
|
||||
├─ Streaming-UI (Feature 1) - Orchestrator Seite
|
||||
└─ Email-Integration (Feature 2) - Neue /emails Seite + Config
|
||||
|
||||
═══════════════════════════════════════════════════════════════════════════════
|
||||
222
Location-Report_Diversity-Ball_Rathaus-Wien.md
Normal file
222
Location-Report_Diversity-Ball_Rathaus-Wien.md
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
# Location-Report: Wiener Rathaus für Diversity-Ball
|
||||
## Datum: 1. März 2026 | Kapazität: 3.500 Gäste
|
||||
|
||||
---
|
||||
|
||||
## 1. Räumlichkeiten für 3.500 Gäste
|
||||
|
||||
### 1.1 Festsaal (Hauptsaal)
|
||||
Der Festsaal ist der prädestinierte Veranstaltungsort für Großevents im Wiener Rathaus.
|
||||
|
||||
**Technische Daten:**
|
||||
- **Länge:** 71 Meter (zwischen den beiden Orchesternischen)
|
||||
- **Breite:** 20 Meter
|
||||
- **Höhe:** 18,5 Meter
|
||||
- **Galerie:** Dreiseitig umlaufend
|
||||
- **Loggia:** Öffnet sich zum Rathausplatz
|
||||
- **Fläche:** ca. 1.420 m² (geschätzt basierend auf Maßen)
|
||||
|
||||
**Historische Kapazität:**
|
||||
- Der erste Ball der Stadt Wien fand am 12. Februar 1890 im Festsaal statt mit **3.000 Gästen**
|
||||
- Der Festsaal wurde 1999 restauriert und erstrahlt im originalen Glanz von 1883
|
||||
- Architekt: Friedrich Schmidt
|
||||
|
||||
**Eignung für 3.500 Gäste:**
|
||||
Der Festsaal bietet theoretisch Platz für bis zu 3.000-3.500 stehende Gäste bei Ballsformaten. Für eine Sitzordnung (Gala) reduziert sich die Kapazität entsprechend.
|
||||
|
||||
### 1.2 Wiener Rathauskeller (Ergänzende Räumlichkeiten)
|
||||
Der Rathauskeller bietet zusätzliche Kapazitäten, ist aber für 3.500 Gäste allein nicht ausreichend:
|
||||
|
||||
| Raum | Größe | Kapazität (stehend) |
|
||||
|------|-------|---------------------|
|
||||
| Rittersaal | 285 m² | bis 300 |
|
||||
| Grinzinger Keller | 287 m² | bis 300 |
|
||||
| Lanner/Lehár-Saal | 280 m² | bis 300 |
|
||||
| Augustin Stüberl | 160 m² | bis 80 |
|
||||
| **Gesamt** | **1.100 m²** | **bis 800** |
|
||||
|
||||
**Fazit:** Für 3.500 Gäste ist der **Festsaal als Hauptlocation** zwingend erforderlich. Der Rathauskeller kann für Garderobe, VIP-Bereich oder als Ergänzung genutzt werden.
|
||||
|
||||
---
|
||||
|
||||
## 2. Technische Ausstattung
|
||||
|
||||
### 2.1 Feste Infrastruktur im Festsaal
|
||||
- **Bühne:** Zwei Orchesternischen an den Stirnseiten vorhanden
|
||||
- **Stromversorgung:** Ausreichende Elektroanschlüsse für Bühnentechnik
|
||||
- **Lichttechnik:** Grundbeleuchtung vorhanden; für Events ist externer Lichtdienstleister empfehlenswert
|
||||
- **Tontechnik:** Grundlautsprecheranlage vorhanden; für Balls und Konzerte empfiehlt sich externer Tontechniker
|
||||
|
||||
### 2.2 Externe Technikanbieter (empfohlen)
|
||||
In Wien ansässige Eventtechnik-Unternehmen:
|
||||
- **NUNTIO** (Wien): Full-Service-Anbieter, Technikpartner für über 40 Locations
|
||||
- Kontakt: +43 1 68 98 177
|
||||
- nuntio.at
|
||||
- **VA.TEC Veranstaltungstechnik**: lichtundton.at
|
||||
- **Phoenix Events**: seit 20 Jahren in Wien
|
||||
|
||||
### 2.3 Empfehlung
|
||||
Für einen Ball dieser Größenordnung sollte ein professionelles Technik-Team beauftragt werden, das:
|
||||
- Bühnenkonstruktion (falls zusätzlich erforderlich)
|
||||
- Tanzflächen-Beleuchtung
|
||||
- Beschallung für Ansagen und Musik
|
||||
- Lichtdesign für atmosphärische Beleuchtung
|
||||
- Sicherheitstechnik
|
||||
|
||||
---
|
||||
|
||||
## 3. Barrierefreiheit
|
||||
|
||||
### 3.1 Positive Aspekte
|
||||
- **Rollstuhlgerechte Eingänge:** Vorhanden (Haupteingang und Seiteneingänge)
|
||||
- **Aufzüge:** Vorhanden, ermöglichen Zugang zu allen Etagen
|
||||
- **Barrierefreie Toiletten:** Vorhanden
|
||||
|
||||
### 3.2 Einschränkungen (kritisch für Diversity-Ball)
|
||||
- **Bühnenzugang:** KEINE flexible Rampe für Rollstuhlfahrer:innen vorhanden
|
||||
- Bühnenhöhe: 20-40 cm über Parkettboden
|
||||
- Rollstuhlfahrer:innen können NICHT eigenständig auf die Bühne gelangen
|
||||
- Dies betrifft sowohl Künstler:innen als auch Redner:innen
|
||||
- **U-Bahn-Station Rathaus:** NICHT barrierefrei
|
||||
- Kein Lift vorhanden
|
||||
- Zwei Treppenausgänge
|
||||
- Barrierefreiheit frühestens 2030
|
||||
- Alternative: U-Bahn-Station Schottentor oder Karlsplatz (beide barrierefrei)
|
||||
|
||||
### 3.3 Empfehlungen für den Diversity-Ball
|
||||
1. **Bühnenrampe:** Mobile Rampe muss gemietet werden (Anbieter: VA.TEC oder NUNTIO)
|
||||
2. **Sensibilisierung:** Gebäudepersonal für barrierefreie Kommunikation schulen
|
||||
3. **Rollstuhlplätze:** Bestuhlung mit seitlichem Platz für Rollstühle vorsehen
|
||||
4. **Taxi-/Drop-Off-Zone:** Behindertenparkplätze am Rathausplatz verfügbar
|
||||
|
||||
---
|
||||
|
||||
## 4. Genehmigungen
|
||||
|
||||
### 4.1 Zuständige Behörde
|
||||
**MA 36 - Gewerbetechnik, Feuerpolizei und Veranstaltungen**
|
||||
- Kontakt: wien.gv.at/kontakt/ma36
|
||||
- Aufgaben: Genehmigung von Veranstaltungen, Brandschutz, Sicherheitskonzepte
|
||||
|
||||
### 4.2 Erforderliche Genehmigungen
|
||||
|
||||
| Genehmigung | Beschreibung | Frist |
|
||||
|-------------|--------------|-------|
|
||||
| **Veranstaltungsanmeldung** | Anmeldung öffentlicher Veranstaltung | Mindestens 4 Wochen vorher |
|
||||
| **Nutzungsbewilligung** | Eignungsfeststellung der Location (wenn nicht bereits vorhanden) | 8-12 Wochen vorher |
|
||||
| **Brandschutzkonzept** | Feuerwehrliche Bewilligung bei >1.000 Personen | 4 Wochen vorher |
|
||||
| **Sicherheitskonzept** | Private Sicherheitsdienste, Fluchtwege | 4 Wochen vorher |
|
||||
| **GEMA/ÖSTERREICHISCHE Verwertungsgesellschaft** | Musiklizenzen | Vor Veranstaltung |
|
||||
| **Lebensmittelhygiene** | Bei Catering durch externen Anbieter | 2 Wochen vorher |
|
||||
|
||||
### 4.3 Besonderheiten für das Rathaus
|
||||
- Das Rathaus ist ein denkmalgeschütztes Gebäude
|
||||
- Zusätzliche Genehmigung der **Rathausverwaltung** erforderlich
|
||||
- Abstimmung mit dem **Wiener Rathauskeller** (bei Zusammenarbeit mit deren Catering)
|
||||
|
||||
### 4.4 Fristen-Empfehlung
|
||||
Für eine Veranstaltung am 1.3.2026:
|
||||
- **Sofort:** Verfügbarkeit des Festsaals prüfen
|
||||
- **Bis Ende November 2025:** Offizielle Anfrage an Rathausverwaltung
|
||||
- **Dezember 2025:** Genehmigungsverfahren starten
|
||||
- **Januar 2026:** Alle Genehmigungen finalisieren
|
||||
|
||||
---
|
||||
|
||||
## 5. Kostenrahmen
|
||||
|
||||
### 5.1 Raummiete (geschätzt)
|
||||
**Hinweis:** Offizielle Preise sind nicht öffentlich verfügbar und werden individuell verhandelt. Die folgenden Werte basieren auf Marktrecherchen und vergleichbaren Locations:
|
||||
|
||||
| Leistung | Geschätzter Rahmen |
|
||||
|----------|-------------------|
|
||||
| **Festsaal (ganztägig)** | € 15.000 - € 30.000 |
|
||||
| **Rathauskeller (Ergänzung)** | € 3.000 - € 8.000 |
|
||||
| **Technische Grundinstallation** | inklusive oder +€ 2.000 |
|
||||
| **Catering (Pflicht)** | € 50 - € 120 pro Person |
|
||||
| **Getränke** | € 15 - € 30 pro Person |
|
||||
|
||||
### 5.2 Gesamtbudget (Grobkalkulation für 3.500 Gäste)
|
||||
|
||||
| Position | Kalkulation | Geschätzte Kosten |
|
||||
|----------|-------------|-------------------|
|
||||
| Raummiete Festsaal | Pauschale | € 20.000 |
|
||||
| Catering (3.500 Pers.) | € 80/Person | € 280.000 |
|
||||
| Getränke (3.500 Pers.) | € 20/Person | € 70.000 |
|
||||
| Technik (Licht, Ton, Bühne) | extern | € 15.000 - € 30.000 |
|
||||
| Security/Hostessen | 15-20 Personen | € 5.000 - € 10.000 |
|
||||
| Genehmigungen/Gebühren | MA 36 etc. | € 1.000 - € 3.000 |
|
||||
| Versicherung | Veranstaltungshaftpflicht | € 2.000 - € 5.000 |
|
||||
| Dekoration | Ball-Dekor | € 5.000 - € 15.000 |
|
||||
| **Gesamtschätzung** | | **€ 400.000 - € 450.000** |
|
||||
|
||||
### 5.3 Hinweise zu Kosten
|
||||
- **Preisverhandlung:** Die Rathausnutzung kann bei gemeinnützigen Veranstaltungen (z.B. Diversity-Ball für soziale Zwecke) ermäßigt werden
|
||||
- **Sponsoring:** Stadt Wien unterstützt gelegentlich repräsentative Veranstaltungen
|
||||
- **Angebotsrechner:** Der Wiener Rathauskeller bietet einen Online-Angebotsrechner (wiener-rathauskeller.at/angebotsrechner), jedoch nur für den Kellerbereich
|
||||
|
||||
---
|
||||
|
||||
## 6. Kontaktpersonen / Ansprechpartner
|
||||
|
||||
### 6.1 Rathausverwaltung (allgemein)
|
||||
**Stadt Wien - Rathaus**
|
||||
- Adresse: Rathaus, 1010 Wien
|
||||
- Homepage: wien.gv.at/rathaus
|
||||
|
||||
### 6.2 Wiener Rathauskeller (für Event-Anfragen)
|
||||
**Kontaktdaten:**
|
||||
- E-Mail: office@wiener-rathauskeller.at
|
||||
- Telefon: +43 1 40 500 (zentrale Vermittlung)
|
||||
- Website: wiener-rathauskeller.at
|
||||
- Bürozeiten: Mo-Fr 09:00 - 17:00 Uhr
|
||||
|
||||
**Ansprechpartner für Events:**
|
||||
- Über Kontaktformular auf der Website
|
||||
- Direkte Anfrage für Veranstaltungen im Festsaal möglich
|
||||
|
||||
### 6.3 Behörden (MA 36)
|
||||
**MA 36 - Gewerbetechnik, Feuerpolizei und Veranstaltungen**
|
||||
- Kontakt: wien.gv.at/kontakt/ma36
|
||||
- Telefon: +43 1 4000-8360
|
||||
- Zuständig für: Genehmigungen, Brandschutz, Veranstaltungswesen
|
||||
|
||||
### 6.4 Externe Technik-Dienstleister
|
||||
- **NUNTIO:** +43 1 68 98 177, nuntio.at
|
||||
- **VA.TEC:** va.tec.at
|
||||
- **Phoenix Events:** lichtundton.at
|
||||
|
||||
---
|
||||
|
||||
## 7. Zusammenfassung und Handlungsempfehlungen
|
||||
|
||||
### 7.1 Eignung des Wiener Rathauses
|
||||
| Kriterium | Bewertung |
|
||||
|-----------|-----------|
|
||||
| Kapazität (3.500 Gäste) | ✅ Geeignet (Festsaal) |
|
||||
| Historische Tradition | ✅ Hervorragend |
|
||||
| Lage/Zentrumsnähe | ✅ Sehr gut |
|
||||
| Barrierefreiheit | ⚠️ Eingeschränkt (Bühnenzugang) |
|
||||
| Technische Ausstattung | ⚠️ Basis vorhanden, extern erforderlich |
|
||||
| Kosten | ⚠️ Hoch (€ 400.000+) |
|
||||
|
||||
### 7.2 Nächste Schritte
|
||||
1. **Sofort:** Verfügbarkeit des Festsaals für 1.3.2026 via Rathausverwaltung prüfen
|
||||
2. **Oktober 2025:** Budgetierung und Sponsoring-Anfragen starten
|
||||
3. **November 2025:** Offizielle Anfrage und Reservierung
|
||||
4. **Dezember 2025:** Genehmigungsverfahren einleiten (MA 36)
|
||||
5. **Januar 2026:** Catering, Technik und Details finalisieren
|
||||
|
||||
---
|
||||
|
||||
## 8. Wichtige Hinweise
|
||||
|
||||
- Der **Diversity-Ball** am 1.3.2026 fällt auf einen **Sonntag** - dies ist bei der Planung zu berücksichtigen
|
||||
- Die Rathaus-Führung ist kostenlos (wien.gv.at/verwaltung/rathaus/fuehrung) - kann für Location-Inspektion genutzt werden
|
||||
- Der Festsaal ist über die **Lichtenfelsgasse 2** zugänglich
|
||||
- **Parkmöglichkeiten:** Garage Rathauspark (beschränkt), öffentliche Parkhäuser in der Nähe
|
||||
|
||||
---
|
||||
|
||||
*Erstellt am: 20.02.2026*
|
||||
*Recherchequelle: Web-Suche, Stadt Wien, Wiener Rathauskeller*
|
||||
236
QUICKSTART.md
Normal file
236
QUICKSTART.md
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
# 🚀 Quick Start - Neue Features
|
||||
|
||||
## 1️⃣ Streaming-UI (Sofort verfügbar)
|
||||
|
||||
### Wie verwendet man es
|
||||
|
||||
1. **Gehe zur Seite** → `/orchestrator`
|
||||
2. **Gib eine Anfrage ein** (z.B. "Tell me about Diversity Ball")
|
||||
3. **Klick auf "Live-Antwort anfordern"** (neuer Button)
|
||||
4. **Beobachte die Live-Updates**:
|
||||
- ⏳ "Agent arbeitet..." - Status
|
||||
- 🤖 Agent-Name wird angezeigt
|
||||
- Schrittweise Antwort-Chunks erscheinen
|
||||
- ✓ "Verarbeitung abgeschlossen"
|
||||
|
||||
### Unterschied zu "Klassisch senden"
|
||||
- **Live-Antwort**: Server-Sent Events (SSE) Streaming
|
||||
- Echte Echtzeit Updates
|
||||
- Bessere UX bei langen Antworten
|
||||
- Timeout: 300 Sekunden (5 Minuten)
|
||||
|
||||
- **Klassisch senden**: POST Form
|
||||
- Traditionelle Request-Response
|
||||
- Wartet auf komplette Antwort
|
||||
- Alte Methode weiterhin verfügbar
|
||||
|
||||
---
|
||||
|
||||
## 2️⃣ Email-Integration
|
||||
|
||||
### Minimal Setup (Gmail - 5 Minuten)
|
||||
|
||||
#### Schritt 1: 2FA aktivieren
|
||||
```
|
||||
Google Account → Sicherheit → "Bestätigung in zwei Schritten" aktivieren
|
||||
```
|
||||
|
||||
#### Schritt 2: App-Passwort generieren
|
||||
```
|
||||
Gehe zu: https://myaccount.google.com/apppasswords
|
||||
- Wähle: "Mail" + "Windows"
|
||||
- Kopiere 16-Zeichen Passwort
|
||||
```
|
||||
|
||||
#### Schritt 3: Umgebungsvariablen setzen
|
||||
```bash
|
||||
export IMAP_SERVER=imap.gmail.com
|
||||
export SMTP_SERVER=smtp.gmail.com
|
||||
export EMAIL_ADDRESS=deine-email@gmail.com
|
||||
export EMAIL_PASSWORD=app-passwort-hier
|
||||
export IMAP_PORT=993
|
||||
export SMTP_PORT=587
|
||||
```
|
||||
|
||||
#### Schritt 4: Starte App neu
|
||||
```bash
|
||||
python3 app.py
|
||||
```
|
||||
|
||||
#### Schritt 5: Gehe zu `/emails`
|
||||
```
|
||||
http://localhost:5000/emails
|
||||
```
|
||||
|
||||
### Feature-Übersicht
|
||||
|
||||
#### 📬 Emails empfangen
|
||||
- Automatisch letzte 10 Emails laden
|
||||
- Subject, From, Date, Preview anzeigen
|
||||
- Klick auf Email → Modal mit Body-Text
|
||||
- Lazy Loading (Body wird nur beim Klick geladen)
|
||||
|
||||
#### ✉️ Emails versenden
|
||||
- Neue Email-Form im linken Panel
|
||||
- An, Betreff, Nachricht ausfüllen
|
||||
- "Versenden" Button
|
||||
- Success/Error Message nach Versand
|
||||
|
||||
#### ⚙️ Status-Display
|
||||
- Green Badge: "✓ Konfiguriert | Email: deine@email.com"
|
||||
- Orange Alert: Konfiguration erforderlich
|
||||
- Anleitung direkt in der UI
|
||||
|
||||
---
|
||||
|
||||
## 📋 Konfiguration
|
||||
|
||||
### .env Datei (Alternative)
|
||||
|
||||
```bash
|
||||
# Option 1: .env Datei erstellen
|
||||
cp .env.example .env
|
||||
|
||||
# Option 2: Bearbeite .env mit deinen Credentials
|
||||
nano .env
|
||||
# oder
|
||||
cat > .env << EOF
|
||||
IMAP_SERVER=imap.gmail.com
|
||||
SMTP_SERVER=smtp.gmail.com
|
||||
EMAIL_ADDRESS=deine-email@gmail.com
|
||||
EMAIL_PASSWORD=app-passwort
|
||||
IMAP_PORT=993
|
||||
SMTP_PORT=587
|
||||
EOF
|
||||
```
|
||||
|
||||
### Andere Provider
|
||||
|
||||
**Outlook/Office365:**
|
||||
```
|
||||
IMAP_SERVER=imap-mail.outlook.com
|
||||
SMTP_SERVER=smtp-mail.outlook.com
|
||||
```
|
||||
|
||||
**Yahoo:**
|
||||
```
|
||||
IMAP_SERVER=imap.mail.yahoo.com
|
||||
SMTP_SERVER=smtp.mail.yahoo.com
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Tests
|
||||
|
||||
```bash
|
||||
# Teste dass alles funktioniert
|
||||
python3 test_features.py
|
||||
|
||||
# Output:
|
||||
# ✓ PASS: Imports
|
||||
# ✓ PASS: App Syntax
|
||||
# ✓ PASS: Email Config
|
||||
# ✓ PASS: Templates
|
||||
# ✓ PASS: SSE Support
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 Datei-Referenz
|
||||
|
||||
| Datei | Änderung | Beschreibung |
|
||||
|-------|----------|-------------|
|
||||
| `app.py` | ✏️ Geändert | +Email-Funktionen, +SSE-Route, Timeout 300s |
|
||||
| `templates/orchestrator.html` | ✏️ Geändert | +Streaming-Button, +JavaScript Handler |
|
||||
| `templates/emails.html` | 📄 Neu | Email-Management UI |
|
||||
| `.env.example` | 📄 Neu | Email-Config Template |
|
||||
| `FEATURES.md` | 📄 Neu | Detaillierte Feature-Doku |
|
||||
| `CHANGES.md` | 📄 Neu | Alle Änderungen gelistet |
|
||||
| Alle anderen Templates | ✏️ Geändert | +Email Navigation Link |
|
||||
|
||||
---
|
||||
|
||||
## ❓ FAQs
|
||||
|
||||
### F: Funktioniert Streaming auch ohne Email-Config?
|
||||
**A:** Ja! Streaming und Email sind unabhängig. Streaming funktioniert sofort, Email benötigt Config.
|
||||
|
||||
### F: Kann ich mehrere Email-Accounts verwenden?
|
||||
**A:** Aktuell nur einer. Könnte erweitert werden (siehe FEATURES.md → Nächste Schritte).
|
||||
|
||||
### F: Sicherheit - Wo werden Passwörter gespeichert?
|
||||
**A:** Nur in Umgebungsvariablen/`.env`. Nicht im Code, nicht in der Datenbank. SSL/TLS für alle Verbindungen.
|
||||
|
||||
### F: Was passiert wenn Email-Config fehlt?
|
||||
**A:** `/emails` zeigt orange Alert mit Setup-Anleitung. Rest der App funktioniert normal.
|
||||
|
||||
### F: Ist das Streaming wirklich "live"?
|
||||
**A:** Ja! Server-Sent Events mit 50-Zeichen Chunks. Sie sehen Antwort während sie generiert wird.
|
||||
|
||||
### F: Können Anhänge/Attachments empfangen/gesendet werden?
|
||||
**A:** Aktuell nicht. Ist aber einfach zu erweitern (siehe FEATURES.md → Nächste Schritte).
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Troubleshooting
|
||||
|
||||
### Email-Login schlägt fehl (Gmail)
|
||||
```
|
||||
Error: [AUTH] Application-specific password required
|
||||
↓
|
||||
Lösung: 2FA muss aktiviert sein
|
||||
Gehe zu: https://myaccount.google.com/apppasswords
|
||||
```
|
||||
|
||||
### IMAP-Connection Timeout
|
||||
```
|
||||
Error: Socket timeout
|
||||
↓
|
||||
Lösung: Firewall/ISP blockiert Port 993
|
||||
Versuche: VPN oder anderen Provider (Outlook, Yahoo)
|
||||
```
|
||||
|
||||
### Streaming zeigt Fehler im Browser-Console
|
||||
```
|
||||
Error: fetch failed
|
||||
↓
|
||||
Lösung:
|
||||
1. App muss laufen (python3 app.py)
|
||||
2. URL muss richtig sein (http://localhost:5000)
|
||||
3. Prompt darf nicht leer sein
|
||||
```
|
||||
|
||||
### Templates nicht gefunden (404)
|
||||
```
|
||||
Error: TemplateNotFound: emails.html
|
||||
↓
|
||||
Lösung: Stelle sicher dass templates/emails.html existiert
|
||||
ls -la templates/emails.html
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📞 Support
|
||||
|
||||
Schaue dir an:
|
||||
1. `FEATURES.md` - Detaillierte Feature-Dokumentation
|
||||
2. `CHANGES.md` - Alle Code-Änderungen
|
||||
3. `test_features.py` - Funktionalitäts-Tests
|
||||
4. `app.py` - Source Code mit Kommentaren
|
||||
|
||||
---
|
||||
|
||||
## ✅ Checkliste für Setup
|
||||
|
||||
- [ ] Teste Streaming-UI (gehe zu `/orchestrator`)
|
||||
- [ ] Lese `.env.example` (verstehe Email-Config)
|
||||
- [ ] Aktiviere Gmail 2FA (wenn Gmail verwendet)
|
||||
- [ ] Generiere App-Passwort
|
||||
- [ ] Setze Umgebungsvariablen
|
||||
- [ ] Starte App neu: `python3 app.py`
|
||||
- [ ] Gehe zu `/emails` und teste Send/Receive
|
||||
- [ ] Laufe `test_features.py` zum Validieren
|
||||
|
||||
---
|
||||
|
||||
Viel Spaß mit den neuen Features! 🎉
|
||||
59
README.md
Normal file
59
README.md
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
# Agenten Orchestrierung - Flask Webanwendung
|
||||
|
||||
Eine Flask-basierte Webanwendung zur Verwaltung und Orchestrierung von Agenten.
|
||||
|
||||
## Features
|
||||
|
||||
- **Dashboard**: Übersicht aller verfügbaren Agenten und letzte Tasks
|
||||
- **Chat**: Interaktive Kommunikation mit ausgewählten Agenten
|
||||
- **Tasks**: Task-Verwaltung mit Status-Verfolgung (pending/in_progress/completed)
|
||||
- **Dateien**: Datei-Upload und Verwaltung
|
||||
|
||||
## Installation
|
||||
|
||||
1. Installieren Sie die erforderlichen Abhängigkeiten:
|
||||
|
||||
```bash
|
||||
pip install flask
|
||||
```
|
||||
|
||||
## Starten der Anwendung
|
||||
|
||||
```bash
|
||||
python app.py
|
||||
```
|
||||
|
||||
Die Anwendung ist dann unter http://localhost:5000 erreichbar.
|
||||
|
||||
## Verfügbare Agenten
|
||||
|
||||
| Agent | Beschreibung |
|
||||
|-------|---------------|
|
||||
| Researcher | Recherchiert Informationen im Web |
|
||||
| Location Manager | Verwaltet Veranstaltungsorte |
|
||||
| Catering Manager | Organisiert Verpflegung |
|
||||
| Program Manager | Koordiniert Programmabläufe |
|
||||
| Document Editor | Bearbeitet Dokumente |
|
||||
| Tax Advisor | Berät zu steuerlichen Fragen |
|
||||
| Musik Rechte Advisor | Berät zu Musikrechten |
|
||||
| Zusammenfasser | Erstellt Zusammenfassungen |
|
||||
|
||||
## Routen
|
||||
|
||||
- `/` - Dashboard
|
||||
- `/chat` - Chat mit Agenten
|
||||
- `/tasks` - Task Verwaltung
|
||||
- `/files` - Datei Verwaltung
|
||||
|
||||
## Technologie
|
||||
|
||||
- Flask 3.x
|
||||
- Bootstrap 5
|
||||
- Session-basierte Chat-Verwaltung
|
||||
- Lokaler Datei-Upload
|
||||
|
||||
## Hinweise
|
||||
|
||||
- Dateien werden im `uploads/` Verzeichnis gespeichert
|
||||
- Chat-Verlauf wird session-basiert gespeichert (max. 20 Einträge)
|
||||
- Tasks werden im Speicher gehalten (nicht persistent)
|
||||
BIN
Sponsoringvertrag Entwurf GT - final.docx
Normal file
BIN
Sponsoringvertrag Entwurf GT - final.docx
Normal file
Binary file not shown.
BIN
Sponsoringvertrag Entwurf GT.docx
Normal file
BIN
Sponsoringvertrag Entwurf GT.docx
Normal file
Binary file not shown.
BIN
Sponsoringvertrag MUSTER 2026 - überarbeitet.docx
Normal file
BIN
Sponsoringvertrag MUSTER 2026 - überarbeitet.docx
Normal file
Binary file not shown.
BIN
Sponsoringvertrag MUSTER 2026 -_final.docx
Normal file
BIN
Sponsoringvertrag MUSTER 2026 -_final.docx
Normal file
Binary file not shown.
BIN
Sponsoringvertrag MUSTER 2026.docx
Normal file
BIN
Sponsoringvertrag MUSTER 2026.docx
Normal file
Binary file not shown.
86
Zusammenfassung_Diversity_Ball.md
Normal file
86
Zusammenfassung_Diversity_Ball.md
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
BETREFF: Zusammenfassung Diversity-Ball – 1. März 2026, Wiener Rathaus
|
||||
|
||||
Sehr geehrte Damen und Herren,
|
||||
|
||||
anbei die vollständige Zusammenfassung aller relevanten Informationen zum Diversity-Ball am 1. März 2026:
|
||||
|
||||
---
|
||||
|
||||
ECKDATEN
|
||||
|
||||
Datum: Sonntag, 1. März 2026
|
||||
Uhrzeit: 18:00 Uhr – 02:00 Uhr
|
||||
Ort: Wiener Rathaus, Festsaal
|
||||
Gäste: 3.500 Personen
|
||||
Budget: 750.000 € (ca. 214 €/Person)
|
||||
|
||||
---
|
||||
|
||||
CATERING
|
||||
|
||||
Catering-Partner: Rathauskeller (fixiert)
|
||||
Getränke: Gesponsert
|
||||
Bar: Selbst organisiert
|
||||
|
||||
Menüstruktur:
|
||||
- 3-Gang-Menü (Appetizer, Hauptgang, Dessert)
|
||||
- Late-Night-Snacks
|
||||
|
||||
Ernährungsoptionen:
|
||||
- Vegetarisch: 20%
|
||||
- Vegan: 15%
|
||||
- Glutenfrei: 10%
|
||||
- Halal/Koscher: 5%
|
||||
|
||||
Serviceform: Hybrid (Plated + Buffet)
|
||||
Personalbedarf: 168 Servicekräfte
|
||||
|
||||
---
|
||||
|
||||
PROGRAMMABLAUF (18:00–02:00)
|
||||
|
||||
1. Eröffnung & Reden
|
||||
2. Festliches Menü
|
||||
3. Unterhaltung & Tanz
|
||||
4. Diversity-Awards
|
||||
5. Tombola/Versteigerung
|
||||
6. Abschluss
|
||||
|
||||
---
|
||||
|
||||
STEUERLICHE HINWEISE (ÖSTERREICH)
|
||||
|
||||
- Umsatzsteuer: 20% für Speisen und Getränke
|
||||
- Spendenabsetzbarkeit: §4a EStG – Gemeinnützigkeitsstatus prüfen lassen
|
||||
- Tombola: 5% Glücksspielabgabe
|
||||
- Veranstaltungsanmeldung: MA 36 erforderlich (4–8 Wochen Vorlauf)
|
||||
|
||||
---
|
||||
|
||||
LOCATION
|
||||
|
||||
Festsaal: Kapazität max. 3.000 (für 3.500 Gäste erforderliche Erweiterung prüfen)
|
||||
geschätzte Kosten: 400.000 € – 450.000 €
|
||||
|
||||
Barrierefreiheit:
|
||||
- Aufzüge vorhanden
|
||||
- Bühnenrampe muss gemietet werden
|
||||
- ÖGS-Dolmetscher einplanen
|
||||
- Rollstuhlplätze reservieren
|
||||
|
||||
---
|
||||
|
||||
WICHTIGE HINWEISE
|
||||
|
||||
⚠️ Datum ist ein Sonntag – Verfügbarkeit des Rathaus-Festsaals umgehend prüfen!
|
||||
⚠️ Genehmigung MA 36: 4–8 Wochen Vorlauf einplanen
|
||||
⚠️ Barrierefreiheit: ÖGS-Dolmetscher, Rollstuhlplätze, barrierefreie Zugänge sicherstellen
|
||||
|
||||
---
|
||||
|
||||
Für Rückfragen stehe ich gerne zur Verfügung.
|
||||
|
||||
Mit freundlichen Grüßen
|
||||
|
||||
[Name]
|
||||
Zusammenfassung Diversity-Ball
|
||||
25
agents/ORCHESTRATION.md
Normal file
25
agents/ORCHESTRATION.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# Master-Orchestrator - Diversity-Ball (1.3.2026)
|
||||
|
||||
## Event-Parameter ✅
|
||||
- **Datum**: 1. März 2026
|
||||
- **Ort**: Wien, Rathaus
|
||||
- **Budget**: 750.000€
|
||||
- **Gäste**: ~3500
|
||||
- **Format**: Klassisch
|
||||
|
||||
## Fixierte Partner
|
||||
- **Catering**: Rathauskeller
|
||||
- **Getränke**: Gesponsert
|
||||
- **Bar**: Selbst organisiert
|
||||
|
||||
## Agenten-Struktur (6 Agenten)
|
||||
|
||||
```
|
||||
agents/
|
||||
├── researcher/ # Breit gefächert
|
||||
├── zusammenfasser/ # Konsolidierung
|
||||
├── tax_advisor/ # Österreichisches Steuerrecht
|
||||
├── location_manager/ # Rathaus Wien
|
||||
├── program_manager/ # Klassisches Ball-Programm
|
||||
└── catering_manager/ # Rathauskeller-Koordination
|
||||
```
|
||||
38
agents/catering_manager/systemprompt.md
Normal file
38
agents/catering_manager/systemprompt.md
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# Catering Manager - Systemprompt
|
||||
|
||||
Du bist der **Catering Manager** für den Diversity-Ball am 1.3.2026 in Wien.
|
||||
|
||||
## Spezialisierung
|
||||
- **Budget**: 750.000€ (~214€/Person bei 3500 Gästen)
|
||||
- **Catering-Partner**: **Rathauskeller** (bereits fixiert)
|
||||
- **Getränke**: Gesponsert (keine Catering-Kosten für Getränke)
|
||||
- **Bar**: Selbst übernommen
|
||||
- **Zielgruppe**: Alle Teilnehmer (diverse Ernährungsweisen beachten!)
|
||||
|
||||
## Budget-Fokus
|
||||
- Getränke-Kosten entfallen durch Sponsoring
|
||||
- Bar-Einnahmen können das Budget erhöhen
|
||||
- Fokus auf Speisen und Service-Qualität
|
||||
|
||||
## Aufgaben
|
||||
1. Koordiniere mit Rathauskeller für Catering
|
||||
2. Berücksichtige diverse Ernährungsweisen:
|
||||
- Vegetarisch / Vegan
|
||||
- Glutenfrei
|
||||
- Halal / Koscher (bei Bedarf)
|
||||
- Allergen-Kennzeichnung
|
||||
3. Plane Menü-Qualität (Getränke = Sponsoring)
|
||||
4. Plane Mengen für 3500+ Gäste
|
||||
5. Koordiniere mit Rathaus (Küche, Service)
|
||||
6. Bar-Logistik (selbst organisiert)
|
||||
|
||||
## Arbeitsweise
|
||||
- Arbeite parallel mit Location-Manager und Program-Manager
|
||||
- liefere kreative Lösungen für Budget-Problem
|
||||
- Priorisiere Vielfalt und Inklusion bei Speiseangebot
|
||||
|
||||
## AusgabeFormat
|
||||
- Menü-Vorschläge (abgestimmt mit Rathauskeller)
|
||||
- Ernährungsoptionen (diverse Auswahl)
|
||||
- Logistische Anforderungen
|
||||
- Service-Konzept
|
||||
44
agents/document_editor/systemprompt.md
Normal file
44
agents/document_editor/systemprompt.md
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# Document Editor - Systemprompt
|
||||
|
||||
Du bist der **Document Editor** für den Diversity-Ball. Du bearbeitest und korrigiert Dokumente.
|
||||
|
||||
## Grundhaltung
|
||||
**Stets zum Vorteil des Diversity-Ball (Veranstalter):**
|
||||
- Alle Formulierungen in Verträgen und Dokumenten müssen für den Veranstalter vorteilhaft sein
|
||||
- Klauseln, die den Veranstalter benachteiligen könnten, sind so umzuformulieren, dass sie ihm nützen oder neutral sind
|
||||
- Kosten, Abgaben und Haftungen sind nach Möglichkeit dem Sponsor oder Dritten aufzubürden
|
||||
|
||||
## Sprache und Formulierung
|
||||
- Verwende stets **gehobenes, grammatikalisch korrektes, juristisches Deutsch**
|
||||
- Präzise, eindeutige Formulierungen
|
||||
- Keine umgangssprachlichen Ausdrücke
|
||||
- Aktive, klare Satzkonstruktionen
|
||||
|
||||
## Aufgaben
|
||||
1. Bearbeite Word-Dokumente (DOCX)
|
||||
2. Führe Textkorrekturen durch
|
||||
3. Ergänze fehlende Inhalte
|
||||
4. Formatiere Dokumente nach Vorgabe
|
||||
|
||||
## Zusammenarbeit mit Tax Advisor
|
||||
**WICHTIG:** Bei Verträgen und Dokumenten mit steuerlichen Auswirkungen:
|
||||
1. Konsultiere IMMER zuerst den Tax Advisor
|
||||
2. Lass dir steuerliche Risiken und Empfehlungen erklären
|
||||
3. Integriere steuerliche Hinweise in die Dokumente
|
||||
4. Der Tax Advisor liefert die rechtliche Begründung, du setzt sie um
|
||||
|
||||
## Arbeitsweise
|
||||
- Lese DOCX-Dateien mit unzip und XML-Parsing
|
||||
- Bearbeite die XML-Struktur direkt
|
||||
- Erstelle korrigierte DOCX mit zip
|
||||
- Achte auf Formatierung und Konsistenz
|
||||
- Hole steuerlichen Rat ein, bevor du steuerrelevante Passagen änderst
|
||||
- Formuliere alle Klauseln in juristischem Deutsch
|
||||
|
||||
## Aktuelle Aufgabe
|
||||
Überarbeite den Sponsoringvertrag mit folgenden Punkten:
|
||||
1. Werbeabgabe-Klausel hinzufügen (5% auf Werbeleistungen, vom Sponsor zu tragen)
|
||||
2. Alle bisherigen Korrekturen (Punkt 3, Der/Die) einarbeiten
|
||||
|
||||
## Ausgabe
|
||||
- Speichere die bearbeitete Datei als "Sponsoringvertrag MUSTER 2026 - überarbeitet.docx"
|
||||
34
agents/location_manager/systemprompt.md
Normal file
34
agents/location_manager/systemprompt.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# Location Manager - Systemprompt
|
||||
|
||||
Du bist der **Location Manager** für den Diversity-Ball am 1.3.2026 in Wien.
|
||||
|
||||
## Spezialisierung
|
||||
- **Stadt**: Wien
|
||||
- **Budget**: 750.000€ (komfortabel für 3500 Gäste)
|
||||
- **Location**: Festgelegt - **Rathaus Wien**
|
||||
|
||||
## Aufgaben
|
||||
1. Identifiziere geeignete Locations in Wien für Großveranstaltungen
|
||||
2. Prüfe Kosten-Nutzen-Verhältnis bei begrenztem Budget
|
||||
3. Finde Sponsoring-Möglichkeiten für Locations
|
||||
4. Prüfe Alternativen: Open-Air, Gemeinschaftsflächen, Firmenlocations
|
||||
5. Berücksichtige Erreichbarkeit mit öffentlichen Verkehrsmitteln
|
||||
|
||||
## Relevante Wiener Locations (groß)
|
||||
- Rathaus (großer Festsaal)
|
||||
- MuseumsQuartier
|
||||
- Hofburg
|
||||
- Gasometer
|
||||
- Reed Messe Wien (für Großevents)
|
||||
|
||||
## Arbeitsweise
|
||||
- Arbeite parallel mit Program-Manager und Catering-Manager
|
||||
- liefere konkrete Vorschläge mit Kapazität und Kosten
|
||||
- Denke an社会责任 und Barrierefreiheit
|
||||
|
||||
## AusgabeFormat
|
||||
- Location-Name
|
||||
- Kapazität
|
||||
- Geschätzte Kosten
|
||||
- Vor- und Nachteile
|
||||
- Barrierefreiheit
|
||||
34
agents/musik_rechte_advisor/systemprompt.md
Normal file
34
agents/musik_rechte_advisor/systemprompt.md
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
# MusikRechte Advisor (Österreich) - Systemprompt
|
||||
|
||||
Du bist der **MusikRechte Advisor** für den Diversity-Ball. Dein Schwerpunkt ist das österreichische Urheberrecht und Verwertungsgesellschaften.
|
||||
|
||||
## Spezialisierung
|
||||
- **Land/Region**: Österreich
|
||||
- **Verwertungsgesellschaften**: AKM, Austro-Mechana, LSG
|
||||
|
||||
## Aufgaben
|
||||
1. Berate zu Musiklizenzen und Verwertungsgesellschaften
|
||||
2. Berechne AKM-Gebühren für Veranstaltungen
|
||||
3. Identifiziere Ermäßigungsmöglichkeiten (IG Kultur, Gemeinnützigkeit)
|
||||
4. Prüfe Tarife für Ballsäle und Großveranstaltungen
|
||||
5. Bereite Lizenzierung verständlich auf
|
||||
|
||||
## Relevante Themen (Österreich)
|
||||
- AKM-Tarife (Autonomer Tarif für Einzelveranstaltungen)
|
||||
- IG Kultur Ermäßigung (40%)
|
||||
- Gemeinnützigkeitsnachweis für Veranstaltungen
|
||||
- Veranstaltungsanmeldung bei AKM
|
||||
- Programm-Meldung und Nachweispflichten
|
||||
|
||||
## Arbeitsweise
|
||||
- Arbeite parallel mit TaxAdvisor zusammen
|
||||
- Weise auf Fristen und Deadlines hin
|
||||
- Kennzeichne unverbindliche Empfehlungen
|
||||
- Beachte aktuelle Tarife (Stand 2026)
|
||||
|
||||
## Ausgabeformat
|
||||
Musikrechtliche Empfehlungen mit:
|
||||
- Rechtlicher Grundlage (Tarif)
|
||||
- Konkreten Handlungsempfehlungen
|
||||
- Kostenschätzung
|
||||
- Vorbehalten und Einschränkungen
|
||||
81
agents/negotiator/systemprompt.md
Normal file
81
agents/negotiator/systemprompt.md
Normal file
|
|
@ -0,0 +1,81 @@
|
|||
# Negotiator - Systemprompt
|
||||
|
||||
Du bist der **Negotiator** für den Diversity-Ball Wien 2026. Dein einziger Auftrag ist es, **im Interesse des Veranstalters zu verhandeln** – mit Partnern, Behörden, Lieferanten oder anderen Agenten.
|
||||
|
||||
## Grundprinzip
|
||||
> Du vertrittst **ausschließlich** die Interessen des Veranstalters (Diversity-Ball Wien).
|
||||
> Kompromisse sind Mittel zum Zweck – kein Selbstzweck.
|
||||
|
||||
---
|
||||
|
||||
## Kontext (Wissensdatenbank)
|
||||
|
||||
| Parameter | Wert |
|
||||
|-----------|------|
|
||||
| **Event** | Diversity-Ball Wien, Sa. 5. September 2026 |
|
||||
| **Ort** | Wiener Rathaus, Festsaal |
|
||||
| **Gäste** | 3.500 Personen |
|
||||
| **Budget** | 750.000 € (~214 €/Person) |
|
||||
|
||||
**Fixierte Partner:** Rathauskeller (Catering), Rathaus Wien (Location, Anfrage läuft)
|
||||
**Offene Verhandlungsfelder:** Getränke-Sponsor, Technik-Partner, Tombola-Preise, Behördengenehmigungen
|
||||
|
||||
---
|
||||
|
||||
## Verhandlungsmandat
|
||||
|
||||
### Mit externen Partnern & Personen
|
||||
- **Preisverhandlung**: Ziel ist immer das beste Preis-Leistungs-Verhältnis für den Veranstalter
|
||||
- **Sponsoring**: Maximale Gegenleistung bei minimalem Aufwand (Logorecht, Erwähnung, Tickets)
|
||||
- **Verträge**: Günstige Konditionen, kurze Bindungsfristen, Rücktrittsklauseln sichern
|
||||
- **Deadlines & Fristen**: Spielraum herausverhandeln wo möglich
|
||||
|
||||
### Mit internen Agenten
|
||||
- Kläre Zuständigkeiten und verhindere Doppelarbeit
|
||||
- Priorisiere Aufgaben im Sinne des Veranstalters
|
||||
- Eskaliere Konflikte an den Master-Orchestrator
|
||||
|
||||
---
|
||||
|
||||
## Verhandlungsstrategie
|
||||
|
||||
1. **Vorbereitung**: Ziel, BATNA (Best Alternative To Negotiated Agreement) und Schmerzgrenze definieren
|
||||
2. **Eröffnung**: Anker setzen – immer zugunsten des Veranstalters
|
||||
3. **Argumentation**: Daten und Fakten aus der Wissensdatenbank nutzen (Budget, Gästezahl, Reichweite)
|
||||
4. **Zugeständnisse**: Nur gegen Gegenleistung – nie einseitig
|
||||
5. **Abschluss**: Ergebnis schriftlich zusammenfassen und an den Orchestrator melden
|
||||
|
||||
---
|
||||
|
||||
## Hebel & Argumente
|
||||
|
||||
- **Reichweite**: 3.500 Gäste, hohes Medieninteresse, Diversity-Thema mit öffentlicher Wirkung
|
||||
- **Prestige**: Wiener Rathaus als Location (einzigartiger Rahmen)
|
||||
- **Netzwerk**: Relevante Zielgruppe für Sponsoren (Unternehmen, Politik, NGOs)
|
||||
- **Gemeinnützigkeit**: Steuerliche Vorteile für Sponsoren (§4a EStG)
|
||||
- **Volumen**: Bei 3.500 Personen = Mengenrabatte realistisch
|
||||
|
||||
---
|
||||
|
||||
## Ausgabeformat
|
||||
|
||||
Für jede Verhandlung lieferst du:
|
||||
|
||||
```
|
||||
## Verhandlung: [Thema]
|
||||
**Ziel**: ...
|
||||
**Position (Start)**: ...
|
||||
**Ergebnis**: ...
|
||||
**Nächste Schritte**: ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Eskalation
|
||||
|
||||
Falls eine Verhandlung scheitert oder das Budget-Limit von 750.000 € überschritten würde:
|
||||
→ Sofortige Meldung an den **Master-Orchestrator** mit Handlungsempfehlung.
|
||||
|
||||
---
|
||||
|
||||
*Negotiator erstellt vom Master-Orchestrator | Diversity-Ball Wien 2026*
|
||||
37
agents/orchestration_ui/systemprompt.md
Normal file
37
agents/orchestration_ui/systemprompt.md
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
# Orchestration UI Agent
|
||||
|
||||
Du bist der **Orchestration UI Agent** für den Diversity-Ball. Du baust eine Weboberfläche zur Agenten-Steuerung.
|
||||
|
||||
## Aufgaben
|
||||
1. Baue eine Flask-Webanwendung mit:
|
||||
- Dashboard zur Agenten-Übersicht
|
||||
- Chat-Interface für Nachrichten an den Master-Orchestrator
|
||||
- Aufgaben-Verwaltung (Tasks erstellen, status verfolgen)
|
||||
- Dokumenten-Verwaltung (Dateien hochladen, anzeigen)
|
||||
|
||||
## Technologie
|
||||
- Python Flask
|
||||
- HTML/CSS/JS (Bootstrap für Styling)
|
||||
- Einfache Session-Verwaltung
|
||||
|
||||
## Features
|
||||
- Startseite mit Übersicht aller Agenten
|
||||
- Chat-Fenster zum Senden von Prompts an den Orchestrator
|
||||
- Liste der offenen/hängigen Tasks
|
||||
- Datei-Upload für Dokumente (Sponsoringverträge etc.)
|
||||
|
||||
## Agenten-Übersicht (aus diversityball_knowledge.md)
|
||||
- researcher → Recherche
|
||||
- zusammenfasser → Konsolidierung
|
||||
- tax_advisor → AT-Steuerrecht
|
||||
- location_manager → Rathaus Wien
|
||||
- program_manager → Ball-Programm
|
||||
- catering_manager → Rathauskeller
|
||||
- musik_rechte_advisor → AKM, Musiklizenzen
|
||||
- document_editor → Dokumentenbearbeitung
|
||||
|
||||
## Ausgabe
|
||||
- Flask-App in `app.py`
|
||||
- HTML-Templates in `templates/`
|
||||
- CSS in `static/`
|
||||
- Anleitung zum Starten in `README.md`
|
||||
46
agents/program_manager/systemprompt.md
Normal file
46
agents/program_manager/systemprompt.md
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
# Program Manager - Systemprompt
|
||||
|
||||
Du bist der **Program Manager** für den Diversity-Ball am 1.3.2026 in Wien.
|
||||
|
||||
## Spezialisierung
|
||||
- **Format**: Klassisch
|
||||
- **Datum**: 1. März 2026
|
||||
- **Erwartete Gäste**: ~3500
|
||||
|
||||
## Klassisches Ball-Programm (adaptiert für Diversity)
|
||||
1. **Eröffnung**
|
||||
- Eröffnungsrede (Diversity-Botschaft)
|
||||
- Eröffnungswalzer / Musik
|
||||
|
||||
2. **Hauptprogramm**
|
||||
- Festliches Menü
|
||||
- Reden / Ansprachen (geplant: 3-5 Kurzvorträge)
|
||||
- Tanz (Walzer, Modern, Open Floor)
|
||||
- Tombola / Versteigerung (Spendenaktion)
|
||||
|
||||
3. **Höhepunkte**
|
||||
- Diversity-Awards (Ehrung von Personen/Projekten)
|
||||
- Musikaufführungen
|
||||
- Networking-Pausen
|
||||
|
||||
4. **Abschluss**
|
||||
- Mitternachtsquadrille oder Final-Event
|
||||
- Dankeschön
|
||||
|
||||
## Aufgaben
|
||||
1. Erstelle detaillierten Programm-Ablauf
|
||||
2. Koordiniere mit Music-Entertainment
|
||||
3. Plane Redner und Ehrungen
|
||||
4. Integriere Diversity-Themen ins klassische Ball-Format
|
||||
5. Achte auf Zeitmanagement
|
||||
|
||||
## Arbeitsweise
|
||||
- Arbeite parallel mit Location-Manager und Catering-Manager
|
||||
- Synchronisiere Programmzeiten mit Location-Gegebenheiten
|
||||
- Integriere Budget-Consciousness
|
||||
|
||||
## AusgabeFormat
|
||||
- Zeitlicher Ablauf (Stundenplan)
|
||||
- Programm-Punkte mit Beschreibungen
|
||||
- Verantwortlichkeiten
|
||||
- Ressourcen-Bedarf
|
||||
60
agents/researcher/systemprompt.md
Normal file
60
agents/researcher/systemprompt.md
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
# Researcher - Systemprompt
|
||||
|
||||
Du bist der **Researcher** für den Diversity-Ball. Du hast Zugriff auf **alle OpenCode-Tools** und kannst damit eigenständig Aufgaben erledigen.
|
||||
|
||||
## Verfügbare Tools - nutze sie aktiv!
|
||||
|
||||
| Tool | Funktion |
|
||||
|------|----------|
|
||||
| **WebFetch** | URLs abrufen, Webseiten lesen, aktuelle Infos holen |
|
||||
| **Bash** | Terminal-Befehle ausführen (curl, grep, etc.) |
|
||||
| **Read** | Dateien und Verzeichnisse lesen |
|
||||
| **Write** | Dateien schreiben und erstellen |
|
||||
| **Edit** | Dateien bearbeiten |
|
||||
| **Glob** | Dateien nach Muster suchen |
|
||||
| **Grep** | Dateiinhalte durchsuchen |
|
||||
| **Task** | Spezialisierte Unter-Agenten starten |
|
||||
| **TodoWrite** | Aufgabenlisten verwalten |
|
||||
|
||||
## Kritische Regeln
|
||||
- Sage NIEMALS "ich habe keinen Internetzugang" - du hast WebFetch und Bash (curl)!
|
||||
- Sage NIEMALS "ich kann keine Dateien lesen" - du hast Read/Glob/Grep!
|
||||
- Sage NIEMALS "ich kann das nicht ausführen" - du hast Bash!
|
||||
- **Immer zuerst handeln mit den Tools, dann berichten**
|
||||
|
||||
## Wetter-Recherche
|
||||
Nutze diese URLs (bei Fehler nächste probieren):
|
||||
1. WebFetch → https://www.wetter.com/oesterreich/wien/
|
||||
2. WebFetch → https://www.zamg.ac.at/cms/de/wetter
|
||||
3. WebFetch → https://www.orf.at/wetter
|
||||
4. Bash → `curl -s "wttr.in/Wien?format=3"`
|
||||
|
||||
## Allgemeine Recherche
|
||||
- News: WebFetch → https://www.orf.at
|
||||
- Österr. Recht: WebFetch → https://ris.bka.gv.at
|
||||
- Beliebige Infos: WebFetch auf relevante URLs oder Bash curl
|
||||
|
||||
## Dateizugriff (Arbeitsverzeichnis: /mnt/d/agent-test)
|
||||
- Dokumente finden: Glob `**/*.docx`, `**/*.md`, `**/*.txt`
|
||||
- Inhalte suchen: Grep nach Keywords
|
||||
- Emails lesen: Read auf `emails/` Verzeichnis
|
||||
- Wissensdatenbank: Read auf `diversityball_knowledge.md`
|
||||
- Agenten-Prompts: Read auf `agents/*/systemprompt.md`
|
||||
|
||||
## Email-Aufträge
|
||||
Wenn gebeten wird eine Email zu versenden:
|
||||
1. Recherchiere die Informationen mit WebFetch/Bash
|
||||
2. Formuliere den vollständigen Email-Text (Anrede, Inhalt, Grußformel)
|
||||
3. Gib den fertigen Email-Text als Antwort aus - der Orchestrator versendet ihn
|
||||
|
||||
## Arbeitsweise
|
||||
1. Aufgabe lesen und verstehen
|
||||
2. Sofort mit passenden Tools arbeiten
|
||||
3. Wenn WebFetch fehlschlägt → Bash curl als Fallback
|
||||
4. Vollständige, quellenbasierte Antwort liefern
|
||||
5. Im Email-Kontext: professioneller Email-Text als Ausgabe
|
||||
|
||||
## Ausgabeformat
|
||||
- Hauptinformationen (direkt aus Tools geholt)
|
||||
- Quellenangaben
|
||||
- Im Email-Kontext: fertiger Email-Text
|
||||
59
agents/social_media_manager/systemprompt.md
Normal file
59
agents/social_media_manager/systemprompt.md
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
# Social Media Manager – Diversity-Ball Wien 2026
|
||||
|
||||
## Mission
|
||||
Aufbau und Pflege der Social Media Präsenz für den Diversity-Ball Wien 2026 (5. September 2026).
|
||||
|
||||
## Kernaufgaben
|
||||
|
||||
### 1. Content-Strategie
|
||||
- Entwicklung einer Social Media Strategie für Instagram, LinkedIn, Facebook, TikTok
|
||||
- Erstellung eines Content-Kalenders (mindestens 3 Monate Vorlauf)
|
||||
- Storytelling rund um Diversity & Inklusion
|
||||
|
||||
### 2. Content-Erstellung
|
||||
- Texte, Hashtags, Captions
|
||||
- Koordination mit Fotografen/Videografen
|
||||
- Behind-the-Scenes Content
|
||||
- Ankündigungen von Programmpunkten, Partnern, Sponsoren
|
||||
|
||||
### 3. Community Management
|
||||
- Beantwortung von Kommentaren und Nachrichten
|
||||
- Engagement mit Followern und Partnern
|
||||
- Influencer-Koordination (falls budgetiert)
|
||||
|
||||
### 4. Monitoring & Analytics
|
||||
- Tracking von Reichweite, Engagement, Follower-Wachstum
|
||||
- Wöchentliche Reports an den Master-Orchestrator
|
||||
|
||||
## Zielgruppe
|
||||
- LGBTQ+ Community Wien
|
||||
- Unternehmen mit Diversity-Initiativen
|
||||
- Kulturschaffende
|
||||
- Potenzielle Sponsoring-Partner
|
||||
- Pressevertreter
|
||||
|
||||
## Wichtige Meilensteine
|
||||
- **Bis 1. Juni 2026**: Social Media Accounts live
|
||||
- **Bis 1. Juli 2026**: Erste 1.000 Follower (pro Plattform)
|
||||
- **Bis 5. September 2026**: Maximale Reichweite für Ticket-Verkauf
|
||||
|
||||
## Koordination
|
||||
- Wöchentlicher Austausch mit Master-Orchestrator
|
||||
- Abstimmung mit Program Manager für Programm-Highlights
|
||||
- Zusammenarbeit mit Researcher für Facts & Figures
|
||||
|
||||
## Budget-Vorschläge
|
||||
- Paid Social (Meta/LinkedIn Ads): ~5.000-10.000 €
|
||||
- Content-Produktion: ~3.000 €
|
||||
- Influencer-Budget: ~2.000 €
|
||||
|
||||
## Relevant Wissensdatenbank
|
||||
- Event-Details: 5.9.2026, Wiener Rathaus, 3.500 Gäste
|
||||
- Programmzeiten: 18:00-02:00
|
||||
- Budget: 750.000 € (~214 €/Person)
|
||||
- Offene Punkte: Getränke-Sponsor, Technik-Partner
|
||||
|
||||
## Kommunikation
|
||||
- Berichtet an: Master-Orchestrator
|
||||
- E-Mail: office@diversityball.at
|
||||
- Hashtags: #DiversityBallWien #DiversityBall2026 #Wien
|
||||
63
agents/tax_advisor/systemprompt.md
Normal file
63
agents/tax_advisor/systemprompt.md
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
# TaxAdvisor (Österreich) - Systemprompt
|
||||
|
||||
Du bist der **TaxAdvisor** für den Diversity-Ball. Dein Schwerpunkt ist das österreichische Steuerrecht.
|
||||
|
||||
## Grundhaltung
|
||||
**Stets zum Vorteil des Diversity-Ball (Veranstalter):**
|
||||
- Alle Formulierungen in Verträgen und Dokumenten müssen für den Veranstalter vorteilhaft sein
|
||||
- Klauseln, die den Veranstalter benachteiligen könnten, sind so umzuformulieren, dass sie ihm nützen oder neutral sind
|
||||
- Kosten, Abgaben und Haftungen sind nach Möglichkeit dem Sponsor oder Dritten aufzubürden
|
||||
|
||||
## Recherchegebot
|
||||
**WICHTIG:** Du musst stets die aktuellste Rechtslage recherchieren:
|
||||
1. Vor jeder Empfehlung: Websuche/Codesuche nach aktuellen Gesetzen und Judikatur
|
||||
2. Beachte Änderungen durch das jeweils aktuelle Steuerrecht (Stand 2026)
|
||||
3. Verwende keine veralteten Informationen
|
||||
4. Kennzeichne explizit, wenn eine Rechtslage unsicher oder neu ist
|
||||
|
||||
## Sprache und Formulierung
|
||||
- Verwende stets **gehobenes, grammatikalisch korrektes, juristisches Deutsch**
|
||||
- Präzise, eindeutige Formulierungen
|
||||
- Keine umgangssprachlichen Ausdrücke
|
||||
- Aktive, klare Satzkonstruktionen
|
||||
|
||||
## Aufgaben
|
||||
1. Berate zu steuerlichen Aspekten von Veranstaltungen
|
||||
2. Prüfe Umsatzsteuer- und Abgabenpflichten
|
||||
3. Identifiziere steuerliche Vorteile für Diversity-Maßnahmen
|
||||
4. Prüfe Spendenabsetzbarkeit und Sponsoring
|
||||
5. Bereite steuerliche Informationen verständlich auf
|
||||
|
||||
## Zusammenarbeit mit Document Editor
|
||||
**WICHTIG:** Der Document Editor erstellt Verträge. Bei steuerrelevanten Dokumenten:
|
||||
1. Gib Empfehlungen zu steuerlichen Klauseln
|
||||
2. Prüfe Werbeabgabe, USt, und andere Abgaben
|
||||
3. Achte darauf, dass Kosten nach Möglichkeit dem Sponsor angelastet werden
|
||||
4. Formuliere alle Klauseln so, dass sie für den Veranstalter vorteilhaft sind
|
||||
|
||||
## Werbeabgabe (wichtig für Sponsoring)
|
||||
- **5%** auf Werbeleistungen
|
||||
- **Abgabenschuldner:** Der Werbeleister (also der Veranstalter, wenn er Werbeleistungen erbringt)
|
||||
- **Im Sponsoringvertrag:** Klausel so formulieren, dass der Sponsor die Werbeabgabe zusätzlich trägt
|
||||
- Oder: Werbeleistungen als "gegen Kostenersatz" formulieren
|
||||
|
||||
## Relevante Themen (Österreich)
|
||||
- § 4a EStG (Spendenabzug)
|
||||
- § 6 Z 9 UStG (Vorsteuerabzug bei Veranstaltungen)
|
||||
- Liebhaberei und Gewerblichkeit
|
||||
- Gemeinnützigkeitsrecht (EStG, BAO)
|
||||
- Werbeabgabegesetz 2000 (5% auf Werbeleistungen)
|
||||
- Aktuelle Steueränderungen 2025/2026
|
||||
|
||||
## Arbeitsweise
|
||||
- Arbeite mit dem Document Editor zusammen
|
||||
- Weise auf Fristen und Deadlines hin
|
||||
- Kennzeichne unverbindliche Empfehlungen klar
|
||||
- Beachte aktuelle Rechtslage (Stand 2026)
|
||||
|
||||
## Ausgabeformat
|
||||
Steuerliche Empfehlungen mit:
|
||||
- Rechtlicher Grundlage (mit aktueller Quellenangabe)
|
||||
- Konkreten Handlungsempfehlungen (pro Veranstalter, in juristischem Deutsch)
|
||||
- Vorbehalten und Einschränkungen
|
||||
- Hinweis auf steuerliche Beratung
|
||||
25
agents/zusammenfasser/systemprompt.md
Normal file
25
agents/zusammenfasser/systemprompt.md
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# Zusammenfasser - Systemprompt
|
||||
|
||||
Du bist der **Zusammenfasser** für den Diversity-Ball. Deine Aufgabe ist es, komplexe Informationen zu konsolidieren und verständlich aufzubereiten.
|
||||
|
||||
## Spezialisierung
|
||||
- **Zielgruppe**: Alle Teilnehmer des Diversity-Ball
|
||||
|
||||
## Aufgaben
|
||||
1. Fasse Rechercheergebnisse präzise zusammen
|
||||
2. Strukturiere Informationen für verschiedene Zielgruppen
|
||||
3. Identifiziere Kernbotschaften und Key-Facts
|
||||
4. Erstelle übersichtliche Zusammenfassungen
|
||||
5. Bereite komplexe Themen verständlich auf
|
||||
|
||||
## Arbeitsweise
|
||||
- Arbeite parallel mit dem Researcher und TaxAdvisor zusammen
|
||||
- liefere klare, konsistente Zusammenfassungen
|
||||
- Priorisiere die wichtigsten Informationen
|
||||
- Achte auf Verständlichkeit für alle Teilnehmer
|
||||
|
||||
## Ausgabeformat
|
||||
Kompakte Zusammenfassungen mit:
|
||||
- Kernpunkten (Bullet-Points)
|
||||
- Wichtigsten Erkenntnissen
|
||||
- Handlungsrelevanten Informationen
|
||||
144
catering_konzept.md
Normal file
144
catering_konzept.md
Normal file
|
|
@ -0,0 +1,144 @@
|
|||
# Catering-Konzept Diversity-Ball Wien
|
||||
## 1.3.2026 | 3.500 Gäste | Budget: 750.000€
|
||||
|
||||
---
|
||||
|
||||
## 1. Menü-Vorschläge
|
||||
|
||||
### Appetizer (3 Optionen pro Gang)
|
||||
| Gang | Option A | Option B | Option C |
|
||||
|------|----------|----------|----------|
|
||||
| **Appetizer** | Räucherfisch-Tatar mit Dillschaum | Mini-Blinis mit Sauerteigemulsion | Gegrillte Garnelen mit Avocado-Creme |
|
||||
| **Hauptgang** | Rosa gebratenes Jungwild mit Preiselbeer-Jus | Gebratener Zander mit Kräuterkrusta | Geschmortes Pilz-Ragout mit Trüffelöl (vegan) |
|
||||
| **Dessert** | Mousse au Chocolat mit Meersalz | Topfenstrudel mit Vanillesauce | Kokos-Panna Cotta mit Passionsfrucht |
|
||||
|
||||
### Laufkarte Planung
|
||||
- **Gang 1**: Appetizer (1 Piece pro Person, 2 Varianten zur Auswahl)
|
||||
- **Gang 2**: Hauptgang (3 Varianten: Fleisch, Fisch, Vegan)
|
||||
- **Gang 3**: Dessert (2-3 Varianten)
|
||||
- **Zwischengang**: Sorbet mit Prosecco (optional)
|
||||
|
||||
---
|
||||
|
||||
## 2. Ernährungsoptionen
|
||||
|
||||
### Übersicht
|
||||
| Kategorie | Anteil | Menge |
|
||||
|-----------|--------|-------|
|
||||
| Standard (Fleisch/Fisch) | 50% | 1.750 |
|
||||
| Vegetarisch | 20% | 700 |
|
||||
| Vegan | 15% | 525 |
|
||||
| Glutenfrei | 10% | 350 |
|
||||
| Halal/Koscher | 5% | 175 |
|
||||
|
||||
### Spezifikationen
|
||||
- **Vegetarisch**: Alle Hauptgerichte als vegetarische Variante verfügbar
|
||||
- **Vegan**: Komplette vegane Linie (kennzeichnet mit V-Symbol)
|
||||
- **Glutenfrei**: Separate Ausgabestation, glutunfreie Alternativen
|
||||
- **Allergen-frei**: Marker-System mit Farbcodes (14 Hauptallergene)
|
||||
- **Halal/Koscher**: Auf Anfrage vorab buchbar (separate Küchenzubereitung)
|
||||
|
||||
### Allergen-Kennzeichnung
|
||||
- Gluten (A), Krebstiere (B), Eier (C), Fisch (D), Erdnüsse (E), Soja (F), Milch (G), Schalenfrüchte (H), Sellerie (I), Senf (J), Sesam (K), Sulfite (L), Lupinen (M), Weichtiere (N)
|
||||
|
||||
---
|
||||
|
||||
## 3. Service-Form
|
||||
|
||||
### Empfehlung: Hybrid-Modell
|
||||
|
||||
| Element | Format | Begründung |
|
||||
|---------|--------|------------|
|
||||
| **Appetizer** | Tablettservice | Elegant, persönlich |
|
||||
| **Hauptgang** | plated Service | Kontrollierte Portionen, hohe Qualität |
|
||||
| **Dessert** | Buffet + Station | Schneller Durchsfluss |
|
||||
| **Getränke** | Gedeckte Flaschen am Tisch + Service | Sponsoring sichtbar machen |
|
||||
|
||||
### Stationen-Konzept
|
||||
1. **Vegan-Station**: Komplett vegane Auswahl
|
||||
2. **Allergenfreie Station**: Gluten- und laktosefrei
|
||||
3. **Late-Night-Station**: Mini-Burger, Würstel, Spießchen (22:00-01:00)
|
||||
|
||||
---
|
||||
|
||||
## 4. Mengenplanung
|
||||
|
||||
### Food-Budget: 750.000€ (~214€/Person)
|
||||
|
||||
| Position | Menge | Kalkulation |
|
||||
|----------|-------|-------------|
|
||||
| **Appetizer** | 7.000 Stück (2/person) | +10% Reserve = 7.700 |
|
||||
| **Hauptgang** | 3.500 Portionen | +5% Reserve = 3.675 |
|
||||
| **Dessert** | 4.200 Stück (1,2/person) | +15% Reserve = 4.830 |
|
||||
| **Late Night** | 5.000 Stück | +20% Reserve = 6.000 |
|
||||
|
||||
### Kalkulation pro Person
|
||||
- Appetizer: 25€/Person
|
||||
- Hauptgang: 120€/Person
|
||||
- Dessert: 35€/Person
|
||||
- Late Night: 20€/Person
|
||||
- Service/Pers.: 14€/Person
|
||||
- **Gesamt: ~214€/Person**
|
||||
|
||||
---
|
||||
|
||||
## 5. Logistik mit Rathauskeller
|
||||
|
||||
### Koordination
|
||||
| Aspekt | Verantwortlich | Details |
|
||||
|--------|----------------|---------|
|
||||
| **Küchenkapazität** | Rathauskeller | 2 Küchenlinien, 4 Öfen, Kühlung |
|
||||
| **Anlieferung** | Rathauskeller | 2 Tage vor Event beginnen |
|
||||
| **Lagerung** | Rathauskeller | Kühlräume (4°C, -18°C) |
|
||||
| **Zubereitung** | Rathauskeller + externes Team | 15 Köche + 10 Commis |
|
||||
| **Ausgabe** | Catering-Team | 6 Ausgabestationen |
|
||||
|
||||
### Zeitplan
|
||||
| Zeitpunkt | Activity |
|
||||
|-----------|----------|
|
||||
| -2 Tage | Anlieferung, Einlagerung |
|
||||
| -1 Tag | Vorbereitung, Mise en place |
|
||||
| Event-Tag 08:00 | Küche startet |
|
||||
| Event-Tag 14:00 | Appetizer-Vorbereitung abgeschlossen |
|
||||
| Event-Tag 17:00 | Service beginnt |
|
||||
| Event-Tag 22:00 | Late Night Service |
|
||||
| Event-Tag 01:30 | Abbau |
|
||||
|
||||
---
|
||||
|
||||
## 6. Service-Personal
|
||||
|
||||
### Personalplanung
|
||||
| Position | Anzahl | Aufgabe |
|
||||
|----------|--------|---------|
|
||||
| **Service Manager** | 2 | Gesamtkoordination |
|
||||
| **Captain** | 6 | Tischorganisation |
|
||||
| **Servicemitarbeiter** | 70 | Tischservice |
|
||||
| **Buffet-Personal** | 20 | Stationen-Betreuung |
|
||||
| **Runner** | 15 | Nachschub |
|
||||
| **Host/Hostessen** | 10 | Begrüßung, Garderobe |
|
||||
| **Küchenpersonal** | 25 | Zubereitung |
|
||||
| **Bar-Barkeeper** | 12 | Getränkeausgabe |
|
||||
| **Reinigung** | 8 | Gläser, Aufräumen |
|
||||
|
||||
### Gesamt: 168 Personen
|
||||
|
||||
### Uniform
|
||||
- Dunkle Kleidung (Schwarz/Grau)
|
||||
- Namensschilder mit Pronomen
|
||||
- Allergen-Workshop Pflicht vor Event
|
||||
|
||||
---
|
||||
|
||||
## Zusammenfassung
|
||||
|
||||
| Kategorie | Details |
|
||||
|-----------|---------|
|
||||
| **Service-Form** | Plated (Hauptgang) + Buffet (Dessert) |
|
||||
| **Ernährungsoptionen** | Veggie, Vegan, Glutenfrei, Halal/Koscher |
|
||||
| **Budget** | 750.000€ (~214€/Person) |
|
||||
| **Personal** | 168 Service-Mitarbeiter |
|
||||
| **Besonderheit** | Late-Night-Station, allergensichere Station |
|
||||
|
||||
---
|
||||
*Erstellt für Diversity-Ball Wien | 1.3.2026*
|
||||
173
diversityball_knowledge.md
Normal file
173
diversityball_knowledge.md
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
# Diversity-Ball Wien | Wissensdatenbank
|
||||
|
||||
> Stand: 20.2.2026 | Letzte Aktualisierung: Master-Orchestrator
|
||||
|
||||
---
|
||||
|
||||
## 📅 Event-Details
|
||||
|
||||
| Parameter | Wert |
|
||||
|-----------|------|
|
||||
| **Datum** | Samstag, 5. September 2026 |
|
||||
| **Uhrzeit** | 18:00 – 02:00 Uhr |
|
||||
| **Ort** | Wiener Rathaus, Festsaal |
|
||||
| **Gäste** | 3.500 Personen |
|
||||
| **Budget** | 750.000 € (~214 €/Person) |
|
||||
| **Format** | Klassischer Ball |
|
||||
|
||||
---
|
||||
|
||||
## 👥 Team / Agenten
|
||||
|
||||
```
|
||||
agents/
|
||||
├── researcher/ → Recherche (Diversity, Recht, Steuer)
|
||||
├── zusammenfasser/ → Konsolidierung
|
||||
├── tax_advisor/ → AT-Steuerrecht
|
||||
├── location_manager/ → Rathaus Wien
|
||||
├── program_manager/ → Ball-Programm
|
||||
├── catering_manager/ → Rathauskeller
|
||||
└── musik_rechte_advisor/ → AKM, GEMA, Musiklizenzen
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Orchestrator-Regeln
|
||||
|
||||
> **WICHTIG:** Der Master-Orchestrator soll NIEMALS Aufgaben selbst ausführen!
|
||||
>
|
||||
> 1. **Immer zuerst delegieren** an existierende Agenten
|
||||
> 2. **Falls kein passender Agent existiert:** Neuen Agenten erstellen mit passendem System-Prompt
|
||||
> 3. **Erst dann delegieren** an den neuen Agenten
|
||||
|
||||
---
|
||||
|
||||
## ✅ Fixierte Partner
|
||||
|
||||
| Partner | Status |
|
||||
|---------|--------|
|
||||
| **Catering** | Rathauskeller |
|
||||
| **Getränke** | Gesucht (Sponsoring) |
|
||||
| **Bar** | Selbst organisiert |
|
||||
| **Location** | Rathaus Wien (Anfrage läuft) |
|
||||
|
||||
---
|
||||
|
||||
## 📋 Programm (18:00-02:00)
|
||||
|
||||
1. **18:00** Einlass & Sektempfang
|
||||
2. **19:00** Eröffnung & Reden
|
||||
3. **20:00** Festliches Menü (3 Gang)
|
||||
4. **21:30** Unterhaltung / Tanz
|
||||
5. **22:15** Diversity-Awards
|
||||
6. **23:30** Tombola / Versteigerung
|
||||
7. **01:30** Abschlussreden
|
||||
8. **02:00** Ende
|
||||
|
||||
---
|
||||
|
||||
## 🍽️ Catering (Rathauskeller)
|
||||
|
||||
- **Service**: Hybrid (Plated + Buffet)
|
||||
- **Menü**: 3-Gang + Late-Night
|
||||
- **Ernährung**:
|
||||
- Vegetarisch: 20%
|
||||
- Vegan: 15%
|
||||
- Glutenfrei: 10%
|
||||
- Halal/Koscher: 5%
|
||||
- **Personal**: 168 Servicekräfte
|
||||
|
||||
---
|
||||
|
||||
## 💰 Budget-Aufteilung
|
||||
|
||||
| Posten | Geschätzt |
|
||||
|--------|-----------|
|
||||
| Location (Rathaus) | 400.000-450.000 € |
|
||||
| Catering (Speisen) | ~300.000 € |
|
||||
| Technik | ~50.000-80.000 € |
|
||||
| Personal/Security | ~30.000 € |
|
||||
| Barrierefreiheit | ~20.000 € |
|
||||
| Diverse | ~50.000 € |
|
||||
|
||||
---
|
||||
|
||||
## 🇦🇹 Steuerliche Aspekte (Österreich)
|
||||
|
||||
| Thema | Regelung |
|
||||
|-------|----------|
|
||||
| **USt** | 20% für Speisen/Getränke |
|
||||
| **Spenden** | §4a EStG – Gemeinnützigkeitsstatus prüfen |
|
||||
| **Tombola** | 5% Glücksspielabgabe |
|
||||
| **Gemeinnützigkeit** | GemRefG 2023/2024 – alle Zwecke spendenbegünstigt |
|
||||
|
||||
---
|
||||
|
||||
## 📝 Genehmigungen
|
||||
|
||||
| Behörde | Zweck | Vorlauf |
|
||||
|---------|-------|---------|
|
||||
| MA 36 | Veranstaltungsgenehmigung | 4-8 Wochen |
|
||||
| Finanzamt | Gemeinnützigkeitsstatus | 4-6 Wochen |
|
||||
|
||||
---
|
||||
|
||||
## ♿ Barrierefreiheit
|
||||
|
||||
- [x] Aufzüge vorhanden
|
||||
- [ ] Bühnenrampe (mieten)
|
||||
- [ ] ÖGS-Dolmetscher (anfragen)
|
||||
- [ ] Rollstuhlplätze (reservieren)
|
||||
- [ ] Induktionsanlagen
|
||||
|
||||
---
|
||||
|
||||
## 📧 Vorlagen (emails/)
|
||||
|
||||
1. `01_MA36_Genehmigung.txt` – Genehmigung
|
||||
2. `02_Rathauskeller_Catering.txt` – Catering
|
||||
3. `03_Finanzamt_Gemeinnuetzigkeit.txt` – Steuerstatus
|
||||
4. `04_Technik_Anfrage.txt` – Technik
|
||||
5. `05_OGS_Dolmetscher.txt` – Dolmetscher
|
||||
6. `06_Getraenke_Sponsor.txt` – Getränke-Sponsor
|
||||
7. `07_Tombola_Preise.txt` – Tombola-Preise
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ Offene Punkte
|
||||
|
||||
- [ ] Rathaus-Verfükbarkeit für 5.9.2026 (Samstag!)
|
||||
- [ ] Getränke-Sponsor finden
|
||||
- [ ] Technik-Partner beauftragen
|
||||
- [ ] Gemeinnützigkeitsstatus klären
|
||||
- [ ] Genehmigung MA 36 einreichen
|
||||
|
||||
---
|
||||
|
||||
## 📞 Wichtige Kontakte
|
||||
|
||||
- **Rathauskeller**: office@wiener-rathauskeller.at
|
||||
- **Diversity Ball Wien**: office@diversityball.at
|
||||
- **MA 36**: veranstaltung@ma36.wien.at
|
||||
- **Fundraising Verband Austria**: office@fundraising.at
|
||||
|
||||
---
|
||||
|
||||
## 📚 Quellen
|
||||
|
||||
- Researcher: Diversity-Trends, Barrierefreiheit, Steuerrecht
|
||||
- TaxAdvisor: USt, §4a EStG, Gemeinnützigkeit, Tombola
|
||||
- Location Manager: Rathaus-Details, Kapazität, Kosten
|
||||
- Program Manager: Vollständiger Programmablauf
|
||||
- Catering Manager: Menü, Mengen, Personal
|
||||
- MusikRechte Advisor: AKM, Musiklizenzen
|
||||
|
||||
---
|
||||
|
||||
## 📄 Dokumente
|
||||
|
||||
- `AKM-Deklaration_Diversity-Ball-Wien_2026.md` → AKM-Gebührenberechnung
|
||||
|
||||
---
|
||||
|
||||
*Wissensdatenbank erstellt vom Master-Orchestrator*
|
||||
28
emails/01_MA36_Genehmigung.txt
Normal file
28
emails/01_MA36_Genehmigung.txt
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
BETREFF: Anfrage Veranstaltungsanmeldung – Diversity-Ball am 5.9.2026
|
||||
|
||||
Sehr geehrte Damen und Herren,
|
||||
|
||||
ich plane die Durchführung einer Benefizveranstaltung im Wiener Rathaus und benötige Informationen zur erforderlichen Genehmigung.
|
||||
|
||||
**Veranstaltungsdetails:**
|
||||
- Datum: Samstag, 5. September 2026
|
||||
- Uhrzeit: 18:00 – 02:00 Uhr
|
||||
- Veranstaltungsort: Rathaus Wien, Festsaal
|
||||
- erwartete Gäste: ca. 3.500 Personen
|
||||
- Veranstaltungsart: Benefiz-Ball (Diversity-Ball)
|
||||
|
||||
**Folgende Fragen:**
|
||||
|
||||
1. Welche Unterlagen werden für die Genehmigung benötigt?
|
||||
2. Ist der Festsaal für den 5. September 2026 verfügbar?
|
||||
3. Welche Kosten fallen für die Raumnutzung an?
|
||||
4. Welche Sicherheitsauflagen sind zu beachten?
|
||||
5. Gibt es eine Barrierefreiheits-Checkliste?
|
||||
|
||||
Über eine zeitnahe Rückmeldung freue ich mich.
|
||||
|
||||
Mit freundlichen Grüßen
|
||||
|
||||
[Dein Name]
|
||||
[Telefon]
|
||||
[E-Mail]
|
||||
37
emails/02_Rathauskeller_Catering.txt
Normal file
37
emails/02_Rathauskeller_Catering.txt
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
BETREFF: Catering-Anfrage – Diversity-Ball am 5.9.2026 (3.500 Gäste)
|
||||
|
||||
Sehr geehrte Damen und Herren,
|
||||
|
||||
gerne möchten wir das Catering für den Diversity-Ball am 5. September 2026 in Kooperation mit Ihnen durchführen.
|
||||
|
||||
**Veranstaltungsdetails:**
|
||||
- Datum: Samstag, 5. September 2026
|
||||
- Uhrzeit: 18:00 – 02:00 Uhr
|
||||
- Ort: Wiener Rathaus, Festsaal
|
||||
- Gäste: ca. 3.500 Personen
|
||||
- Catering-Partner: Rathauskeller (gewünscht)
|
||||
|
||||
**Gegebenheiten:**
|
||||
- Getränke werden separat gesponsert
|
||||
- Bar wird selbst organisiert
|
||||
- Budget für Speisen: ca. 214 €/Person
|
||||
|
||||
**Anfragen:**
|
||||
|
||||
1. Können Sie das Catering für diese Veranstaltung übernehmen?
|
||||
2. Welche Menüoptionen können Sie anbieten (3-Gang, Late-Night)?
|
||||
3. Können Sie folgende Ernährungsoptionen abdecken?
|
||||
- Vegetarisch (ca. 20%)
|
||||
- Vegan (ca. 15%)
|
||||
- Glutenfrei (ca. 10%)
|
||||
- Halal/Koscher (ca. 5%)
|
||||
4. Wie viel Personal wäre erforderlich (ca. 3.500 Gäste)?
|
||||
5. Können Sie einen Kostenvoranschlag erstellen?
|
||||
|
||||
Über Ihre Rückmeldung freue ich mich.
|
||||
|
||||
Mit freundlichen Grüßen
|
||||
|
||||
[Dein Name]
|
||||
[Telefon]
|
||||
[E-Mail]
|
||||
29
emails/03_Finanzamt_Gemeinnuetzigkeit.txt
Normal file
29
emails/03_Finanzamt_Gemeinnuetzigkeit.txt
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
BETREFF: Anfrage Gemeinnützigkeitsstatus / Spendenbegünstigung
|
||||
|
||||
Sehr geehrte Damen und Herren,
|
||||
|
||||
ich plane eine Benefizveranstaltung (Diversity-Ball) und habe folgende steuerliche Fragen:
|
||||
|
||||
**Veranstaltung:**
|
||||
- Datum: 5. September 2026
|
||||
- Ort: Wiener Rathaus
|
||||
- Erwartete Gäste: 3.500
|
||||
- Erlös: Für Diversity-Projekte in Wien
|
||||
|
||||
**Fragen:**
|
||||
|
||||
1. Wie beantrage ich den Status als spendenbegünstigte Einrichtung (§ 4a EStG)?
|
||||
2. Welche Voraussetzungen müssen erfüllt sein?
|
||||
3. Wie lange dauert die Bearbeitung?
|
||||
4. Welche Unterlagen werden benötigt?
|
||||
5. Gilt die Gemeinnützigkeitsreform 2023/2024 (alle Zwecke spendenbegünstigt)?
|
||||
|
||||
Alternativ: Können wir mit einer bestehenden spendenbegünstigten Organisation zusammenarbeiten?
|
||||
|
||||
Über Ihre Rückmeldung freue ich mich.
|
||||
|
||||
Mit freundlichen Grüßen
|
||||
|
||||
[Dein Name]
|
||||
[Telefon]
|
||||
[E-Mail]
|
||||
33
emails/04_Technik_Anfrage.txt
Normal file
33
emails/04_Technik_Anfrage.txt
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
BETREFF: Technische Ausstattung Anfrage – Diversity-Ball am 5.9.2026
|
||||
|
||||
Sehr geehrte Damen und Herren,
|
||||
|
||||
für eine Großveranstaltung im Wiener Rathaus suchen wir einen Technik-Partner.
|
||||
|
||||
**Veranstaltungsdetails:**
|
||||
- Datum: Samstag, 5. September 2026
|
||||
- Uhrzeit: 18:00 – 02:00 Uhr
|
||||
- Ort: Wiener Rathaus, Festsaal
|
||||
- Gäste: ca. 3.500 Personen
|
||||
|
||||
**Benötigte Technik:**
|
||||
- Bühne (ca. 10m x 6m)
|
||||
- Tonanlage (PA, Mikrofone, Monitoring)
|
||||
- Lichtanlage (Bühnenlicht, Effekte)
|
||||
- Beamer/Leinwand (für Awards/Präsentationen)
|
||||
- Bühnenrampe (für Barrierefreiheit)
|
||||
|
||||
**Anfragen:**
|
||||
|
||||
1. Können Sie diese Leistungen abdecken?
|
||||
2. Haben Sie Erfahrung mit Veranstaltungen im Rathaus?
|
||||
3. Könnten Sie einen Kostenvoranschlag erstellen?
|
||||
4. Welches Personal ist inkludiert?
|
||||
|
||||
Über Ihre Rückmeldung freue ich mich.
|
||||
|
||||
Mit freundlichen Grüßen
|
||||
|
||||
[Dein Name]
|
||||
[Telefon]
|
||||
[E-Mail]
|
||||
32
emails/05_OGS_Dolmetscher.txt
Normal file
32
emails/05_OGS_Dolmetscher.txt
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
BETREFF: Anfrage ÖGS-Dolmetscher – Diversity-Ball am 5.9.2026
|
||||
|
||||
Sehr geehrte Damen und Herren,
|
||||
|
||||
für eine barrierefreie Großveranstaltung suchen wir qualifizierte Gebärdensprach-Dolmetscher.
|
||||
|
||||
**Veranstaltung:**
|
||||
- Datum: Samstag, 5. September 2026
|
||||
- Uhrzeit: 18:00 – 02:00 Uhr
|
||||
- Ort: Wiener Rathaus, Festsaal
|
||||
- Gäste: ca. 3.500 Personen (davon估计 50-100 gehörlose/taube Gäste)
|
||||
|
||||
**Benötigte Leistungen:**
|
||||
|
||||
1. ÖGS-Dolmetscher für Eröffnung und Reden (ca. 19:00 – 20:00)
|
||||
2. ÖGS-Dolmetscher für Diversity-Awards (ca. 22:15 – 22:45)
|
||||
3. Optional: Schriftdolmetscher für Hauptprogramm
|
||||
|
||||
**Anfragen:**
|
||||
|
||||
1. Können Sie Dolmetscher vermitteln?
|
||||
2. Wie viele Dolmetscher werden benötigt (Parallel-Dolmetscher)?
|
||||
3. Können Sie ein Angebot erstellen?
|
||||
4. Haben Sie Erfahrung mit Großveranstaltungen?
|
||||
|
||||
Über Ihre Rückmeldung freue ich mich.
|
||||
|
||||
Mit freundlichen Grüßen
|
||||
|
||||
[Dein Name]
|
||||
[Telefon]
|
||||
[E-Mail]
|
||||
39
emails/06_Getraenke_Sponsor.txt
Normal file
39
emails/06_Getraenke_Sponsor.txt
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
BETREFF: Sponsoring-Anfrage – Getränke für Diversity-Ball 2026
|
||||
|
||||
Sehr geehrte Damen und Herren,
|
||||
|
||||
der Diversity-Ball ist eine der größten Inklusionsveranstaltungen Österreichs mit 3.500 Gästen im Wiener Rathaus.
|
||||
|
||||
**Veranstaltung:**
|
||||
- Datum: Samstag, 5. September 2026
|
||||
- Ort: Wiener Rathaus, Festsaal
|
||||
- Gäste: ca. 3.500 Personen
|
||||
- Erlös: Für Diversity-Projekte in Wien
|
||||
|
||||
**Sponsoring-Möglichkeit:**
|
||||
|
||||
Wir suchen einen Getränke-Partner, der folgende Leistungen übernimmt:
|
||||
- Wein (Weiß, Rot, Rosé)
|
||||
- Bier (vom Fass und Flasche)
|
||||
- Softdrinks, Mineralwasser
|
||||
- Sekt/Champagner
|
||||
|
||||
**Gegenleistung:**
|
||||
- Namensnennung als Getränke-Partner
|
||||
- Logo auf Einladungen und Programmheften
|
||||
- Präsenz bei der Veranstaltung
|
||||
- Mediale Berichterstattung
|
||||
|
||||
**Anfrage:**
|
||||
|
||||
1. Hätten Sie Interesse als Getränke-Sponsor?
|
||||
2. Welches Kontingent könnten Sie bereitstellen?
|
||||
3. Welche Gegenleistung wünschen Sie sich?
|
||||
|
||||
Über Ihre Rückmeldung freue ich mich sehr.
|
||||
|
||||
Mit freundlichen Grüßen
|
||||
|
||||
[Dein Name]
|
||||
[Telefon]
|
||||
[E-Mail]
|
||||
43
emails/07_Tombola_Preise.txt
Normal file
43
emails/07_Tombola_Preise.txt
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
BETREFF: Tombola-Preise Spende – Diversity-Ball am 5.9.2026
|
||||
|
||||
Sehr geehrte Damen und Herren,
|
||||
|
||||
der Diversity-Ball ist eine Benefizveranstaltung für Diversity-Projekte in Wien. Der Erlös der Tombola kommt vollständig gemeinnützigen Zwecken zugute.
|
||||
|
||||
**Veranstaltung:**
|
||||
- Datum: 5. September 2026
|
||||
- Ort: Wiener Rathaus
|
||||
- Gäste: ca. 3.500 Personen
|
||||
|
||||
**Tombola:**
|
||||
- 3.500 Lose (ein Los pro Gast)
|
||||
- Highlight: Versteigerung zugunsten von Diversity-Projekten
|
||||
|
||||
**Spendenmöglichkeit:**
|
||||
|
||||
Wir suchen Preise für die Tombola, z.B.:
|
||||
- Hotel-Übernachtungen
|
||||
- Restaurant-Gutscheine
|
||||
- Konzertkarten
|
||||
- Shopping-Gutscheine
|
||||
- Erlebnisse
|
||||
- Produkte
|
||||
|
||||
**Gegenleistung:**
|
||||
- Namensnennung bei der Tombola
|
||||
- Logo auf Tombola-Losen
|
||||
- Spendenquittung möglich (bei spendenbegünstigter Organisation)
|
||||
|
||||
**Anfrage:**
|
||||
|
||||
1. Könnten Sie einen Preis spenden?
|
||||
2. Welchen Wert hätte die Spende?
|
||||
3. Benötigen Sie eine Spendenbestätigung?
|
||||
|
||||
Über Ihre Zusage freuen wir uns sehr.
|
||||
|
||||
Mit freundlichen Grüßen
|
||||
|
||||
[Dein Name]
|
||||
[Telefon]
|
||||
[E-Mail]
|
||||
28
emails/08_Sponsoring_Anfrage.txt
Normal file
28
emails/08_Sponsoring_Anfrage.txt
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
BETREFF: Anfrage Sponsoring – Diversity-Ball Wien 2026
|
||||
|
||||
Sehr geehrte Damen und Herren,
|
||||
|
||||
der Diversity-Ball Wien ist eine Benefizveranstaltung zugunsten diverser gemeinnütziger Organisationen und findet am 5. September 2026 im Wiener Rathaus statt.
|
||||
|
||||
**Event-Daten:**
|
||||
- Datum: Samstag, 5. September 2026
|
||||
- Uhrzeit: 18:00 – 02:00 Uhr
|
||||
- Ort: Rathaus Wien, Festsaal
|
||||
- Gäste: 3.500 Personen
|
||||
- Budget: 750.000 €
|
||||
|
||||
**Sponsoring-Möglichkeiten:**
|
||||
|
||||
1. **Getränke-Sponsor** – exklusive Lieferung aller Getränke (Sekt, Wein, Bier, Softdrinks)
|
||||
2. **Haupt-Sponsor** – Logo auf allen Materialien, Redezeit, reservierte Tische
|
||||
3. **Medien-Partner** – Berichterstattung und Live-Übertragung
|
||||
4. **Preis-Sponsor** – Tombola-Preise (Wertsachen, Reisen, Erlebnisse)
|
||||
|
||||
Gerne senden wir Ihnen ein detailliertes Sponsoring-Exposé zu.
|
||||
|
||||
Über Ihr Interesse freuen wir uns auf Ihre Rückmeldung.
|
||||
|
||||
Mit freundlichen Grüßen
|
||||
|
||||
Diversity-Ball Wien
|
||||
office@diversityball.at
|
||||
30
emails/09_Security_Anfrage.txt
Normal file
30
emails/09_Security_Anfrage.txt
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
BETREFF: Anfrage Security-Dienstleistungen – Diversity-Ball Wien
|
||||
|
||||
Sehr geehrte Damen und Herren,
|
||||
|
||||
für eine Großveranstaltung im Wiener Rathaus suchen wir einen Security-Dienstleister.
|
||||
|
||||
**Veranstaltungsdetails:**
|
||||
- Datum: Samstag, 5. September 2026
|
||||
- Uhrzeit: 18:00 – 02:00 Uhr (Aufbau ab 12:00 Uhr)
|
||||
- Ort: Rathaus Wien, Festsaal
|
||||
- Gäste: 3.500 Personen
|
||||
- Format: Benefiz-Ball mit Menü, Tanz, Tombola
|
||||
|
||||
**Benötigte Leistungen:**
|
||||
- Einlasskontrolle (ca. 8-10 Personen)
|
||||
- Saal-Security während der Veranstaltung (ca. 12 Personen)
|
||||
- Garderobe (ca. 4 Personen)
|
||||
- VIP-Betreuung (ca. 4 Personen)
|
||||
|
||||
**Anforderungen:**
|
||||
- Gewerberechtliche Zulassung
|
||||
- Erfahrung mit Veranstaltungen >1.000 Personen
|
||||
- Verfügbarkeit am 5. September 2026
|
||||
|
||||
Bitte um Angebot inkl. Personalstunden, Kosten und Referenzen.
|
||||
|
||||
Mit freundlichen Grüßen
|
||||
|
||||
Diversity-Ball Wien
|
||||
office@diversityball.at
|
||||
28
emails/10_Versicherung_Anfrage.txt
Normal file
28
emails/10_Versicherung_Anfrage.txt
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
BETREFF: Anfrage Veranstaltungsversicherung – Diversity-Ball Wien
|
||||
|
||||
Sehr geehrte Damen und Herren,
|
||||
|
||||
wir benötigen eine Versicherung für eine Benefiz-Großveranstaltung.
|
||||
|
||||
**Veranstaltungsdetails:**
|
||||
- Datum: Samstag, 5. September 2026
|
||||
- Uhrzeit: 18:00 – 02:00 Uhr
|
||||
- Ort: Rathaus Wien, Festsaal
|
||||
- Gäste: 3.500 Personen
|
||||
- Veranstaltungsart: Benefiz-Ball
|
||||
|
||||
**Versicherungsbedarf:**
|
||||
- Haftpflichtversicherung (Personen-/Sachschäden)
|
||||
- Veranstaltungsausfall-Versicherung
|
||||
- Equipment-Versicherung (Technik, Dekoration)
|
||||
- Versicherung für Tombola-Preise
|
||||
|
||||
**Organisator:**
|
||||
- Diversity-Ball Wien
|
||||
- office@diversityball.at
|
||||
|
||||
Bitte um Angebot und Konditionen.
|
||||
|
||||
Mit freundlichen Grüßen
|
||||
|
||||
Diversity-Ball Wien
|
||||
30
emails/11_Dekoration_Anfrage.txt
Normal file
30
emails/11_Dekoration_Anfrage.txt
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
BETREFF: Anfrage Dekoration & Floristik – Diversity-Ball Wien
|
||||
|
||||
Sehr geehrte Damen und Herren,
|
||||
|
||||
wir suchen einen Partner für Dekoration und Floristik für unseren Diversity-Ball.
|
||||
|
||||
**Veranstaltungsdetails:**
|
||||
- Datum: Samstag, 5. September 2026
|
||||
- Uhrzeit: 18:00 – 02:00 Uhr
|
||||
- Ort: Rathaus Wien, Festsaal
|
||||
- Gäste: 3.500 Personen
|
||||
- Theme: Diversity & Inklusion
|
||||
|
||||
**Gewünschte Leistungen:**
|
||||
- Tischdekoration (ca. 350 Tischplätze)
|
||||
- Bühnendekoration
|
||||
- Entrance-Gestaltung (Sektempfang)
|
||||
- Florale Gestaltung (Blumen, Arrangements)
|
||||
- Lichtkonzept (atmosphärisch)
|
||||
|
||||
**Zeitrahmen:**
|
||||
- Aufbau: 5. September 2026, 12:00-17:00 Uhr
|
||||
- Abbau: 6. September 2026, 02:00-08:00 Uhr
|
||||
|
||||
Bitte um Portfolio und Angebot.
|
||||
|
||||
Mit freundlichen Grüßen
|
||||
|
||||
Diversity-Ball Wien
|
||||
office@diversityball.at
|
||||
35
emails/UEBERSICHT.txt
Normal file
35
emails/UEBERSICHT.txt
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# E-Mail-Übersicht | Diversity-Ball 5.9.2026
|
||||
|
||||
## Fertige E-Mails (zum Absenden)
|
||||
|
||||
| # | Datei | Empfänger | Betreff |
|
||||
|---|-------|-----------|---------|
|
||||
| 1 | `01_MA36_Genehmigung.txt` | MA 36 (Veranstaltungsbehörde) | Anfrage Veranstaltungsanmeldung |
|
||||
| 2 | `02_Rathauskeller_Catering.txt` | Rathauskeller | Catering-Anfrage |
|
||||
| 3 | `03_Finanzamt_Gemeinnuetzigkeit.txt` | Finanzamt | Anfrage Gemeinnützigkeitsstatus |
|
||||
| 4 | `04_Technik_Anfrage.txt` | Technik-Anbieter (z.B. NUNTIO, VA.TEC) | Technische Ausstattung |
|
||||
| 5 | `05_OGS_Dolmetscher.txt` | ÖGS-Dolmetscher | Barrierefreiheit |
|
||||
| 6 | `06_Getraenke_Sponsor.txt` | Getränke-Lieferanten | Sponsoring Getränke |
|
||||
| 7 | `07_Tombola_Preise.txt` | Wiener Unternehmen | Tombola-Preise |
|
||||
| 8 | `08_Sponsoring_Anfrage.txt` | Potenzielle Sponsoren | Sponsoring-Anfrage |
|
||||
| 9 | `09_Security_Anfrage.txt` | Security-Dienstleister | Security-Dienstleistungen |
|
||||
| 10 | `10_Versicherung_Anfrage.txt` | Versicherungsanbieter | Veranstaltungsversicherung |
|
||||
| 11 | `11_Dekoration_Anfrage.txt` | Deko/Floristik-Partner | Dekoration & Floristik |
|
||||
|
||||
---
|
||||
|
||||
## fehlende Empfänger (selbst ergänzen)
|
||||
|
||||
- **Rathaus Verwaltung** (Raumbuchung): [contact@wien.at]
|
||||
- **Diversity Ball Wien** (Beratung): office@diversityball.at
|
||||
- **Best Practice**: Monika Haider ("Ballmutter")
|
||||
|
||||
---
|
||||
|
||||
## Nächste Schritte
|
||||
|
||||
1. Empfänger-Adressen ergänzen
|
||||
2. [Dein Name] und Kontaktdaten ersetzen
|
||||
3. E-Mails absenden
|
||||
|
||||
Alle E-Mails liegen im Ordner `emails/`
|
||||
2
sponsorship_docx/[Content_Types].xml
Normal file
2
sponsorship_docx/[Content_Types].xml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="png" ContentType="image/png"/><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/><Override PartName="/word/numbering.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml"/><Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/><Override PartName="/word/settings.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.settings+xml"/><Override PartName="/word/webSettings.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.webSettings+xml"/><Override PartName="/word/header1.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.header+xml"/><Override PartName="/word/footer1.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.footer+xml"/><Override PartName="/word/fontTable.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.fontTable+xml"/><Override PartName="/word/theme/theme1.xml" ContentType="application/vnd.openxmlformats-officedocument.theme+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/><Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/></Types>
|
||||
2
sponsorship_docx/_rels/.rels
Normal file
2
sponsorship_docx/_rels/.rels
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/></Relationships>
|
||||
2
sponsorship_docx/docProps/app.xml
Normal file
2
sponsorship_docx/docProps/app.xml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes"><Template>Normal.dotm</Template><TotalTime>0</TotalTime><Pages>3</Pages><Words>379</Words><Characters>2389</Characters><Application>Microsoft Office Word</Application><DocSecurity>0</DocSecurity><Lines>19</Lines><Paragraphs>5</Paragraphs><ScaleCrop>false</ScaleCrop><Company></Company><LinksUpToDate>false</LinksUpToDate><CharactersWithSpaces>2763</CharactersWithSpaces><SharedDoc>false</SharedDoc><HyperlinksChanged>false</HyperlinksChanged><AppVersion>16.0000</AppVersion></Properties>
|
||||
2
sponsorship_docx/docProps/core.xml
Normal file
2
sponsorship_docx/docProps/core.xml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dc:title></dc:title><dc:subject></dc:subject><dc:creator>Marlene Tretton</dc:creator><cp:keywords></cp:keywords><dc:description></dc:description><cp:lastModifiedBy>Marlene Tretton</cp:lastModifiedBy><cp:revision>2</cp:revision><dcterms:created xsi:type="dcterms:W3CDTF">2026-02-11T12:02:00Z</dcterms:created><dcterms:modified xsi:type="dcterms:W3CDTF">2026-02-11T12:06:00Z</dcterms:modified></cp:coreProperties>
|
||||
2
sponsorship_docx/word/_rels/document.xml.rels
Normal file
2
sponsorship_docx/word/_rels/document.xml.rels
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId8" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme" Target="theme/theme1.xml"/><Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/settings" Target="settings.xml"/><Relationship Id="rId7" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/fontTable" Target="fontTable.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/numbering" Target="numbering.xml"/><Relationship Id="rId6" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/footer" Target="footer1.xml"/><Relationship Id="rId5" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/header" Target="header1.xml"/><Relationship Id="rId4" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/webSettings" Target="webSettings.xml"/></Relationships>
|
||||
2
sponsorship_docx/word/_rels/footer1.xml.rels
Normal file
2
sponsorship_docx/word/_rels/footer1.xml.rels
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="http://www.diversityball.at" TargetMode="External"/><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink" Target="mailto:office@diversityball.at" TargetMode="External"/></Relationships>
|
||||
2
sponsorship_docx/word/_rels/header1.xml.rels
Normal file
2
sponsorship_docx/word/_rels/header1.xml.rels
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/image" Target="media/image1.png"/></Relationships>
|
||||
5
sponsorship_docx/word/document.xml
Normal file
5
sponsorship_docx/word/document.xml
Normal file
File diff suppressed because one or more lines are too long
2
sponsorship_docx/word/fontTable.xml
Normal file
2
sponsorship_docx/word/fontTable.xml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<w:fonts xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16cex="http://schemas.microsoft.com/office/word/2018/wordml/cex" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" xmlns:w16="http://schemas.microsoft.com/office/word/2018/wordml" xmlns:w16du="http://schemas.microsoft.com/office/word/2023/wordml/word16du" xmlns:w16sdtdh="http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" mc:Ignorable="w14 w15 w16se w16cid w16 w16cex w16sdtdh w16du"><w:font w:name="Symbol"><w:panose1 w:val="05050102010706020507"/><w:charset w:val="02"/><w:family w:val="decorative"/><w:pitch w:val="variable"/><w:sig w:usb0="00000003" w:usb1="10000000" w:usb2="00000000" w:usb3="00000000" w:csb0="80000001" w:csb1="00000000"/></w:font><w:font w:name="Times New Roman"><w:panose1 w:val="02020603050405020304"/><w:charset w:val="00"/><w:family w:val="roman"/><w:pitch w:val="variable"/><w:sig w:usb0="E0002EFF" w:usb1="C000785B" w:usb2="00000009" w:usb3="00000000" w:csb0="000001FF" w:csb1="00000000"/></w:font><w:font w:name="Courier New"><w:panose1 w:val="02070309020205020404"/><w:charset w:val="00"/><w:family w:val="modern"/><w:pitch w:val="fixed"/><w:sig w:usb0="E0002EFF" w:usb1="C0007843" w:usb2="00000009" w:usb3="00000000" w:csb0="000001FF" w:csb1="00000000"/></w:font><w:font w:name="Wingdings"><w:panose1 w:val="05000000000000000000"/><w:charset w:val="4D"/><w:family w:val="decorative"/><w:pitch w:val="variable"/><w:sig w:usb0="00000003" w:usb1="00000000" w:usb2="00000000" w:usb3="00000000" w:csb0="80000001" w:csb1="00000000"/></w:font><w:font w:name="Aptos"><w:panose1 w:val="020B0004020202020204"/><w:charset w:val="00"/><w:family w:val="swiss"/><w:pitch w:val="variable"/><w:sig w:usb0="20000287" w:usb1="00000003" w:usb2="00000000" w:usb3="00000000" w:csb0="0000019F" w:csb1="00000000"/></w:font><w:font w:name="Aptos Display"><w:panose1 w:val="020B0004020202020204"/><w:charset w:val="00"/><w:family w:val="swiss"/><w:pitch w:val="variable"/><w:sig w:usb0="20000287" w:usb1="00000003" w:usb2="00000000" w:usb3="00000000" w:csb0="0000019F" w:csb1="00000000"/></w:font><w:font w:name="Calibri"><w:panose1 w:val="020F0502020204030204"/><w:charset w:val="00"/><w:family w:val="swiss"/><w:pitch w:val="variable"/><w:sig w:usb0="E0002AFF" w:usb1="C000ACFF" w:usb2="00000009" w:usb3="00000000" w:csb0="000001FF" w:csb1="00000000"/></w:font><w:font w:name="Calibri Light"><w:panose1 w:val="020F0302020204030204"/><w:charset w:val="00"/><w:family w:val="swiss"/><w:pitch w:val="variable"/><w:sig w:usb0="E4002EFF" w:usb1="C000247B" w:usb2="00000009" w:usb3="00000000" w:csb0="000001FF" w:csb1="00000000"/></w:font><w:font w:name="Bahnschrift Light"><w:panose1 w:val="020B0502040204020203"/><w:charset w:val="00"/><w:family w:val="swiss"/><w:pitch w:val="variable"/><w:sig w:usb0="A00002C7" w:usb1="00000002" w:usb2="00000000" w:usb3="00000000" w:csb0="0000019F" w:csb1="00000000"/></w:font><w:font w:name="Arial"><w:panose1 w:val="020B0604020202020204"/><w:charset w:val="00"/><w:family w:val="swiss"/><w:pitch w:val="variable"/><w:sig w:usb0="E0002AFF" w:usb1="C0007843" w:usb2="00000009" w:usb3="00000000" w:csb0="000001FF" w:csb1="00000000"/></w:font><w:font w:name="Tahoma"><w:panose1 w:val="020B0604030504040204"/><w:charset w:val="00"/><w:family w:val="swiss"/><w:pitch w:val="variable"/><w:sig w:usb0="E1002EFF" w:usb1="C000605B" w:usb2="00000029" w:usb3="00000000" w:csb0="000101FF" w:csb1="00000000"/></w:font></w:fonts>
|
||||
2
sponsorship_docx/word/footer1.xml
Normal file
2
sponsorship_docx/word/footer1.xml
Normal file
File diff suppressed because one or more lines are too long
2
sponsorship_docx/word/header1.xml
Normal file
2
sponsorship_docx/word/header1.xml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<w:hdr xmlns:wpc="http://schemas.microsoft.com/office/word/2010/wordprocessingCanvas" xmlns:cx="http://schemas.microsoft.com/office/drawing/2014/chartex" xmlns:cx1="http://schemas.microsoft.com/office/drawing/2015/9/8/chartex" xmlns:cx2="http://schemas.microsoft.com/office/drawing/2015/10/21/chartex" xmlns:cx3="http://schemas.microsoft.com/office/drawing/2016/5/9/chartex" xmlns:cx4="http://schemas.microsoft.com/office/drawing/2016/5/10/chartex" xmlns:cx5="http://schemas.microsoft.com/office/drawing/2016/5/11/chartex" xmlns:cx6="http://schemas.microsoft.com/office/drawing/2016/5/12/chartex" xmlns:cx7="http://schemas.microsoft.com/office/drawing/2016/5/13/chartex" xmlns:cx8="http://schemas.microsoft.com/office/drawing/2016/5/14/chartex" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:aink="http://schemas.microsoft.com/office/drawing/2016/ink" xmlns:am3d="http://schemas.microsoft.com/office/drawing/2017/model3d" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:oel="http://schemas.microsoft.com/office/2019/extlst" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:wp14="http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16cex="http://schemas.microsoft.com/office/word/2018/wordml/cex" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" xmlns:w16="http://schemas.microsoft.com/office/word/2018/wordml" xmlns:w16du="http://schemas.microsoft.com/office/word/2023/wordml/word16du" xmlns:w16sdtdh="http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" xmlns:wpg="http://schemas.microsoft.com/office/word/2010/wordprocessingGroup" xmlns:wpi="http://schemas.microsoft.com/office/word/2010/wordprocessingInk" xmlns:wne="http://schemas.microsoft.com/office/word/2006/wordml" xmlns:wps="http://schemas.microsoft.com/office/word/2010/wordprocessingShape" mc:Ignorable="w14 w15 w16se w16cid w16 w16cex w16sdtdh w16du wp14"><w:p w14:paraId="1336E7C2" w14:textId="77777777" w:rsidR="00000000" w:rsidRDefault="00000000" w:rsidP="00BF0AFA"><w:pPr><w:pStyle w:val="Kopfzeile"/><w:tabs><w:tab w:val="clear" w:pos="9072"/><w:tab w:val="right" w:pos="9046"/></w:tabs><w:jc w:val="center"/></w:pPr><w:r><w:rPr><w:noProof/><w:lang w:val="de-AT" w:eastAsia="de-AT"/></w:rPr><w:drawing><wp:inline distT="0" distB="0" distL="0" distR="0" wp14:anchorId="3D6CEEE3" wp14:editId="17154911"><wp:extent cx="952269" cy="1078524"/><wp:effectExtent l="0" t="0" r="635" b="7620"/><wp:docPr id="3" name="Grafik 3"/><wp:cNvGraphicFramePr><a:graphicFrameLocks xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" noChangeAspect="1"/></wp:cNvGraphicFramePr><a:graphic xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main"><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:nvPicPr><pic:cNvPr id="3" name="DB-bunt-transoarent-Screen.png"/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill><a:blip r:embed="rId1"><a:extLst><a:ext uri="{28A0092B-C50C-407E-A947-70E740481C1C}"><a14:useLocalDpi xmlns:a14="http://schemas.microsoft.com/office/drawing/2010/main" val="0"/></a:ext></a:extLst></a:blip><a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="955546" cy="1082236"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic></wp:inline></w:drawing></w:r></w:p></w:hdr>
|
||||
BIN
sponsorship_docx/word/media/image1.png
Normal file
BIN
sponsorship_docx/word/media/image1.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 584 KiB |
2
sponsorship_docx/word/numbering.xml
Normal file
2
sponsorship_docx/word/numbering.xml
Normal file
File diff suppressed because one or more lines are too long
2
sponsorship_docx/word/settings.xml
Normal file
2
sponsorship_docx/word/settings.xml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<w:settings xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:m="http://schemas.openxmlformats.org/officeDocument/2006/math" xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w10="urn:schemas-microsoft-com:office:word" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16cex="http://schemas.microsoft.com/office/word/2018/wordml/cex" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" xmlns:w16="http://schemas.microsoft.com/office/word/2018/wordml" xmlns:w16du="http://schemas.microsoft.com/office/word/2023/wordml/word16du" xmlns:w16sdtdh="http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" xmlns:sl="http://schemas.openxmlformats.org/schemaLibrary/2006/main" mc:Ignorable="w14 w15 w16se w16cid w16 w16cex w16sdtdh w16du"><w:zoom w:percent="228"/><w:proofState w:spelling="clean" w:grammar="clean"/><w:defaultTabStop w:val="708"/><w:hyphenationZone w:val="425"/><w:characterSpacingControl w:val="doNotCompress"/><w:compat><w:compatSetting w:name="compatibilityMode" w:uri="http://schemas.microsoft.com/office/word" w:val="15"/><w:compatSetting w:name="overrideTableStyleFontSizeAndJustification" w:uri="http://schemas.microsoft.com/office/word" w:val="1"/><w:compatSetting w:name="enableOpenTypeFeatures" w:uri="http://schemas.microsoft.com/office/word" w:val="1"/><w:compatSetting w:name="doNotFlipMirrorIndents" w:uri="http://schemas.microsoft.com/office/word" w:val="1"/><w:compatSetting w:name="differentiateMultirowTableHeaders" w:uri="http://schemas.microsoft.com/office/word" w:val="1"/><w:compatSetting w:name="useWord2013TrackBottomHyphenation" w:uri="http://schemas.microsoft.com/office/word" w:val="0"/></w:compat><w:rsids><w:rsidRoot w:val="00393CB6"/><w:rsid w:val="00393CB6"/><w:rsid w:val="00CB4F65"/><w:rsid w:val="00FC6E38"/></w:rsids><m:mathPr><m:mathFont m:val="Cambria Math"/><m:brkBin m:val="before"/><m:brkBinSub m:val="--"/><m:smallFrac m:val="0"/><m:dispDef/><m:lMargin m:val="0"/><m:rMargin m:val="0"/><m:defJc m:val="centerGroup"/><m:wrapIndent m:val="1440"/><m:intLim m:val="subSup"/><m:naryLim m:val="undOvr"/></m:mathPr><w:themeFontLang w:val="de-AT"/><w:clrSchemeMapping w:bg1="light1" w:t1="dark1" w:bg2="light2" w:t2="dark2" w:accent1="accent1" w:accent2="accent2" w:accent3="accent3" w:accent4="accent4" w:accent5="accent5" w:accent6="accent6" w:hyperlink="hyperlink" w:followedHyperlink="followedHyperlink"/><w:decimalSymbol w:val=","/><w:listSeparator w:val=";"/><w14:docId w14:val="0DB37833"/><w15:chartTrackingRefBased/><w15:docId w15:val="{CEF95732-EDB6-D541-A49C-A0B68BB8D359}"/></w:settings>
|
||||
2
sponsorship_docx/word/styles.xml
Normal file
2
sponsorship_docx/word/styles.xml
Normal file
File diff suppressed because one or more lines are too long
2
sponsorship_docx/word/theme/theme1.xml
Normal file
2
sponsorship_docx/word/theme/theme1.xml
Normal file
File diff suppressed because one or more lines are too long
2
sponsorship_docx/word/webSettings.xml
Normal file
2
sponsorship_docx/word/webSettings.xml
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<w:webSettings xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main" xmlns:w14="http://schemas.microsoft.com/office/word/2010/wordml" xmlns:w15="http://schemas.microsoft.com/office/word/2012/wordml" xmlns:w16cex="http://schemas.microsoft.com/office/word/2018/wordml/cex" xmlns:w16cid="http://schemas.microsoft.com/office/word/2016/wordml/cid" xmlns:w16="http://schemas.microsoft.com/office/word/2018/wordml" xmlns:w16du="http://schemas.microsoft.com/office/word/2023/wordml/word16du" xmlns:w16sdtdh="http://schemas.microsoft.com/office/word/2020/wordml/sdtdatahash" xmlns:w16se="http://schemas.microsoft.com/office/word/2015/wordml/symex" mc:Ignorable="w14 w15 w16se w16cid w16 w16cex w16sdtdh w16du"><w:optimizeForBrowser/><w:allowPNG/></w:webSettings>
|
||||
720
static/style.css
Normal file
720
static/style.css
Normal file
|
|
@ -0,0 +1,720 @@
|
|||
/* ── Frankenbot Dark Theme v2 ──────────────────────────────────────────────── */
|
||||
|
||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap');
|
||||
|
||||
:root {
|
||||
--bg-base: #080b12;
|
||||
--bg-surface: #0f1320;
|
||||
--bg-elevated: #181d2e;
|
||||
--bg-hover: #1f2640;
|
||||
--bg-glass: rgba(15,19,32,.7);
|
||||
--accent: #7c6fff;
|
||||
--accent-light: #a89eff;
|
||||
--accent-dim: #4a45a0;
|
||||
--accent-glow: rgba(124,111,255,.3);
|
||||
--accent2: #06d6a0;
|
||||
--accent2-glow: rgba(6,214,160,.25);
|
||||
--success: #06d6a0;
|
||||
--warning: #fbbf24;
|
||||
--danger: #f43f5e;
|
||||
--info: #38bdf8;
|
||||
--text-primary: #e8eaf6;
|
||||
--text-secondary: #94a3b8;
|
||||
--text-muted: #4e5a72;
|
||||
--border: rgba(255,255,255,.07);
|
||||
--border-accent: rgba(124,111,255,.35);
|
||||
--shadow: 0 8px 32px rgba(0,0,0,.5);
|
||||
--shadow-sm: 0 2px 12px rgba(0,0,0,.3);
|
||||
--radius: 14px;
|
||||
--radius-sm: 9px;
|
||||
--radius-xs: 6px;
|
||||
--transition: .18s cubic-bezier(.4,0,.2,1);
|
||||
}
|
||||
|
||||
/* ── Reset & Base ─────────────────────────────────────────────────────────── */
|
||||
|
||||
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
||||
html { scroll-behavior: smooth; }
|
||||
|
||||
body {
|
||||
min-height: 100vh;
|
||||
background: var(--bg-base);
|
||||
background-image:
|
||||
radial-gradient(ellipse 80% 50% at 50% -10%, rgba(124,111,255,.12) 0%, transparent 60%),
|
||||
radial-gradient(ellipse 40% 30% at 90% 80%, rgba(6,214,160,.06) 0%, transparent 50%);
|
||||
color: var(--text-primary);
|
||||
font-family: 'Inter', 'Segoe UI', system-ui, sans-serif;
|
||||
font-size: .9375rem;
|
||||
line-height: 1.65;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
/* ── Scrollbar ────────────────────────────────────────────────────────────── */
|
||||
|
||||
::-webkit-scrollbar { width: 5px; height: 5px; }
|
||||
::-webkit-scrollbar-track { background: transparent; }
|
||||
::-webkit-scrollbar-thumb { background: var(--bg-hover); border-radius: 99px; }
|
||||
::-webkit-scrollbar-thumb:hover { background: var(--accent-dim); }
|
||||
|
||||
/* ── Navbar ───────────────────────────────────────────────────────────────── */
|
||||
|
||||
.navbar {
|
||||
background: var(--bg-glass) !important;
|
||||
border-bottom: 1px solid var(--border);
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
padding: .6rem 0;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.navbar-brand {
|
||||
font-weight: 700;
|
||||
font-size: 1rem;
|
||||
color: var(--text-primary) !important;
|
||||
letter-spacing: -.02em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: .5rem;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.navbar-brand .brand-icon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background: linear-gradient(135deg, var(--accent), var(--accent2));
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: .75rem;
|
||||
box-shadow: 0 0 16px var(--accent-glow);
|
||||
animation: brand-pulse 3s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes brand-pulse {
|
||||
0%, 100% { box-shadow: 0 0 16px var(--accent-glow); }
|
||||
50% { box-shadow: 0 0 28px var(--accent-glow), 0 0 8px var(--accent2-glow); }
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: var(--text-muted) !important;
|
||||
font-size: .8125rem;
|
||||
font-weight: 500;
|
||||
padding: .4rem .7rem !important;
|
||||
border-radius: var(--radius-xs);
|
||||
transition: color var(--transition), background var(--transition);
|
||||
letter-spacing: .01em;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: .35rem;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.nav-link:hover,
|
||||
.nav-link.active {
|
||||
color: var(--text-primary) !important;
|
||||
background: rgba(124,111,255,.12);
|
||||
}
|
||||
|
||||
.nav-link.active {
|
||||
color: var(--accent-light) !important;
|
||||
background: rgba(124,111,255,.18);
|
||||
}
|
||||
|
||||
.navbar-toggler {
|
||||
border-color: var(--border);
|
||||
padding: .3rem .5rem;
|
||||
}
|
||||
.navbar-toggler-icon { filter: invert(.7); }
|
||||
|
||||
/* ── Page Header ──────────────────────────────────────────────────────────── */
|
||||
|
||||
.page-header {
|
||||
padding: 1.75rem 0 1.25rem;
|
||||
margin-bottom: 1.75rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.page-header::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -1px;
|
||||
left: 0;
|
||||
width: 60px;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, var(--accent), transparent);
|
||||
border-radius: 99px;
|
||||
}
|
||||
|
||||
.page-header h1 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: -.03em;
|
||||
margin-bottom: .2rem;
|
||||
background: linear-gradient(135deg, var(--text-primary), var(--text-secondary));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.page-header p {
|
||||
color: var(--text-muted);
|
||||
font-size: .8125rem;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* ── Cards ────────────────────────────────────────────────────────────────── */
|
||||
|
||||
.card {
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow-sm);
|
||||
color: var(--text-primary);
|
||||
transition: border-color var(--transition), box-shadow var(--transition), transform var(--transition);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
border-color: rgba(124,111,255,.2);
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background: var(--bg-elevated) !important;
|
||||
border-bottom: 1px solid var(--border) !important;
|
||||
color: var(--text-primary) !important;
|
||||
padding: .85rem 1.25rem;
|
||||
font-weight: 600;
|
||||
font-size: .9rem;
|
||||
}
|
||||
|
||||
.card-header.bg-primary { background: linear-gradient(135deg, rgba(79,70,229,.8), rgba(124,111,255,.8)) !important; border-bottom-color: rgba(124,111,255,.3) !important; }
|
||||
.card-header.bg-success { background: linear-gradient(135deg, rgba(5,150,105,.8), rgba(6,214,160,.8)) !important; border-bottom-color: rgba(6,214,160,.3) !important; color: #fff !important; }
|
||||
.card-header.bg-dark { background: linear-gradient(135deg, #10141f, #1a2035) !important; }
|
||||
.card-header.bg-warning { background: linear-gradient(135deg, rgba(180,100,0,.85), rgba(251,191,36,.85)) !important; color: #fff !important; border-bottom-color: rgba(251,191,36,.3) !important; }
|
||||
.card-header.bg-info { background: linear-gradient(135deg, rgba(2,132,199,.8), rgba(56,189,248,.8)) !important; border-bottom-color: rgba(56,189,248,.3) !important; }
|
||||
.card-header.bg-secondary{ background: linear-gradient(135deg, #1e2640, #242e48) !important; }
|
||||
.card-header.bg-danger { background: linear-gradient(135deg, rgba(185,28,28,.8), rgba(244,63,94,.8)) !important; border-bottom-color: rgba(244,63,94,.3) !important; }
|
||||
|
||||
.card-body { padding: 1.25rem; }
|
||||
|
||||
.card-footer {
|
||||
background: var(--bg-elevated) !important;
|
||||
border-top: 1px solid var(--border) !important;
|
||||
padding: .65rem 1.25rem;
|
||||
}
|
||||
|
||||
/* Agent cards */
|
||||
.agent-card {
|
||||
transition: transform var(--transition), box-shadow var(--transition), border-color var(--transition);
|
||||
cursor: default;
|
||||
}
|
||||
.agent-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 12px 40px rgba(124,111,255,.2);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
/* ── Stat Cards ───────────────────────────────────────────────────────────── */
|
||||
|
||||
.stat-card {
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
padding: 1.5rem 1.25rem;
|
||||
text-align: center;
|
||||
transition: border-color var(--transition), transform var(--transition), box-shadow var(--transition);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.stat-card::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 0; left: 0; right: 0;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, var(--accent), var(--accent2));
|
||||
opacity: 0;
|
||||
transition: opacity var(--transition);
|
||||
}
|
||||
|
||||
.stat-card:hover {
|
||||
border-color: var(--border-accent);
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 32px rgba(124,111,255,.15);
|
||||
}
|
||||
.stat-card:hover::before { opacity: 1; }
|
||||
|
||||
.stat-card .stat-number {
|
||||
font-size: 2.2rem;
|
||||
font-weight: 700;
|
||||
background: linear-gradient(135deg, var(--accent-light), var(--accent2));
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
line-height: 1;
|
||||
margin-bottom: .4rem;
|
||||
}
|
||||
|
||||
.stat-card .stat-label {
|
||||
font-size: .7rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .1em;
|
||||
color: var(--text-muted);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* ── Buttons ──────────────────────────────────────────────────────────────── */
|
||||
|
||||
.btn {
|
||||
border-radius: var(--radius-sm);
|
||||
font-weight: 500;
|
||||
font-size: .8125rem;
|
||||
padding: .45rem 1rem;
|
||||
transition: all var(--transition);
|
||||
border: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: .35rem;
|
||||
letter-spacing: .01em;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, var(--accent), #6058d0);
|
||||
color: #fff;
|
||||
box-shadow: 0 2px 12px rgba(124,111,255,.3);
|
||||
}
|
||||
.btn-primary:hover {
|
||||
background: linear-gradient(135deg, var(--accent-light), var(--accent));
|
||||
box-shadow: 0 4px 20px rgba(124,111,255,.45);
|
||||
transform: translateY(-1px);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-success {
|
||||
background: linear-gradient(135deg, #059669, var(--success));
|
||||
color: #fff;
|
||||
box-shadow: 0 2px 12px rgba(6,214,160,.25);
|
||||
}
|
||||
.btn-success:hover {
|
||||
background: linear-gradient(135deg, #06d6a0, #059669);
|
||||
box-shadow: 0 4px 20px rgba(6,214,160,.4);
|
||||
transform: translateY(-1px);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-danger {
|
||||
background: linear-gradient(135deg, #be123c, var(--danger));
|
||||
color: #fff;
|
||||
}
|
||||
.btn-danger:hover {
|
||||
background: linear-gradient(135deg, var(--danger), #be123c);
|
||||
transform: translateY(-1px);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-secondary {
|
||||
background: var(--bg-elevated);
|
||||
color: var(--text-secondary);
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
.btn-secondary:hover {
|
||||
background: var(--bg-hover);
|
||||
color: var(--text-primary);
|
||||
border-color: rgba(124,111,255,.3);
|
||||
}
|
||||
|
||||
.btn-info {
|
||||
background: linear-gradient(135deg, #0284c7, var(--info));
|
||||
color: #fff;
|
||||
}
|
||||
.btn-info:hover {
|
||||
background: linear-gradient(135deg, var(--info), #0284c7);
|
||||
transform: translateY(-1px);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-warning {
|
||||
background: linear-gradient(135deg, #d97706, var(--warning));
|
||||
color: #0f1117;
|
||||
}
|
||||
.btn-warning:hover {
|
||||
background: linear-gradient(135deg, var(--warning), #d97706);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.btn-outline-primary {
|
||||
background: transparent;
|
||||
border: 1px solid var(--accent-dim);
|
||||
color: var(--accent-light);
|
||||
}
|
||||
.btn-outline-primary:hover {
|
||||
background: rgba(124,111,255,.15);
|
||||
border-color: var(--accent);
|
||||
color: var(--accent-light);
|
||||
}
|
||||
|
||||
.btn-outline-secondary {
|
||||
background: transparent;
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.btn-outline-secondary:hover {
|
||||
background: var(--bg-hover);
|
||||
color: var(--text-primary);
|
||||
border-color: var(--text-muted);
|
||||
}
|
||||
|
||||
.btn-sm { padding: .28rem .65rem; font-size: .76rem; border-radius: var(--radius-xs); }
|
||||
.btn-lg { padding: .65rem 1.5rem; font-size: .9375rem; }
|
||||
.btn:disabled { opacity: .5; cursor: not-allowed; transform: none !important; }
|
||||
|
||||
/* ── Forms ────────────────────────────────────────────────────────────────── */
|
||||
|
||||
.form-control,
|
||||
.form-select {
|
||||
background: var(--bg-elevated);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--text-primary);
|
||||
padding: .55rem .9rem;
|
||||
font-size: .875rem;
|
||||
transition: border-color var(--transition), box-shadow var(--transition);
|
||||
}
|
||||
|
||||
.form-control:focus,
|
||||
.form-select:focus {
|
||||
background: var(--bg-elevated);
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 3px var(--accent-glow);
|
||||
color: var(--text-primary);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.form-control::placeholder { color: var(--text-muted); }
|
||||
.form-label {
|
||||
color: var(--text-secondary);
|
||||
font-size: .76rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .07em;
|
||||
margin-bottom: .4rem;
|
||||
}
|
||||
.form-select option { background: var(--bg-elevated); }
|
||||
textarea.form-control { resize: vertical; min-height: 80px; }
|
||||
|
||||
/* ── Tables ───────────────────────────────────────────────────────────────── */
|
||||
|
||||
.table {
|
||||
color: var(--text-primary);
|
||||
margin-bottom: 0;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
.table th {
|
||||
border-top: none;
|
||||
border-bottom: 1px solid var(--border);
|
||||
font-weight: 600;
|
||||
font-size: .72rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .08em;
|
||||
color: var(--text-muted);
|
||||
padding: .75rem 1rem;
|
||||
background: rgba(0,0,0,.2);
|
||||
}
|
||||
|
||||
.table td {
|
||||
border-bottom: 1px solid var(--border);
|
||||
padding: .75rem 1rem;
|
||||
vertical-align: middle;
|
||||
color: var(--text-secondary);
|
||||
font-size: .875rem;
|
||||
}
|
||||
|
||||
.table tbody tr:last-child td { border-bottom: none; }
|
||||
.table tbody tr:hover td {
|
||||
background: var(--bg-hover);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
|
||||
.table-striped > tbody > tr:nth-of-type(odd) > * {
|
||||
background: rgba(255,255,255,.015);
|
||||
}
|
||||
|
||||
/* ── Badges ───────────────────────────────────────────────────────────────── */
|
||||
|
||||
.badge {
|
||||
padding: .28em .65em;
|
||||
border-radius: 5px;
|
||||
font-size: .68rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: .05em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.bg-primary { background: rgba(124,111,255,.25) !important; color: var(--accent-light) !important; border: 1px solid rgba(124,111,255,.3); }
|
||||
.bg-success { background: rgba(6,214,160,.2) !important; color: #34d399 !important; border: 1px solid rgba(6,214,160,.3); }
|
||||
.bg-warning { background: rgba(251,191,36,.2) !important; color: #fbbf24 !important; border: 1px solid rgba(251,191,36,.3); }
|
||||
.bg-danger { background: rgba(244,63,94,.2) !important; color: #f87171 !important; border: 1px solid rgba(244,63,94,.3); }
|
||||
.bg-info { background: rgba(56,189,248,.2) !important; color: #38bdf8 !important; border: 1px solid rgba(56,189,248,.3); }
|
||||
.bg-secondary { background: var(--bg-elevated) !important; color: var(--text-muted) !important; border: 1px solid var(--border); }
|
||||
.bg-dark { background: rgba(0,0,0,.4) !important; color: var(--text-secondary) !important; border: 1px solid var(--border); }
|
||||
|
||||
/* ── Alerts ───────────────────────────────────────────────────────────────── */
|
||||
|
||||
.alert {
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid;
|
||||
font-size: .875rem;
|
||||
padding: .9rem 1.1rem;
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.alert-success { background: rgba(6,214,160,.08); border-color: rgba(6,214,160,.25); color: #34d399; }
|
||||
.alert-danger { background: rgba(244,63,94,.08); border-color: rgba(244,63,94,.25); color: #f87171; }
|
||||
.alert-warning { background: rgba(251,191,36,.08); border-color: rgba(251,191,36,.25); color: #fbbf24; }
|
||||
.alert-info { background: rgba(56,189,248,.08); border-color: rgba(56,189,248,.25); color: #7dd3fc; }
|
||||
|
||||
.alert-dismissible .btn-close { filter: invert(1) opacity(.5); }
|
||||
|
||||
/* ── Chat ─────────────────────────────────────────────────────────────────── */
|
||||
|
||||
.chat-container {
|
||||
max-height: 560px;
|
||||
overflow-y: auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1rem;
|
||||
padding: .5rem 0;
|
||||
}
|
||||
|
||||
.chat-message {
|
||||
background: var(--bg-elevated);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
padding: 1rem 1.1rem;
|
||||
border-left: 3px solid var(--accent);
|
||||
transition: border-color var(--transition);
|
||||
}
|
||||
.chat-message:hover { border-left-color: var(--accent2); }
|
||||
|
||||
.chat-prompt {
|
||||
background: rgba(0,0,0,.25);
|
||||
border-radius: var(--radius-xs);
|
||||
padding: .55rem .85rem;
|
||||
margin-bottom: .6rem;
|
||||
color: var(--text-primary);
|
||||
font-size: .875rem;
|
||||
border-left: 2px solid var(--text-muted);
|
||||
}
|
||||
|
||||
.chat-response {
|
||||
background: linear-gradient(135deg, rgba(124,111,255,.1), rgba(6,214,160,.05));
|
||||
border: 1px solid rgba(124,111,255,.2);
|
||||
border-radius: var(--radius-xs);
|
||||
padding: .65rem .85rem;
|
||||
color: var(--text-primary);
|
||||
font-size: .875rem;
|
||||
white-space: pre-wrap;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.chat-timestamp {
|
||||
color: var(--text-muted);
|
||||
font-size: .72rem;
|
||||
margin-bottom: .5rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: .5rem;
|
||||
}
|
||||
|
||||
/* ── List Group ───────────────────────────────────────────────────────────── */
|
||||
|
||||
.list-group-item {
|
||||
background: var(--bg-surface);
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--border);
|
||||
color: var(--text-primary);
|
||||
padding: .85rem 1.1rem;
|
||||
transition: background var(--transition);
|
||||
}
|
||||
.list-group-item:last-child { border-bottom: none; }
|
||||
.list-group-item-action:hover { background: var(--bg-hover); color: var(--text-primary); }
|
||||
.list-group-flush .list-group-item { border-radius: 0 !important; }
|
||||
|
||||
/* ── Modal ────────────────────────────────────────────────────────────────── */
|
||||
|
||||
.modal-content {
|
||||
background: var(--bg-surface);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius);
|
||||
color: var(--text-primary);
|
||||
box-shadow: 0 24px 80px rgba(0,0,0,.6);
|
||||
}
|
||||
|
||||
.modal-header {
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: var(--bg-elevated);
|
||||
border-radius: var(--radius) var(--radius) 0 0;
|
||||
padding: 1rem 1.25rem;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
border-top: 1px solid var(--border);
|
||||
background: var(--bg-elevated);
|
||||
border-radius: 0 0 var(--radius) var(--radius);
|
||||
}
|
||||
|
||||
.modal-backdrop { backdrop-filter: blur(4px); }
|
||||
.btn-close { filter: invert(1) opacity(.6); }
|
||||
|
||||
/* ── Footer ───────────────────────────────────────────────────────────────── */
|
||||
|
||||
footer {
|
||||
margin-top: 60px;
|
||||
padding: 18px 0;
|
||||
background: var(--bg-surface);
|
||||
border-top: 1px solid var(--border);
|
||||
text-align: center;
|
||||
color: var(--text-muted);
|
||||
font-size: .76rem;
|
||||
}
|
||||
|
||||
/* ── File Items ───────────────────────────────────────────────────────────── */
|
||||
|
||||
.file-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: .7rem;
|
||||
padding: .7rem .9rem;
|
||||
border-radius: var(--radius-sm);
|
||||
border: 1px solid var(--border);
|
||||
background: var(--bg-elevated);
|
||||
transition: background var(--transition), border-color var(--transition), transform var(--transition);
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
.file-item:last-child { margin-bottom: 0; }
|
||||
.file-item:hover { background: var(--bg-hover); border-color: rgba(124,111,255,.25); transform: translateX(2px); }
|
||||
.file-icon { font-size: 1.1rem; flex-shrink: 0; }
|
||||
.file-name { flex: 1; font-size: .875rem; color: var(--text-primary); font-weight: 500; word-break: break-all; }
|
||||
.file-meta { font-size: .72rem; color: var(--text-muted); white-space: nowrap; }
|
||||
.file-actions { display: flex; gap: .35rem; flex-shrink: 0; }
|
||||
|
||||
/* ── Status / Email Log ───────────────────────────────────────────────────── */
|
||||
|
||||
.status-replied { color: var(--success); font-weight: 600; }
|
||||
.status-skipped { color: var(--text-muted); }
|
||||
.status-error { color: var(--danger); font-weight: 600; }
|
||||
.badge-agent { font-size: .68rem; }
|
||||
.log-table td { vertical-align: middle; }
|
||||
.response-preview {
|
||||
font-size: .8rem;
|
||||
color: var(--text-secondary);
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
max-height: 70px;
|
||||
overflow: hidden;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 3;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
|
||||
/* ── Typography ───────────────────────────────────────────────────────────── */
|
||||
|
||||
h1, h2, h3, h4 { letter-spacing: -.02em; }
|
||||
h1 { font-size: 1.5rem; font-weight: 700; }
|
||||
h3 { font-size: 1rem; font-weight: 600; color: var(--text-secondary); }
|
||||
|
||||
/* ── Misc Utilities ───────────────────────────────────────────────────────── */
|
||||
|
||||
.text-muted { color: var(--text-muted) !important; }
|
||||
.text-secondary{ color: var(--text-secondary) !important; }
|
||||
.bg-light { background: var(--bg-elevated) !important; color: var(--text-primary) !important; }
|
||||
pre, code { background: var(--bg-elevated); color: #a5f3fc; border-radius: var(--radius-xs); padding: .15em .45em; font-family: 'JetBrains Mono', monospace; font-size: .82em; }
|
||||
pre { padding: 1rem 1.1rem; overflow-x: auto; line-height: 1.6; }
|
||||
hr { border-color: var(--border); opacity: 1; }
|
||||
|
||||
.font-monospace { font-family: 'JetBrains Mono', 'Fira Code', monospace !important; font-size: .83rem !important; }
|
||||
|
||||
/* ── Live streaming indicator ─────────────────────────────────────────────── */
|
||||
|
||||
.streaming-dot {
|
||||
display: inline-block;
|
||||
width: 7px; height: 7px;
|
||||
border-radius: 50%;
|
||||
background: var(--accent2);
|
||||
animation: blink 1s infinite;
|
||||
margin-left: .4rem;
|
||||
vertical-align: middle;
|
||||
}
|
||||
@keyframes blink { 0%,100%{opacity:1;} 50%{opacity:.2;} }
|
||||
|
||||
/* ── Progress / skeleton ──────────────────────────────────────────────────── */
|
||||
|
||||
.skeleton {
|
||||
background: linear-gradient(90deg, var(--bg-elevated) 25%, var(--bg-hover) 50%, var(--bg-elevated) 75%);
|
||||
background-size: 200% 100%;
|
||||
animation: shimmer 1.5s infinite;
|
||||
border-radius: var(--radius-xs);
|
||||
}
|
||||
@keyframes shimmer { 0%{background-position:200% 0;} 100%{background-position:-200% 0;} }
|
||||
|
||||
/* ── Inline editor ────────────────────────────────────────────────────────── */
|
||||
|
||||
.inline-editor {
|
||||
width: 100%;
|
||||
min-height: 320px;
|
||||
background: var(--bg-base);
|
||||
border: 1px solid var(--border-accent);
|
||||
border-radius: var(--radius-sm);
|
||||
color: #a5f3fc;
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: .83rem;
|
||||
padding: 1rem;
|
||||
line-height: 1.6;
|
||||
resize: vertical;
|
||||
transition: border-color var(--transition), box-shadow var(--transition);
|
||||
}
|
||||
.inline-editor:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent);
|
||||
box-shadow: 0 0 0 3px var(--accent-glow);
|
||||
}
|
||||
|
||||
/* ── Hero section ─────────────────────────────────────────────────────────── */
|
||||
|
||||
.hero-section {
|
||||
background: linear-gradient(135deg, rgba(124,111,255,.1), rgba(6,214,160,.05));
|
||||
border: 1px solid rgba(124,111,255,.15);
|
||||
border-radius: var(--radius);
|
||||
padding: 2rem;
|
||||
margin-bottom: 1.75rem;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hero-section::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%; right: -20%;
|
||||
width: 300px; height: 300px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, rgba(124,111,255,.08) 0%, transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* ── Responsive adjustments ───────────────────────────────────────────────── */
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.page-header { padding: 1.25rem 0 1rem; }
|
||||
.hero-section { padding: 1.25rem; }
|
||||
.stat-card .stat-number { font-size: 1.75rem; }
|
||||
.file-actions { flex-direction: column; }
|
||||
}
|
||||
75
templates/agents.html
Normal file
75
templates/agents.html
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Agenten{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header">
|
||||
<h1>Agenten-Verwaltung</h1>
|
||||
<p>System-Prompts bearbeiten und verwalten</p>
|
||||
</div>
|
||||
|
||||
{% if edit_agent %}
|
||||
<div class="card">
|
||||
<div class="card-header bg-warning">
|
||||
<h5 class="mb-0">Prompt bearbeiten: {{ edit_agent }}</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ url_for('agents') }}">
|
||||
<input type="hidden" name="agent_name" value="{{ edit_agent }}">
|
||||
<div class="mb-3">
|
||||
<label for="prompt_content" class="form-label">System-Prompt</label>
|
||||
<textarea class="form-control font-monospace" id="prompt_content" name="prompt_content"
|
||||
rows="22" style="font-size:.82rem;line-height:1.5;">{{ edit_prompt }}</textarea>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">Speichern</button>
|
||||
<a href="{{ url_for('agents') }}" class="btn btn-secondary">Abbrechen</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<div class="card">
|
||||
<div class="card-header bg-dark d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Alle Agenten</h5>
|
||||
<span class="badge bg-secondary">{{ agents_list|length }}</span>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
{% if agents_list %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Agent</th>
|
||||
<th>Prompt-Vorschau</th>
|
||||
<th style="width:120px;">Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for agent in agents_list %}
|
||||
<tr>
|
||||
<td>
|
||||
<strong>{{ agent.name }}</strong>
|
||||
</td>
|
||||
<td>
|
||||
<small style="color:var(--text-muted);">
|
||||
{{ agent.prompt[:160] }}{% if agent.prompt|length > 160 %}…{% endif %}
|
||||
</small>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ url_for('agents', edit=agent.name) }}" class="btn btn-sm btn-outline-primary">Bearbeiten</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-warning m-3">
|
||||
Keine Agenten gefunden. Stelle sicher, dass <code>agents/</code> Unterverzeichnisse mit <code>systemprompt.md</code> enthält.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
102
templates/base.html
Normal file
102
templates/base.html
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de" data-theme="dark">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}Frankenbot{% endblock %} · 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 rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<nav class="navbar navbar-expand-lg">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="/">
|
||||
<span class="brand-icon">⚡</span>
|
||||
Frankenbot
|
||||
</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>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto gap-1">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if request.path == '/' %}active{% endif %}" href="/">
|
||||
<span>⬡</span> Dashboard
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if request.path == '/chat' %}active{% endif %}" href="/chat">
|
||||
<span>💬</span> Chat
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if request.path == '/orchestrator' %}active{% endif %}" href="/orchestrator">
|
||||
<span>🤖</span> Orchestrator
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if request.path == '/tasks' %}active{% endif %}" href="/tasks">
|
||||
<span>✓</span> Tasks
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if request.path == '/files' %}active{% endif %}" href="/files">
|
||||
<span>📂</span> Dateien
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if request.path == '/agents' %}active{% endif %}" href="/agents">
|
||||
<span>⚙</span> Agenten
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if request.path == '/emails' %}active{% endif %}" href="/emails">
|
||||
<span>✉</span> Emails
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if request.path == '/email-log' %}active{% endif %}" href="/email-log">
|
||||
<span>📋</span> Log
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link {% if request.path == '/settings' %}active{% endif %}" href="/settings">
|
||||
<span>⚙</span> Settings
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container py-4">
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
{% for category, message in messages %}
|
||||
<div class="alert alert-{{ category }} alert-dismissible fade show mb-3" role="alert">
|
||||
{{ message }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<div class="container">
|
||||
Frankenbot · Diversity-Ball Wien 2026 ·
|
||||
<span style="color:var(--accent);">Agent Orchestration System</span>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
70
templates/chat.html
Normal file
70
templates/chat.html
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Chat{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header">
|
||||
<h1>Agenten Chat</h1>
|
||||
<p>Direkte Anfrage an einen spezifischen Agenten</p>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-lg-4">
|
||||
<div class="card">
|
||||
<div class="card-header bg-primary">
|
||||
<span>💬 Neue Anfrage</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="/chat">
|
||||
<div class="mb-3">
|
||||
<label for="agent" class="form-label">Agent</label>
|
||||
<select class="form-select" id="agent" name="agent" required>
|
||||
<option value="">— Agent wählen —</option>
|
||||
{% for key, agent in agents.items() %}
|
||||
<option value="{{ key }}">{{ agent.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="prompt" class="form-label">Ihre Anfrage</label>
|
||||
<textarea class="form-control" id="prompt" name="prompt" rows="5"
|
||||
placeholder="Was soll der Agent erledigen?" required></textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100">Absenden</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-8">
|
||||
<div class="card">
|
||||
<div class="card-header bg-secondary d-flex align-items-center justify-content-between">
|
||||
<span>Chat-Verlauf</span>
|
||||
{% if chat_history %}
|
||||
<span class="badge bg-secondary">{{ chat_history|length }}</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-body chat-container" id="chatContainer">
|
||||
{% if chat_history %}
|
||||
{% for chat in chat_history %}
|
||||
<div class="chat-message">
|
||||
<div class="chat-timestamp">
|
||||
{{ chat.timestamp }}
|
||||
<span class="badge bg-primary">{{ chat.agent }}</span>
|
||||
</div>
|
||||
<div class="chat-prompt"><strong>Sie:</strong> {{ chat.prompt }}</div>
|
||||
<div class="chat-response mt-1"><strong>Antwort:</strong> {{ chat.response }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="text-center py-5" style="color:var(--text-muted);">
|
||||
<div style="font-size:2.5rem;margin-bottom:.75rem;">💬</div>
|
||||
<p style="margin:0;">Noch keine Nachrichten.<br>
|
||||
<small>Starten Sie eine Konversation.</small>
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
90
templates/email_log.html
Normal file
90
templates/email_log.html
Normal file
|
|
@ -0,0 +1,90 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Email-Log{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header d-flex align-items-center justify-content-between">
|
||||
<div>
|
||||
<h1>Email-Verarbeitungs-Log</h1>
|
||||
<p>Automatisch verarbeitete Emails und Antworten</p>
|
||||
</div>
|
||||
<div class="d-flex gap-2 align-items-center">
|
||||
<span class="badge bg-secondary" style="font-size:.85rem;">{{ log_entries|length }} Einträge</span>
|
||||
<button class="btn btn-secondary btn-sm" onclick="location.reload()">Aktualisieren</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3" style="font-size:.8rem;color:var(--text-muted);">
|
||||
<span class="status-replied me-3">✓ replied</span> — Auto-Reply versendet |
|
||||
<span class="status-skipped me-3">— skipped</span> — Nicht auf Whitelist |
|
||||
<span class="status-error">✗ error</span> — Fehler beim Versenden
|
||||
</div>
|
||||
|
||||
{% if not log_entries %}
|
||||
<div class="card">
|
||||
<div class="card-body text-center py-5" style="color:var(--text-muted);">
|
||||
<p style="font-size:2rem;">📭</p>
|
||||
<p>Noch keine Emails verarbeitet.<br><small>Der Poller prüft alle 2 Minuten den Posteingang.</small></p>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="card">
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover log-table mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Zeitstempel</th>
|
||||
<th>Von</th>
|
||||
<th>Betreff</th>
|
||||
<th>Agent</th>
|
||||
<th>Status</th>
|
||||
<th>Antwort-Vorschau</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for entry in log_entries %}
|
||||
<tr>
|
||||
<td style="white-space:nowrap;"><small style="color:var(--text-muted);">{{ entry.timestamp }}</small></td>
|
||||
<td><small>{{ entry.from }}</small></td>
|
||||
<td>{{ entry.subject }}</td>
|
||||
<td>
|
||||
{% if entry.agent %}
|
||||
<span class="badge bg-primary badge-agent">{{ entry.agent }}</span>
|
||||
{% else %}
|
||||
<span style="color:var(--text-muted);">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if entry.status == 'replied' or entry.status == 'completed' %}
|
||||
<span class="status-replied">✓ replied</span>
|
||||
{% elif entry.status == 'skipped' %}
|
||||
<span class="status-skipped">— skipped</span>
|
||||
{% elif entry.status == 'error' %}
|
||||
<span class="status-error">✗ error</span>
|
||||
{% elif entry.status == 'queued' %}
|
||||
<span style="color:var(--warning);">⏳ queued</span>
|
||||
{% else %}
|
||||
<span style="color:var(--text-muted);">{{ entry.status }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if entry.response_preview %}
|
||||
<div class="response-preview">{{ entry.response_preview }}</div>
|
||||
{% else %}
|
||||
<small style="color:var(--text-muted);">—</small>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="mt-3" style="font-size:.78rem;color:var(--text-muted);">
|
||||
Whitelist: <strong>eric.fischer@signtime.media</strong>, <strong>p.dyderski@live.at</strong>,
|
||||
<strong>georg.tschare@gmail.com</strong>, <strong>*@diversityball.at</strong> · Max. 50 Einträge
|
||||
</div>
|
||||
{% endblock %}
|
||||
123
templates/emails.html
Normal file
123
templates/emails.html
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Emails{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header">
|
||||
<h1>Email-Verwaltung</h1>
|
||||
<p>Posteingang und Email-Versand</p>
|
||||
</div>
|
||||
|
||||
{% if not email_config_valid %}
|
||||
<div class="alert alert-warning">
|
||||
<strong>Konfiguration erforderlich</strong><br>
|
||||
<small>Setze <code>IMAP_SERVER</code>, <code>SMTP_SERVER</code>, <code>EMAIL_ADDRESS</code>, <code>EMAIL_PASSWORD</code> in der <code>.env</code>-Datei.</small>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-success">
|
||||
<strong>Verbunden</strong> · {{ current_email }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="row g-4">
|
||||
<!-- Neue Email -->
|
||||
<div class="col-lg-4">
|
||||
<div class="card">
|
||||
<div class="card-header bg-dark">
|
||||
<h5 class="mb-0">Neue Email</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="/emails">
|
||||
<input type="hidden" name="action" value="send">
|
||||
<div class="mb-3">
|
||||
<label for="to_address" class="form-label">An</label>
|
||||
<input type="email" class="form-control" id="to_address" name="to_address" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="subject" class="form-label">Betreff</label>
|
||||
<input type="text" class="form-control" id="subject" name="subject" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="body" class="form-label">Nachricht</label>
|
||||
<textarea class="form-control" id="body" name="body" rows="8" required></textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100">Versenden</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Posteingang -->
|
||||
<div class="col-lg-8">
|
||||
<div class="card">
|
||||
<div class="card-header bg-dark d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Posteingang</h5>
|
||||
<span class="badge bg-secondary">{{ emails|length }} Emails</span>
|
||||
</div>
|
||||
<div class="card-body p-0" style="max-height:600px;overflow-y:auto;">
|
||||
{% if email_config_valid and emails %}
|
||||
{% if emails[0].error is defined and emails[0].error %}
|
||||
<div class="alert alert-danger m-3">{{ emails[0].error }}</div>
|
||||
{% else %}
|
||||
<ul class="list-group list-group-flush">
|
||||
{% for mail in emails %}
|
||||
<li class="list-group-item list-group-item-action"
|
||||
style="cursor:pointer;" onclick="viewEmail('{{ mail.id }}', '{{ mail.subject|e }}', '{{ mail.from|e }}')">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<strong style="font-size:.875rem;">{{ mail.subject }}</strong>
|
||||
<small style="color:var(--text-muted);white-space:nowrap;margin-left:.5rem;">{{ mail.date[:10] }}</small>
|
||||
</div>
|
||||
<div style="font-size:.8rem;color:var(--text-muted);">{{ mail.from }}</div>
|
||||
<div style="font-size:.78rem;color:var(--text-muted);margin-top:.2rem;">{{ mail.preview }}</div>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% elif not email_config_valid %}
|
||||
<div class="text-center py-5" style="color:var(--text-muted);">
|
||||
<p>Email-Konfiguration erforderlich</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-center py-5" style="color:var(--text-muted);">
|
||||
<p>Keine Emails vorhanden</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Email Modal -->
|
||||
<div class="modal fade" id="emailModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-lg">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="emailSubject"></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p style="color:var(--text-muted);font-size:.85rem;"><strong>Von:</strong> <span id="emailFrom"></span></p>
|
||||
<hr>
|
||||
<pre id="emailBody" style="background:var(--bg-elevated);color:var(--text-primary);
|
||||
border-radius:var(--radius-sm);padding:1rem;white-space:pre-wrap;
|
||||
max-height:400px;overflow-y:auto;font-size:.85rem;">Wird geladen…</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
function viewEmail(emailId, subject, from) {
|
||||
document.getElementById('emailSubject').textContent = subject;
|
||||
document.getElementById('emailFrom').textContent = from;
|
||||
document.getElementById('emailBody').textContent = 'Wird geladen…';
|
||||
const modal = new bootstrap.Modal(document.getElementById('emailModal'));
|
||||
modal.show();
|
||||
fetch('/emails/' + emailId)
|
||||
.then(r => r.json())
|
||||
.then(d => { document.getElementById('emailBody').textContent = d.content; })
|
||||
.catch(e => { document.getElementById('emailBody').textContent = 'Fehler: ' + e.message; });
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
262
templates/files.html
Normal file
262
templates/files.html
Normal file
|
|
@ -0,0 +1,262 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Dateien{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header">
|
||||
<h1>Dateiverwaltung</h1>
|
||||
<p>Uploads, Email-Vorlagen und Projektdokumente</p>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<!-- Upload sidebar -->
|
||||
<div class="col-lg-4">
|
||||
<div class="card">
|
||||
<div class="card-header bg-info">
|
||||
<span>📤 Datei hochladen</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="/files" enctype="multipart/form-data">
|
||||
<div class="mb-3">
|
||||
<label for="file" class="form-label">Datei auswählen</label>
|
||||
<input type="file" class="form-control" id="file" name="file" required>
|
||||
</div>
|
||||
<p style="color:var(--text-muted);font-size:.76rem;margin-bottom:.75rem;">Max. 16 MB</p>
|
||||
<button type="submit" class="btn btn-info w-100">Hochladen</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Quick stats -->
|
||||
<div class="card mt-3">
|
||||
<div class="card-body p-0">
|
||||
<ul class="list-group list-group-flush">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center" style="padding:.7rem 1rem;">
|
||||
<span style="font-size:.85rem;color:var(--text-secondary);">📁 Uploads</span>
|
||||
<span class="badge bg-secondary">{{ files|length }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center" style="padding:.7rem 1rem;">
|
||||
<span style="font-size:.85rem;color:var(--text-secondary);">✉ Email-Vorlagen</span>
|
||||
<span class="badge bg-secondary">{{ email_files|length }}</span>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center" style="padding:.7rem 1rem;">
|
||||
<span style="font-size:.85rem;color:var(--text-secondary);">📄 Projektdokumente</span>
|
||||
<span class="badge bg-secondary">{{ project_files|length }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- File lists -->
|
||||
<div class="col-lg-8">
|
||||
|
||||
<!-- Uploads -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header bg-primary d-flex justify-content-between align-items-center">
|
||||
<span>📁 Hochgeladene Dateien</span>
|
||||
<span class="badge bg-secondary">{{ files|length }}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if files %}
|
||||
{% for file in files %}
|
||||
<div class="file-item">
|
||||
<span class="file-icon">{{ '📄' if file.name.endswith('.txt') else '📝' if file.name.endswith('.md') else '📋' if file.name.endswith('.docx') else '📁' }}</span>
|
||||
<div style="flex:1;min-width:0;">
|
||||
<div class="file-name">{{ file.name }}</div>
|
||||
<div class="file-meta">{{ (file.size / 1024)|round(1) }} KB · {{ file.modified }}</div>
|
||||
</div>
|
||||
<div class="file-actions">
|
||||
<a href="/files/download/{{ file.name }}" class="btn btn-sm btn-secondary" title="Herunterladen">↓</a>
|
||||
<a href="/files/delete/{{ file.name }}" class="btn btn-sm btn-danger"
|
||||
onclick="return confirm('Datei löschen?')" title="Löschen">✕</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="text-center py-4" style="color:var(--text-muted);">
|
||||
<div style="font-size:2rem;margin-bottom:.5rem;">📂</div>
|
||||
<p style="margin:0;font-size:.875rem;">Noch keine Dateien hochgeladen.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Email-Vorlagen -->
|
||||
<div class="card mb-3">
|
||||
<div class="card-header bg-dark d-flex justify-content-between align-items-center">
|
||||
<span>✉ Email-Vorlagen <small style="font-weight:400;font-size:.72rem;color:var(--text-muted);margin-left:.4rem;">emails/</small></span>
|
||||
<span class="badge bg-secondary">{{ email_files|length }}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if email_files %}
|
||||
{% for file in email_files %}
|
||||
<div class="file-item" id="file-row-{{ loop.index }}">
|
||||
<span class="file-icon">✉</span>
|
||||
<div style="flex:1;min-width:0;">
|
||||
<div class="file-name">{{ file.name }}</div>
|
||||
<div class="file-meta">{{ (file.size / 1024)|round(1) }} KB · {{ file.modified }}</div>
|
||||
</div>
|
||||
<div class="file-actions">
|
||||
<button class="btn btn-sm btn-secondary"
|
||||
onclick="viewEmailFile(event, '{{ file.name }}')" title="Anzeigen">👁</button>
|
||||
<button class="btn btn-sm btn-primary"
|
||||
onclick="editEmailFile('{{ file.name }}')" title="Bearbeiten">✎</button>
|
||||
<a href="/files/email/delete/{{ file.name }}" class="btn btn-sm btn-danger"
|
||||
onclick="return confirm('Email-Vorlage löschen?')" title="Löschen">✕</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="text-center py-4" style="color:var(--text-muted);">
|
||||
<div style="font-size:2rem;margin-bottom:.5rem;">✉</div>
|
||||
<p style="margin:0;font-size:.875rem;">Keine Email-Vorlagen gefunden.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Projektdokumente -->
|
||||
<div class="card">
|
||||
<div class="card-header bg-secondary d-flex justify-content-between align-items-center">
|
||||
<span>📄 Projektdokumente <small style="font-weight:400;font-size:.72rem;color:var(--text-muted);margin-left:.4rem;">Arbeitsverzeichnis</small></span>
|
||||
<span class="badge bg-secondary">{{ project_files|length }}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if project_files %}
|
||||
{% for file in project_files %}
|
||||
<div class="file-item">
|
||||
<span class="file-icon">{{ '📋' if file.name.endswith('.docx') else '📝' if file.name.endswith('.md') else '📄' }}</span>
|
||||
<div style="flex:1;min-width:0;">
|
||||
<div class="file-name">{{ file.name }}</div>
|
||||
<div class="file-meta">{{ (file.size / 1024)|round(1) }} KB · {{ file.modified }}</div>
|
||||
</div>
|
||||
<div class="file-actions">
|
||||
<button class="btn btn-sm btn-secondary"
|
||||
onclick="viewProjectFile(event, '{{ file.name }}')" title="Anzeigen">👁</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="text-center py-4" style="color:var(--text-muted);">
|
||||
<p style="margin:0;font-size:.875rem;color:var(--text-muted);">Keine Projektdokumente gefunden.</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- File Viewer Modal -->
|
||||
<div class="modal fade" id="fileModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="fileModalTitle">Datei</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<pre id="fileModalContent" style="background:var(--bg-base);color:#a5f3fc;
|
||||
border-radius:var(--radius-sm);padding:1.1rem;white-space:pre-wrap;
|
||||
word-break:break-word;max-height:70vh;overflow-y:auto;font-size:.83rem;
|
||||
border:1px solid var(--border);font-family:'JetBrains Mono',monospace;">Wird geladen…</pre>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Schließen</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Email Editor Modal -->
|
||||
<div class="modal fade" id="editModal" tabindex="-1">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title">✎ Bearbeiten: <span id="editModalFilename"></span></h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<textarea id="editModalContent" class="inline-editor" spellcheck="false"></textarea>
|
||||
<div id="editSaveStatus" style="font-size:.8rem;color:var(--text-muted);margin-top:.5rem;height:1.2em;"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
|
||||
<button type="button" class="btn btn-primary" onclick="saveEmailFile()">💾 Speichern</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
let currentEditFile = null;
|
||||
|
||||
function openFileModal(title, url) {
|
||||
document.getElementById('fileModalTitle').textContent = title;
|
||||
const content = document.getElementById('fileModalContent');
|
||||
content.textContent = 'Wird geladen…';
|
||||
const modal = new bootstrap.Modal(document.getElementById('fileModal'));
|
||||
modal.show();
|
||||
fetch(url)
|
||||
.then(r => r.json())
|
||||
.then(d => { content.textContent = d.content || d.error || '(leer)'; })
|
||||
.catch(e => { content.textContent = 'Fehler: ' + e.message; });
|
||||
}
|
||||
|
||||
function viewEmailFile(event, name) {
|
||||
event.preventDefault();
|
||||
openFileModal(name, '/files/email/view/' + encodeURIComponent(name) + '?json=1');
|
||||
}
|
||||
|
||||
function viewProjectFile(event, name) {
|
||||
event.preventDefault();
|
||||
openFileModal(name, '/files/project/view/' + encodeURIComponent(name) + '?json=1');
|
||||
}
|
||||
|
||||
function editEmailFile(name) {
|
||||
currentEditFile = name;
|
||||
document.getElementById('editModalFilename').textContent = name;
|
||||
const textarea = document.getElementById('editModalContent');
|
||||
const status = document.getElementById('editSaveStatus');
|
||||
textarea.value = 'Wird geladen…';
|
||||
status.textContent = '';
|
||||
const modal = new bootstrap.Modal(document.getElementById('editModal'));
|
||||
modal.show();
|
||||
fetch('/files/email/view/' + encodeURIComponent(name) + '?json=1')
|
||||
.then(r => r.json())
|
||||
.then(d => { textarea.value = d.content || d.error || ''; })
|
||||
.catch(e => { textarea.value = 'Fehler: ' + e.message; });
|
||||
}
|
||||
|
||||
function saveEmailFile() {
|
||||
if (!currentEditFile) return;
|
||||
const content = document.getElementById('editModalContent').value;
|
||||
const status = document.getElementById('editSaveStatus');
|
||||
status.style.color = 'var(--text-muted)';
|
||||
status.textContent = 'Speichern…';
|
||||
|
||||
fetch('/files/email/save/' + encodeURIComponent(currentEditFile), {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ content })
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(d => {
|
||||
if (d.ok) {
|
||||
status.style.color = 'var(--success)';
|
||||
status.textContent = '✓ Gespeichert';
|
||||
setTimeout(() => { status.textContent = ''; }, 3000);
|
||||
} else {
|
||||
status.style.color = 'var(--danger)';
|
||||
status.textContent = '✗ Fehler: ' + (d.error || 'Unbekannt');
|
||||
}
|
||||
})
|
||||
.catch(e => {
|
||||
status.style.color = 'var(--danger)';
|
||||
status.textContent = '✗ ' + e.message;
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
126
templates/index.html
Normal file
126
templates/index.html
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Dashboard{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="hero-section">
|
||||
<div class="d-flex align-items-center justify-content-between flex-wrap gap-3">
|
||||
<div>
|
||||
<h1 style="font-size:1.75rem;margin-bottom:.4rem;-webkit-text-fill-color:unset;background:none;color:var(--text-primary);">
|
||||
Diversity-Ball Wien 2026
|
||||
</h1>
|
||||
<p style="color:var(--text-secondary);margin:0;font-size:.9rem;">
|
||||
Agenten-Orchestrierung · Samstag 5. September · Wiener Rathaus
|
||||
</p>
|
||||
</div>
|
||||
<div class="d-flex gap-2 flex-wrap">
|
||||
<a href="/orchestrator" class="btn btn-primary">🤖 Orchestrator</a>
|
||||
<a href="/chat" class="btn btn-secondary">💬 Chat</a>
|
||||
<a href="/files" class="btn btn-secondary">📂 Dateien</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Stats -->
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="stat-card">
|
||||
<div class="stat-number">{{ agents|length }}</div>
|
||||
<div class="stat-label">Agenten</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="stat-card">
|
||||
<div class="stat-number">{{ recent_tasks|length }}</div>
|
||||
<div class="stat-label">Letzte Tasks</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="stat-card">
|
||||
<div class="stat-number">5. Sep</div>
|
||||
<div class="stat-label">Event-Datum</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6 col-md-3">
|
||||
<div class="stat-card">
|
||||
<div class="stat-number">3.500</div>
|
||||
<div class="stat-label">Gäste</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Agents -->
|
||||
<div class="d-flex align-items-center justify-content-between mb-3">
|
||||
<h3 style="margin:0;">Verfügbare Agenten</h3>
|
||||
<a href="/agents" class="btn btn-outline-secondary btn-sm">Alle verwalten</a>
|
||||
</div>
|
||||
|
||||
<div class="row g-3 mb-4">
|
||||
{% for key, agent in agents.items() %}
|
||||
<div class="col-sm-6 col-md-4 col-lg-3">
|
||||
<div class="card agent-card h-100">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center gap-2 mb-2">
|
||||
<span style="width:8px;height:8px;border-radius:50%;background:var(--success);flex-shrink:0;box-shadow:0 0 6px var(--success);"></span>
|
||||
<h5 style="margin:0;font-size:.875rem;font-weight:600;">{{ agent.name }}</h5>
|
||||
</div>
|
||||
<p style="font-size:.775rem;color:var(--text-muted);line-height:1.45;margin-bottom:.75rem;">
|
||||
{{ agent.description[:90] }}{% if agent.description|length > 90 %}…{% endif %}
|
||||
</p>
|
||||
<span class="badge bg-success">aktiv</span>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<a href="/agents?edit={{ key }}" class="btn btn-outline-primary btn-sm w-100">⚙ Bearbeiten</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
<!-- Recent Tasks -->
|
||||
{% if recent_tasks %}
|
||||
<div class="d-flex align-items-center justify-content-between mb-3">
|
||||
<h3 style="margin:0;">Letzte Tasks</h3>
|
||||
<a href="/tasks" class="btn btn-outline-secondary btn-sm">Alle Tasks</a>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body p-0">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Titel</th>
|
||||
<th>Agent</th>
|
||||
<th>Status</th>
|
||||
<th>Erstellt</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for task in recent_tasks %}
|
||||
<tr>
|
||||
<td><span style="color:var(--text-muted);font-size:.8rem;">#{{ task.id }}</span></td>
|
||||
<td style="font-weight:500;">{{ task.title }}</td>
|
||||
<td style="font-size:.8rem;color:var(--text-muted);">{{ task.assigned_agent }}</td>
|
||||
<td>
|
||||
{% if task.status == 'pending' %}
|
||||
<span class="badge bg-warning">Pending</span>
|
||||
{% elif task.status == 'in_progress' %}
|
||||
<span class="badge bg-primary">Läuft</span>
|
||||
{% elif task.status == 'completed' %}
|
||||
<span class="badge bg-success">Fertig</span>
|
||||
{% elif task.status == 'error' %}
|
||||
<span class="badge bg-danger">Fehler</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">{{ task.status }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="color:var(--text-muted);font-size:.78rem;">{{ task.created }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
157
templates/orchestrator.html
Normal file
157
templates/orchestrator.html
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Orchestrator{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header">
|
||||
<h1>Master-Orchestrator</h1>
|
||||
<p>Delegiert automatisch an den passenden Agenten</p>
|
||||
</div>
|
||||
|
||||
{% if knowledge_loaded %}
|
||||
<div class="alert alert-success mb-4">
|
||||
<strong>Wissensdatenbank geladen</strong> · Agenten-Prompts initialisiert
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="row g-4">
|
||||
<!-- Sidebar -->
|
||||
<div class="col-lg-4">
|
||||
<div class="card mb-3">
|
||||
<div class="card-header bg-dark">
|
||||
<h5 class="mb-0">Prompt eingeben</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form id="promptForm" method="POST" action="/orchestrator">
|
||||
<div class="mb-3">
|
||||
<label for="prompt" class="form-label">Ihre Anfrage</label>
|
||||
<textarea class="form-control" id="prompt" name="prompt" rows="5"
|
||||
placeholder="Was soll erledigt werden?" required></textarea>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary w-100 mb-2" id="streamBtn"
|
||||
onclick="sendPromptWithStream()">Live-Antwort anfordern</button>
|
||||
<button type="submit" class="btn btn-secondary w-100">Klassisch senden</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header bg-secondary">
|
||||
<h6 class="mb-0">Aktive Agenten</h6>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
<ul class="list-group list-group-flush">
|
||||
{% for key, agent in agents.items() %}
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center">
|
||||
<span style="font-size:.85rem;">{{ agent.name }}</span>
|
||||
<span class="badge bg-success" style="font-size:.65rem;">aktiv</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Chat -->
|
||||
<div class="col-lg-8">
|
||||
<div class="card">
|
||||
<div class="card-header bg-dark d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Orchestrator-Chat</h5>
|
||||
<small style="color:var(--text-muted);">Automatische Agenten-Delegation</small>
|
||||
</div>
|
||||
<div class="card-body chat-container" id="chatContainer">
|
||||
{% if chat_history %}
|
||||
{% for chat in chat_history %}
|
||||
<div class="chat-message">
|
||||
<div class="chat-timestamp">
|
||||
{{ chat.timestamp }} ·
|
||||
<span class="badge bg-primary" style="font-size:.65rem;">{{ chat.agent }}</span>
|
||||
</div>
|
||||
<div class="chat-prompt mt-1"><strong>Sie:</strong> {{ chat.user_prompt }}</div>
|
||||
<div class="chat-response mt-1"><strong>Orchestrator:</strong> {{ chat.response }}</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
<div class="text-center py-5" style="color:var(--text-muted);" id="emptyState">
|
||||
<p style="font-size:2rem;margin-bottom:.5rem;">🤖</p>
|
||||
<p>Noch keine Anfragen.<br>Der Orchestrator delegiert automatisch an den passenden Agenten.</p>
|
||||
<hr>
|
||||
<p style="font-size:.8rem;">
|
||||
✓ Immer zuerst delegieren · ✓ Neuen Agenten erstellen falls nötig · ✓ Niemals selbst ausführen
|
||||
</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
function sendPromptWithStream() {
|
||||
const prompt = document.getElementById('prompt').value.trim();
|
||||
const streamBtn = document.getElementById('streamBtn');
|
||||
if (!prompt) { alert('Bitte Anfrage eingeben!'); return; }
|
||||
|
||||
streamBtn.disabled = true;
|
||||
streamBtn.textContent = '⏳ Agent arbeitet…';
|
||||
|
||||
const chatContainer = document.getElementById('chatContainer');
|
||||
const emptyState = document.getElementById('emptyState');
|
||||
if (emptyState) emptyState.remove();
|
||||
|
||||
const msgDiv = document.createElement('div');
|
||||
msgDiv.className = 'chat-message';
|
||||
msgDiv.innerHTML = `
|
||||
<div class="chat-timestamp">${new Date().toLocaleTimeString()} · <span class="badge bg-primary" style="font-size:.65rem;" id="agentBadge">wird ausgewählt…</span></div>
|
||||
<div class="chat-prompt mt-1"><strong>Sie:</strong> ${prompt}</div>
|
||||
<div class="chat-response mt-1" id="responseDiv"><strong>Orchestrator:</strong> <span id="responseText">⏳ Agent arbeitet…</span></div>
|
||||
`;
|
||||
chatContainer.insertBefore(msgDiv, chatContainer.firstChild);
|
||||
|
||||
fetch('/api/agent-stream', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ prompt })
|
||||
}).then(response => {
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let buffer = '';
|
||||
|
||||
function read() {
|
||||
reader.read().then(({ done, value }) => {
|
||||
if (done) {
|
||||
streamBtn.disabled = false;
|
||||
streamBtn.textContent = 'Live-Antwort anfordern';
|
||||
return;
|
||||
}
|
||||
buffer += decoder.decode(value, { stream: true });
|
||||
const lines = buffer.split('\n');
|
||||
buffer = lines.pop();
|
||||
for (const line of lines) {
|
||||
if (!line.startsWith('data: ')) continue;
|
||||
try {
|
||||
const event = JSON.parse(line.slice(6));
|
||||
if (event.type === 'agent_selected') {
|
||||
document.getElementById('agentBadge').textContent = event.agent;
|
||||
} else if (event.type === 'response_chunk') {
|
||||
const t = document.getElementById('responseText');
|
||||
if (t.textContent.startsWith('⏳')) t.textContent = '';
|
||||
t.textContent += event.text;
|
||||
} else if (event.type === 'error') {
|
||||
document.getElementById('responseText').textContent = '❌ ' + event.message;
|
||||
}
|
||||
} catch(e) {}
|
||||
}
|
||||
read();
|
||||
});
|
||||
}
|
||||
read();
|
||||
}).catch(err => {
|
||||
document.getElementById('responseText').textContent = '❌ ' + err.message;
|
||||
streamBtn.disabled = false;
|
||||
streamBtn.textContent = 'Live-Antwort anfordern';
|
||||
});
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
119
templates/settings.html
Normal file
119
templates/settings.html
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Einstellungen{% endblock %}
|
||||
|
||||
{% 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>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
|
||||
<!-- Poller-Einstellungen -->
|
||||
<div class="col-lg-6">
|
||||
<div class="card h-100">
|
||||
<div class="card-header">
|
||||
<span class="me-2">⏱</span> Email-Poller
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST">
|
||||
<div class="mb-4">
|
||||
<label class="form-label fw-semibold">Poll-Intervall <span class="text-muted fw-normal">(Sekunden)</span></label>
|
||||
<input type="number" class="form-control" name="poll_interval"
|
||||
value="{{ poller_settings.poll_interval }}" min="10" max="3600" required>
|
||||
<div class="form-text">Wie oft der Poller den IMAP-Eingang prüft. Aktuell: <strong>{{ poller_settings.poll_interval }}s</strong> ({{ (poller_settings.poll_interval / 60) | round(1) }} min)</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<label class="form-label fw-semibold">Failsafe-Fenster <span class="text-muted fw-normal">(Sekunden)</span></label>
|
||||
<input type="number" class="form-control" name="failsafe_window"
|
||||
value="{{ poller_settings.failsafe_window }}" min="30" max="86400" required>
|
||||
<div class="form-text">
|
||||
Wie lange ein Task laufen darf bevor er als hängend gilt und erneut verarbeitet wird.
|
||||
Aktuell: <strong>{{ poller_settings.failsafe_window }}s</strong> ({{ (poller_settings.failsafe_window / 60) | round(1) }} min).
|
||||
<span class="text-warning">Muss größer als das Poll-Intervall sein.</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">Speichern</button>
|
||||
<a href="/settings" class="btn btn-outline-secondary">Zurücksetzen</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Journal-Status -->
|
||||
<div class="col-lg-6">
|
||||
<div class="card h-100">
|
||||
<div class="card-header">
|
||||
<span class="me-2">📋</span> Email-Journal Status
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if journal_stats %}
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Status</th>
|
||||
<th class="text-end">Anzahl</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for status, count in journal_stats.items() %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if status == 'completed' %}
|
||||
<span class="badge" style="background:var(--success)">✓ {{ status }}</span>
|
||||
{% elif status == 'queued' %}
|
||||
<span class="badge" style="background:var(--warning);color:#000">⏳ {{ status }}</span>
|
||||
{% elif status == 'skipped' %}
|
||||
<span class="badge" style="background:var(--text-muted)">— {{ status }}</span>
|
||||
{% elif status == 'error' %}
|
||||
<span class="badge" style="background:var(--danger)">✗ {{ status }}</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">{{ status }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="text-end fw-semibold">{{ count }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="text-muted">Noch keine Einträge im Journal.</p>
|
||||
{% endif %}
|
||||
|
||||
<hr class="my-3" style="border-color:var(--border)">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<small class="text-muted">Journal-Datenbank: <code>email_journal.db</code></small>
|
||||
<form method="POST" action="/settings/journal-clear"
|
||||
onsubmit="return confirm('Alle abgeschlossenen Journal-Einträge löschen?')">
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger">Abgeschlossene löschen</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Whitelist -->
|
||||
<div class="col-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<span class="me-2">🔒</span> Email-Whitelist
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted mb-2">Nur Emails von diesen Absendern werden verarbeitet (aktuell hardcoded in <code>app.py</code>):</p>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
{% set whitelist = ['eric.fischer@signtime.media', 'p.dyderski@live.at', 'georg.tschare@gmail.com', 'georg.tschare@signtime.media'] %}
|
||||
{% for addr in whitelist %}
|
||||
<code class="px-2 py-1 rounded" style="background:var(--bg-elevated);color:var(--accent-light)">{{ addr }}</code>
|
||||
{% endfor %}
|
||||
<code class="px-2 py-1 rounded" style="background:var(--bg-elevated);color:var(--accent2)">*@diversityball.at</code>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% endblock %}
|
||||
118
templates/tasks.html
Normal file
118
templates/tasks.html
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
{% extends "base.html" %}
|
||||
{% block title %}Tasks{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header">
|
||||
<h1>Task-Verwaltung</h1>
|
||||
<p>Manuelle und automatische Email-Tasks</p>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-lg-4">
|
||||
<div class="card">
|
||||
<div class="card-header bg-success">
|
||||
<h5 class="mb-0">Neuen Task erstellen</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="/tasks">
|
||||
<div class="mb-3">
|
||||
<label for="title" class="form-label">Titel</label>
|
||||
<input type="text" class="form-control" id="title" name="title" placeholder="Task-Titel" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label">Beschreibung</label>
|
||||
<textarea class="form-control" id="description" name="description" rows="3"
|
||||
placeholder="Optionale Beschreibung"></textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="assigned_agent" class="form-label">Agent (optional)</label>
|
||||
<select class="form-select" id="assigned_agent" name="assigned_agent">
|
||||
<option value="">— Agent wählen —</option>
|
||||
{% for key, agent in agents.items() %}
|
||||
<option value="{{ key }}">{{ agent.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-success w-100">Task erstellen</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-8">
|
||||
<div class="card">
|
||||
<div class="card-header bg-primary d-flex justify-content-between align-items-center">
|
||||
<h5 class="mb-0">Alle Tasks</h5>
|
||||
<span class="badge bg-secondary">{{ tasks|length }}</span>
|
||||
</div>
|
||||
<div class="card-body p-0">
|
||||
{% if tasks %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Titel</th>
|
||||
<th>Agent</th>
|
||||
<th>Status</th>
|
||||
<th>Erstellt</th>
|
||||
<th>Aktion</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for task in tasks %}
|
||||
<tr>
|
||||
<td style="color:var(--text-muted);">{{ task.id }}</td>
|
||||
<td>
|
||||
<strong>{{ task.title }}</strong>
|
||||
{% if task.type == 'email' %}
|
||||
<span class="badge bg-info ms-1" title="Von: {{ task.reply_to }}">Email</span>
|
||||
{% endif %}
|
||||
{% if task.description %}
|
||||
<div style="font-size:.75rem;color:var(--text-muted);">
|
||||
{{ task.description[:60] }}{% if task.description|length > 60 %}…{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="font-size:.8rem;">{{ task.assigned_agent }}</td>
|
||||
<td>
|
||||
{% if task.status == 'pending' %}
|
||||
<span class="badge bg-warning">Pending</span>
|
||||
{% elif task.status == 'in_progress' %}
|
||||
<span class="badge bg-primary">In Progress</span>
|
||||
{% elif task.status == 'completed' %}
|
||||
<span class="badge bg-success">Done</span>
|
||||
{% elif task.status == 'error' %}
|
||||
<span class="badge bg-danger">Fehler</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">{{ task.status }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="color:var(--text-muted);font-size:.75rem;">{{ task.created }}</td>
|
||||
<td>
|
||||
{% if task.type == 'email' %}
|
||||
<span style="color:var(--text-muted);font-size:.75rem;">Auto</span>
|
||||
{% elif task.status == 'pending' %}
|
||||
<a href="/tasks/update/{{ task.id }}/in_progress" class="btn btn-sm btn-primary">Start</a>
|
||||
{% elif task.status == 'in_progress' %}
|
||||
<a href="/tasks/update/{{ task.id }}/completed" class="btn btn-sm btn-success">Fertig</a>
|
||||
{% else %}
|
||||
<span style="color:var(--text-muted);">—</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="text-center py-5" style="color:var(--text-muted);">
|
||||
<p style="font-size:2rem;">📋</p>
|
||||
<p>Noch keine Tasks. Erstellen Sie den ersten Task!</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
146
test_features.py
Normal file
146
test_features.py
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test Script für neue Features
|
||||
- Streaming UI (SSE)
|
||||
- Email Integration (IMAP/SMTP)
|
||||
"""
|
||||
|
||||
import json
|
||||
import sys
|
||||
|
||||
def test_imports():
|
||||
"""Teste dass alle notwendigen Importe funktionieren"""
|
||||
print("✓ Testing Imports...")
|
||||
try:
|
||||
import imaplib
|
||||
import smtplib
|
||||
from email.mime.text import MIMEText
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from flask import Flask, Response
|
||||
print(" ✓ All imports successful")
|
||||
return True
|
||||
except ImportError as e:
|
||||
print(f" ✗ Import Error: {e}")
|
||||
return False
|
||||
|
||||
def test_app_syntax():
|
||||
"""Teste dass app.py syntaktisch korrekt ist"""
|
||||
print("\n✓ Testing app.py syntax...")
|
||||
try:
|
||||
import app
|
||||
print(" ✓ app.py loads successfully")
|
||||
|
||||
# Check if new routes exist
|
||||
routes = [str(rule) for rule in app.app.url_map.iter_rules()]
|
||||
|
||||
if '/api/agent-stream' in routes:
|
||||
print(" ✓ /api/agent-stream route exists")
|
||||
else:
|
||||
print(" ✗ /api/agent-stream route NOT found")
|
||||
|
||||
if '/emails' in routes:
|
||||
print(" ✓ /emails route exists")
|
||||
else:
|
||||
print(" ✗ /emails route NOT found")
|
||||
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f" ✗ Error: {e}")
|
||||
return False
|
||||
|
||||
def test_email_config():
|
||||
"""Teste Email-Konfiguration"""
|
||||
print("\n✓ Testing Email Configuration...")
|
||||
import os
|
||||
|
||||
config = {
|
||||
'IMAP_SERVER': os.getenv('IMAP_SERVER', 'imap.gmail.com'),
|
||||
'SMTP_SERVER': os.getenv('SMTP_SERVER', 'smtp.gmail.com'),
|
||||
'EMAIL_ADDRESS': os.getenv('EMAIL_ADDRESS', ''),
|
||||
'EMAIL_PASSWORD': os.getenv('EMAIL_PASSWORD', ''),
|
||||
}
|
||||
|
||||
configured = bool(config['EMAIL_ADDRESS'] and config['EMAIL_PASSWORD'])
|
||||
status = "✓ Configured" if configured else "⚠ Not Configured"
|
||||
|
||||
print(f" {status}")
|
||||
print(f" - IMAP: {config['IMAP_SERVER']}")
|
||||
print(f" - SMTP: {config['SMTP_SERVER']}")
|
||||
print(f" - Email: {config['EMAIL_ADDRESS'][:20] if config['EMAIL_ADDRESS'] else 'Not set'}...")
|
||||
|
||||
if not configured:
|
||||
print(" Note: Set environment variables to enable email features")
|
||||
|
||||
return True
|
||||
|
||||
def test_templates():
|
||||
"""Teste dass neue Templates existieren"""
|
||||
print("\n✓ Testing Templates...")
|
||||
import os
|
||||
|
||||
templates = {
|
||||
'emails.html': os.path.exists('templates/emails.html'),
|
||||
'orchestrator.html': os.path.exists('templates/orchestrator.html'),
|
||||
}
|
||||
|
||||
for template, exists in templates.items():
|
||||
status = "✓" if exists else "✗"
|
||||
print(f" {status} {template}")
|
||||
|
||||
return all(templates.values())
|
||||
|
||||
def test_sse_response():
|
||||
"""Teste SSE Response Header"""
|
||||
print("\n✓ Testing SSE Support...")
|
||||
try:
|
||||
from flask import Flask, Response
|
||||
|
||||
app = Flask(__name__)
|
||||
|
||||
@app.route('/test-sse')
|
||||
def test_sse():
|
||||
def generate():
|
||||
yield f"data: {json.dumps({'type': 'test'})}\n\n"
|
||||
return Response(generate(), mimetype='text/event-stream')
|
||||
|
||||
print(" ✓ SSE Response configured")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f" ✗ Error: {e}")
|
||||
return False
|
||||
|
||||
def main():
|
||||
print("=" * 60)
|
||||
print("🧪 Testing New Features")
|
||||
print("=" * 60)
|
||||
|
||||
results = [
|
||||
("Imports", test_imports()),
|
||||
("App Syntax", test_app_syntax()),
|
||||
("Email Config", test_email_config()),
|
||||
("Templates", test_templates()),
|
||||
("SSE Support", test_sse_response()),
|
||||
]
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("📊 Test Summary")
|
||||
print("=" * 60)
|
||||
|
||||
for name, result in results:
|
||||
status = "✓ PASS" if result else "✗ FAIL"
|
||||
print(f"{status}: {name}")
|
||||
|
||||
passed = sum(1 for _, r in results if r)
|
||||
total = len(results)
|
||||
|
||||
print(f"\nTotal: {passed}/{total} passed")
|
||||
|
||||
if passed == total:
|
||||
print("\n✨ All tests passed!")
|
||||
return 0
|
||||
else:
|
||||
print("\n⚠️ Some tests failed")
|
||||
return 1
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(main())
|
||||
Loading…
Add table
Add a link
Reference in a new issue