feat: BotKonzept Frontend erstellt

Neue Dateien:
- index.html: Landing Page mit Registrierung
- dashboard.html: Kunden-Dashboard
- css/style.css: Haupt-Stylesheet (1500+ Zeilen)
- css/dashboard.css: Dashboard-Styles (800+ Zeilen)
- js/main.js: Landing Page JavaScript
- js/dashboard.js: Dashboard JavaScript
- logo/20250119_Logo_Botkozept.svg: Logo

Features:
- Modernes, responsives Design
- Hero-Section mit Chat-Preview Animation
- Feature-Übersicht und Pricing-Tabelle
- Registrierungsformular mit Validierung
- FAQ-Akkordeon
- Dashboard mit PDF-Upload (Drag & Drop)
- Chatbot-Test-Interface
- Embed-Code Generator
- Trial-Status und Upgrade-Banner
- Mobile-optimiert
This commit is contained in:
root
2026-01-28 22:31:54 +01:00
parent 13927f8bbf
commit b3224ed296
8 changed files with 5244 additions and 0 deletions

181
README.md
View File

@@ -0,0 +1,181 @@
# BotKonzept Frontend
Modernes, responsives Frontend für die BotKonzept SaaS-Plattform.
## 📁 Projektstruktur
```
customer-frontend/
├── index.html # Landing Page mit Registrierung
├── dashboard.html # Kunden-Dashboard
├── css/
│ ├── style.css # Haupt-Stylesheet
│ └── dashboard.css # Dashboard-spezifische Styles
├── js/
│ ├── main.js # Landing Page JavaScript
│ └── dashboard.js # Dashboard JavaScript
└── logo/
└── 20250119_Logo_Botkozept.svg
```
## 🚀 Features
### Landing Page (index.html)
- ✅ Modernes, responsives Design
- ✅ Hero-Section mit animiertem Chat-Preview
- ✅ Feature-Übersicht
- ✅ "So funktioniert's" Sektion
- ✅ Pricing-Tabelle mit Rabatt-Timeline
- ✅ Registrierungsformular
- ✅ FAQ-Akkordeon
- ✅ Mobile-optimiert
### Dashboard (dashboard.html)
- ✅ Übersicht mit Statistiken
- ✅ PDF-Upload mit Drag & Drop
- ✅ Dokumenten-Verwaltung
- ✅ Chatbot-Test-Interface
- ✅ Embed-Code Generator
- ✅ Einstellungen
- ✅ Trial-Status & Upgrade-Banner
## 🎨 Design-System
### Farben
```css
--primary: #6366f1; /* Indigo */
--secondary: #0ea5e9; /* Sky Blue */
--accent: #f59e0b; /* Amber */
--success: #10b981; /* Emerald */
--error: #ef4444; /* Red */
```
### Typografie
- Font: Inter (Google Fonts)
- Responsive Schriftgrößen mit `clamp()`
### Komponenten
- Buttons (Primary, Outline, White)
- Cards
- Forms
- Modals
- Notifications
## 🔧 Konfiguration
### API-Endpunkte
In `js/main.js`:
```javascript
const CONFIG = {
WEBHOOK_URL: 'https://n8n.userman.de/webhook/botkonzept-registration',
API_BASE_URL: 'https://api.botkonzept.de',
};
```
### Webhook-Integration
Das Registrierungsformular sendet folgende Daten an den Webhook:
```json
{
"firstName": "Max",
"lastName": "Mustermann",
"email": "max@beispiel.de",
"company": "Muster GmbH",
"website": "https://beispiel.de",
"newsletter": true,
"timestamp": "2025-01-25T10:00:00.000Z",
"source": "website"
}
```
## 📱 Responsive Breakpoints
- Desktop: > 1024px
- Tablet: 768px - 1024px
- Mobile: < 768px
- Small Mobile: < 480px
## 🚀 Deployment
### Lokales Testen
```bash
cd customer-frontend
python3 -m http.server 8000
# Öffnen: http://localhost:8000
```
### Produktion
```bash
# Dateien auf Webserver kopieren
rsync -avz . user@botkonzept.de:/var/www/botkonzept/
```
### NGINX Konfiguration
```nginx
server {
listen 80;
server_name botkonzept.de www.botkonzept.de;
root /var/www/botkonzept;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
# Cache static assets
location ~* \.(css|js|svg|png|jpg|jpeg|gif|ico)$ {
expires 30d;
add_header Cache-Control "public, immutable";
}
}
```
## 🔒 Sicherheit
- HTTPS erforderlich für Produktion
- CORS-Header für API-Calls
- Input-Validierung auf Client-Seite
- XSS-Schutz durch HTML-Escaping
## 📊 Analytics
Google Analytics 4 Integration vorbereitet:
```html
<script async src="https://www.googletagmanager.com/gtag/js?id=GA_MEASUREMENT_ID"></script>
```
Conversion-Tracking:
```javascript
trackConversion('registration_complete', { email: '...' });
```
## 🎯 Customer Journey
1. **Besucher** kommt auf botkonzept.de
2. **Registrierung** über Formular
3. **Instanz-Erstellung** (automatisch via n8n Workflow)
4. **Dashboard-Zugang** per E-Mail
5. **PDF-Upload** in Wissensdatenbank
6. **Embed-Code** für Website kopieren
7. **Trial-Reminder** (Tag 3, 5, 7)
8. **Upgrade** oder Ablauf
## 📝 TODO
- [ ] Stripe/PayPal Integration
- [ ] E-Mail-Verifizierung
- [ ] Passwort-Reset
- [ ] Multi-Language Support
- [ ] Dark Mode
- [ ] PWA Support
## 📄 Lizenz
Proprietär - Alle Rechte vorbehalten
---
**Version:** 1.0.0
**Erstellt:** Januar 2025

1128
css/dashboard.css Normal file

File diff suppressed because it is too large Load Diff

1573
css/style.css Normal file

File diff suppressed because it is too large Load Diff

480
dashboard.html Normal file
View File

