/** * Pipeline Runner - Client Side Logic * Gestion de l'exécution des pipelines sauvegardés */ // État global const state = { pipelines: [], selectedPipeline: null, running: false }; // ==================== // INITIALIZATION // ==================== window.onload = async function() { await loadPipelinesList(); }; // Charger la liste des pipelines async function loadPipelinesList() { try { const response = await fetch('/api/pipeline/list'); const data = await response.json(); if (data.success) { state.pipelines = data.pipelines; renderPipelinesDropdown(); } } catch (error) { showStatus(`Erreur chargement pipelines: ${error.message}`, 'error'); } } // Rendre le dropdown de pipelines function renderPipelinesDropdown() { const select = document.getElementById('pipelineSelect'); select.innerHTML = ''; state.pipelines.forEach(pipeline => { const option = document.createElement('option'); option.value = pipeline.name; option.textContent = `${pipeline.name} (${pipeline.steps} étapes, ~${pipeline.estimatedDuration})`; select.appendChild(option); }); } // ==================== // PIPELINE LOADING // ==================== async function loadPipeline() { const select = document.getElementById('pipelineSelect'); const pipelineName = select.value; if (!pipelineName) { document.getElementById('pipelinePreview').style.display = 'none'; document.getElementById('btnRun').disabled = true; return; } try { const response = await fetch(`/api/pipeline/${pipelineName}`); const data = await response.json(); if (data.success) { state.selectedPipeline = data.pipeline; displayPipelinePreview(data.pipeline); document.getElementById('btnRun').disabled = false; } } catch (error) { showStatus(`Erreur chargement pipeline: ${error.message}`, 'error'); } } // Afficher la prévisualisation du pipeline function displayPipelinePreview(pipeline) { const preview = document.getElementById('pipelinePreview'); preview.style.display = 'block'; document.getElementById('pipelineName').textContent = pipeline.name; document.getElementById('pipelineDesc').textContent = pipeline.description || 'Pas de description'; document.getElementById('summarySteps').textContent = pipeline.pipeline.length; // Estimation durée const estimatedSeconds = pipeline.pipeline.length * 20; // Rough estimate const minutes = Math.floor(estimatedSeconds / 60); const seconds = estimatedSeconds % 60; document.getElementById('summaryDuration').textContent = minutes > 0 ? `${minutes}m ${seconds}s` : `${seconds}s`; // Liste des étapes const stepList = document.getElementById('stepList'); stepList.innerHTML = ''; pipeline.pipeline.forEach(step => { const div = document.createElement('div'); div.className = 'step-item'; div.textContent = `${step.step}. ${step.module} (${step.mode}) - Intensité: ${step.intensity}`; stepList.appendChild(div); }); } // ==================== // PIPELINE EXECUTION // ==================== async function runPipeline() { if (!state.selectedPipeline) { showStatus('Aucun pipeline sélectionné', 'error'); return; } if (state.running) { showStatus('Une exécution est déjà en cours', 'error'); return; } const rowNumber = parseInt(document.getElementById('rowNumber').value); const saveIntermediateSteps = document.getElementById('saveIntermediateSteps').checked; if (!rowNumber || rowNumber < 2) { showStatus('Numéro de ligne invalide (minimum 2)', 'error'); return; } state.running = true; document.getElementById('btnRun').disabled = true; // Show progress section document.getElementById('progressSection').style.display = 'block'; document.getElementById('progressBar').style.display = 'block'; document.getElementById('progressText').style.display = 'block'; document.getElementById('resultsSection').style.display = 'none'; showStatus('🚀 Exécution du pipeline en cours...', 'loading'); updateProgress(0, 'Initialisation...'); try { const response = await fetch('/api/pipeline/execute', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ pipelineConfig: state.selectedPipeline, rowNumber: rowNumber, options: { saveIntermediateSteps: saveIntermediateSteps } }) }); updateProgress(50, 'Traitement en cours...'); const data = await response.json(); updateProgress(100, 'Terminé!'); if (data.success) { displayResults(data.result); showStatus('✅ Pipeline exécuté avec succès!', 'success'); } else { showStatus(`❌ Erreur: ${data.error}`, 'error'); } } catch (error) { showStatus(`❌ Erreur exécution: ${error.message}`, 'error'); console.error('Execution error:', error); } finally { state.running = false; document.getElementById('btnRun').disabled = false; setTimeout(() => { document.getElementById('progressSection').style.display = 'none'; }, 2000); } } // ==================== // RESULTS DISPLAY // ==================== function displayResults(result) { const resultsSection = document.getElementById('resultsSection'); resultsSection.style.display = 'block'; // Log complet des résultats dans la console console.log('=== RÉSULTAT PIPELINE ==='); console.log('Contenu final:', result.finalContent || result.content); console.log('Stats:', result.stats); console.log('Version history:', result.versionHistory); console.log('Résultat complet:', result); console.log('========================'); // Stats document.getElementById('statDuration').textContent = `${result.stats.totalDuration}ms`; document.getElementById('statSuccessSteps').textContent = `${result.stats.successfulSteps}/${result.stats.totalSteps}`; document.getElementById('statPersonality').textContent = result.stats.personality || 'N/A'; // Final Content Display const finalContentContainer = document.getElementById('finalContentContainer'); let rawContent = result.finalContent || result.content || result.organicContent; // Extraire le texte si c'est un objet let finalContent; let isStructuredContent = false; if (typeof rawContent === 'string') { finalContent = rawContent; } else if (rawContent && typeof rawContent === 'object') { // Vérifier si c'est un contenu structuré (H2_1, H3_2, etc.) const keys = Object.keys(rawContent); if (keys.some(k => k.match(/^(H2|H3|P)_\d+$/))) { isStructuredContent = true; // Formater le contenu structuré finalContent = keys .sort((a, b) => { // Trier par type (H2, H3, P) puis par numéro const aMatch = a.match(/^([A-Z]+)_(\d+)$/); const bMatch = b.match(/^([A-Z]+)_(\d+)$/); if (!aMatch || !bMatch) return 0; if (aMatch[1] !== bMatch[1]) return aMatch[1].localeCompare(bMatch[1]); return parseInt(aMatch[2]) - parseInt(bMatch[2]); }) .map(key => { const match = key.match(/^([A-Z0-9]+)_(\d+)$/); if (match) { const tag = match[1]; return `[${tag}]\n${rawContent[key]}\n`; } return `${key}: ${rawContent[key]}\n`; }) .join('\n'); } else { // Si c'est un objet, essayer d'extraire le texte finalContent = rawContent.text || rawContent.content || rawContent.organicContent || JSON.stringify(rawContent, null, 2); } } if (finalContent) { finalContentContainer.innerHTML = ''; // Warning si contenu incomplet const elementCount = Object.keys(rawContent || {}).length; if (isStructuredContent && elementCount < 30) { const warningDiv = document.createElement('div'); warningDiv.style.cssText = 'padding: 10px; margin-bottom: 15px; background: #fed7d7; border: 1px solid #f56565; border-radius: 6px; color: #822727;'; warningDiv.innerHTML = `⚠️ Génération incomplète: ${elementCount} éléments générés (attendu ~33). Vérifiez les logs pour plus de détails.`; finalContentContainer.appendChild(warningDiv); } // Créer un élément pre pour préserver le formatage const contentDiv = document.createElement('div'); contentDiv.style.cssText = 'white-space: pre-wrap; line-height: 1.6; color: var(--text-dark); font-size: 14px;'; contentDiv.textContent = finalContent; // Ajouter un bouton pour copier const copyBtn = document.createElement('button'); copyBtn.textContent = '📋 Copier le contenu'; copyBtn.style.cssText = 'margin-bottom: 15px; padding: 8px 16px; background: var(--primary); color: white; border: none; border-radius: 6px; cursor: pointer; font-weight: 600;'; copyBtn.onclick = () => { navigator.clipboard.writeText(finalContent); copyBtn.textContent = '✓ Copié!'; setTimeout(() => { copyBtn.textContent = '📋 Copier le contenu'; }, 2000); }; finalContentContainer.appendChild(copyBtn); finalContentContainer.appendChild(contentDiv); // Ajouter les métadonnées si disponibles if (result.stats) { const metaDiv = document.createElement('div'); metaDiv.style.cssText = 'margin-top: 15px; padding: 10px; background: white; border-radius: 6px; font-size: 12px; color: var(--text-light);'; const contentLength = finalContent.length; const wordCount = finalContent.split(/\s+/).length; metaDiv.innerHTML = `Métadonnées: ${contentLength} caractères, ~${wordCount} mots`; finalContentContainer.appendChild(metaDiv); } } else { finalContentContainer.innerHTML = '
⚠️ Aucun contenu final disponible dans le résultat
'; } // Version History const versionHistoryContainer = document.getElementById('versionHistory'); versionHistoryContainer.innerHTML = ''; if (result.versionHistory && result.versionHistory.length > 0) { result.versionHistory.forEach(version => { const div = document.createElement('div'); div.style.cssText = 'padding: 10px; margin-bottom: 8px; background: white; border-radius: 6px; border-left: 4px solid var(--success);'; const versionLabel = document.createElement('strong'); versionLabel.textContent = `Version ${version.version}`; versionLabel.style.color = 'var(--primary)'; const articleId = document.createElement('span'); articleId.textContent = ` - Article ID: ${version.articleId}`; articleId.style.color = 'var(--text-dark)'; const modifications = document.createElement('span'); modifications.textContent = ` - ${version.modifications || 0} modifications`; modifications.style.color = 'var(--text-light)'; modifications.style.fontSize = '13px'; modifications.style.marginLeft = '10px'; div.appendChild(versionLabel); div.appendChild(articleId); div.appendChild(modifications); versionHistoryContainer.appendChild(div); }); } else { versionHistoryContainer.innerHTML = 'Aucune version sauvegardée
'; } // Google Sheets Link if (result.gsheetsLink) { document.getElementById('gsheetsLinkContainer').style.display = 'block'; document.getElementById('gsheetsLink').href = result.gsheetsLink; } else { document.getElementById('gsheetsLinkContainer').style.display = 'none'; } // Execution log const logContainer = document.getElementById('executionLog'); logContainer.innerHTML = ''; if (result.executionLog && result.executionLog.length > 0) { result.executionLog.forEach(logEntry => { const div = document.createElement('div'); div.className = `log-entry ${logEntry.success ? 'log-success' : 'log-error'}`; const status = logEntry.success ? '✓' : '✗'; const text = `${status} Étape ${logEntry.step}: ${logEntry.module} (${logEntry.mode}) ` + `- ${logEntry.duration}ms`; if (logEntry.modifications !== undefined) { div.textContent = text + ` - ${logEntry.modifications} modifs`; } else { div.textContent = text; } if (!logEntry.success && logEntry.error) { div.textContent += ` - Erreur: ${logEntry.error}`; } logContainer.appendChild(div); }); } else { logContainer.textContent = 'Aucun log d\'exécution disponible'; } } // ==================== // HELPERS // ==================== function updateProgress(percentage, text) { document.getElementById('progressFill').style.width = percentage + '%'; document.getElementById('progressText').textContent = text; } function showStatus(message, type) { const status = document.getElementById('status'); status.textContent = message; status.className = `status ${type}`; status.style.display = 'block'; if (type !== 'loading') { setTimeout(() => { status.style.display = 'none'; }, 5000); } }