# 🚀 Production Ready - Interface Configuration & Runner

**Date**: 2025-10-08
**Version**: 1.0.0
**Objectif**: Créer une interface complète pour configurer, sauvegarder et exécuter des workflows modulaires de production

---

## 📋 Table des Matières

1. [Vue d'Ensemble](#vue-densemble)
2. [Architecture Proposée](#architecture-proposée)
3. [Architecture Modulaire Actuelle](#architecture-modulaire-actuelle)
4. [Composants à Créer](#composants-à-créer)
5. [Backend - Nouveaux Endpoints](#backend---nouveaux-endpoints)
6. [Schéma des Données](#schéma-des-données)
7. [Design System](#design-system)
8. [JavaScript - Fonctions Clés](#javascript---fonctions-clés)
9. [Plan d'Exécution Séquentiel](#plan-dexécution-séquentiel)
10. [Tests à Effectuer](#tests-à-effectuer)
11. [Notes Importantes](#notes-importantes)

---

## 🎯 Vue d'Ensemble

### Problématique

Actuellement, l'interface `test-modulaire.html` permet de tester des configurations modulaires, mais :
- ❌ Pas de sauvegarde des configurations testées
- ❌ Pas de réutilisation facile des configs
- ❌ Pas d'interface dédiée pour la production (Google Sheets)

### Solution Proposée

Créer **3 pages web** interconnectées :

1. **`index.html`** - Page d'accueil avec navigation
2. **`config-editor.html`** - Éditeur de configuration avec Save/Load/Delete
3. **`production-runner.html`** - Runner de production avec logs temps réel

---

## 🏗️ Architecture Proposée

```
┌─────────────────────────────────────────────────────────┐
│                    INDEX.HTML                          │
│                  (Page d'accueil)                      │
│                                                         │
│  ┌──────────────────────┐  ┌────────────────────────┐ │
│  │  🔧 Configuration   │  │  🚀 Production Run     │ │
│  │  Editor             │  │  Runner                │ │
│  └──────────────────────┘  └────────────────────────┘ │
└─────────────────────────────────────────────────────────┘
                 │                      │
                 ▼                      ▼
    ┌────────────────────┐   ┌──────────────────────┐
    │ config-editor.html │   │ production-runner.html│
    │                    │   │                       │
    │ - Éditer config    │   │ - Load config         │
    │ - Save/Load        │   │ - Select GSheet row   │
    │ - Test live        │   │ - Run production      │
    │ - Delete           │   │ - View results        │
    └────────────────────┘   └──────────────────────┘
```

### Flux de Travail

1. **Création Config** → `config-editor.html`
   - Éditer les 4 couches modulaires
   - Tester en direct
   - Sauvegarder avec nom

2. **Exécution Production** → `production-runner.html`
   - Charger config sauvegardée
   - Sélectionner ligne Google Sheets
   - Lancer workflow complet
   - Voir résultats + lien GSheets

---

## 📐 Architecture Modulaire Actuelle

### 4 Couches Modulaires

| Couche | Stacks Disponibles | Total |
|--------|-------------------|-------|
| **Selective Enhancement** | `lightEnhancement`, `standardEnhancement`, `fullEnhancement`, `personalityFocus`, `fluidityFocus`, `adaptive` | **6** |
| **Adversarial Generation** | `none`, `light`, `standard`, `heavy`, `adaptive` | **5** |
| **Human Simulation** | `none`, `lightSimulation`, `standardSimulation`, `heavySimulation`, `adaptiveSimulation`, `personalityFocus`, `temporalFocus` | **7** |
| **Pattern Breaking** | `none`, `lightPatternBreaking`, `standardPatternBreaking`, `heavyPatternBreaking`, `adaptivePatternBreaking`, `syntaxFocus`, `connectorsFocus` | **7** |

### Configuration Standard Actuelle

```javascript
{
  rowNumber: 2,
  selectiveStack: 'standardEnhancement',
  adversarialMode: 'light',
  humanSimulationMode: 'none',
  patternBreakingMode: 'none',
  saveIntermediateSteps: true,
  source: 'web_interface'
}
```

### Fichiers Modulaires Existants

- `lib/selective-enhancement/SelectiveLayers.js` - 6 stacks prédéfinis
- `lib/adversarial-generation/AdversarialLayers.js` - 5 modes défense
- `lib/human-simulation/HumanSimulationLayers.js` - 6 modes simulation
- `lib/pattern-breaking/PatternBreakingLayers.js` - 7 modes cassage patterns

---

## 🔧 Composants à Créer

### 1. Page d'Accueil : `public/index.html`

**Fonctionnalités :**
- Design simple et clean avec 2 grandes cards cliquables
- Navigation vers config-editor.html ou production-runner.html
- Affichage du status serveur (API `/api/status`)
- Statistiques rapides (nombre de configs sauvegardées, dernière exécution)

**Structure HTML :**

```html
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SEO Generator - Accueil</title>
    <style>
        /* Design system (voir section Design System) */
    </style>
</head>
<body>
    <header>
        <h1>🎯 SEO Generator - Dashboard</h1>
        <div class="server-status"></div>
    </header>

    <main>
        <div class="card-container">
            <!-- Card 1: Configuration Editor -->
            <div class="card" onclick="navigateTo('config-editor.html')">
                <div class="card-icon">🔧</div>
                <h2>Éditeur de Configuration</h2>
                <p>Créer, éditer et sauvegarder des configurations modulaires</p>
                <ul>
                    <li>4 couches configurables</li>
                    <li>Save/Load configs</li>
                    <li>Test en direct</li>
                </ul>
            </div>

            <!-- Card 2: Production Runner -->
            <div class="card" onclick="navigateTo('production-runner.html')">
                <div class="card-icon">🚀</div>
                <h2>Runner de Production</h2>
                <p>Exécuter un workflow complet sur Google Sheets</p>
                <ul>
                    <li>Load config sauvegardée</li>
                    <li>Sélection ligne GSheet</li>
                    <li>Logs temps réel</li>
                </ul>
            </div>
        </div>

        <div class="stats-panel">
            <h3>📊 Statistiques</h3>
            <div class="stats-grid">
                <div class="stat-item">
                    <span class="stat-value" id="configCount">-</span>
                    <span class="stat-label">Configs sauvegardées</span>
                </div>
                <div class="stat-item">
                    <span class="stat-value" id="lastRun">-</span>
                    <span class="stat-label">Dernière exécution</span>
                </div>
            </div>
        </div>
    </main>

    <script>
        function navigateTo(page) {
            window.location.href = page;
        }

        async function loadStats() {
            try {
                const statusResponse = await fetch('/api/status');
                const statusData = await statusResponse.json();

                const configResponse = await fetch('/api/config/list');
                const configData = await configResponse.json();

                document.getElementById('configCount').textContent = configData.count || 0;

                const uptime = Math.floor(statusData.uptime / 1000);
                document.getElementById('lastRun').textContent = `${uptime}s ago`;
            } catch (error) {
                console.error('Erreur chargement stats:', error);
            }
        }

        window.onload = loadStats;
    </script>
</body>
</html>
```

**API Calls :**
- `GET /api/status` : Status serveur + uptime
- `GET /api/config/list` : Nombre de configs sauvegardées

---

### 2. Éditeur de Config : `public/config-editor.html`

**Fonctionnalités :**
- **Formulaire complet** : 4 couches modulaires (selects inspirés de test-modulaire.html)
- **Ligne GSheet** : Input pour rowNumber
- **Actions :**
  - 💾 **Save Config** : Sauvegarder avec nom personnalisé
  - 📂 **Load Config** : Charger depuis liste déroulante
  - 🗑️ **Delete Config** : Supprimer une config existante
  - 🚀 **Test Live** : Tester la config immédiatement (comme test-modulaire.html)
- **Logs Temps Réel** : Container avec WebSocket (comme test-modulaire.html)
- **Preview Config** : Afficher JSON de la config actuelle

**Structure HTML Clé :**

```html
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <title>Éditeur de Configuration</title>
</head>
<body>
    <header>
        <h1>🔧 Éditeur de Configuration Modulaire</h1>
        <a href="index.html" class="btn-back">← Retour Accueil</a>
    </header>

    <div class="editor-container">
        <!-- Section Configuration -->
        <div class="config-section">
            <h2>⚙️ Configuration Modulaire</h2>

            <!-- Row Number -->
            <div class="form-group">
                <label>Ligne Google Sheet</label>
                <input type="number" id="rowNumber" value="2" min="2">
            </div>

            <!-- Selective Enhancement -->
            <div class="module-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>

            <!-- Adversarial Mode -->
            <div class="module-group">
                <h3>🎯 Adversarial Generation</h3>
                <select id="adversarialMode">
                    <option value="none">None</option>
                    <option value="light" selected>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>

            <!-- Human Simulation -->
            <div class="module-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>

            <!-- Pattern Breaking -->
            <div class="module-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>

        <!-- Section Save/Load -->
        <div class="actions-section">
            <h2>💾 Gestion Configurations</h2>

            <!-- Save -->
            <div class="save-group">
                <input type="text" id="configName" placeholder="Nom configuration...">
                <button onclick="saveConfig()" class="btn-primary">💾 Save Config</button>
            </div>

            <!-- Load -->
            <div class="load-group">
                <select id="savedConfigs">
                    <option value="">-- Charger une config --</option>
                </select>
                <button onclick="loadConfig()" class="btn-secondary">📂 Load</button>
                <button onclick="deleteConfig()" class="btn-danger">🗑️ Delete</button>
            </div>

            <!-- Test Live -->
            <div class="test-group">
                <button onclick="testLive()" class="btn-primary">🚀 Test Live</button>
            </div>
        </div>

        <!-- Preview JSON -->
        <div class="preview-section">
            <h3>📄 Preview Configuration</h3>
            <pre id="configPreview"></pre>
        </div>

        <!-- Logs Temps Réel -->
        <div class="logs-section">
            <h3>🔍 Logs Temps Réel</h3>
            <button onclick="clearLogs()" class="btn-clear">Clear</button>
            <div class="logs-container" id="logsContainer"></div>
        </div>
    </div>

    <script src="config-editor.js"></script>
</body>
</html>
```

**API Calls :**
- `POST /api/config/save` : Sauvegarder config avec nom
- `GET /api/config/list` : Lister toutes les configs
- `GET /api/config/:name` : Charger une config par nom
- `DELETE /api/config/:name` : Supprimer une config
- `POST /api/test-modulaire` : Tester la config (existant)

---

### 3. Runner de Production : `public/production-runner.html`

**Fonctionnalités :**
- **Load Config** : Dropdown pour sélectionner une config sauvegardée
- **Row Selection** : Input pour choisir la ligne Google Sheets
- **Run Button** : Lancer le workflow complet avec sauvegarde versionnée
- **Progress Tracking** : Barre de progression + étapes
- **Results Display** :
  - Statistiques finales (word count, LLM utilisés, coût, durée)
  - Lien vers Google Sheets
  - Logs détaillés
- **WebSocket Logs** : Logs temps réel pendant l'exécution

**Structure HTML Clé :**

```html
<!DOCTYPE html>
<html lang="fr">
<head>
    <meta charset="UTF-8">
    <title>Production Runner</title>
</head>
<body>
    <div class="runner-container">
        <!-- Header -->
        <header>
            <h1>🚀 Production Runner</h1>
            <a href="index.html" class="btn-back">← Retour Accueil</a>
        </header>

        <!-- Configuration Selection -->
        <div class="config-selection">
            <h2>⚙️ Sélection Configuration</h2>

            <div class="select-group">
                <label>Configuration :</label>
                <select id="configSelect" onchange="loadSelectedConfig()">
                    <option value="">-- Sélectionner une config --</option>
                </select>
                <button onclick="refreshConfigs()">🔄 Refresh</button>
            </div>

            <div class="config-display" id="configDisplay">
                <!-- Affichage config sélectionnée -->
            </div>
        </div>

        <!-- Row Selection -->
        <div class="row-selection">
            <h2>📊 Google Sheets</h2>
            <div class="form-group">
                <label>Ligne à traiter :</label>
                <input type="number" id="rowNumber" value="2" min="2" max="1000">
                <span class="help-text">Ligne du Google Sheet "Instructions"</span>
            </div>
        </div>

        <!-- Run Controls -->
        <div class="run-controls">
            <button onclick="runProduction()" class="btn-run" id="btnRun">
                🚀 Lancer Production
            </button>
            <button onclick="cancelRun()" class="btn-cancel" id="btnCancel" disabled>
                🛑 Annuler
            </button>
        </div>

        <!-- Progress Tracking -->
        <div class="progress-section" id="progressSection" style="display: none;">
            <h2>⏳ Progression</h2>
            <div class="progress-bar">
                <div class="progress-fill" id="progressFill"></div>
            </div>
            <div class="progress-steps" id="progressSteps">
                <!-- Étapes dynamiques -->
            </div>
        </div>

        <!-- Results -->
        <div class="results-section" id="resultsSection" style="display: none;">
            <h2>📊 Résultats</h2>

            <div class="stats-grid">
                <div class="stat-card">
                    <span class="stat-label">Mots Générés</span>
                    <span class="stat-value" id="wordCount">-</span>
                </div>
                <div class="stat-card">
                    <span class="stat-label">Durée Totale</span>
                    <span class="stat-value" id="duration">-</span>
                </div>
                <div class="stat-card">
                    <span class="stat-label">LLM Utilisés</span>
                    <span class="stat-value" id="llmUsed">-</span>
                </div>
                <div class="stat-card">
                    <span class="stat-label">Coût Estimé</span>
                    <span class="stat-value" id="cost">-</span>
                </div>
            </div>

            <div class="links-section">
                <a href="#" id="gsheetsLink" target="_blank" class="btn-link">
                    📊 Voir dans Google Sheets
                </a>
            </div>
        </div>

        <!-- Logs Temps Réel -->
        <div class="logs-section">
            <h3>🔍 Logs Temps Réel</h3>
            <button onclick="clearLogs()" class="btn-clear">Clear</button>
            <div class="logs-container" id="logsContainer"></div>
        </div>
    </div>

    <script src="production-runner.js"></script>
</body>
</html>
```

**API Calls :**
- `GET /api/config/list` : Lister configs disponibles
- `GET /api/config/:name` : Charger config sélectionnée
- `POST /api/production-run` : Lancer workflow complet (nouveau endpoint)

---

## 🔧 Backend - Nouveaux Endpoints

### 1. Système de Stockage des Configurations

**Fichier : `lib/ConfigManager.js`**

```javascript
// ========================================
// FICHIER: ConfigManager.js
// RESPONSABILITÉ: Gestion CRUD des configurations modulaires
// STOCKAGE: Fichiers JSON dans configs/
// ========================================

const fs = require('fs').promises;
const path = require('path');
const { logSh } = require('./ErrorReporting');

class ConfigManager {
  constructor() {
    this.configDir = path.join(__dirname, '../configs');
    this.ensureConfigDir();
  }

  async ensureConfigDir() {
    try {
      await fs.mkdir(this.configDir, { recursive: true });
    } catch (error) {
      logSh(`⚠️ Erreur création dossier configs: ${error.message}`, 'WARNING');
    }
  }

  /**
   * Sauvegarder une configuration
   */
  async saveConfig(name, config) {
    const sanitizedName = name.replace(/[^a-zA-Z0-9-_]/g, '_');
    const filePath = path.join(this.configDir, `${sanitizedName}.json`);

    const configData = {
      name: sanitizedName,
      displayName: name,
      config,
      createdAt: new Date().toISOString(),
      updatedAt: new Date().toISOString()
    };

    await fs.writeFile(filePath, JSON.stringify(configData, null, 2), 'utf-8');
    logSh(`💾 Config sauvegardée: ${name}`, 'INFO');

    return { success: true, name: sanitizedName };
  }

  /**
   * Charger une configuration
   */
  async loadConfig(name) {
    const sanitizedName = name.replace(/[^a-zA-Z0-9-_]/g, '_');
    const filePath = path.join(this.configDir, `${sanitizedName}.json`);

    try {
      const data = await fs.readFile(filePath, 'utf-8');
      const configData = JSON.parse(data);
      logSh(`📂 Config chargée: ${name}`, 'DEBUG');
      return configData;
    } catch (error) {
      logSh(`❌ Config non trouvée: ${name}`, 'ERROR');
      throw new Error(`Configuration "${name}" non trouvée`);
    }
  }

  /**
   * Lister toutes les configurations
   */
  async listConfigs() {
    try {
      const files = await fs.readdir(this.configDir);
      const jsonFiles = files.filter(f => f.endsWith('.json'));

      const configs = await Promise.all(
        jsonFiles.map(async (file) => {
          const filePath = path.join(this.configDir, file);
          const data = await fs.readFile(filePath, 'utf-8');
          const configData = JSON.parse(data);

          return {
            name: configData.name,
            displayName: configData.displayName || configData.name,
            createdAt: configData.createdAt,
            updatedAt: configData.updatedAt
          };
        })
      );

      return configs.sort((a, b) =>
        new Date(b.updatedAt) - new Date(a.updatedAt)
      );

    } catch (error) {
      logSh(`⚠️ Erreur listing configs: ${error.message}`, 'WARNING');
      return [];
    }
  }

  /**
   * Supprimer une configuration
   */
  async deleteConfig(name) {
    const sanitizedName = name.replace(/[^a-zA-Z0-9-_]/g, '_');
    const filePath = path.join(this.configDir, `${sanitizedName}.json`);

    await fs.unlink(filePath);
    logSh(`🗑️ Config supprimée: ${name}`, 'INFO');

    return { success: true };
  }
}

module.exports = { ConfigManager };
```

---

### 2. Nouveaux Endpoints dans `ManualServer.js`

**Ajouter dans `setupAPIRoutes()` :**

```javascript
// ========================================
// ENDPOINTS GESTION CONFIGURATIONS
// ========================================

// Sauvegarder une configuration
this.app.post('/api/config/save', async (req, res) => {
  try {
    const { name, config } = req.body;

    if (!name || !config) {
      return res.status(400).json({
        success: false,
        error: 'Nom et configuration requis'
      });
    }

    const { ConfigManager } = require('../ConfigManager');
    const configManager = new ConfigManager();

    const result = await configManager.saveConfig(name, config);

    res.json({
      success: true,
      message: `Configuration "${name}" sauvegardée`,
      savedName: result.name
    });

  } catch (error) {
    logSh(`❌ Erreur save config: ${error.message}`, 'ERROR');
    res.status(500).json({
      success: false,
      error: error.message
    });
  }
});

// Lister les configurations
this.app.get('/api/config/list', async (req, res) => {
  try {
    const { ConfigManager } = require('../ConfigManager');
    const configManager = new ConfigManager();

    const configs = await configManager.listConfigs();

    res.json({
      success: true,
      configs,
      count: configs.length
    });

  } catch (error) {
    logSh(`❌ Erreur list configs: ${error.message}`, 'ERROR');
    res.status(500).json({
      success: false,
      error: error.message
    });
  }
});

// Charger une configuration
this.app.get('/api/config/:name', async (req, res) => {
  try {
    const { name } = req.params;

    const { ConfigManager } = require('../ConfigManager');
    const configManager = new ConfigManager();

    const configData = await configManager.loadConfig(name);

    res.json({
      success: true,
      config: configData
    });

  } catch (error) {
    logSh(`❌ Erreur load config: ${error.message}`, 'ERROR');
    res.status(404).json({
      success: false,
      error: error.message
    });
  }
});

// Supprimer une configuration
this.app.delete('/api/config/:name', async (req, res) => {
  try {
    const { name } = req.params;

    const { ConfigManager } = require('../ConfigManager');
    const configManager = new ConfigManager();

    await configManager.deleteConfig(name);

    res.json({
      success: true,
      message: `Configuration "${name}" supprimée`
    });

  } catch (error) {
    logSh(`❌ Erreur delete config: ${error.message}`, 'ERROR');
    res.status(500).json({
      success: false,
      error: error.message
    });
  }
});

// ========================================
// ENDPOINT PRODUCTION RUN
// ========================================

this.app.post('/api/production-run', async (req, res) => {
  try {
    const {
      rowNumber,
      selectiveStack,
      adversarialMode,
      humanSimulationMode,
      patternBreakingMode,
      saveIntermediateSteps = true
    } = req.body;

    if (!rowNumber) {
      return res.status(400).json({
        success: false,
        error: 'rowNumber requis'
      });
    }

    logSh(`🚀 PRODUCTION RUN: Row ${rowNumber}`, 'INFO');

    // Appel handleFullWorkflow depuis Main.js
    const { handleFullWorkflow } = require('../Main');

    const result = await handleFullWorkflow({
      rowNumber,
      selectiveStack: selectiveStack || 'standardEnhancement',
      adversarialMode: adversarialMode || 'light',
      humanSimulationMode: humanSimulationMode || 'none',
      patternBreakingMode: patternBreakingMode || 'none',
      saveIntermediateSteps,
      source: 'production_web'
    });

    res.json({
      success: true,
      result: {
        wordCount: result.compiledWordCount,
        duration: result.totalDuration,
        llmUsed: result.llmUsed,
        cost: result.estimatedCost,
        slug: result.slug,
        gsheetsLink: `https://docs.google.com/spreadsheets/d/${process.env.GOOGLE_SHEETS_ID}`
      }
    });

  } catch (error) {
    logSh(`❌ Erreur production run: ${error.message}`, 'ERROR');
    res.status(500).json({
      success: false,
      error: error.message
    });
  }
});
```

---

## 📊 Schéma des Données

### Format Config Sauvegardée

**Fichier : `configs/nom-config.json`**

```json
{
  "name": "config_standard_heavy",
  "displayName": "Config Standard Heavy",
  "config": {
    "rowNumber": 2,
    "selectiveStack": "standardEnhancement",
    "adversarialMode": "heavy",
    "humanSimulationMode": "standardSimulation",
    "patternBreakingMode": "standardPatternBreaking",
    "saveIntermediateSteps": true,
    "source": "web_interface"
  },
  "createdAt": "2025-10-08T14:30:00.000Z",
  "updatedAt": "2025-10-08T14:30:00.000Z"
}
```

### API Responses

#### `GET /api/config/list`

```json
{
  "success": true,
  "configs": [
    {
      "name": "config_standard_heavy",
      "displayName": "Config Standard Heavy",
      "createdAt": "2025-10-08T14:30:00.000Z",
      "updatedAt": "2025-10-08T14:30:00.000Z"
    }
  ],
  "count": 1
}
```

#### `POST /api/production-run`

```json
{
  "success": true,
  "result": {
    "wordCount": 2151,
    "duration": 45320,
    "llmUsed": "claude,openai,mistral",
    "cost": 0.15,
    "slug": "plaque-personnalisee",
    "gsheetsLink": "https://docs.google.com/spreadsheets/d/xxx"
  }
}
```

---

## 🎨 Design System

### Palette de Couleurs

```css
:root {
  /* Primary Colors */
  --primary: #667eea;
  --secondary: #764ba2;

  /* Semantic Colors */
  --success: #48bb78;
  --warning: #ed8936;
  --error: #f56565;
  --info: #4299e1;

  /* Backgrounds */
  --bg-light: #f7fafc;
  --bg-dark: #1a202c;
  --bg-card: #ffffff;

  /* Text */
  --text-dark: #2d3748;
  --text-light: #a0aec0;
  --text-muted: #718096;

  /* Borders */
  --border-light: #e2e8f0;
  --border-dark: #2d3748;
}
```

### Typography

```css
body {
  font-family: 'SF Pro Display', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  font-size: 14px;
  line-height: 1.6;
  color: var(--text-dark);
}

h1 {
  font-size: 1.8em;
  font-weight: 700;
  margin-bottom: 25px;
}

h2 {
  font-size: 1.4em;
  font-weight: 600;
  margin: 20px 0 15px 0;
}

h3 {
  font-size: 1.2em;
  font-weight: 600;
  margin: 15px 0 10px 0;
}
```

### Components

```css
/* Buttons */
.btn-primary {
  background: linear-gradient(135deg, var(--primary), var(--secondary));
  color: white;
  padding: 12px 24px;
  border: none;
  border-radius: 8px;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.2s;
}

.btn-primary:hover {
  transform: translateY(-2px);
  box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
}

/* Cards */
.card {
  background: var(--bg-card);
  border-radius: 15px;
  padding: 25px;
  box-shadow: 0 10px 30px rgba(0,0,0,0.1);
  cursor: pointer;
  transition: all 0.2s;
}

.card:hover {
  transform: translateY(-5px);
  box-shadow: 0 15px 40px rgba(0,0,0,0.15);
}

/* Form Groups */
.form-group {
  margin-bottom: 20px;
}

.form-group label {
  display: block;
  font-weight: 600;
  margin-bottom: 8px;
  color: var(--text-dark);
}

.form-group input,
.form-group select {
  width: 100%;
  padding: 12px;
  border: 2px solid var(--border-light);
  border-radius: 8px;
  font-size: 14px;
  transition: border-color 0.2s;
}

.form-group input:focus,
.form-group select:focus {
  outline: none;
  border-color: var(--primary);
}

/* Logs Container */
.logs-container {
  background: var(--bg-dark);
  color: #e2e8f0;
  border-radius: 8px;
  padding: 15px;
  font-family: 'Fira Code', monospace;
  font-size: 12px;
  height: 300px;
  overflow-y: auto;
  border: 1px solid var(--border-dark);
}

.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; }
```

---

## 💻 JavaScript - Fonctions Clés

### config-editor.js

```javascript
// ========================================
// GESTION CONFIGURATION EDITOR
// ========================================

let ws = null;

// WebSocket connection
function connectWebSocket() {
  ws = new WebSocket('ws://localhost:8081');

  ws.onopen = () => console.log('WebSocket connected');

  ws.onmessage = (event) => {
    try {
      const logData = JSON.parse(event.data);
      displayLog(logData);
    } catch (e) {
      displayLog({ level: 'info', msg: event.data });
    }
  };

  ws.onclose = () => {
    console.log('WebSocket disconnected');
    setTimeout(connectWebSocket, 3000);
  };
}

// Display log entry
function displayLog(logData) {
  const logsDiv = document.getElementById('logsContainer');
  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);
  logsDiv.scrollTop = logsDiv.scrollHeight;
}

// Sauvegarder configuration
async function saveConfig() {
  const configName = document.getElementById('configName').value;

  if (!configName) {
    showError('Veuillez entrer un nom de configuration');
    return;
  }

  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,
    saveIntermediateSteps: true,
    source: 'web_interface'
  };

  try {
    const response = await fetch('/api/config/save', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({ name: configName, config })
    });

    const result = await response.json();

    if (result.success) {
      showSuccess(`Configuration "${configName}" sauvegardée !`);
      await refreshConfigsList();
      document.getElementById('configName').value = '';
    } else {
      showError(result.error);
    }

  } catch (error) {
    showError(`Erreur sauvegarde: ${error.message}`);
  }
}

// Charger configuration
async function loadConfig() {
  const configName = document.getElementById('savedConfigs').value;

  if (!configName) return;

  try {
    const response = await fetch(`/api/config/${configName}`);
    const result = await response.json();

    if (result.success) {
      const config = result.config.config;

      // Remplir les champs
      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;

      showSuccess(`Configuration "${configName}" chargée !`);
      updateConfigPreview();
    } else {
      showError(result.error);
    }

  } catch (error) {
    showError(`Erreur chargement: ${error.message}`);
  }
}

// Supprimer configuration
async function deleteConfig() {
  const configName = document.getElementById('savedConfigs').value;

  if (!configName) return;

  if (!confirm(`Supprimer la configuration "${configName}" ?`)) return;

  try {
    const response = await fetch(`/api/config/${configName}`, {
      method: 'DELETE'
    });

    const result = await response.json();

    if (result.success) {
      showSuccess(`Configuration "${configName}" supprimée !`);
      await refreshConfigsList();
    } else {
      showError(result.error);
    }

  } catch (error) {
    showError(`Erreur suppression: ${error.message}`);
  }
}

// Refresh configs list
async function refreshConfigsList() {
  try {
    const response = await fetch('/api/config/list');
    const result = await response.json();

    const select = document.getElementById('savedConfigs');
    select.innerHTML = '<option value="">-- Charger une config --</option>';

    result.configs.forEach(config => {
      const option = document.createElement('option');
      option.value = config.name;
      option.textContent = config.displayName;
      select.appendChild(option);
    });

  } catch (error) {
    console.error('Erreur refresh configs:', error);
  }
}

// Update config preview
function updateConfigPreview() {
  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
  };

  document.getElementById('configPreview').textContent = JSON.stringify(config, null, 2);
}