@@ -0,0 +1,480 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="robots" content="noindex, nofollow">
<title>Dashboard - BotKonzept</title>
<!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="logo/20250119_Logo_Botkozept.svg">
<!-- Google Fonts -->
<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;800&display=swap" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<!-- Custom CSS -->
<link rel="stylesheet" href="css/style.css">
<link rel="stylesheet" href="css/dashboard.css">
</head>
<body class="dashboard-body">
<!-- Dashboard Navigation -->
<nav class="dashboard-nav">
<div class="nav-brand">
<a href="index.html" class="logo">
<img src="logo/20250119_Logo_Botkozept.svg" alt="BotKonzept Logo">
<span>BotKonzept</span>
</a>
</div>
<div class="nav-user">
<div class="trial-badge" id="trialBadge">
<i class="fas fa-clock"></i>
<span>Trial: <strong id="trialDays">7</strong> Tage übrig</span>
</div>
<div class="user-menu">
<button class="user-btn" id="userMenuBtn">
<div class="user-avatar">
<i class="fas fa-user"></i>
</div>
<span id="userName">Max Mustermann</span>
<i class="fas fa-chevron-down"></i>
</button>
<div class="user-dropdown" id="userDropdown">
<a href="#settings"><i class="fas fa-cog"></i> Einstellungen</a>
<a href="#billing"><i class="fas fa-credit-card"></i> Abrechnung</a>
<a href="#support"><i class="fas fa-life-ring"></i> Support</a>
<hr>
<a href="index.html" class="logout"><i class="fas fa-sign-out-alt"></i> Abmelden</a>
</div>
</div>
</div>
</nav>
<!-- Dashboard Layout -->
<div class="dashboard-layout">
<!-- Sidebar -->
<aside class="dashboard-sidebar">
<nav class="sidebar-nav">
<a href="#overview" class="nav-item active" data-section="overview">
<i class="fas fa-home"></i>
<span>Übersicht</span>
</a>
<a href="#documents" class="nav-item" data-section="documents">
<i class="fas fa-file-pdf"></i>
<span>Dokumente</span>
</a>
<a href="#chatbot" class="nav-item" data-section="chatbot">
<i class="fas fa-robot"></i>
<span>Chatbot</span>
</a>
<a href="#embed" class="nav-item" data-section="embed">
<i class="fas fa-code"></i>
<span>Einbinden</span>
</a>
<a href="#analytics" class="nav-item" data-section="analytics">
<i class="fas fa-chart-line"></i>
<span>Analytics</span>
</a>
<a href="#settings" class="nav-item" data-section="settings">
<i class="fas fa-cog"></i>
<span>Einstellungen</span>
</a>
</nav>
<!-- Upgrade CTA -->
<div class="sidebar-cta">
<div class="cta-icon">
<i class="fas fa-crown"></i>
</div>
<h4>Upgrade auf Starter</h4>
<p>Unbegrenzte Dokumente & mehr Features</p>
<a href="#upgrade" class="btn btn-primary btn-sm btn-block">
<span class="discount-badge">30% Rabatt</span>
Jetzt upgraden
</a>
</div>
</aside>
<!-- Main Content -->
<main class="dashboard-main">
<!-- Overview Section -->
<section id="overview" class="dashboard-section active">
<div class="section-header">
<h1>Willkommen zurück! 👋</h1>
<p>Hier ist eine Übersicht Ihres Chatbots</p>
</div>
<!-- Stats Cards -->
<div class="stats-grid">
<div class="stat-card">
<div class="stat-icon blue">
<i class="fas fa-file-pdf"></i>
</div>
<div class="stat-content">
<span class="stat-value" id="docCount">0</span>
<span class="stat-label">Dokumente</span>
</div>
</div>
<div class="stat-card">
<div class="stat-icon green">
<i class="fas fa-comments"></i>
</div>
<div class="stat-content">
<span class="stat-value" id="messageCount">0</span>
<span class="stat-label">Nachrichten</span>
</div>
</div>
<div class="stat-card">
<div class="stat-icon purple">
<i class="fas fa-users"></i>
</div>
<div class="stat-content">
<span class="stat-value" id="visitorCount">0</span>
<span class="stat-label">Besucher</span>
</div>
</div>
<div class="stat-card">
<div class="stat-icon orange">
<i class="fas fa-check-circle"></i>
</div>
<div class="stat-content">
<span class="stat-value" id="satisfactionRate">-</span>
<span class="stat-label">Zufriedenheit</span>
</div>
</div>
</div>
<!-- Quick Actions -->
<div class="quick-actions">
<h2>Schnellstart</h2>
<div class="actions-grid">
<a href="#documents" class="action-card" data-section="documents">
<div class="action-icon">
<i class="fas fa-upload"></i>
</div>
<h3>PDF hochladen</h3>
<p>Fügen Sie neue Dokumente zu Ihrer Wissensdatenbank hinzu</p>
</a>
<a href="#embed" class="action-card" data-section="embed">
<div class="action-icon">
<i class="fas fa-code"></i>
</div>
<h3>Code kopieren</h3>
<p>Holen Sie sich den Embed-Code für Ihre Website</p>
</a>
<a href="#chatbot" class="action-card" data-section="chatbot">
<div class="action-icon">
<i class="fas fa-comment-dots"></i>
</div>
<h3>Chatbot testen</h3>
<p>Testen Sie Ihren Chatbot live</p>
</a>
</div>
</div>
<!-- Trial Banner -->
<div class="trial-banner" id="trialBanner">
<div class="banner-content">
<div class="banner-icon">
<i class="fas fa-gift"></i>
</div>
<div class="banner-text">
<h3>🎉 Exklusives Angebot: 30% Frühbucher-Rabatt!</h3>
<p>Upgraden Sie jetzt und sparen Sie €176,40 im ersten Jahr. Angebot endet in <strong id="offerCountdown">48:00:00</strong></p>
</div>
<a href="#upgrade" class="btn btn-primary">
Jetzt für €34,30/Monat upgraden
</a>
</div>
</div>
</section>
<!-- Documents Section -->
<section id="documents" class="dashboard-section">
<div class="section-header">
<h1>Dokumente</h1>
<p>Verwalten Sie Ihre Wissensdatenbank</p>
</div>
<!-- Upload Area -->
<div class="upload-area" id="uploadArea">
<div class="upload-content">
<div class="upload-icon">
<i class="fas fa-cloud-upload-alt"></i>
</div>
<h3>PDF-Dateien hochladen</h3>
<p>Ziehen Sie Dateien hierher oder klicken Sie zum Auswählen</p>
<input type="file" id="fileInput" accept=".pdf" multiple hidden>
<button class="btn btn-primary" onclick="document.getElementById('fileInput').click()">
<i class="fas fa-plus"></i> Dateien auswählen
</button>
<p class="upload-hint">Unterstützt: PDF (max. 10MB pro Datei)</p>
</div>
</div>
<!-- Upload Progress -->
<div class="upload-progress" id="uploadProgress" style="display: none;">
<div class="progress-header">
<span>Wird hochgeladen...</span>
<span id="uploadPercent">0%</span>
</div>
<div class="progress-bar">
<div class="progress-fill" id="progressFill"></div>
</div>
</div>
<!-- Documents List -->
<div class="documents-list">
<div class="list-header">
<h2>Hochgeladene Dokumente</h2>
<span class="doc-count" id="totalDocs">0 Dokumente</span>
</div>
<div class="documents-table" id="documentsTable">
<div class="empty-state">
<i class="fas fa-folder-open"></i>
<h3>Noch keine Dokumente</h3>
<p>Laden Sie Ihr erstes PDF hoch, um zu beginnen</p>
</div>
</div>
</div>
</section>
<!-- Chatbot Section -->
<section id="chatbot" class="dashboard-section">
<div class="section-header">
<h1>Chatbot testen</h1>
<p>Testen Sie Ihren Chatbot mit Ihren hochgeladenen Dokumenten</p>
</div>
<div class="chatbot-test-container">
<div class="chat-window">
<div class="chat-header">
<div class="chat-avatar">
<i class="fas fa-robot"></i>
</div>
<div class="chat-info">
<span class="chat-name">Ihr Chatbot</span>
<span class="chat-status"><i class="fas fa-circle"></i> Online</span>
</div>
<button class="btn btn-sm btn-outline" id="clearChat">
<i class="fas fa-trash"></i> Leeren
</button>
</div>
<div class="chat-messages" id="chatMessages">
<div class="message bot">
<p>Hallo! Ich bin Ihr KI-Assistent. Wie kann ich Ihnen helfen?</p>
</div>
</div>
<div class="chat-input-area">
<input type="text" id="chatInput" placeholder="Stellen Sie eine Frage...">
<button class="send-btn" id="sendBtn">
<i class="fas fa-paper-plane"></i>
</button>
</div>
</div>
<div class="chat-tips">
<h3>💡 Tipps</h3>
<ul>
<li>Stellen Sie Fragen zu Ihren hochgeladenen Dokumenten</li>
<li>Der Bot antwortet basierend auf Ihrer Wissensdatenbank</li>
<li>Je mehr Dokumente, desto besser die Antworten</li>
</ul>
</div>
</div>
</section>
<!-- Embed Section -->
<section id="embed" class="dashboard-section">
<div class="section-header">
<h1>Chatbot einbinden</h1>
<p>Integrieren Sie den Chatbot in Ihre Website</p>
</div>
<div class="embed-container">
<!-- Embed Code -->
<div class="embed-card">
<h2><i class="fas fa-code"></i> Embed-Code</h2>
<p>Kopieren Sie diesen Code und fügen Sie ihn vor dem <code>&lt;/body&gt;</code>-Tag Ihrer Website ein:</p>
<div class="code-block">
<pre id="embedCode">&lt;script src="https://botkonzept.de/embed/<span id="customerId">IHRE-ID</span>.js"&gt;&lt;/script&gt;</pre>
<button class="copy-btn" onclick="copyEmbedCode()">
<i class="fas fa-copy"></i> Kopieren
</button>
</div>
<div class="copy-success" id="copySuccess" style="display: none;">
<i class="fas fa-check"></i> Code kopiert!
</div>
</div>
<!-- Webhook URLs -->
<div class="embed-card">
<h2><i class="fas fa-link"></i> API-Endpunkte</h2>
<p>Für fortgeschrittene Integrationen:</p>
<div class="endpoint-list">
<div class="endpoint-item">
<label>Chat-Webhook:</label>
<div class="endpoint-url">
<code id="chatWebhook">https://sb-XXXXX.userman.de/webhook/rag-chat-webhook/chat</code>
<button class="copy-btn-sm" onclick="copyToClipboard('chatWebhook')">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<div class="endpoint-item">
<label>Upload-Formular:</label>
<div class="endpoint-url">
<code id="uploadUrl">https://sb-XXXXX.userman.de/form/rag-upload-form</code>
<button class="copy-btn-sm" onclick="copyToClipboard('uploadUrl')">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
</div>
</div>
<!-- Installation Guide -->
<div class="embed-card">
<h2><i class="fas fa-book"></i> Installationsanleitung</h2>
<div class="install-steps">
<div class="install-step">
<div class="step-num">1</div>
<div class="step-content">
<h4>Code kopieren</h4>
<p>Klicken Sie auf "Kopieren" um den Embed-Code in die Zwischenablage zu kopieren.</p>
</div>
</div>
<div class="install-step">
<div class="step-num">2</div>
<div class="step-content">
<h4>In Website einfügen</h4>
<p>Fügen Sie den Code vor dem schließenden <code>&lt;/body&gt;</code>-Tag ein.</p>
</div>
</div>
<div class="install-step">
<div class="step-num">3</div>
<div class="step-content">
<h4>Fertig!</h4>
<p>Der Chatbot erscheint automatisch unten rechts auf Ihrer Website.</p>
</div>
</div>
</div>
<div class="platform-guides">
<h4>Plattform-spezifische Anleitungen:</h4>
<div class="platform-links">
<a href="#" class="platform-link">
<i class="fab fa-wordpress"></i> WordPress
</a>
<a href="#" class="platform-link">
<i class="fab fa-shopify"></i> Shopify
</a>
<a href="#" class="platform-link">
<i class="fab fa-wix"></i> Wix
</a>
<a href="#" class="platform-link">
<i class="fab fa-squarespace"></i> Squarespace
</a>
</div>
</div>
</div>
</div>
</section>
<!-- Analytics Section -->
<section id="analytics" class="dashboard-section">
<div class="section-header">
<h1>Analytics</h1>
<p>Verstehen Sie, wie Besucher mit Ihrem Chatbot interagieren</p>
</div>
<div class="analytics-placeholder">
<div class="placeholder-icon">
<i class="fas fa-chart-bar"></i>
</div>
<h3>Analytics verfügbar im Starter-Plan</h3>
<p>Upgraden Sie, um detaillierte Statistiken zu sehen:</p>
<ul>
<li><i class="fas fa-check"></i> Häufigste Fragen</li>
<li><i class="fas fa-check"></i> Antwortqualität</li>
<li><i class="fas fa-check"></i> Besucherstatistiken</li>
<li><i class="fas fa-check"></i> Conversion-Tracking</li>
</ul>
<a href="#upgrade" class="btn btn-primary">
<i class="fas fa-crown"></i> Jetzt upgraden
</a>
</div>
</section>
<!-- Settings Section -->
<section id="settings" class="dashboard-section">
<div class="section-header">
<h1>Einstellungen</h1>
<p>Passen Sie Ihren Chatbot an</p>
</div>
<div class="settings-grid">
<!-- Appearance Settings -->
<div class="settings-card">
<h2><i class="fas fa-palette"></i> Erscheinungsbild</h2>
<div class="setting-group">
<label>Primärfarbe</label>
<div class="color-picker">
<input type="color" id="primaryColor" value="#6366f1">
<span>#6366f1</span>
</div>
</div>
<div class="setting-group">
<label>Chatbot-Name</label>
<input type="text" id="botName" value="Assistent" placeholder="z.B. Support-Bot">
</div>
<div class="setting-group">
<label>Begrüßungsnachricht</label>
<textarea id="welcomeMessage" rows="3">Hallo! Wie kann ich Ihnen helfen?</textarea>
</div>
<button class="btn btn-primary" onclick="saveSettings()">
<i class="fas fa-save"></i> Speichern
</button>
</div>
<!-- Account Settings -->
<div class="settings-card">
<h2><i class="fas fa-user"></i> Konto</h2>
<div class="setting-group">
<label>E-Mail</label>
<input type="email" id="accountEmail" value="max@beispiel.de" disabled>
</div>
<div class="setting-group">
<label>Plan</label>
<div class="plan-info">
<span class="plan-badge trial">Trial</span>
<span>Endet am <strong id="trialEndDate">01.02.2025</strong></span>
</div>
</div>
<a href="#upgrade" class="btn btn-outline btn-block">
<i class="fas fa-crown"></i> Plan upgraden
</a>
</div>
</div>
</section>
</main>
</div>
<!-- Scripts -->
<script src="js/dashboard.js"></script>
</body>
</html>

