/** * ChatGPT Assistent Modul * Verwaltet die Interaktion mit der OpenAI API und die Benutzeroberfläche des Assistenten */ class ChatGPTAssistant { constructor() { this.messages = []; this.isOpen = false; this.isLoading = false; this.container = null; this.chatHistory = null; this.inputField = null; } /** * Initialisiert den Assistenten und fügt die UI zum DOM hinzu */ init() { // Assistent-Container erstellen this.createAssistantUI(); // Event-Listener hinzufügen this.setupEventListeners(); // Ersten Willkommensnachricht anzeigen this.addMessage("assistant", "Frage den KI-Assistenten"); } /** * Erstellt die UI-Elemente für den Assistenten */ createAssistantUI() { // Hauptcontainer erstellen this.container = document.createElement('div'); this.container.id = 'chatgpt-assistant'; this.container.className = 'fixed bottom-4 right-4 z-50 flex flex-col'; // Button zum Öffnen/Schließen des Assistenten const toggleButton = document.createElement('button'); toggleButton.id = 'assistant-toggle'; toggleButton.className = 'ml-auto bg-primary-600 hover:bg-primary-700 text-white rounded-full p-3 shadow-lg transition-all duration-300 mb-2'; toggleButton.innerHTML = ''; // Chat-Container const chatContainer = document.createElement('div'); chatContainer.id = 'assistant-chat'; chatContainer.className = 'bg-white dark:bg-dark-800 rounded-lg shadow-xl overflow-hidden transition-all duration-300 w-80 sm:w-96 max-h-0 opacity-0'; // Chat-Header const header = document.createElement('div'); header.className = 'bg-primary-600 text-white p-3 flex items-center justify-between'; header.innerHTML = `
KI-Assistent
`; // Chat-Verlauf this.chatHistory = document.createElement('div'); this.chatHistory.id = 'assistant-history'; this.chatHistory.className = 'p-3 overflow-y-auto max-h-80 space-y-3'; // Chat-Eingabe const inputContainer = document.createElement('div'); inputContainer.className = 'border-t border-gray-200 dark:border-dark-600 p-3 flex items-center'; this.inputField = document.createElement('input'); this.inputField.type = 'text'; this.inputField.placeholder = 'Frage den KI-Assistenten'; this.inputField.className = 'flex-1 border border-gray-300 dark:border-dark-600 dark:bg-dark-700 dark:text-white rounded-l-lg px-3 py-2 focus:outline-none focus:ring-2 focus:ring-primary-500'; const sendButton = document.createElement('button'); sendButton.id = 'assistant-send'; sendButton.className = 'bg-primary-600 hover:bg-primary-700 text-white px-4 py-2 rounded-r-lg'; sendButton.innerHTML = ''; // Elemente zusammenfügen inputContainer.appendChild(this.inputField); inputContainer.appendChild(sendButton); chatContainer.appendChild(header); chatContainer.appendChild(this.chatHistory); chatContainer.appendChild(inputContainer); this.container.appendChild(toggleButton); this.container.appendChild(chatContainer); // Zum DOM hinzufügen document.body.appendChild(this.container); } /** * Richtet Event-Listener für die Benutzeroberfläche ein */ setupEventListeners() { // Toggle-Button const toggleButton = document.getElementById('assistant-toggle'); toggleButton.addEventListener('click', () => this.toggleAssistant()); // Schließen-Button const closeButton = document.getElementById('assistant-close'); closeButton.addEventListener('click', () => this.toggleAssistant(false)); // Senden-Button const sendButton = document.getElementById('assistant-send'); sendButton.addEventListener('click', () => this.sendMessage()); // Enter-Taste im Eingabefeld this.inputField.addEventListener('keyup', (e) => { if (e.key === 'Enter') { this.sendMessage(); } }); } /** * Öffnet oder schließt den Assistenten * @param {boolean} state - Optional: erzwingt einen bestimmten Zustand */ toggleAssistant(state = null) { const chatContainer = document.getElementById('assistant-chat'); this.isOpen = state !== null ? state : !this.isOpen; if (this.isOpen) { chatContainer.classList.remove('max-h-0', 'opacity-0'); chatContainer.classList.add('max-h-96', 'opacity-100'); this.inputField.focus(); } else { chatContainer.classList.remove('max-h-96', 'opacity-100'); chatContainer.classList.add('max-h-0', 'opacity-0'); } } /** * Fügt eine Nachricht zum Chat-Verlauf hinzu * @param {string} sender - 'user' oder 'assistant' * @param {string} text - Nachrichtentext */ addMessage(sender, text) { // Nachricht zum Verlauf hinzufügen this.messages.push({ role: sender, content: text }); // DOM-Element erstellen const messageEl = document.createElement('div'); messageEl.className = `flex ${sender === 'user' ? 'justify-end' : 'justify-start'}`; const bubble = document.createElement('div'); bubble.className = sender === 'user' ? 'bg-primary-100 dark:bg-primary-900 text-gray-800 dark:text-white rounded-lg py-2 px-3 max-w-[85%]' : 'bg-gray-100 dark:bg-dark-700 text-gray-800 dark:text-white rounded-lg py-2 px-3 max-w-[85%]'; bubble.textContent = text; messageEl.appendChild(bubble); this.chatHistory.appendChild(messageEl); // Scroll zum Ende des Verlaufs this.chatHistory.scrollTop = this.chatHistory.scrollHeight; } /** * Sendet die Benutzernachricht an den Server und zeigt die Antwort an */ async sendMessage() { const userInput = this.inputField.value.trim(); if (!userInput || this.isLoading) return; // Benutzernachricht anzeigen this.addMessage('user', userInput); // Eingabefeld zurücksetzen this.inputField.value = ''; // Ladeindikator anzeigen this.isLoading = true; this.showLoadingIndicator(); try { // Anfrage an den Server senden const response = await fetch('/api/assistant', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ messages: this.messages }), }); if (!response.ok) { throw new Error('Netzwerkfehler oder Serverproblem'); } const data = await response.json(); // Ladeindikator entfernen this.removeLoadingIndicator(); // Antwort anzeigen this.addMessage('assistant', data.response); } catch (error) { console.error('Fehler bei der Kommunikation mit dem Assistenten:', error); // Ladeindikator entfernen this.removeLoadingIndicator(); // Fehlermeldung anzeigen this.addMessage('assistant', 'Es tut mir leid, aber es gab ein Problem bei der Verarbeitung deiner Anfrage. Bitte versuche es später noch einmal.'); } finally { this.isLoading = false; } } /** * Zeigt einen Ladeindikator im Chat an */ showLoadingIndicator() { const loadingEl = document.createElement('div'); loadingEl.id = 'assistant-loading'; loadingEl.className = 'flex justify-start'; const bubble = document.createElement('div'); bubble.className = 'bg-gray-100 dark:bg-dark-700 text-gray-800 dark:text-white rounded-lg py-2 px-3'; bubble.innerHTML = '
'; loadingEl.appendChild(bubble); this.chatHistory.appendChild(loadingEl); // Scroll zum Ende des Verlaufs this.chatHistory.scrollTop = this.chatHistory.scrollHeight; // Stil für den Typing-Indikator const style = document.createElement('style'); style.textContent = ` .typing-indicator { display: flex; align-items: center; } .typing-indicator span { height: 8px; width: 8px; background-color: #888; border-radius: 50%; display: inline-block; margin: 0 2px; opacity: 0.4; animation: typing 1.5s infinite ease-in-out; } .typing-indicator span:nth-child(2) { animation-delay: 0.2s; } .typing-indicator span:nth-child(3) { animation-delay: 0.4s; } @keyframes typing { 0% { transform: translateY(0); } 50% { transform: translateY(-5px); } 100% { transform: translateY(0); } } `; document.head.appendChild(style); } /** * Entfernt den Ladeindikator aus dem Chat */ removeLoadingIndicator() { const loadingEl = document.getElementById('assistant-loading'); if (loadingEl) { loadingEl.remove(); } } } // Exportiere die Klasse für die Verwendung in anderen Modulen export default ChatGPTAssistant;