// Test live
async function testLive() {
  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('🚀 Test en cours...', 'loading');
  clearLogs();

  try {
    const response = await fetch('/api/test-modulaire', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(config)
    });

    const result = await response.json();

    if (result.success) {
      showStatus('✅ Test terminé avec succès!', 'success');
    } else {
      showStatus('❌ Erreur: ' + (result.error || 'Erreur inconnue'), 'error');
    }

  } catch (error) {
    showStatus('❌ Erreur: ' + error.message, 'error');
  }
}

// Clear logs
function clearLogs() {
  document.getElementById('logsContainer').innerHTML = '';
}

// Show status
function showStatus(message, type) {
  // Implementation
}

// Show success
function showSuccess(message) {
  showStatus(message, 'success');
}

// Show error
function showError(message) {
  showStatus(message, 'error');
}

// Initialize
window.onload = async () => {
  connectWebSocket();
  await refreshConfigsList();
  updateConfigPreview();

  // Auto-update preview on change
  ['rowNumber', 'selectiveStack', 'adversarialMode', 'humanSimulationMode', 'patternBreakingMode'].forEach(id => {
    document.getElementById(id).addEventListener('change', updateConfigPreview);
  });
};
```

---

### production-runner.js

```javascript
// ========================================
// GESTION PRODUCTION RUNNER
// ========================================

