Files
webval/assets/js/chatbot_settings.js

440 lines
14 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* JavaScript pour la gestion des clés API du chatbot
*/
// État global
let currentProvider = null;
let editMode = false;
// Helper URLs
const API_HELP_URLS = {
'gemini': 'https://aistudio.google.com/apikey',
'mistral': 'https://console.mistral.ai/api-keys/',
'openai': 'https://platform.openai.com/api-keys'
};
// ========== Modal Management ==========
function openAddKeyModal(provider = null) {
const modal = document.getElementById('keyModal');
const title = document.getElementById('modalTitle');
const form = document.getElementById('keyForm');
const providerSelect = document.getElementById('provider');
const testResult = document.getElementById('testResult');
// Reset form
form.reset();
testResult.style.display = 'none';
document.getElementById('keyAction').value = 'add';
editMode = false;
// Important : activer le select AVANT de définir la valeur
providerSelect.disabled = false;
// Pre-select provider if provided
if (provider) {
providerSelect.value = provider;
providerSelect.dispatchEvent(new Event('change')); // Trigger updateModelsList
}
title.textContent = 'Ajouter une clé API';
modal.classList.add('show');
}
function editKey(provider) {
currentProvider = provider;
editMode = true;
// Récupérer les infos de la clé
fetch(`chatbot_api_ajax.php?action=get&provider=${provider}`)
.then(response => response.json())
.then(data => {
if (data.success) {
const modal = document.getElementById('keyModal');
const title = document.getElementById('modalTitle');
const form = document.getElementById('keyForm');
const providerSelect = document.getElementById('provider');
const modelSelect = document.getElementById('model_default');
const testResult = document.getElementById('testResult');
testResult.style.display = 'none';
document.getElementById('keyAction').value = 'edit';
// Remplir le formulaire
providerSelect.value = provider;
providerSelect.disabled = true;
// Champ hidden pour envoyer provider même si disabled
let hiddenProvider = document.getElementById('hidden_provider');
if (!hiddenProvider) {
hiddenProvider = document.createElement('input');
hiddenProvider.type = 'hidden';
hiddenProvider.name = 'provider';
hiddenProvider.id = 'hidden_provider';
document.getElementById('keyForm').appendChild(hiddenProvider);
}
hiddenProvider.value = provider;
updateModelsList();
// Attendre que la liste des modèles soit chargée
setTimeout(() => {
modelSelect.value = data.data.model_default;
if (data.data.quota_max) {
document.getElementById('set_quota').checked = true;
document.getElementById('quota_max').value = data.data.quota_max;
toggleQuotaInput();
}
}, 100);
title.textContent = `Modifier la clé ${provider}`;
modal.classList.add('show');
} else {
showToast(data.error || 'Erreur lors du chargement', 'error');
}
})
.catch(error => {
console.error('Error:', error);
showToast('Erreur de connexion', 'error');
});
}
function closeKeyModal() {
const modal = document.getElementById('keyModal');
modal.classList.remove('show');
currentProvider = null;
editMode = false;
}
// ========== Form Handling ==========
function updateModelsList() {
const provider = document.getElementById('provider').value;
const modelSelect = document.getElementById('model_default');
const keyHelp = document.getElementById('keyHelp');
// Clear current options
modelSelect.innerHTML = '<option value="">-- Sélectionner un modèle --</option>';
if (!provider) {
keyHelp.innerHTML = '';
return;
}
// Populate models for selected provider
if (models[provider]) {
models[provider].forEach(model => {
const option = document.createElement('option');
option.value = model.model_id;
option.textContent = model.display_name;
if (model.recommended) {
option.textContent += ' ⭐';
}
modelSelect.appendChild(option);
});
// Auto-select first recommended model
const recommended = models[provider].find(m => m.recommended);
if (recommended) {
modelSelect.value = recommended.model_id;
}
}
// Update help text
if (API_HELP_URLS[provider]) {
keyHelp.innerHTML = `💡 Obtenir une clé : <a href="${API_HELP_URLS[provider]}" target="_blank">${API_HELP_URLS[provider]}</a>`;
} else {
keyHelp.innerHTML = '';
}
}
function toggleQuotaInput() {
const checkbox = document.getElementById('set_quota');
const quotaGroup = document.getElementById('quotaGroup');
quotaGroup.style.display = checkbox.checked ? 'block' : 'none';
}
function toggleKeyVisibility() {
const input = document.getElementById('api_key');
const button = event.target;
if (input.type === 'password') {
input.type = 'text';
button.textContent = '🙈';
} else {
input.type = 'password';
button.textContent = '👁️';
}
}
// ========== Form Submission ==========
document.getElementById('keyForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
formData.append('action', 'save');
// Show loading
const submitBtn = this.querySelector('button[type="submit"]');
const originalText = submitBtn.innerHTML;
submitBtn.innerHTML = '⏳ Enregistrement...';
submitBtn.disabled = true;
fetch('chatbot_api_ajax.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
showToast(data.message || 'Clé enregistrée avec succès', 'success');
closeKeyModal();
// Reload page to show updated keys
setTimeout(() => {
window.location.reload();
}, 1000);
} else {
showToast(data.error || 'Erreur lors de l\'enregistrement', 'error');
}
})
.catch(error => {
console.error('Error:', error);
showToast('Erreur de connexion', 'error');
})
.finally(() => {
submitBtn.innerHTML = originalText;
submitBtn.disabled = false;
});
});
// ========== Test Connection ==========
function testKeyBeforeSave() {
const provider = document.getElementById('provider').value;
const apiKey = document.getElementById('api_key').value;
const model = document.getElementById('model_default').value;
if (!provider || !apiKey || !model) {
showToast('Veuillez remplir tous les champs obligatoires', 'error');
return;
}
testConnectionWithParams(provider, apiKey, model);
}
function testConnection(provider) {
testConnectionWithParams(provider, null, null);
}
function testConnectionWithParams(provider, apiKey = null, model = null) {
const testResult = document.getElementById('testResult');
testResult.style.display = 'block';
testResult.className = 'test-result';
testResult.innerHTML = '🔄 Test en cours...';
const formData = new FormData();
formData.append('action', 'test');
formData.append('provider', provider);
if (apiKey) formData.append('api_key', apiKey);
if (model) formData.append('model', model);
fetch('chatbot_api_ajax.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
testResult.className = 'test-result success';
testResult.innerHTML = `${data.message}<br><small>Latence: ${data.latency_ms}ms</small>`;
} else {
testResult.className = 'test-result error';
testResult.innerHTML = `${data.message}`;
}
})
.catch(error => {
console.error('Error:', error);
testResult.className = 'test-result error';
testResult.innerHTML = '❌ Erreur de connexion au serveur';
});
}
// ========== Delete Key ==========
function deleteKey(provider) {
if (!confirm(`Êtes-vous sûr de vouloir supprimer la clé ${provider} ?\n\nCette action est irréversible.`)) {
return;
}
const formData = new FormData();
formData.append('action', 'delete');
formData.append('provider', provider);
fetch('chatbot_api_ajax.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
showToast('Clé supprimée avec succès', 'success');
setTimeout(() => {
window.location.reload();
}, 1000);
} else {
showToast(data.error || 'Erreur lors de la suppression', 'error');
}
})
.catch(error => {
console.error('Error:', error);
showToast('Erreur de connexion', 'error');
});
}
// ========== Set Default Provider ==========
function setAsDefault(provider, model) {
const formData = new FormData();
formData.append('action', 'set_default');
formData.append('provider', provider);
formData.append('model', model);
fetch('chatbot_api_ajax.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
showToast(data.message || 'Provider par défaut mis à jour', 'success');
setTimeout(() => {
window.location.reload();
}, 1000);
} else {
showToast(data.error || 'Erreur', 'error');
}
})
.catch(error => {
console.error('Error:', error);
showToast('Erreur de connexion', 'error');
});
}
// ========== Configure Ollama ==========
function configureOllama() {
const availableModels = models['ollama'] || [];
if (availableModels.length === 0) {
showToast('Aucun modèle Ollama disponible', 'error');
return;
}
// Créer une liste des modèles
let modelOptions = '<select id="ollama-model-select" style="width: 100%; padding: 0.5rem; margin: 1rem 0; border: 2px solid #e2e8f0; border-radius: 8px;">';
availableModels.forEach(model => {
modelOptions += `<option value="${model.model_id}">${model.display_name}</option>`;
});
modelOptions += '</select>';
// Simple prompt
const modelHtml = `
<div style="padding: 1rem;">
<p style="margin-bottom: 1rem;">Choisir le modèle Ollama par défaut :</p>
${modelOptions}
</div>
`;
// Utiliser une confirm box simple pour l'instant
// (dans une vraie app, utiliser le modal existant)
const modal = document.getElementById('keyModal');
const modalContent = modal.querySelector('.modal-content');
const originalContent = modalContent.innerHTML;
modalContent.innerHTML = `
<div class="modal-header">
<h2>Configuration Ollama</h2>
<button class="modal-close" onclick="closeKeyModal()">×</button>
</div>
<div style="padding: 1.5rem;">
<p style="margin-bottom: 1rem;">Choisir le modèle Ollama à utiliser par défaut :</p>
<select id="ollama-model-select" style="width: 100%; padding: 0.75rem; border: 2px solid #e2e8f0; border-radius: 8px; font-family: 'JetBrains Mono', monospace;">
${availableModels.map(m => `<option value="${m.model_id}">${m.display_name}</option>`).join('')}
</select>
<div style="display: flex; gap: 1rem; justify-content: flex-end; margin-top: 1.5rem; padding-top: 1.5rem; border-top: 2px solid #e2e8f0;">
<button class="btn-secondary" onclick="closeKeyModal(); document.querySelector('.modal-content').innerHTML = \`${originalContent.replace(/`/g, '\\`')}\`;">Annuler</button>
<button class="btn-primary" onclick="saveOllamaConfig()">💾 Enregistrer</button>
</div>
</div>
`;
modal.classList.add('show');
}
function saveOllamaConfig() {
const select = document.getElementById('ollama-model-select');
const model = select.value;
if (!model) {
showToast('Veuillez sélectionner un modèle', 'error');
return;
}
setAsDefault('ollama', model);
}
// ========== Toast Notifications ==========
function showToast(message, type = 'info') {
const container = document.getElementById('toast-container');
const toast = document.createElement('div');
toast.className = `toast ${type}`;
const icons = {
'success': '✅',
'error': '❌',
'warning': '⚠️',
'info': ''
};
toast.innerHTML = `
<span class="toast-icon">${icons[type] || icons.info}</span>
<span class="toast-message">${message}</span>
`;
container.appendChild(toast);
// Auto-remove after 4 seconds
setTimeout(() => {
toast.style.opacity = '0';
toast.style.transform = 'translateX(100%)';
setTimeout(() => toast.remove(), 300);
}, 4000);
}
// ========== Close modal on outside click ==========
document.getElementById('keyModal').addEventListener('click', function(e) {
if (e.target === this) {
closeKeyModal();
}
});
// ========== Keyboard shortcuts ==========
document.addEventListener('keydown', function(e) {
// ESC to close modal
if (e.key === 'Escape') {
const modal = document.getElementById('keyModal');
if (modal.classList.contains('show')) {
closeKeyModal();
}
}
});
// ========== Initialize ==========
console.log('✅ Chatbot Settings JS loaded');
console.log('Available models:', models);