Files
customer-frontend/js/dashboard.js

745 lines
24 KiB
JavaScript
Raw Normal View History

/**
* 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);