let ws = null;
let currentConfig = null;

// WebSocket connection (same as config-editor.js)
function connectWebSocket() {
  // Same implementation
}

// Load selected config
async function loadSelectedConfig() {
  const configName = document.getElementById('configSelect').value;

  if (!configName) {
    document.getElementById('configDisplay').innerHTML = '';
    currentConfig = null;
    return;
  }

  try {
    const response = await fetch(`/api/config/${configName}`);
    const result = await response.json();

    if (result.success) {
      currentConfig = result.config.config;
      displayConfigDetails(currentConfig);
    }

  } catch (error) {
    showError(`Erreur chargement config: ${error.message}`);
  }
}

// Display config details
function displayConfigDetails(config) {
  const html = `
    <div class="config-details">
      <h3>Configuration Chargée</h3>
      <ul>
        <li><strong>Selective:</strong> ${config.selectiveStack}</li>
        <li><strong>Adversarial:</strong> ${config.adversarialMode}</li>
        <li><strong>Human Simulation:</strong> ${config.humanSimulationMode}</li>
        <li><strong>Pattern Breaking:</strong> ${config.patternBreakingMode}</li>
      </ul>
    </div>
  `;

  document.getElementById('configDisplay').innerHTML = html;
}

// Refresh configs dropdown
async function refreshConfigs() {
  try {
    const response = await fetch('/api/config/list');
    const result = await response.json();

    const select = document.getElementById('configSelect');
    select.innerHTML = '<option value="">-- Sélectionner une config --</option>';

    result.configs.forEach(config => {
      const option = document.createElement('option');
      option.value = config.name;
      option.textContent = config.displayName;
      select.appendChild(option);
    });

    showSuccess('Configs rafraîchies');

  } catch (error) {
    showError(`Erreur refresh: ${error.message}`);
  }
}