595
index.html Normal file
View File

@@ -0,0 +1,595 @@
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="BotKonzept - Ihr KI-Chatbot für die Website. Einfach einrichten, PDF hochladen, fertig!">
<meta name="keywords" content="KI Chatbot, Website Chatbot, RAG, PDF Chatbot, Kundenservice Automation">
<title>BotKonzept - KI-Chatbot für Ihre Website</title>
<!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="logo/20250119_Logo_Botkozept.svg">
<!-- Google Fonts -->
<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;800&display=swap" rel="stylesheet">
<!-- Font Awesome -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
<!-- Custom CSS -->
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<!-- Navigation -->
<nav class="navbar" id="navbar">
<div class="container">
<a href="#" class="logo">
<img src="logo/20250119_Logo_Botkozept.svg" alt="BotKonzept Logo">
<span>BotKonzept</span>
</a>
<div class="nav-links" id="navLinks">
<a href="#features">Features</a>
<a href="#how-it-works">So funktioniert's</a>
<a href="#pricing">Preise</a>
<a href="#faq">FAQ</a>
<a href="#register" class="btn btn-primary btn-sm">Kostenlos testen</a>
</div>
<button class="mobile-menu-btn" id="mobileMenuBtn" aria-label="Menü öffnen">
<i class="fas fa-bars"></i>
</button>
</div>
</nav>
<!-- Hero Section -->
<section class="hero">
<div class="container">
<div class="hero-content">
<div class="hero-badge">
<i class="fas fa-rocket"></i>
<span>7 Tage kostenlos testen</span>
</div>
<h1>Ihr intelligenter <span class="gradient-text">KI-Chatbot</span> für die Website</h1>
<p class="hero-subtitle">
Laden Sie einfach Ihre PDFs hoch und Ihr Chatbot beantwortet Kundenfragen automatisch.
Keine Programmierung erforderlich in 5 Minuten einsatzbereit.
</p>
<div class="hero-cta">
<a href="#register" class="btn btn-primary btn-lg">
<i class="fas fa-play"></i>
Jetzt kostenlos starten
</a>
<a href="#how-it-works" class="btn btn-outline btn-lg">
<i class="fas fa-info-circle"></i>
Mehr erfahren
</a>
</div>
<div class="hero-stats">
<div class="stat">
<span class="stat-number">5 Min</span>
<span class="stat-label">Setup-Zeit</span>
</div>
<div class="stat">
<span class="stat-number">100%</span>
<span class="stat-label">DSGVO-konform</span>
</div>
<div class="stat">
<span class="stat-number">24/7</span>
<span class="stat-label">Verfügbar</span>
</div>
</div>
</div>
<div class="hero-visual">
<div class="chat-preview">
<div class="chat-header">
<div class="chat-avatar">
<i class="fas fa-robot"></i>
</div>
<div class="chat-info">
<span class="chat-name">BotKonzept Assistent</span>
<span class="chat-status"><i class="fas fa-circle"></i> Online</span>
</div>
</div>
<div class="chat-messages">
<div class="message bot">
<p>Hallo! 👋 Wie kann ich Ihnen heute helfen?</p>
</div>
<div class="message user">
<p>Was sind Ihre Öffnungszeiten?</p>
</div>
<div class="message bot">
<p>Unsere Öffnungszeiten sind Montag bis Freitag von 9:00 bis 18:00 Uhr. Am Wochenende sind wir geschlossen.</p>
</div>
<div class="message user">
<p>Wie kann ich eine Bestellung aufgeben?</p>
</div>
<div class="message bot typing">
<span class="typing-indicator">
<span></span><span></span><span></span>
</span>
</div>
</div>
<div class="chat-input">
<input type="text" placeholder="Nachricht eingeben...">
<button><i class="fas fa-paper-plane"></i></button>
</div>
</div>
</div>
</div>
<div class="hero-bg">
<div class="gradient-orb orb-1"></div>
<div class="gradient-orb orb-2"></div>
<div class="gradient-orb orb-3"></div>
</div>
</section>
<!-- Trusted By Section -->
<section class="trusted-by">
<div class="container">
<p class="trusted-label">Vertraut von innovativen Unternehmen</p>
<div class="trusted-logos">
<div class="logo-placeholder"><i class="fas fa-building"></i> Unternehmen A</div>
<div class="logo-placeholder"><i class="fas fa-building"></i> Unternehmen B</div>
<div class="logo-placeholder"><i class="fas fa-building"></i> Unternehmen C</div>
<div class="logo-placeholder"><i class="fas fa-building"></i> Unternehmen D</div>
</div>
</div>
</section>
<!-- Features Section -->
<section class="features" id="features">
<div class="container">
<div class="section-header">
<span class="section-badge">Features</span>
<h2>Alles was Sie für einen erfolgreichen Chatbot brauchen</h2>
<p>Leistungsstarke Funktionen, die Ihren Kundenservice revolutionieren</p>
</div>
<div class="features-grid">
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-file-pdf"></i>
</div>
<h3>PDF-Upload</h3>
<p>Laden Sie einfach Ihre Dokumente hoch. Der Chatbot lernt automatisch aus Ihren Inhalten.</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-brain"></i>
</div>
<h3>KI-gestützte Antworten</h3>
<p>Modernste KI-Technologie für natürliche und präzise Antworten auf Kundenfragen.</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-code"></i>
</div>
<h3>Einfache Integration</h3>
<p>Ein Zeile Code mehr brauchen Sie nicht. Funktioniert mit jeder Website.</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-shield-alt"></i>
</div>
<h3>DSGVO-konform</h3>
<p>Alle Daten werden in Deutschland gehostet. 100% DSGVO-konform.</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-palette"></i>
</div>
<h3>Anpassbares Design</h3>
<p>Passen Sie Farben und Stil an Ihre Marke an. Ihr Chatbot, Ihr Look.</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-chart-line"></i>
</div>
<h3>Analytics Dashboard</h3>
<p>Verstehen Sie, was Ihre Kunden fragen. Detaillierte Statistiken und Insights.</p>
</div>
</div>
</div>
</section>
<!-- How It Works Section -->
<section class="how-it-works" id="how-it-works">
<div class="container">
<div class="section-header">
<span class="section-badge">So funktioniert's</span>
<h2>In 3 einfachen Schritten zum eigenen KI-Chatbot</h2>
<p>Keine technischen Kenntnisse erforderlich</p>
</div>
<div class="steps">
<div class="step">
<div class="step-number">1</div>
<div class="step-content">
<h3>Registrieren</h3>
<p>Erstellen Sie Ihr kostenloses Konto in weniger als einer Minute. Keine Kreditkarte erforderlich.</p>
</div>
<div class="step-icon">
<i class="fas fa-user-plus"></i>
</div>
</div>
<div class="step-connector"></div>
<div class="step">
<div class="step-number">2</div>
<div class="step-content">
<h3>PDFs hochladen</h3>
<p>Laden Sie Ihre Dokumente hoch FAQs, Produktinfos, Anleitungen. Der Bot lernt automatisch.</p>
</div>
<div class="step-icon">
<i class="fas fa-cloud-upload-alt"></i>
</div>
</div>
<div class="step-connector"></div>
<div class="step">
<div class="step-number">3</div>
<div class="step-content">
<h3>Code einbinden</h3>
<p>Kopieren Sie den Code-Snippet und fügen Sie ihn in Ihre Website ein. Fertig!</p>
</div>
<div class="step-icon">
<i class="fas fa-code"></i>
</div>
</div>
</div>
<div class="code-preview">
<div class="code-header">
<span class="code-dot red"></span>
<span class="code-dot yellow"></span>
<span class="code-dot green"></span>
<span class="code-title">Ihr Embed-Code</span>
</div>
<pre><code>&lt;script src="https://botkonzept.de/embed/IHRE-ID.js"&gt;&lt;/script&gt;</code></pre>
</div>
</div>
</section>
<!-- Pricing Section -->
<section class="pricing" id="pricing">
<div class="container">
<div class="section-header">
<span class="section-badge">Preise</span>
<h2>Transparente Preise, keine versteckten Kosten</h2>
<p>Starten Sie kostenlos und upgraden Sie, wenn Sie bereit sind</p>
</div>
<div class="pricing-grid">
<!-- Trial Plan -->
<div class="pricing-card">
<div class="pricing-header">
<h3>Trial</h3>
<div class="price">
<span class="currency"></span>
<span class="amount">0</span>
<span class="period">/7 Tage</span>
</div>
<p class="pricing-description">Perfekt zum Testen</p>
</div>
<ul class="pricing-features">
<li><i class="fas fa-check"></i> 100 Dokumente</li>
<li><i class="fas fa-check"></i> 1.000 Nachrichten</li>
<li><i class="fas fa-check"></i> 1 Chatbot</li>
<li><i class="fas fa-check"></i> Standard Support</li>
<li class="disabled"><i class="fas fa-times"></i> Custom Branding</li>
<li class="disabled"><i class="fas fa-times"></i> Analytics</li>
</ul>
<a href="#register" class="btn btn-outline btn-block">Kostenlos starten</a>
</div>
<!-- Starter Plan -->
<div class="pricing-card featured">
<div class="pricing-badge">Beliebt</div>
<div class="pricing-header">
<h3>Starter</h3>
<div class="price">
<span class="currency"></span>
<span class="amount">49</span>
<span class="period">/Monat</span>
</div>
<p class="pricing-description">Für kleine Unternehmen</p>
</div>
<ul class="pricing-features">
<li><i class="fas fa-check"></i> Unbegrenzte Dokumente</li>
<li><i class="fas fa-check"></i> 10.000 Nachrichten/Monat</li>
<li><i class="fas fa-check"></i> 1 Chatbot</li>
<li><i class="fas fa-check"></i> Prioritäts-Support</li>
<li><i class="fas fa-check"></i> Custom Branding</li>
<li><i class="fas fa-check"></i> Analytics Dashboard</li>
</ul>
<a href="#register" class="btn btn-primary btn-block">Jetzt starten</a>
<p class="pricing-note">
<i class="fas fa-gift"></i>
<span class="discount-tag">30% Rabatt</span> bei Upgrade innerhalb von 3 Tagen
</p>
</div>
<!-- Business Plan -->
<div class="pricing-card">
<div class="pricing-header">
<h3>Business</h3>
<div class="price">
<span class="currency"></span>
<span class="amount">149</span>
<span class="period">/Monat</span>
</div>
<p class="pricing-description">Für wachsende Teams</p>
</div>
<ul class="pricing-features">
<li><i class="fas fa-check"></i> Unbegrenzte Dokumente</li>
<li><i class="fas fa-check"></i> 50.000 Nachrichten/Monat</li>
<li><i class="fas fa-check"></i> 5 Chatbots</li>
<li><i class="fas fa-check"></i> Dedizierter Support</li>
<li><i class="fas fa-check"></i> API-Zugriff</li>
<li><i class="fas fa-check"></i> SLA-Garantie</li>
</ul>
<a href="#register" class="btn btn-outline btn-block">Kontakt aufnehmen</a>
</div>
</div>
<!-- Discount Timeline -->
<div class="discount-timeline">
<h3><i class="fas fa-clock"></i> Frühbucher-Rabatte</h3>
<div class="timeline">
<div class="timeline-item">
<div class="timeline-marker active"></div>
<div class="timeline-content">
<span class="timeline-day">Tag 1-3</span>
<span class="timeline-discount">30% Rabatt</span>
<span class="timeline-price">€34,30/Monat</span>
</div>
</div>
<div class="timeline-item">
<div class="timeline-marker"></div>
<div class="timeline-content">
<span class="timeline-day">Tag 4-5</span>
<span class="timeline-discount">15% Rabatt</span>
<span class="timeline-price">€41,65/Monat</span>
</div>
</div>
<div class="timeline-item">
<div class="timeline-marker"></div>
<div class="timeline-content">
<span class="timeline-day">Tag 6-7</span>
<span class="timeline-discount">Normalpreis</span>
<span class="timeline-price">€49/Monat</span>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Registration Section -->
<section class="register" id="register">
<div class="container">
<div class="register-wrapper">
<div class="register-info">
<h2>Starten Sie Ihre <span class="gradient-text">7-Tage Trial</span></h2>
<p>Keine Kreditkarte erforderlich. Voller Funktionsumfang. Jederzeit kündbar.</p>
<ul class="register-benefits">
<li><i class="fas fa-check-circle"></i> Sofortiger Zugang</li>
<li><i class="fas fa-check-circle"></i> Keine Zahlungsdaten nötig</li>
<li><i class="fas fa-check-circle"></i> Persönlicher Support</li>
<li><i class="fas fa-check-circle"></i> Alle Features inklusive</li>
</ul>
</div>
<div class="register-form-wrapper">
<form id="registerForm" class="register-form">
<div class="form-row">
<div class="form-group">
<label for="firstName">Vorname *</label>
<input type="text" id="firstName" name="firstName" required placeholder="Max">
</div>
<div class="form-group">
<label for="lastName">Nachname *</label>
<input type="text" id="lastName" name="lastName" required placeholder="Mustermann">
</div>
</div>
<div class="form-group">
<label for="email">E-Mail-Adresse *</label>
<input type="email" id="email" name="email" required placeholder="max@beispiel.de">
</div>
<div class="form-group">
<label for="company">Unternehmen (optional)</label>
<input type="text" id="company" name="company" placeholder="Muster GmbH">
</div>
<div class="form-group">
<label for="website">Website (optional)</label>
<input type="url" id="website" name="website" placeholder="https://www.beispiel.de">
</div>
<div class="form-group checkbox-group">
<input type="checkbox" id="privacy" name="privacy" required>
<label for="privacy">
Ich akzeptiere die <a href="#" target="_blank">Datenschutzerklärung</a> und
<a href="#" target="_blank">AGB</a> *
</label>
</div>
<div class="form-group checkbox-group">
<input type="checkbox" id="newsletter" name="newsletter">
<label for="newsletter">
Ich möchte Updates und Tipps per E-Mail erhalten (optional)
</label>
</div>
<button type="submit" class="btn btn-primary btn-lg btn-block" id="submitBtn">
<span class="btn-text">Kostenlos registrieren</span>
<span class="btn-loading" style="display: none;">
<i class="fas fa-spinner fa-spin"></i> Wird erstellt...
</span>
</button>
</form>
<div id="formSuccess" class="form-success" style="display: none;">
<div class="success-icon">
<i class="fas fa-check-circle"></i>
</div>
<h3>Willkommen bei BotKonzept!</h3>
<p>Ihre Instanz wird gerade erstellt. Sie erhalten in Kürze eine E-Mail mit Ihren Zugangsdaten.</p>
<div class="success-details">
<p><strong>Nächste Schritte:</strong></p>
<ol>
<li>Prüfen Sie Ihren Posteingang</li>
<li>Klicken Sie auf den Aktivierungslink</li>
<li>Laden Sie Ihr erstes PDF hoch</li>
</ol>
</div>
</div>
<div id="formError" class="form-error" style="display: none;">
<div class="error-icon">
<i class="fas fa-exclamation-circle"></i>
</div>
<h3>Ups, etwas ist schiefgelaufen</h3>
<p id="errorMessage">Bitte versuchen Sie es später erneut.</p>
<button class="btn btn-outline" onclick="resetForm()">Erneut versuchen</button>
</div>
</div>
</div>
</div>
</section>
<!-- FAQ Section -->
<section class="faq" id="faq">
<div class="container">
<div class="section-header">
<span class="section-badge">FAQ</span>
<h2>Häufig gestellte Fragen</h2>
<p>Alles was Sie wissen müssen</p>
</div>
<div class="faq-grid">
<div class="faq-item">
<button class="faq-question">
<span>Wie funktioniert der KI-Chatbot?</span>
<i class="fas fa-chevron-down"></i>
</button>
<div class="faq-answer">
<p>Unser Chatbot nutzt modernste KI-Technologie (RAG - Retrieval Augmented Generation).
Sie laden Ihre Dokumente hoch, und der Bot durchsucht diese, um präzise Antworten auf
Kundenfragen zu geben. Die KI versteht den Kontext und formuliert natürliche Antworten.</p>
</div>
</div>
<div class="faq-item">
<button class="faq-question">
<span>Welche Dateiformate werden unterstützt?</span>
<i class="fas fa-chevron-down"></i>
</button>
<div class="faq-answer">
<p>Aktuell unterstützen wir PDF-Dateien. Weitere Formate wie Word, Excel und
Textdateien sind in Planung. Sie können beliebig viele PDFs hochladen
FAQs, Produktkataloge, Anleitungen, etc.</p>
</div>
</div>
<div class="faq-item">
<button class="faq-question">
<span>Ist der Service DSGVO-konform?</span>
<i class="fas fa-chevron-down"></i>
</button>
<div class="faq-answer">
<p>Ja, 100%! Alle Daten werden ausschließlich auf Servern in Deutschland gehostet.
Wir verarbeiten keine Daten außerhalb der EU. Sie erhalten einen
Auftragsverarbeitungsvertrag (AVV) auf Anfrage.</p>
</div>
</div>
<div class="faq-item">
<button class="faq-question">
<span>Kann ich den Chatbot an mein Design anpassen?</span>
<i class="fas fa-chevron-down"></i>
</button>
<div class="faq-answer">
<p>Ja! Im Starter- und Business-Plan können Sie Farben, Logo und Begrüßungstext
anpassen. Der Chatbot fügt sich nahtlos in Ihr Website-Design ein.</p>
</div>
</div>
<div class="faq-item">
<button class="faq-question">
<span>Was passiert nach der Trial-Phase?</span>
<i class="fas fa-chevron-down"></i>
</button>
<div class="faq-answer">
<p>Nach 7 Tagen endet Ihre Trial automatisch. Sie können jederzeit auf einen
bezahlten Plan upgraden. Wenn Sie innerhalb der ersten 3 Tage upgraden,
erhalten Sie 30% Rabatt!</p>
</div>
</div>
<div class="faq-item">
<button class="faq-question">
<span>Wie integriere ich den Chatbot in meine Website?</span>
<i class="fas fa-chevron-down"></i>
</button>
<div class="faq-answer">
<p>Ganz einfach! Sie erhalten einen Code-Snippet, den Sie vor dem &lt;/body&gt;-Tag
Ihrer Website einfügen. Das funktioniert mit WordPress, Shopify, Wix,
und jeder anderen Website.</p>
</div>
</div>
</div>
</div>
</section>
<!-- CTA Section -->
<section class="cta">
<div class="container">
<div class="cta-content">
<h2>Bereit, Ihren Kundenservice zu revolutionieren?</h2>
<p>Starten Sie noch heute Ihre kostenlose 7-Tage-Trial</p>
<a href="#register" class="btn btn-white btn-lg">
<i class="fas fa-rocket"></i>
Jetzt kostenlos starten
</a>
</div>
</div>
</section>
<!-- Footer -->
<footer class="footer">
<div class="container">
<div class="footer-grid">
<div class="footer-brand">
<a href="#" class="logo">
<img src="logo/20250119_Logo_Botkozept.svg" alt="BotKonzept Logo">
<span>BotKonzept</span>
</a>
<p>Ihr intelligenter KI-Chatbot für besseren Kundenservice.</p>
<div class="social-links">
<a href="#" aria-label="LinkedIn"><i class="fab fa-linkedin"></i></a>
<a href="#" aria-label="Twitter"><i class="fab fa-twitter"></i></a>
<a href="#" aria-label="GitHub"><i class="fab fa-github"></i></a>
</div>
</div>
<div class="footer-links">
<h4>Produkt</h4>
<ul>
<li><a href="#features">Features</a></li>
<li><a href="#pricing">Preise</a></li>
<li><a href="#faq">FAQ</a></li>
<li><a href="#">Dokumentation</a></li>
</ul>
</div>
<div class="footer-links">
<h4>Unternehmen</h4>
<ul>
<li><a href="#">Über uns</a></li>
<li><a href="#">Blog</a></li>
<li><a href="#">Karriere</a></li>
<li><a href="#">Kontakt</a></li>
</ul>
</div>
<div class="footer-links">
<h4>Rechtliches</h4>
<ul>
<li><a href="#">Impressum</a></li>
<li><a href="#">Datenschutz</a></li>
<li><a href="#">AGB</a></li>
<li><a href="#">Cookie-Einstellungen</a></li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>&copy; 2025 BotKonzept. Alle Rechte vorbehalten.</p>
<p>Made with <i class="fas fa-heart"></i> in Germany</p>
</div>
</div>
</footer>
<!-- Custom JS -->
<script src="js/main.js"></script>
</body>
</html>

