class DocumentSearchChatBot { constructor() { this.selectedDocuments = new Set(); this.searchResults = []; this.chatHistory = []; this.initializeEventListeners(); this.updateThresholdDisplay(); } initializeEventListeners() { // Search form document.getElementById('search-form').addEventListener('submit', (e) => { e.preventDefault(); this.performSearch(); }); // Threshold slider document.getElementById('threshold').addEventListener('input', (e) => { this.updateThresholdDisplay(); }); // Selection controls document.getElementById('select-all').addEventListener('click', () => { this.selectAllDocuments(); }); document.getElementById('unselect-all').addEventListener('click', () => { this.unselectAllDocuments(); }); // Chat launch document.getElementById('start-chat').addEventListener('click', () => { this.startChat(); }); // Chat form document.getElementById('chat-form').addEventListener('submit', (e) => { e.preventDefault(); this.sendChatMessage(); }); // Back to search document.getElementById('back-to-search').addEventListener('click', () => { this.backToSearch(); }); // Modal close document.querySelector('.modal-close').addEventListener('click', () => { this.closeModal(); }); // Close modal on background click document.getElementById('modal').addEventListener('click', (e) => { if (e.target.id === 'modal') { this.closeModal(); } }); } updateThresholdDisplay() { const threshold = document.getElementById('threshold').value; document.getElementById('threshold-value').textContent = threshold; } async performSearch() { const keyword = document.getElementById('keyword').value.trim(); const threshold = parseFloat(document.getElementById('threshold').value); if (!keyword) return; this.showLoading(); try { const response = await fetch('/search', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ keyword: keyword, threshold: threshold }) }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); this.searchResults = data.results || []; this.displayResults(); } catch (error) { console.error('Error on search:', error); this.showError(`Error on search. Please try again. : ${error}`); } finally { this.hideLoading(); } } displayResults() { const resultsContainer = document.getElementById('results-container'); const resultsSection = document.getElementById('results-section'); if (this.searchResults.length === 0) { resultsContainer.innerHTML = '

No results has been found.

'; resultsSection.classList.remove('hidden'); return; } resultsContainer.innerHTML = ''; this.searchResults.forEach((result, index) => { const card = this.createResultCard(result, index); resultsContainer.appendChild(card); let viewBtn = card.querySelector('.view-btn'); viewBtn.addEventListener('click', () => { this.showDocumentContent(result.id, result.section, result.content); }); }); resultsSection.classList.remove('hidden'); this.updateChatButtonState(); } escapeHtml(str) { if (!str) return ''; const div = document.createElement('div'); div.textContent = str; return div.innerHTML; } createResultCard(result, index) { const card = document.createElement('div'); card.className = 'result-card'; card.dataset.index = index; card.innerHTML = `
ID: ${this.escapeHtml(result.id)}
${this.escapeHtml(result.title)}
${this.escapeHtml(result.section)}
${this.escapeHtml(result.similarity)}%
`; return card; } toggleDocumentSelection(index) { const card = document.querySelector(`[data-index="${index}"]`); const checkbox = card.querySelector('.select-checkbox'); if (checkbox.checked) { this.selectedDocuments.add(index); card.classList.add('selected'); } else { this.selectedDocuments.delete(index); card.classList.remove('selected'); } this.updateChatButtonState(); } selectAllDocuments() { const checkboxes = document.querySelectorAll('.select-checkbox'); checkboxes.forEach((checkbox, index) => { checkbox.checked = true; this.selectedDocuments.add(index); checkbox.closest('.result-card').classList.add('selected'); }); this.updateChatButtonState(); } unselectAllDocuments() { const checkboxes = document.querySelectorAll('.select-checkbox'); checkboxes.forEach((checkbox, index) => { checkbox.checked = false; this.selectedDocuments.delete(index); checkbox.closest('.result-card').classList.remove('selected'); }); this.updateChatButtonState(); } updateChatButtonState() { const chatButton = document.getElementById('start-chat'); chatButton.disabled = this.selectedDocuments.size === 0; } showDocumentContent(id, section, content) { // Simuler le contenu du document (remplacer par un appel API réel) document.getElementById('modal-title').textContent = `Specification n°${id} - ${section}`; document.getElementById('modal-body').textContent = content; document.getElementById('modal').style.display = 'block'; } closeModal() { document.getElementById('modal').style.display = 'none'; } startChat() { if (this.selectedDocuments.size === 0) return; const selectedDocs = Array.from(this.selectedDocuments).map(index => { const doc = this.searchResults[index]; return `(${doc.id} ${doc.title} ${doc.section} ${doc.content || ""})` }).join("\n"); this.chatHistory = [{ "role": "system", "content": `You are a helpful AI assistant. You will answer any questions related to the following specifications: ${selectedDocs}` }]; document.getElementById('search-section').classList.add('hidden'); document.getElementById('results-section').classList.add('hidden'); document.getElementById('chat-section').classList.remove('hidden'); // Ajouter un message de bienvenue this.addChatMessage('bot', `Hello ! I'm ready to answer to all of your questions regarding the ${this.selectedDocuments.size} selected section(s) of different specifications. What do you want to know ?`); } backToSearch() { document.getElementById('chat-section').classList.add('hidden'); document.getElementById('search-section').classList.remove('hidden'); document.getElementById('results-section').classList.remove('hidden'); // Vider les messages du chat document.getElementById('chat-messages').innerHTML = ''; } async sendChatMessage() { const input = document.getElementById('chat-input'); const message = input.value.trim(); const model = document.getElementById('model-select').value; if (!message) return; // Ajouter le message de l'utilisateur this.addChatMessage('user', message); input.value = ''; this.chatHistory.push({ "role": "user", "content": message }); // Désactiver le formulaire pendant l'envoi const form = document.getElementById('chat-form'); const submitBtn = form.querySelector('button'); submitBtn.disabled = true; try { const response = await fetch('/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ messages: this.chatHistory, model: model, }) }); if (!response.ok) { this.chatHistory.push({ "role": "assistant", "content": `HTTP error! status: ${response.status}` }) throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); this.addChatMessage('bot', data.response); this.chatHistory.push({ "role": "assistant", "content": data.response }) } catch (error) { console.error('Error on sending message:', error); this.addChatMessage('bot', `Sorry, an error has occurred. Please try again. : ${error}`); } finally { submitBtn.disabled = false; } } addChatMessage(sender, message) { const messagesContainer = document.getElementById('chat-messages'); const messageDiv = document.createElement('div'); messageDiv.className = `message ${sender}`; try { // Vérifier si marked est disponible if (typeof marked !== 'undefined' && marked.parse) { messageDiv.innerHTML = marked.parse(message); } else if (typeof marked !== 'undefined' && marked.marked) { // Pour les versions plus anciennes messageDiv.innerHTML = marked.marked(message); } else { console.error('Marked library not loaded properly'); messageDiv.textContent = message; // Fallback } } catch (error) { console.error('Markdown parsing error:', error); messageDiv.textContent = message; // Fallback en cas d'erreur } messagesContainer.appendChild(messageDiv); messagesContainer.scrollTop = messagesContainer.scrollHeight; } showLoading() { document.getElementById('loading').classList.remove('hidden'); } hideLoading() { document.getElementById('loading').classList.add('hidden'); } showError(message) { // Vous pouvez implémenter une notification d'erreur plus sophistiquée alert(message); } } // Initialiser l'application const app = new DocumentSearchChatBot(); // Fonction globale pour les événements onclick dans le HTML window.app = app;