// Run production workflow
async function runProduction() {
  const configName = document.getElementById('configSelect').value;

  if (!configName) {
    showError('Veuillez sélectionner une configuration');
    return;
  }

  if (!currentConfig) {
    showError('Configuration non chargée');
    return;
  }

  // Override rowNumber from input
  const config = {
    ...currentConfig,
    rowNumber: parseInt(document.getElementById('rowNumber').value)
  };

  // UI Updates
  document.getElementById('btnRun').disabled = true;
  document.getElementById('btnCancel').disabled = false;
  showProgressSection();
  clearLogs();

  try {
    const response = await fetch('/api/production-run', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(config)
    });

    const result = await response.json();

    if (result.success) {
      hideProgressSection();
      displayResults(result.result);
      showSuccess('✅ Workflow terminé avec succès!');
    } else {
      hideProgressSection();
      showError(result.error);
    }

  } catch (error) {
    hideProgressSection();
    showError(`Erreur production: ${error.message}`);
  } finally {
    document.getElementById('btnRun').disabled = false;
    document.getElementById('btnCancel').disabled = true;
  }
}

// Display results
function displayResults(result) {
  document.getElementById('resultsSection').style.display = 'block';

  document.getElementById('wordCount').textContent = result.wordCount || '-';
  document.getElementById('duration').textContent = `${(result.duration / 1000).toFixed(1)}s`;
  document.getElementById('llmUsed').textContent = result.llmUsed || '-';
  document.getElementById('cost').textContent = `$${result.cost?.toFixed(4) || '0.00'}`;

  document.getElementById('gsheetsLink').href = result.gsheetsLink;
}

