280 lines
10 KiB
JavaScript
280 lines
10 KiB
JavaScript
/**
|
|
* 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 = '<i class="fas fa-robot text-xl"></i>';
|
|
|
|
// 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 = `
|
|
<div class="flex items-center">
|
|
<i class="fas fa-robot mr-2"></i>
|
|
<span>KI-Assistent</span>
|
|
</div>
|
|
<button id="assistant-close" class="text-white hover:text-gray-200">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
`;
|
|
|
|
// 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 = '<i class="fas fa-paper-plane"></i>';
|
|
|
|
// 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 = '<div class="typing-indicator"><span></span><span></span><span></span></div>';
|
|
|
|
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;
|