744
js/dashboard.js Normal file
View File

@@ -0,0 +1,744 @@
/**
* BotKonzept Dashboard JavaScript
* ================================
* Handles all dashboard functionality
*/
// Configuration
const DASHBOARD_CONFIG = {
// API Endpoints - Update for production
API_BASE_URL: 'https://api.botkonzept.de',
CHAT_WEBHOOK_URL: '', // Will be set from customer data
UPLOAD_URL: '', // Will be set from customer data
// Customer data (loaded from localStorage or API)
customerId: null,
customerData: null,
// Trial settings
trialDays: 7,
discountDay3: 0.30, // 30% discount
discountDay5: 0.15, // 15% discount
};
// ============================================
// DOM Ready
// ============================================
document.addEventListener('DOMContentLoaded', () => {
initDashboard();
initNavigation();
initUserMenu();
initFileUpload();
initChatbot();
initSectionNavigation();
loadCustomerData();
updateTrialStatus();
});
// ============================================
// Dashboard Initialization
// ============================================
function initDashboard() {
// Check if user is logged in (for demo, we'll simulate this)
const isLoggedIn = localStorage.getItem('botkonzept_session') || true; // Demo mode
if (!isLoggedIn) {
window.location.href = 'index.html';
return;
}
// Load customer data from localStorage or use demo data
const storedData = localStorage.getItem('botkonzept_customer');
if (storedData) {
DASHBOARD_CONFIG.customerData = JSON.parse(storedData);
} else {
// Demo data
DASHBOARD_CONFIG.customerData = {
id: 'demo-' + Date.now(),
firstName: 'Max',
lastName: 'Mustermann',
email: 'max@beispiel.de',
company: 'Muster GmbH',
trialStartDate: new Date().toISOString(),
instanceUrl: 'https://sb-demo.userman.de',
chatWebhook: 'https://sb-demo.userman.de/webhook/rag-chat-webhook/chat',
uploadUrl: 'https://sb-demo.userman.de/form/rag-upload-form',
};
}
// Update UI with customer data
updateCustomerUI();
}
// ============================================
// Navigation
// ============================================
function initNavigation() {
// Sidebar navigation
const navItems = document.querySelectorAll('.sidebar-nav .nav-item');
const sections = document.querySelectorAll('.dashboard-section');
navItems.forEach(item => {
item.addEventListener('click', (e) => {
e.preventDefault();
const sectionId = item.getAttribute('data-section');
// Update active states
navItems.forEach(nav => nav.classList.remove('active'));
item.classList.add('active');
// Show corresponding section
sections.forEach(section => {
section.classList.remove('active');
if (section.id === sectionId) {
section.classList.add('active');
}
});
// Update URL hash
window.location.hash = sectionId;
});
});
// Handle initial hash
const hash = window.location.hash.slice(1);
if (hash) {
const targetNav = document.querySelector(`[data-section="${hash}"]`);
if (targetNav) {
targetNav.click();
}
}
}
function initSectionNavigation() {
// Quick action cards
const actionCards = document.querySelectorAll('.action-card');
actionCards.forEach(card => {
card.addEventListener('click', (e) => {
e.preventDefault();
const sectionId = card.getAttribute('data-section');
const targetNav = document.querySelector(`[data-section="${sectionId}"]`);
if (targetNav) {
targetNav.click();
}
});
});
}
// ============================================
// User Menu
// ============================================
function initUserMenu() {
const userBtn = document.getElementById('userMenuBtn');
const userDropdown = document.getElementById('userDropdown');
if (!userBtn || !userDropdown) return;
userBtn.addEventListener('click', () => {
userDropdown.classList.toggle('active');
});
// Close on outside click
document.addEventListener('click', (e) => {
if (!userBtn.contains(e.target) && !userDropdown.contains(e.target)) {
userDropdown.classList.remove('active');
}
});
}
// ============================================
// Customer Data
// ============================================
function loadCustomerData() {
const data = DASHBOARD_CONFIG.customerData;
if (!data) return;
// Update customer ID in embed code
const customerIdEl = document.getElementById('customerId');
if (customerIdEl) {
customerIdEl.textContent = data.id;
}
// Update webhook URLs
const chatWebhookEl = document.getElementById('chatWebhook');
if (chatWebhookEl && data.chatWebhook) {
chatWebhookEl.textContent = data.chatWebhook;
}
const uploadUrlEl = document.getElementById('uploadUrl');
if (uploadUrlEl && data.uploadUrl) {
uploadUrlEl.textContent = data.uploadUrl;
}
// Update user name
const userNameEl = document.getElementById('userName');
if (userNameEl) {
userNameEl.textContent = `${data.firstName} ${data.lastName}`;
}
// Update account email
const accountEmailEl = document.getElementById('accountEmail');
if (accountEmailEl) {
accountEmailEl.value = data.email;
}
}
function updateCustomerUI() {
loadCustomerData();
// Update stats (demo data)
updateStats({
documents: 0,
messages: 0,
visitors: 0,
satisfaction: '-'
});
}
function updateStats(stats) {
const docCountEl = document.getElementById('docCount');
const messageCountEl = document.getElementById('messageCount');
const visitorCountEl = document.getElementById('visitorCount');
const satisfactionEl = document.getElementById('satisfactionRate');
if (docCountEl) docCountEl.textContent = stats.documents;
if (messageCountEl) messageCountEl.textContent = stats.messages;
if (visitorCountEl) visitorCountEl.textContent = stats.visitors;
if (satisfactionEl) satisfactionEl.textContent = stats.satisfaction;
}
// ============================================
// Trial Status
// ============================================
function updateTrialStatus() {
const data = DASHBOARD_CONFIG.customerData;
if (!data || !data.trialStartDate) return;
const trialStart = new Date(data.trialStartDate);
const now = new Date();
const daysPassed = Math.floor((now - trialStart) / (1000 * 60 * 60 * 24));
const daysRemaining = Math.max(0, DASHBOARD_CONFIG.trialDays - daysPassed);
// Update trial badge
const trialDaysEl = document.getElementById('trialDays');
if (trialDaysEl) {
trialDaysEl.textContent = daysRemaining;
}
// Update trial end date
const trialEndDateEl = document.getElementById('trialEndDate');
if (trialEndDateEl) {
const endDate = new Date(trialStart);
endDate.setDate(endDate.getDate() + DASHBOARD_CONFIG.trialDays);
trialEndDateEl.textContent = formatDate(endDate);
}
// Show/hide discount banner based on days
const trialBanner = document.getElementById('trialBanner');
if (trialBanner) {
if (daysPassed <= 3) {
// Show 30% discount
trialBanner.querySelector('h3').textContent = '🎉 Exklusives Angebot: 30% Frühbucher-Rabatt!';
trialBanner.querySelector('.btn').textContent = 'Jetzt für €34,30/Monat upgraden';
} else if (daysPassed <= 5) {
// Show 15% discount
trialBanner.querySelector('h3').textContent = '⏰ Nur noch 2 Tage: 15% Rabatt!';
trialBanner.querySelector('.btn').textContent = 'Jetzt für €41,65/Monat upgraden';
} else {
// Show normal price
trialBanner.querySelector('h3').textContent = '⚠️ Ihre Trial endet bald!';
trialBanner.querySelector('.btn').textContent = 'Jetzt für €49/Monat upgraden';
}
}
// Start countdown timer
startOfferCountdown();
}
function startOfferCountdown() {
const countdownEl = document.getElementById('offerCountdown');
if (!countdownEl) return;
// Set countdown to 48 hours from now (for demo)
let timeRemaining = 48 * 60 * 60; // 48 hours in seconds
const updateCountdown = () => {
const hours = Math.floor(timeRemaining / 3600);
const minutes = Math.floor((timeRemaining % 3600) / 60);
const seconds = timeRemaining % 60;
countdownEl.textContent = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
if (timeRemaining > 0) {
timeRemaining--;
setTimeout(updateCountdown, 1000);
}
};
updateCountdown();
}
// ============================================
// File Upload
// ============================================
function initFileUpload() {
const uploadArea = document.getElementById('uploadArea');
const fileInput = document.getElementById('fileInput');
if (!uploadArea || !fileInput) return;
// Drag and drop
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.classList.add('dragover');
});
uploadArea.addEventListener('dragleave', () => {
uploadArea.classList.remove('dragover');
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.classList.remove('dragover');
const files = e.dataTransfer.files;
handleFiles(files);
});
// File input change
fileInput.addEventListener('change', () => {
handleFiles(fileInput.files);
});
}
function handleFiles(files) {
const validFiles = Array.from(files).filter(file => {
if (file.type !== 'application/pdf') {
showNotification('Nur PDF-Dateien werden unterstützt', 'error');
return false;
}
if (file.size > 10 * 1024 * 1024) { // 10MB
showNotification(`${file.name} ist zu groß (max. 10MB)`, 'error');
return false;
}
return true;
});
if (validFiles.length === 0) return;
validFiles.forEach(file => uploadFile(file));
}
async function uploadFile(file) {
const uploadProgress = document.getElementById('uploadProgress');
const progressFill = document.getElementById('progressFill');
const uploadPercent = document.getElementById('uploadPercent');
// Show progress
if (uploadProgress) uploadProgress.style.display = 'block';
try {
// Simulate upload progress (replace with actual upload)
for (let i = 0; i <= 100; i += 10) {
await new Promise(resolve => setTimeout(resolve, 200));
if (progressFill) progressFill.style.width = `${i}%`;
if (uploadPercent) uploadPercent.textContent = `${i}%`;
}
// Add document to list
addDocumentToList({
name: file.name,
size: formatFileSize(file.size),
date: new Date().toLocaleDateString('de-DE'),
status: 'processing'
});
showNotification(`${file.name} wurde hochgeladen`, 'success');
// Simulate processing
setTimeout(() => {
updateDocumentStatus(file.name, 'indexed');
updateDocCount();
}, 3000);
} catch (error) {
console.error('Upload error:', error);
showNotification('Fehler beim Hochladen', 'error');
} finally {
// Hide progress
setTimeout(() => {
if (uploadProgress) uploadProgress.style.display = 'none';
if (progressFill) progressFill.style.width = '0%';
}, 500);
}
}
function addDocumentToList(doc) {
const documentsTable = document.getElementById('documentsTable');
if (!documentsTable) return;
// Remove empty state if present
const emptyState = documentsTable.querySelector('.empty-state');
if (emptyState) emptyState.remove();
const docRow = document.createElement('div');
docRow.className = 'document-row';
docRow.dataset.name = doc.name;
docRow.innerHTML = `
<div class="doc-icon">
<i class="fas fa-file-pdf"></i>
</div>
<div class="doc-info">
<div class="doc-name">${doc.name}</div>
<div class="doc-meta">${doc.size}${doc.date}</div>
</div>
<span class="doc-status ${doc.status}">${doc.status === 'indexed' ? 'Indexiert' : 'Verarbeitung...'}</span>
<div class="doc-actions">
<button title="Herunterladen"><i class="fas fa-download"></i></button>
<button class="delete" title="Löschen" onclick="deleteDocument('${doc.name}')"><i class="fas fa-trash"></i></button>
</div>
`;
documentsTable.appendChild(docRow);
}
function updateDocumentStatus(name, status) {
const docRow = document.querySelector(`.document-row[data-name="${name}"]`);
if (!docRow) return;
const statusEl = docRow.querySelector('.doc-status');
if (statusEl) {
statusEl.className = `doc-status ${status}`;
statusEl.textContent = status === 'indexed' ? 'Indexiert' : 'Verarbeitung...';
}
}
function deleteDocument(name) {
if (!confirm(`Möchten Sie "${name}" wirklich löschen?`)) return;
const docRow = document.querySelector(`.document-row[data-name="${name}"]`);
if (docRow) {
docRow.remove();
updateDocCount();
showNotification('Dokument gelöscht', 'success');
}
// Show empty state if no documents
const documentsTable = document.getElementById('documentsTable');
if (documentsTable && documentsTable.children.length === 0) {
documentsTable.innerHTML = `
<div class="empty-state">
<i class="fas fa-folder-open"></i>
<h3>Noch keine Dokumente</h3>
<p>Laden Sie Ihr erstes PDF hoch, um zu beginnen</p>
</div>
`;
}
}
function updateDocCount() {
const documentsTable = document.getElementById('documentsTable');
const totalDocsEl = document.getElementById('totalDocs');
const docCountEl = document.getElementById('docCount');
if (!documentsTable) return;
const count = documentsTable.querySelectorAll('.document-row').length;
if (totalDocsEl) totalDocsEl.textContent = `${count} Dokument${count !== 1 ? 'e' : ''}`;
if (docCountEl) docCountEl.textContent = count;
}
// Make deleteDocument available globally
window.deleteDocument = deleteDocument;
// ============================================
// Chatbot
// ============================================
function initChatbot() {
const chatInput = document.getElementById('chatInput');
const sendBtn = document.getElementById('sendBtn');
const clearBtn = document.getElementById('clearChat');
if (!chatInput || !sendBtn) return;
// Send message on button click
sendBtn.addEventListener('click', () => sendMessage());
// Send message on Enter
chatInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
sendMessage();
}
});
// Clear chat
if (clearBtn) {
clearBtn.addEventListener('click', () => {
const chatMessages = document.getElementById('chatMessages');
if (chatMessages) {
chatMessages.innerHTML = `
<div class="message bot">
<p>Hallo! Ich bin Ihr KI-Assistent. Wie kann ich Ihnen helfen?</p>
</div>
`;
}
});
}
}
async function sendMessage() {
const chatInput = document.getElementById('chatInput');
const chatMessages = document.getElementById('chatMessages');
if (!chatInput || !chatMessages) return;
const message = chatInput.value.trim();
if (!message) return;
// Add user message
addChatMessage(message, 'user');
chatInput.value = '';
// Show typing indicator
const typingId = showTypingIndicator();
try {
// Send to chat webhook (if configured)
const webhookUrl = DASHBOARD_CONFIG.customerData?.chatWebhook;
if (webhookUrl && webhookUrl !== 'https://sb-demo.userman.de/webhook/rag-chat-webhook/chat') {
const response = await fetch(webhookUrl, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
chatInput: message,
sessionId: getSessionId(),
}),
});
const data = await response.json();
removeTypingIndicator(typingId);
addChatMessage(data.output || data.response || 'Keine Antwort erhalten', 'bot');
} else {
// Demo response
await new Promise(resolve => setTimeout(resolve, 1500));
removeTypingIndicator(typingId);
const demoResponses = [
'Das ist eine Demo-Antwort. Laden Sie Dokumente hoch, um echte Antworten zu erhalten.',
'Ich kann Ihnen helfen, sobald Sie Ihre Wissensdatenbank mit PDFs gefüllt haben.',
'Bitte laden Sie zunächst einige Dokumente hoch, damit ich Ihre Fragen beantworten kann.',
];
addChatMessage(demoResponses[Math.floor(Math.random() * demoResponses.length)], 'bot');
}
} catch (error) {
console.error('Chat error:', error);
removeTypingIndicator(typingId);
addChatMessage('Entschuldigung, es gab einen Fehler. Bitte versuchen Sie es erneut.', 'bot');
}
}
function addChatMessage(text, type) {
const chatMessages = document.getElementById('chatMessages');
if (!chatMessages) return;
const messageDiv = document.createElement('div');
messageDiv.className = `message ${type}`;
messageDiv.innerHTML = `<p>${escapeHtml(text)}</p>`;
chatMessages.appendChild(messageDiv);
chatMessages.scrollTop = chatMessages.scrollHeight;
}
function showTypingIndicator() {
const chatMessages = document.getElementById('chatMessages');
if (!chatMessages) return null;
const id = 'typing-' + Date.now();
const typingDiv = document.createElement('div');
typingDiv.className = 'message bot typing';
typingDiv.id = id;
typingDiv.innerHTML = `
<span class="typing-indicator">
<span></span><span></span><span></span>
</span>
`;
chatMessages.appendChild(typingDiv);
chatMessages.scrollTop = chatMessages.scrollHeight;
return id;
}
function removeTypingIndicator(id) {
if (!id) return;
const typingDiv = document.getElementById(id);
if (typingDiv) typingDiv.remove();
}
function getSessionId() {
let sessionId = sessionStorage.getItem('chat_session_id');
if (!sessionId) {
sessionId = 'session-' + Date.now() + '-' + Math.random().toString(36).substr(2, 9);
sessionStorage.setItem('chat_session_id', sessionId);
}
return sessionId;
}
// ============================================
// Embed Code
// ============================================
function copyEmbedCode() {
const embedCode = document.getElementById('embedCode');
if (!embedCode) return;
const text = embedCode.textContent;
copyToClipboard('embedCode');
// Show success message
const copySuccess = document.getElementById('copySuccess');
if (copySuccess) {
copySuccess.style.display = 'flex';
setTimeout(() => {
copySuccess.style.display = 'none';
}, 2000);
}
}
function copyToClipboard(elementId) {
const element = document.getElementById(elementId);
if (!element) return;
const text = element.textContent;
navigator.clipboard.writeText(text).then(() => {
showNotification('In Zwischenablage kopiert', 'success');
}).catch(err => {
console.error('Copy failed:', err);
// Fallback
const textarea = document.createElement('textarea');
textarea.value = text;
document.body.appendChild(textarea);
textarea.select();
document.execCommand('copy');
document.body.removeChild(textarea);
showNotification('In Zwischenablage kopiert', 'success');
});
}
// Make functions available globally
window.copyEmbedCode = copyEmbedCode;
window.copyToClipboard = copyToClipboard;
// ============================================
// Settings
// ============================================
function saveSettings() {
const primaryColor = document.getElementById('primaryColor')?.value;
const botName = document.getElementById('botName')?.value;
const welcomeMessage = document.getElementById('welcomeMessage')?.value;
// Save to localStorage (in production, save to API)
const settings = {
primaryColor,
botName,
welcomeMessage,
};
localStorage.setItem('botkonzept_settings', JSON.stringify(settings));
showNotification('Einstellungen gespeichert', 'success');
}
// Make saveSettings available globally
window.saveSettings = saveSettings;
// ============================================
// Utility Functions
// ============================================
function showNotification(message, type = 'info') {
// Create notification element
const notification = document.createElement('div');
notification.className = `notification ${type}`;
notification.innerHTML = `
<i class="fas fa-${type === 'success' ? 'check-circle' : type === 'error' ? 'exclamation-circle' : 'info-circle'}"></i>
<span>${message}</span>
`;
// Add styles
notification.style.cssText = `
position: fixed;
bottom: 24px;
right: 24px;
padding: 16px 24px;
background: ${type === 'success' ? '#10b981' : type === 'error' ? '#ef4444' : '#6366f1'};
color: white;
border-radius: 8px;
display: flex;
align-items: center;
gap: 12px;
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1);
z-index: 9999;
animation: slideIn 0.3s ease;
`;
document.body.appendChild(notification);
// Remove after 3 seconds
setTimeout(() => {
notification.style.animation = 'slideOut 0.3s ease';
setTimeout(() => notification.remove(), 300);
}, 3000);
}
function formatDate(date) {
return new Intl.DateTimeFormat('de-DE', {
day: '2-digit',
month: '2-digit',
year: 'numeric'
}).format(date);
}
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Add notification animations
const style = document.createElement('style');
style.textContent = `
@keyframes slideIn {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@keyframes slideOut {
from {
transform: translateX(0);
opacity: 1;
}
to {
transform: translateX(100%);
opacity: 0;
}
}
`;
document.head.appendChild(style);