// Show/hide progress
function showProgressSection() {
  document.getElementById('progressSection').style.display = 'block';
  updateProgress(10);
}

function hideProgressSection() {
  document.getElementById('progressSection').style.display = 'none';
}

function updateProgress(percentage) {
  document.getElementById('progressFill').style.width = percentage + '%';
}

// Cancel run
function cancelRun() {
  // TODO: Implement cancel logic
  showError('Annulation non implémentée');
}

// Clear logs
function clearLogs() {
  document.getElementById('logsContainer').innerHTML = '';
}

// Initialize
window.onload = async () => {
  connectWebSocket();
  await refreshConfigs();
};
```

---

## 📅 Plan d'Exécution Séquentiel

| Étape | Tâche | Fichiers | Durée Estimée |
|-------|-------|----------|---------------|
| **1** | Créer `ConfigManager.js` | `lib/ConfigManager.js` | 30 min |
| **2** | Ajouter endpoints backend | `lib/modes/ManualServer.js` | 45 min |
| **3** | Créer page d'accueil | `public/index.html` + CSS | 1h |
| **4** | Créer éditeur de config | `public/config-editor.html` + `config-editor.js` | 2h |
| **5** | Créer runner de production | `public/production-runner.html` + `production-runner.js` | 2h |
| **6** | Tests end-to-end | Tous les fichiers | 1h |
| **TOTAL** | | | **~7h** |

### Ordre d'Implémentation Recommandé

1. **Backend First** (Étapes 1-2) :
   - Créer ConfigManager.js
   - Ajouter endpoints dans ManualServer.js
   - Tester avec curl/Postman

2. **Frontend Simple → Complexe** (Étapes 3-5) :
   - Page d'accueil (simple navigation)
   - Éditeur de config (formulaires + CRUD)
   - Runner de production (intégration complète)

3. **Tests & Polish** (Étape 6) :
   - Tests fonctionnels
   - Corrections bugs
   - Amélioration UX

---

## 🧪 Tests à Effectuer

### 1. Tests Backend (ConfigManager)

- ✅ **Save Config** : Sauvegarder avec nom normal
- ✅ **Save Config** : Nom avec espaces/accents → sanitization
- ✅ **Load Config** : Charger config existante
- ✅ **Load Config** : Config inexistante → erreur 404
- ✅ **List Configs** : Retourne tableau vide si aucune config
- ✅ **List Configs** : Retourne configs triées par date
- ✅ **Delete Config** : Suppression réussie
- ✅ **Delete Config** : Config inexistante → erreur

### 2. Tests Frontend (Config Editor)

- ✅ **Save** : Sauvegarde + apparaît dans dropdown
- ✅ **Load** : Charge tous les champs correctement
- ✅ **Delete** : Supprime + met à jour dropdown
- ✅ **Test Live** : Exécute test modulaire
- ✅ **Preview** : JSON mis à jour en temps réel
- ✅ **WebSocket** : Logs temps réel reçus

### 3. Tests Frontend (Production Runner)

- ✅ **Load Config** : Affiche détails config
- ✅ **Run Production** : Lance workflow complet
- ✅ **Run Production** : Override rowNumber correct
- ✅ **Results** : Statistiques affichées correctement
- ✅ **Results** : Lien Google Sheets fonctionnel
- ✅ **WebSocket** : Logs temps réel pendant run
- ✅ **Progress** : Barre de progression visible

### 4. Tests End-to-End

- ✅ **Flow Complet** :
  1. Accueil → Config Editor
  2. Créer config + sauvegarder
  3. Retour Accueil → Production Runner
  4. Charger config + run production
  5. Vérifier résultats Google Sheets

---

## 📝 Notes Importantes

### Compatibilité Existante

- ✅ **Ne modifie PAS** `test-modulaire.html` (reste intact)
- ✅ **Réutilise** les endpoints existants (`/api/test-modulaire`)
- ✅ **Compatible** avec architecture MANUAL/AUTO modes
- ✅ **Utilise** le WebSocket existant (port 8081)
- ✅ **Respecte** l'architecture modulaire (4 couches)

### Structure Fichiers Finale

```
seo-generator-server/
├── configs/                    # 🆕 Nouveau dossier
│   ├── config_standard.json
│   ├── config_heavy.json
│   └── config_light.json
│
├── lib/
│   ├── ConfigManager.js       # 🆕 Nouveau fichier
│   └── modes/
│       └── ManualServer.js    # ✏️ Modifié (nouveaux endpoints)
│
├── public/
│   ├── index.html             # 🆕 Nouveau
│   ├── config-editor.html     # 🆕 Nouveau
│   ├── config-editor.js       # 🆕 Nouveau
│   ├── production-runner.html # 🆕 Nouveau
│   ├── production-runner.js   # 🆕 Nouveau
│   └── test-modulaire.html    # ✅ Existant (non modifié)
│
└── ProductionReady.md         # 📋 Ce fichier
```

### Endpoints Backend Créés

| Méthode | Endpoint | Description |
|---------|----------|-------------|
| `POST` | `/api/config/save` | Sauvegarder configuration |
| `GET` | `/api/config/list` | Lister configurations |
| `GET` | `/api/config/:name` | Charger configuration |
| `DELETE` | `/api/config/:name` | Supprimer configuration |
| `POST` | `/api/production-run` | Lancer workflow production |

### Endpoints Existants Réutilisés

| Méthode | Endpoint | Description |
|---------|----------|-------------|
| `GET` | `/api/status` | Status serveur |
| `POST` | `/api/test-modulaire` | Test modulaire (existant) |

### Dépendances

Aucune nouvelle dépendance npm requise. Utilise uniquement :
- Express (existant)
- WebSocket (existant)
- fs/promises (Node.js built-in)
- path (Node.js built-in)

---

## 🚀 Prêt à Implémenter

### Checklist Avant Démarrage

- [ ] Backup du code actuel
- [ ] Créer branche Git `feature/config-interface`
- [ ] Vérifier que `test-modulaire.html` fonctionne
- [ ] Vérifier que le serveur démarre en mode MANUAL

### Commandes de Démarrage

```bash
# 1. Créer branche Git
git checkout -b feature/config-interface

# 2. Créer dossier configs
mkdir -p configs

# 3. Démarrer serveur en mode MANUAL
npm start

# 4. Vérifier que le serveur tourne
curl http://localhost:3000/api/status
```

### Validation Finale

Après implémentation, vérifier :
- ✅ Les 3 pages sont accessibles et navigables
- ✅ Save/Load/Delete configs fonctionne
- ✅ Test Live fonctionne (depuis config-editor)
- ✅ Production Run fonctionne avec sauvegarde GSheets
- ✅ WebSocket logs s'affichent en temps réel
- ✅ `test-modulaire.html` fonctionne toujours (non cassé)

---

## 📞 Support

Pour toute question ou problème durant l'implémentation, se référer à :
- **CLAUDE.md** : Documentation complète du projet
- **lib/modes/ManualServer.js** : Implémentation serveur MANUAL
- **public/test-modulaire.html** : Exemple d'interface modulaire fonctionnelle

---

**Dernière mise à jour** : 2025-10-08
**Auteur** : Claude Code
**Status** : 📋 Prêt pour implémentation
