seo-generator-server/public/test-modulaire.html

681 lines
23 KiB
HTML

<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test Interface Modulaire - SEO Generator</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
color: #333;
}
.container {
max-width: 1400px;
margin: 0 auto;
padding: 20px;
display: grid;
grid-template-columns: 400px 1fr;
gap: 20px;
min-height: 100vh;
}
.controls-panel {
background: white;
border-radius: 15px;
padding: 25px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
height: fit-content;
position: sticky;
top: 20px;
}
.results-panel {
background: white;
border-radius: 15px;
padding: 25px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
display: flex;
flex-direction: column;
}
h1 {
color: #4a5568;
margin-bottom: 25px;
text-align: center;
font-size: 1.8em;
}
h2 {
color: #2d3748;
margin: 20px 0 15px 0;
font-size: 1.2em;
border-bottom: 2px solid #e2e8f0;
padding-bottom: 8px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
font-weight: 600;
margin-bottom: 8px;
color: #4a5568;
}
select, input[type="number"] {
width: 100%;
padding: 12px;
border: 2px solid #e2e8f0;
border-radius: 8px;
font-size: 14px;
transition: border-color 0.2s;
}
select:focus, input[type="number"]:focus {
outline: none;
border-color: #667eea;
}
.option-group {
background: #f7fafc;
padding: 15px;
border-radius: 8px;
margin-bottom: 15px;
}
.option-group h3 {
margin-bottom: 10px;
color: #2d3748;
font-size: 1em;
}
.option-description {
font-size: 12px;
color: #718096;
margin-top: 5px;
font-style: italic;
}
.button-group {
display: flex;
gap: 10px;
margin-top: 25px;
}
button {
flex: 1;
padding: 15px;
border: none;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
font-size: 14px;
}
.btn-primary {
background: linear-gradient(135deg, #667eea, #764ba2);
color: white;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}
.btn-secondary {
background: #e2e8f0;
color: #4a5568;
}
.btn-secondary:hover {
background: #cbd5e0;
}
.status {
padding: 12px;
border-radius: 8px;
margin-bottom: 20px;
font-weight: 500;
text-align: center;
display: none;
}
.status.success {
background: #c6f6d5;
color: #22543d;
border: 1px solid #9ae6b4;
}
.status.error {
background: #fed7d7;
color: #822727;
border: 1px solid #f56565;
}
.status.loading {
background: #bee3f8;
color: #2b6cb0;
border: 1px solid #63b3ed;
}
.logs-container {
background: #1a202c;
color: #e2e8f0;
border-radius: 8px;
padding: 15px;
font-family: 'Fira Code', monospace;
font-size: 12px;
height: 300px;
overflow-y: auto;
margin-bottom: 20px;
border: 1px solid #2d3748;
}
.log-entry {
margin-bottom: 4px;
padding: 2px 0;
}
.log-debug { color: #63b3ed; }
.log-info { color: #68d391; }
.log-warn { color: #f6e05e; }
.log-error { color: #fc8181; }
.log-trace { color: #a0aec0; }
.results-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin-bottom: 20px;
}
.stat-card {
background: #f7fafc;
padding: 15px;
border-radius: 8px;
text-align: center;
border: 1px solid #e2e8f0;
}
.stat-number {
font-size: 24px;
font-weight: 700;
color: #2d3748;
}
.stat-label {
font-size: 12px;
color: #718096;
margin-top: 5px;
}
.comparison-table {
width: 100%;
border-collapse: collapse;
margin-top: 20px;
}
.comparison-table th,
.comparison-table td {
padding: 10px;
text-align: left;
border-bottom: 1px solid #e2e8f0;
}
.comparison-table th {
background: #f7fafc;
font-weight: 600;
color: #4a5568;
}
.comparison-table td:last-child {
font-weight: 600;
}
.clear-logs {
position: absolute;
top: 10px;
right: 15px;
background: #4a5568;
color: white;
border: none;
padding: 5px 10px;
border-radius: 4px;
font-size: 11px;
cursor: pointer;
}
.progress-bar {
width: 100%;
height: 4px;
background: #e2e8f0;
border-radius: 2px;
overflow: hidden;
margin-bottom: 15px;
display: none;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #667eea, #764ba2);
width: 0%;
transition: width 0.3s ease;
}
</style>
</head>
<body>
<div class="container">
<div class="controls-panel">
<h1>🧪 Test Interface Modulaire</h1>
<div class="form-group">
<label for="rowNumber">Ligne Google Sheet</label>
<input type="number" id="rowNumber" value="2" min="2" max="100">
<div class="option-description">Ligne à traiter du Google Sheet</div>
</div>
<div class="option-group">
<h3>🔧 Selective Enhancement</h3>
<select id="selectiveStack">
<option value="lightEnhancement">Light Enhancement</option>
<option value="standardEnhancement" selected>Standard Enhancement</option>
<option value="fullEnhancement">Full Enhancement</option>
<option value="personalityFocus">Personality Focus</option>
<option value="fluidityFocus">Fluidity Focus</option>
<option value="adaptive">Adaptive</option>
</select>
<div class="option-description">Base d'amélioration du contenu généré</div>
</div>
<div class="option-group">
<h3>🎯 Adversarial Mode</h3>
<select id="adversarialMode">
<option value="none" selected>None</option>
<option value="light">Light</option>
<option value="standard">Standard</option>
<option value="heavy">Heavy</option>
<option value="adaptive">Adaptive</option>
</select>
<div class="option-description">Techniques adversariales anti-détection</div>
</div>
<div class="option-group">
<h3>🧠 Human Simulation</h3>
<select id="humanSimulationMode">
<option value="none" selected>None</option>
<option value="lightSimulation">Light Simulation</option>
<option value="standardSimulation">Standard Simulation</option>
<option value="heavySimulation">Heavy Simulation</option>
<option value="adaptiveSimulation">Adaptive Simulation</option>
<option value="personalityFocus">Personality Focus</option>
<option value="temporalFocus">Temporal Focus</option>
</select>
<div class="option-description">Simulation comportement humain (fatigue, erreurs)</div>
</div>
<div class="option-group">
<h3>🔧 Pattern Breaking</h3>
<select id="patternBreakingMode">
<option value="none" selected>None</option>
<option value="lightPatternBreaking">Light Pattern Breaking</option>
<option value="standardPatternBreaking">Standard Pattern Breaking</option>
<option value="heavyPatternBreaking">Heavy Pattern Breaking</option>
<option value="adaptivePatternBreaking">Adaptive Pattern Breaking</option>
<option value="syntaxFocus">Syntax Focus</option>
<option value="connectorsFocus">Connectors Focus</option>
</select>
<div class="option-description">Cassage patterns syntaxiques LLM</div>
</div>
<div class="button-group">
<button class="btn-primary" onclick="runTest()">
🚀 Lancer Test
</button>
<button class="btn-secondary" onclick="runBenchmark()">
📊 Benchmark
</button>
</div>
<div class="button-group">
<button class="btn-secondary" onclick="clearResults()">
🗑️ Clear
</button>
<button class="btn-secondary" onclick="saveConfig()">
💾 Save Config
</button>
</div>
</div>
<div class="results-panel">
<h2>📊 Résultats</h2>
<div class="status" id="status"></div>
<div class="progress-bar" id="progressBar">
<div class="progress-fill" id="progressFill"></div>
</div>
<div class="results-grid" id="statsGrid">
<!-- Stats cards will be inserted here -->
</div>
<h3 style="margin-top: 20px;">🔍 Logs Temps Réel</h3>
<div class="logs-container" id="logsContainer" style="position: relative;">
<button class="clear-logs" onclick="clearLogs()">Clear</button>
<div id="logs"></div>
</div>
<div id="comparisonResults" style="display: none;">
<h3>📈 Comparaison Résultats</h3>
<table class="comparison-table" id="comparisonTable">
<thead>
<tr>
<th>Configuration</th>
<th>Durée (ms)</th>
<th>Selective</th>
<th>Adversarial</th>
<th>Human</th>
<th>Pattern</th>
<th>Total Modifs</th>
</tr>
</thead>
<tbody id="comparisonBody">
</tbody>
</table>
</div>
</div>
</div>
<script>
let ws = null;
let testResults = [];
// WebSocket connection for real-time logs
function connectWebSocket() {
ws = new WebSocket('ws://localhost:8081');
ws.onopen = function() {
console.log('WebSocket connected');
};
ws.onmessage = function(event) {
try {
const logData = JSON.parse(event.data);
displayLog(logData);
} catch (e) {
displayLog({ level: 'info', msg: event.data });
}
};
ws.onclose = function() {
console.log('WebSocket disconnected');
setTimeout(connectWebSocket, 3000);
};
}
// Display log entry
function displayLog(logData) {
const logsDiv = document.getElementById('logs');
const logEntry = document.createElement('div');
logEntry.className = 'log-entry';
let levelClass = 'log-info';
if (logData.level <= 10) levelClass = 'log-trace';
else if (logData.level === 20) levelClass = 'log-debug';
else if (logData.level === 30) levelClass = 'log-info';
else if (logData.level === 40) levelClass = 'log-warn';
else if (logData.level >= 50) levelClass = 'log-error';
logEntry.innerHTML = `<span class="${levelClass}">${logData.msg}</span>`;
logsDiv.appendChild(logEntry);
// Auto-scroll to bottom
document.getElementById('logsContainer').scrollTop = document.getElementById('logsContainer').scrollHeight;
}
// Clear logs
function clearLogs() {
document.getElementById('logs').innerHTML = '';
}
// Show status
function showStatus(message, type) {
const statusDiv = document.getElementById('status');
statusDiv.textContent = message;
statusDiv.className = `status ${type}`;
statusDiv.style.display = 'block';
}
// Hide status
function hideStatus() {
document.getElementById('status').style.display = 'none';
}
// Show progress
function showProgress(percentage) {
const progressBar = document.getElementById('progressBar');
const progressFill = document.getElementById('progressFill');
progressBar.style.display = 'block';
progressFill.style.width = percentage + '%';
}
// Hide progress
function hideProgress() {
document.getElementById('progressBar').style.display = 'none';
}
// Display stats
function displayStats(stats) {
const statsGrid = document.getElementById('statsGrid');
statsGrid.innerHTML = '';
const statCards = [
{ label: 'Durée (ms)', value: stats.totalDuration || 0 },
{ label: 'Éléments Générés', value: stats.elementsGenerated || 0 },
{ label: 'Selective', value: stats.selectiveEnhancements || 0 },
{ label: 'Adversarial', value: stats.adversarialModifications || 0 },
{ label: 'Human Sim', value: stats.humanSimulationModifications || 0 },
{ label: 'Pattern Breaking', value: stats.patternBreakingModifications || 0 },
{ label: 'Taille Finale', value: stats.finalLength || 0 },
{ label: 'Personnalité', value: stats.personality || 'N/A' }
];
statCards.forEach(stat => {
const card = document.createElement('div');
card.className = 'stat-card';
card.innerHTML = `
<div class="stat-number">${stat.value}</div>
<div class="stat-label">${stat.label}</div>
`;
statsGrid.appendChild(card);
});
}
// Run single test
async function runTest() {
const config = {
rowNumber: parseInt(document.getElementById('rowNumber').value),
selectiveStack: document.getElementById('selectiveStack').value,
adversarialMode: document.getElementById('adversarialMode').value,
humanSimulationMode: document.getElementById('humanSimulationMode').value,
patternBreakingMode: document.getElementById('patternBreakingMode').value,
source: 'web_interface'
};
showStatus('🚀 Lancement du test en cours...', 'loading');
showProgress(10);
clearLogs();
try {
const response = await fetch('/api/test-modulaire', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(config)
});
showProgress(50);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const result = await response.json();
showProgress(100);
setTimeout(() => hideProgress(), 500);
if (result.success) {
showStatus('✅ Test terminé avec succès!', 'success');
displayStats(result.stats);
testResults.push({ config, result });
updateComparison();
} else {
showStatus('❌ Erreur: ' + (result.error || 'Erreur inconnue'), 'error');
}
} catch (error) {
hideProgress();
showStatus('❌ Erreur: ' + error.message, 'error');
console.error('Test failed:', error);
}
}
// Run benchmark
async function runBenchmark() {
showStatus('📊 Lancement benchmark...', 'loading');
showProgress(0);
clearLogs();
try {
const response = await fetch('/api/benchmark-modulaire', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
rowNumber: parseInt(document.getElementById('rowNumber').value)
})
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const result = await response.json();
showProgress(100);
setTimeout(() => hideProgress(), 500);
showStatus(`✅ Benchmark terminé: ${result.results.length} tests`, 'success');
// Add all benchmark results
result.results.forEach(benchResult => {
testResults.push({
config: {
selectiveStack: benchResult.stack,
adversarialMode: benchResult.adversarial,
humanSimulationMode: benchResult.humanSimulation,
patternBreakingMode: benchResult.patternBreaking || 'none'
},
result: { stats: benchResult }
});
});
updateComparison();
} catch (error) {
hideProgress();
showStatus('❌ Erreur benchmark: ' + error.message, 'error');
console.error('Benchmark failed:', error);
}
}
// Update comparison table
function updateComparison() {
if (testResults.length === 0) return;
const comparisonDiv = document.getElementById('comparisonResults');
const tbody = document.getElementById('comparisonBody');
comparisonDiv.style.display = 'block';
tbody.innerHTML = '';
testResults.forEach((test, index) => {
const row = document.createElement('tr');
const config = `${test.config.selectiveStack} + ${test.config.adversarialMode} + ${test.config.humanSimulationMode} + ${test.config.patternBreakingMode}`;
const stats = test.result.stats;
const totalMods = (stats.selectiveEnhancements || 0) +
(stats.adversarialModifications || 0) +
(stats.humanSimulationModifications || 0) +
(stats.patternBreakingModifications || 0);
row.innerHTML = `
<td>${config}</td>
<td>${stats.totalDuration || stats.duration || 0}</td>
<td>${stats.selectiveEnhancements || 0}</td>
<td>${stats.adversarialModifications || 0}</td>
<td>${stats.humanSimulationModifications || 0}</td>
<td>${stats.patternBreakingModifications || 0}</td>
<td><strong>${totalMods}</strong></td>
`;
tbody.appendChild(row);
});
}
// Clear results
function clearResults() {
testResults = [];
document.getElementById('statsGrid').innerHTML = '';
document.getElementById('comparisonResults').style.display = 'none';
hideStatus();
hideProgress();
clearLogs();
}
// Save configuration
function saveConfig() {
const config = {
rowNumber: document.getElementById('rowNumber').value,
selectiveStack: document.getElementById('selectiveStack').value,
adversarialMode: document.getElementById('adversarialMode').value,
humanSimulationMode: document.getElementById('humanSimulationMode').value,
patternBreakingMode: document.getElementById('patternBreakingMode').value
};
localStorage.setItem('modulaire-config', JSON.stringify(config));
showStatus('💾 Configuration sauvegardée', 'success');
setTimeout(hideStatus, 2000);
}
// Load configuration
function loadConfig() {
const saved = localStorage.getItem('modulaire-config');
if (saved) {
const config = JSON.parse(saved);
document.getElementById('rowNumber').value = config.rowNumber;
document.getElementById('selectiveStack').value = config.selectiveStack;
document.getElementById('adversarialMode').value = config.adversarialMode;
document.getElementById('humanSimulationMode').value = config.humanSimulationMode;
document.getElementById('patternBreakingMode').value = config.patternBreakingMode;
}
}
// Initialize
window.onload = function() {
connectWebSocket();
loadConfig();
};
</script>
</body>
</html>