468
js/main.js Normal file
View File

@@ -0,0 +1,468 @@
/**
* BotKonzept - Main JavaScript
* ============================
* Handles all interactive functionality for the BotKonzept website
*/
// Configuration
const CONFIG = {
// API Endpoints - Update these for production
WEBHOOK_URL: 'https://n8n.userman.de/webhook/botkonzept-registration',
API_BASE_URL: 'https://api.botkonzept.de',
// Validation
MIN_NAME_LENGTH: 2,
MAX_NAME_LENGTH: 50,
// Animation
SCROLL_OFFSET: 100,
ANIMATION_DELAY: 100,
};
// ============================================
// DOM Ready
// ============================================
document.addEventListener('DOMContentLoaded', () => {
initNavigation();
initMobileMenu();
initSmoothScroll();
initFAQ();
initRegistrationForm();
initScrollAnimations();
initTypingAnimation();
});
// ============================================
// Navigation
// ============================================
function initNavigation() {
const navbar = document.getElementById('navbar');
let lastScroll = 0;
window.addEventListener('scroll', () => {
const currentScroll = window.pageYOffset;
// Add shadow on scroll
if (currentScroll > 50) {
navbar.style.boxShadow = '0 4px 6px -1px rgb(0 0 0 / 0.1)';
} else {
navbar.style.boxShadow = 'none';
}
// Hide/show navbar on scroll (optional)
// if (currentScroll > lastScroll && currentScroll > 200) {
// navbar.style.transform = 'translateY(-100%)';
// } else {
// navbar.style.transform = 'translateY(0)';
// }
lastScroll = currentScroll;
});
}
// ============================================
// Mobile Menu
// ============================================
function initMobileMenu() {
const mobileMenuBtn = document.getElementById('mobileMenuBtn');
const navLinks = document.getElementById('navLinks');
if (!mobileMenuBtn || !navLinks) return;
mobileMenuBtn.addEventListener('click', () => {
navLinks.classList.toggle('active');
// Toggle icon
const icon = mobileMenuBtn.querySelector('i');
if (navLinks.classList.contains('active')) {
icon.classList.remove('fa-bars');
icon.classList.add('fa-times');
} else {
icon.classList.remove('fa-times');
icon.classList.add('fa-bars');
}
});
// Close menu when clicking a link
navLinks.querySelectorAll('a').forEach(link => {
link.addEventListener('click', () => {
navLinks.classList.remove('active');
const icon = mobileMenuBtn.querySelector('i');
icon.classList.remove('fa-times');
icon.classList.add('fa-bars');
});
});
}
// ============================================
// Smooth Scroll
// ============================================
function initSmoothScroll() {
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function(e) {
e.preventDefault();
const targetId = this.getAttribute('href');
if (targetId === '#') return;
const targetElement = document.querySelector(targetId);
if (targetElement) {
const navbarHeight = document.getElementById('navbar').offsetHeight;
const targetPosition = targetElement.offsetTop - navbarHeight - 20;
window.scrollTo({
top: targetPosition,
behavior: 'smooth'
});
}
});
});
}
// ============================================
// FAQ Accordion
// ============================================
function initFAQ() {
const faqItems = document.querySelectorAll('.faq-item');
faqItems.forEach(item => {
const question = item.querySelector('.faq-question');
question.addEventListener('click', () => {
// Close other items
faqItems.forEach(otherItem => {
if (otherItem !== item && otherItem.classList.contains('active')) {
otherItem.classList.remove('active');
}
});
// Toggle current item
item.classList.toggle('active');
});
});
}
// ============================================
// Registration Form
// ============================================
function initRegistrationForm() {
const form = document.getElementById('registerForm');
const submitBtn = document.getElementById('submitBtn');
const formSuccess = document.getElementById('formSuccess');
const formError = document.getElementById('formError');
if (!form) return;
form.addEventListener('submit', async (e) => {
e.preventDefault();
// Validate form
if (!validateForm(form)) {
return;
}
// Show loading state
setLoadingState(submitBtn, true);
// Collect form data
const formData = {
firstName: form.firstName.value.trim(),
lastName: form.lastName.value.trim(),
email: form.email.value.trim(),
company: form.company.value.trim() || null,
website: form.website.value.trim() || null,
newsletter: form.newsletter.checked,
timestamp: new Date().toISOString(),
source: 'website',
userAgent: navigator.userAgent,
language: navigator.language,
};
try {
// Send registration request
const response = await fetch(CONFIG.WEBHOOK_URL, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(formData),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const result = await response.json();
// Show success message
form.style.display = 'none';
formSuccess.style.display = 'block';
// Track conversion (if analytics is set up)
trackConversion('registration_complete', formData);
} catch (error) {
console.error('Registration error:', error);
// Show error message
form.style.display = 'none';
formError.style.display = 'block';
const errorMessage = document.getElementById('errorMessage');
if (errorMessage) {
errorMessage.textContent = getErrorMessage(error);
}
} finally {
setLoadingState(submitBtn, false);
}
});
}
// Form validation
function validateForm(form) {
let isValid = true;
// Clear previous errors
form.querySelectorAll('.error').forEach(el => el.classList.remove('error'));
form.querySelectorAll('.error-message').forEach(el => el.remove());
// Validate first name
const firstName = form.firstName.value.trim();
if (firstName.length < CONFIG.MIN_NAME_LENGTH) {
showFieldError(form.firstName, 'Bitte geben Sie Ihren Vornamen ein');
isValid = false;
}
// Validate last name
const lastName = form.lastName.value.trim();
if (lastName.length < CONFIG.MIN_NAME_LENGTH) {
showFieldError(form.lastName, 'Bitte geben Sie Ihren Nachnamen ein');
isValid = false;
}
// Validate email
const email = form.email.value.trim();
if (!isValidEmail(email)) {
showFieldError(form.email, 'Bitte geben Sie eine gültige E-Mail-Adresse ein');
isValid = false;
}
// Validate website (if provided)
const website = form.website.value.trim();
if (website && !isValidURL(website)) {
showFieldError(form.website, 'Bitte geben Sie eine gültige URL ein (z.B. https://beispiel.de)');
isValid = false;
}
// Validate privacy checkbox
if (!form.privacy.checked) {
showFieldError(form.privacy, 'Bitte akzeptieren Sie die Datenschutzerklärung');
isValid = false;
}
return isValid;
}
// Show field error
function showFieldError(field, message) {
field.classList.add('error');
field.style.borderColor = '#ef4444';
const errorDiv = document.createElement('div');
errorDiv.className = 'error-message';
errorDiv.style.color = '#ef4444';
errorDiv.style.fontSize = '0.75rem';
errorDiv.style.marginTop = '4px';
errorDiv.textContent = message;
field.parentNode.appendChild(errorDiv);
}
// Email validation
function isValidEmail(email) {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
}
// URL validation
function isValidURL(url) {
try {
new URL(url);
return true;
} catch {
return false;
}
}
// Set loading state
function setLoadingState(button, isLoading) {
const btnText = button.querySelector('.btn-text');
const btnLoading = button.querySelector('.btn-loading');
if (isLoading) {
button.disabled = true;
if (btnText) btnText.style.display = 'none';
if (btnLoading) btnLoading.style.display = 'inline-flex';
} else {
button.disabled = false;
if (btnText) btnText.style.display = 'inline';
if (btnLoading) btnLoading.style.display = 'none';
}
}
// Get user-friendly error message
function getErrorMessage(error) {
if (error.message.includes('Failed to fetch')) {
return 'Verbindungsfehler. Bitte überprüfen Sie Ihre Internetverbindung.';
}
if (error.message.includes('500')) {
return 'Serverfehler. Bitte versuchen Sie es später erneut.';
}
if (error.message.includes('400')) {
return 'Ungültige Eingabe. Bitte überprüfen Sie Ihre Daten.';
}
return 'Ein unerwarteter Fehler ist aufgetreten. Bitte versuchen Sie es später erneut.';
}
// Reset form
function resetForm() {
const form = document.getElementById('registerForm');
const formSuccess = document.getElementById('formSuccess');
const formError = document.getElementById('formError');
if (form) {
form.reset();
form.style.display = 'flex';
}
if (formSuccess) formSuccess.style.display = 'none';
if (formError) formError.style.display = 'none';
}
// Make resetForm available globally
window.resetForm = resetForm;
// ============================================
// Scroll Animations
// ============================================
function initScrollAnimations() {
const animatedElements = document.querySelectorAll('.feature-card, .step, .pricing-card, .faq-item');
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry, index) => {
if (entry.isIntersecting) {
setTimeout(() => {
entry.target.classList.add('visible');
entry.target.style.opacity = '1';
entry.target.style.transform = 'translateY(0)';
}, index * CONFIG.ANIMATION_DELAY);
observer.unobserve(entry.target);
}
});
}, {
threshold: 0.1,
rootMargin: '0px 0px -50px 0px'
});
animatedElements.forEach(el => {
el.style.opacity = '0';
el.style.transform = 'translateY(20px)';
el.style.transition = 'opacity 0.5s ease, transform 0.5s ease';
observer.observe(el);
});
}
// ============================================
// Typing Animation (Chat Preview)
// ============================================
function initTypingAnimation() {
const typingMessage = document.querySelector('.message.typing');
if (!typingMessage) return;
// Simulate typing response
setTimeout(() => {
typingMessage.innerHTML = '<p>Sie können Ihre Bestellung ganz einfach über unser Online-Formular aufgeben. Alternativ können Sie uns auch telefonisch unter 0800-123456 erreichen.</p>';
typingMessage.classList.remove('typing');
}, 3000);
}
// ============================================
// Analytics & Tracking
// ============================================
function trackConversion(eventName, data = {}) {
// Google Analytics 4
if (typeof gtag !== 'undefined') {
gtag('event', eventName, {
'event_category': 'conversion',
'event_label': 'registration',
...data
});
}
// Facebook Pixel
if (typeof fbq !== 'undefined') {
fbq('track', 'Lead', data);
}
// Console log for debugging
console.log('Conversion tracked:', eventName, data);
}
// ============================================
// Utility Functions
// ============================================
// Debounce function
function debounce(func, wait) {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
}
// Throttle function
function throttle(func, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
};
}
// Format currency
function formatCurrency(amount, currency = 'EUR') {
return new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: currency
}).format(amount);
}
// Format date
function formatDate(date) {
return new Intl.DateTimeFormat('de-DE', {
year: 'numeric',
month: 'long',
day: 'numeric'
}).format(new Date(date));
}
// ============================================
// Export for testing
// ============================================
if (typeof module !== 'undefined' && module.exports) {
module.exports = {
validateForm,
isValidEmail,
isValidURL,
formatCurrency,
formatDate
};
}

