Spaces:
Sleeping
Sleeping
| 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 = '<p class="no-results">No results has been found.</p>'; | |
| 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 = ` | |
| <div class="card-header"> | |
| <div class="card-info"> | |
| <div class="card-id">ID: ${this.escapeHtml(result.id)}</div> | |
| <div class="card-title">${this.escapeHtml(result.title)}</div> | |
| <div class="card-section">${this.escapeHtml(result.section)}</div> | |
| </div> | |
| <div class="similarity-score">${this.escapeHtml(result.similarity)}%</div> | |
| </div> | |
| <div class="card-actions"> | |
| <button class="view-btn" data-id="${this.escapeHtml(result.id)}" data-section="${this.escapeHtml(result.section)}" data-content="${this.escapeHtml(result.content)}"> | |
| <span class="material-icons">visibility</span> | |
| Voir le contenu | |
| </button> | |
| <input type="checkbox" class="select-checkbox" onchange="app.toggleDocumentSelection(${this.escapeHtml(index)})"> | |
| </div> | |
| `; | |
| 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; | |