View File

@@ -0,0 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- Creator: CorelDRAW -->
<svg xmlns="http://www.w3.org/2000/svg" xml:space="preserve" width="359px" height="60px" version="1.1" style="shape-rendering:geometricPrecision; text-rendering:geometricPrecision; image-rendering:optimizeQuality; fill-rule:evenodd; clip-rule:evenodd"
viewBox="0 0 2150 359"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xodm="http://www.corel.com/coreldraw/odm/2003">
<defs>
<style type="text/css">
<![CDATA[
.fil1 {fill:#FEFEFE;fill-rule:nonzero}
.fil4 {fill:#88CED7;fill-rule:nonzero}
.fil3 {fill:#4FC0EF;fill-rule:nonzero}
.fil2 {fill:#3A7ABD;fill-rule:nonzero}
.fil0 {fill:#234182;fill-rule:nonzero}
]]>
</style>
</defs>
<g id="Bot_x0020_TV">
<metadata id="CorelCorpID_0Corel-Layer"/>
<g id="_1548485603296">
<g>
<path class="fil0" d="M204 168l0 22c0,8 5,14 12,14 3,0 6,-2 9,-4 2,-3 4,-6 4,-10l0 -22c0,-8 -5,-14 -12,-14 -3,0 -6,2 -9,4 -2,3 -4,6 -4,10z"/>
</g>
<g>
<path class="fil0" d="M142 168l0 22c0,8 5,14 12,14 3,0 6,-2 9,-4 2,-3 4,-6 4,-10l0 -22c0,-8 -5,-14 -12,-14 -3,0 -6,2 -9,4 -2,3 -4,6 -4,10z"/>
</g>
</g>
<g id="_1548485603200">
<g>
<path class="fil1" d="M209 192l2 0c1,0 1,0 1,-1l0 -24c0,-1 0,-1 -1,-1l-2 0c-1,0 -1,0 -1,1l0 24c0,1 0,1 1,1z"/>
</g>
<g>
<path class="fil1" d="M147 192l2 0c1,0 1,0 1,-1l0 -24c0,-1 0,-1 -1,-1l-2 0c-1,0 -1,0 -1,1l0 24c0,1 0,1 1,1z"/>
</g>
</g>
<path class="fil0" d="M119 24l0 32 -38 0 0 -32c0,-10 8,-19 19,-19 10,0 19,8 19,19zm221 259l-221 0 0 56c0,10 -8,19 -18,20 -11,1 -20,-8 -20,-19l0 -219 0 -8 47 0c-5,0 -8,3 -9,8 0,1 0,1 0,2l0 113c0,5 4,9 9,9l211 0c10,0 19,8 20,18 1,11 -8,20 -19,20z"/>
<g id="_1548485605696">
<path class="fil2" d="M251 302l38 0 0 38c0,10 -8,19 -19,19 -10,0 -19,-8 -19,-19l0 -38z"/>
</g>
<g id="_1548485607712">
<path class="fil3" d="M359 94c0,10 -8,19 -19,19l-32 0 0 -38 32 0c10,0 19,8 19,19z"/>
</g>
<g id="_1548485606320">
<path class="fil4" d="M19 245l43 0 0 38 -43 0c-10,0 -19,-8 -19,-19 0,-10 8,-19 19,-19z"/>
</g>
<path class="fil2" d="M251 237l0 -2 0 -112c0,-5 -4,-9 -9,-9l-222 0c-10,0 -19,-8 -20,-18 -1,-11 8,-20 19,-20l232 0 0 -55c0,-11 9,-20 20,-19 10,1 18,9 18,20l0 217 0 8 -47 0c4,0 8,-3 9,-8 0,0 0,-1 0,-1z"/>
<path class="fil0" d="M391 213l0 -212 90 0c40,0 60,16 60,49 0,24 -13,41 -39,52 28,5 42,21 42,48 0,42 -22,63 -66,63l-87 0zm86 -22c25,0 38,-13 38,-38 0,-25 -17,-37 -51,-37l-12 0 0 -18c40,-5 61,-20 61,-44 0,-20 -11,-30 -34,-30l-60 0 0 167 58 0z"/>
<path id="_1" class="fil0" d="M609 107c0,57 26,86 77,86 51,0 76,-29 76,-86 0,-56 -25,-85 -76,-85 -52,0 -77,28 -77,85zm-29 1c0,-72 35,-108 106,-108 70,0 105,36 105,108 0,71 -35,107 -105,107 -71,0 -106,-36 -106,-107z"/>
<polygon id="_2" class="fil0" points="977,1 977,24 910,24 910,213 883,213 883,24 816,24 816,1 "/>
<path id="_3" class="fil0" d="M1037 1l0 212 -27 0 0 -212 27 0zm149 0l-95 100 97 112 -38 0 -86 -102 0 -17 88 -93 35 0z"/>
<path id="_4" class="fil0" d="M1216 107c0,57 26,86 77,86 51,0 76,-29 76,-86 0,-56 -25,-85 -76,-85 -52,0 -77,28 -77,85zm-29 1c0,-72 35,-108 106,-108 70,0 105,36 105,108 0,71 -35,107 -105,107 -71,0 -106,-36 -106,-107z"/>
<polygon id="_5" class="fil0" points="1594,1 1594,24 1468,191 1592,191 1592,213 1435,213 1435,191 1565,24 1438,24 1438,1 "/>
<polygon id="_6" class="fil0" points="1777,1 1777,24 1666,24 1666,95 1771,95 1771,117 1666,117 1666,191 1779,191 1779,213 1638,213 1638,1 "/>
<path id="_7" class="fil0" d="M1820 213l0 -212 86 0c43,0 65,19 65,57 0,41 -30,66 -89,75l-6 -23c43,-6 65,-23 65,-51 0,-24 -13,-35 -38,-35l-56 0 0 189 -27 0z"/>
<polygon id="_8" class="fil0" points="2150,1 2150,24 2083,24 2083,213 2056,213 2056,24 1989,24 1989,1 "/>
<polygon class="fil2" points="391,359 391,254 411,254 446,333 481,254 500,254 500,359 482,359 482,286 454,359 437,359 409,286 409,359 "/>
<polygon id="_1_8" class="fil2" points="593,254 593,271 541,271 541,297 591,297 591,315 541,315 541,342 594,342 594,359 522,359 522,254 "/>
<path id="_2_9" class="fil2" d="M653 342c22,0 32,-13 32,-38 0,-22 -11,-32 -32,-32l-21 0 0 71 21 0zm-41 17l0 -104 41 0c34,0 52,16 52,49 0,37 -17,55 -52,55l-41 0z"/>
<polygon id="_3_10" class="fil2" points="740,254 740,359 721,359 721,254 "/>
<polygon id="_4_11" class="fil2" points="773,359 753,359 797,254 818,254 863,359 842,359 830,330 796,330 802,313 823,313 807,274 "/>
<polygon id="_5_12" class="fil2" points="876,359 876,254 896,254 931,333 966,254 985,254 985,359 967,359 967,286 939,359 922,359 893,286 893,359 "/>
<polygon id="_6_13" class="fil2" points="1078,254 1078,271 1026,271 1026,297 1076,297 1076,315 1026,315 1026,342 1079,342 1079,359 1007,359 1007,254 "/>
<polygon id="_7_14" class="fil2" points="1165,254 1165,271 1135,271 1135,359 1116,359 1116,271 1086,271 1086,254 "/>
<polygon id="_8_15" class="fil2" points="1251,254 1251,271 1196,342 1251,342 1251,359 1174,359 1174,342 1232,271 1176,271 1176,254 "/>
<polygon id="_9" class="fil2" points="1332,359 1332,254 1352,254 1407,330 1407,254 1425,254 1425,359 1406,359 1351,281 1351,359 "/>
<polygon id="_10" class="fil2" points="1518,254 1518,271 1466,271 1466,297 1516,297 1516,315 1466,315 1466,342 1519,342 1519,359 1447,359 1447,254 "/>
<polygon id="_11" class="fil2" points="1605,254 1605,271 1576,271 1576,359 1556,359 1556,271 1526,271 1526,254 "/>
<polygon id="_12" class="fil2" points="1608,254 1628,254 1639,329 1666,254 1684,254 1710,326 1721,254 1740,254 1721,359 1703,359 1674,277 1643,359 1625,359 "/>
<path id="_13" class="fil2" d="M1770 306c0,24 11,36 32,36 21,0 31,-12 31,-36 0,-24 -10,-36 -31,-36 -22,0 -32,12 -32,36zm-20 0c0,-35 17,-53 52,-53 34,0 51,18 51,53 0,35 -17,53 -51,53 -33,0 -51,-18 -52,-53z"/>
<path id="_14" class="fil2" d="M1872 358l0 -104 47 0c21,0 32,9 32,28 0,13 -8,23 -25,31l33 45 -24 0 -32 -45 0 -9c18,-3 28,-10 28,-21 0,-8 -4,-12 -13,-12l-26 0 0 87 -20 0z"/>
<path id="_15" class="fil2" d="M1988 254l0 104 -19 0 0 -104 19 0zm73 0l-44 49 49 56 -27 0 -40 -49 0 -13 39 -43 24 0z"/>
<path id="_16" class="fil2" d="M2071 354l0 -17c10,4 22,6 35,6 16,0 24,-5 24,-16 0,-8 -5,-12 -15,-12l-16 0c-21,0 -32,-10 -32,-29 0,-21 15,-32 46,-32 12,0 23,2 33,5l0 17c-10,-4 -21,-6 -33,-6 -17,0 -26,5 -26,15 0,8 4,12 13,12l16 0c23,0 34,10 34,29 0,22 -14,33 -43,33 -13,0 -25,-2 -35,-5z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.3 KiB