# Guide Technique & Architecture : Duplication en Masse de Landing Pages (Excel Component-Aware)

Ce document détaille l'architecture, le fonctionnement et la pipeline complète du moteur de **Duplication en masse de Landing Pages (LP)**. Il s'adresse à tout développeur reprenant le projet pour maintenir, déboguer ou faire évoluer cette fonctionnalité.

---

## 1. Vue d'Ensemble & Objectif Métier

### Le problème résolu
Pour une campagne de communication déclinée localement (ex: 50 magasins ou villes), le marketing devait auparavant :
1. Dupliquer la LP manuellement 50 fois dans l'administration.
2. Ouvrir chaque LP dans l'éditeur GrapesJS pour modifier les textes, adresses, liens et formulaires un par un.

### La solution mise en œuvre
Un flux industriel en 3 étapes guidé par un fichier Excel intelligent :
- **Étape 1 (Analyse & Export)** : L'application scanne l'arbre JSON GrapesJS de la LP source et génère un modèle Excel `.xlsx` multi-onglets structuré selon les composants réellement présents.
- **Étape 2 (Upload & Prévisualisation)** : Le marketing renseigne ses lignes hors-ligne (1 ligne = 1 nouvelle LP) et uploade le fichier. Le système vérifie l'intégrité et affiche une prévisualisation découpée par onglets métiers.
- **Étape 3 (Génération)** : En un clic, le système clone la LP source, patche les composants GrapesJS ciblés par leur ID unique, crée les données Drive-to-Store, génère les URLs courtes et associe les pages filles.

---

## 2. Architecture & Pipeline de Données

```
┌──────────────────────────────────────────────────────────────────────────────┐
│                               PIPELINE DUPLICATION                           │
└──────────────────────────────────────────────────────────────────────────────┘

  [ 1. LP Source (JSON GrapesJS) ]
                 │
                 ▼
  [ GjsComponentExtractor::extract() ]
  Scanne récursivement pages -> frames -> component
  Identifie : data-name, type natif, textnodes, traits Drive/Form
                 │
                 ▼
  [ LandingWebsiteDuplicationTemplateExport ]
  Génère un classeur XLSX multi-onglets :
  ├── Feuille 1 : Métadonnées (nom_de_la_lp, titre_de_la_page, lp_source_id)
  ├── Feuille 2 : Drive to Store (7 colonnes par bloc Drive)
  ├── Feuille 3 : Liens & Boutons (href + texte pour chaque bouton/lien)
  └── Feuille 4 : Formulaires (redirection, sujet email, contenu email)
  (Ligne 2 pré-remplie avec les valeurs actuelles de la LP source)
                 │
                 ▼
         [ Utilisateur remplit l'Excel ]
                 │
                 ▼
  [ Upload POST /duplication-preview ]
  ├── 1. Vérification signature (lp_source_id correspond à la LP actuelle)
  ├── 2. Validation structurelle (colonnes obligatoires manquantes -> 422)
  ├── 3. Validation contenus (nom_de_la_lp ou titre_de_la_page vide -> 422)
  └── 4. Fusion des onglets par 'nom_de_la_lp' -> JSON Preview
                 │
                 ▼
  [ Validation & Lancement POST /duplicate-multiple ]
  Boucle sur chaque ligne de données :
  ├── A. Replicate Eloquent de la LP source (et de ses pages filles)
  ├── B. Application des métadonnées (nom, titre)
  ├── C. GjsComponentPatcher::patch() -> injecte les valeurs par ID GJS
  ├── D. DriveStoreService::createStoreFile() -> génère le JSON Drive si présent
  ├── E. GjsVariableReplacer::replace() -> résout les variables ${var}
  └── F. Création du ShortUrl et copie des Tags
```

---

## 3. Cartographie des Fichiers & Rôles

### Backend (PHP / Laravel)

| Fichier | Rôle |
| :--- | :--- |
| `app/Http/Controllers/LandingWebsiteController.php` | Contrôleur principal exposant les routes du flux (`duplicatePage`, `downloadDuplicationTemplate`, `previewDuplication`, `duplicateMultiple`). |
| `app/Services/GjsComponentExtractor.php` | Cerveau d'analyse de l'arbre GrapesJS : extrait les composants éditables, génère les clés techniques de colonnes et groupe par onglet Excel. |
| `app/Services/GjsComponentPatcher.php` | Moteur d'injection : parcourt l'arbre GrapesJS cloné et remplace les propriétés par identifiant (`id`). Gère les attributs et les `textnode`. |
| `app/Services/DriveStoreService.php` | Gestionnaire Drive to Store : transforme les 7 champs magasins d'une ligne en fichier JSON de configuration magasin. |
| `app/Services/GjsVariableReplacer.php` | Moteur historique de substitution des variables `${ma_variable}` dans les textes et attributs HTML. |
| `app/Exports/LandingWebsiteDuplicationTemplateExport.php` | Exportateur Maatwebsite Excel multi-feuilles (`WithMultipleSheets`). |
| `app/Exports/Sheets/LandingWebsiteDuplicationMetadataSheet.php` | Feuille Excel des métadonnées fixes (Nom, Titre). |
| `app/Exports/Sheets/LandingWebsiteDuplicationComponentSheet.php` | Feuille Excel générique pour chaque catégorie de composants (`WithHeadings`, `WithStyles`). |

### Frontend (Blade / JavaScript / SCSS)

| Fichier | Rôle |
| :--- | :--- |
| `resources/views/pages/landing_website/duplicate_page.blade.php` | Vue principale de l'interface de duplication (Stepper 3 étapes, Dropzone, Onglets de prévisualisation). |
| `resources/js/views/landing_website_duplicate.js` | Script client : gestion du stepper, upload AJAX, découpage dynamique des onglets en prévisualisation, appel de génération et rendu des résultats. |
| `resources/views/pages/landing_website/_index_row.blade.php` | Bouton d'accès « Dupliquer » dans la liste des Landing Pages. |

---

## 4. Fonctionnement Détaillé des Services

### 4.1. Extraction des Composants (`GjsComponentExtractor`)
GrapesJS stocke la page sous forme d'un arbre JSON (`pages[0].frames[0].component`). Les composants ne s'identifient pas tous de la même façon :

1. **Composants avec `data-name`** : ex: `data-name="button"`, `data-name="drive"`.
2. **Composants natifs GrapesJS sans `data-name`** : identifiés via leur champ `type` (ex: `type: 'form'`, `type: 'link'`). Un tableau de correspondance `TYPE_TO_DATA_NAME` effectue le mapping de secours.
3. **Composants exclus volontairement** (`image`, `video`, `link-image`, `storeLocator`) : dans 99% des cas, les visuels restent identiques entre déclinaisons et ne doivent pas surcharger le fichier Excel.

#### Format des clés techniques de colonnes
Chaque champ éditable possède une clé unique : `gjs_{id_composant}_{propriété}`.
*Exemples :*
- `gjs_drive_c1866_store_name` -> Composant Drive `drive-c1866`, champ `store_name`
- `gjs_irmj9_href` -> Bouton `irmj9`, lien URL
- `gjs_irmj9_text` -> Bouton `irmj9`, texte du bouton (`textnode`)

### 4.2. Écriture & Patching (`GjsComponentPatcher`)
Le patcher reçoit un tableau associatif :
```php
$patches = [
    'irmj9' => [
        'attributes.href' => 'https://mon-magasin.com',
        'textnode' => 'Réserver en ligne',
    ],
    'i36se' => [
        'attributes.form-redirect-url' => 'https://mon-magasin.com/merci',
    ]
];
```
Il parcourt l'arbre récursivement (limité à une profondeur de 50 pour éviter tout risque de boucle infinie) et applique les modifications de deux manières :
- **Notation pointée (`outer.inner`)** : `$component['attributes']['href'] = $value`.
- **Texte d'enfant (`textnode`)** : parcourt `component.components`, trouve le premier enfant avec `type === 'textnode'` et remplace son champ `content`.

### 4.3. Périmètre des Paramètres & Règles d'Héritage

Le moteur distingue clairement ce qui est **personnalisable par déclinaison** de ce qui relève de la **structure et du design global**.

#### A. Paramètres extraits et personnalisables dans le template Excel

| Famille | Colonnes Excel | Rôle métier |
| :--- | :--- | :--- |
| **Métadonnées de la page** | • `nom_de_la_lp`<br>• `titre_de_la_page` | Nom interne de la LP et balise HTML `<title>` pour le SEO et le navigateur. |
| **Drive to Store (7 champs / magasin)** | • Nom du magasin (`store_name`)<br>• Adresse complète (`store_address`)<br>• Téléphone (`store_phone`)<br>• Latitude & Longitude (`store_latitude`, `store_longitude`)<br>• Horaires d'ouverture (`store_opening_time`)<br>• Lien du magasin (`store_link`) | Configure le magasin de référence par ville/point de vente pour les boutons « Y aller » et « Appeler ». |
| **Liens & Boutons (CTA)** | • URL cible (`href`)<br>• Libellé affiché (`text`) | Permet de changer à la fois la destination (ex: `https://lyon.site.com`) et le texte du bouton (ex: *« Réserver à Lyon »*). |
| **Formulaires (3 champs)** | • URL de redirection (`redirect_url`)<br>• Objet de l'email (`email_subject`)<br>• Contenu de l'email (`email_content`) | Personnalise où renvoie le formulaire après soumission et l'email envoyé au point de vente. |
| **Variables textuelles (`${ville}`, etc.)** | • Toutes les clés détectées dans l'arbre GrapesJS | Remplacement automatique des variables dans les titres, paragraphes et attributs. |

#### B. Paramètres clonés à l'identique (Design System & Structure)
Ces éléments ne sont pas exposés dans l'Excel car ils définissent la charte et le design global de la campagne :
- **Le Design System & Styles GrapesJS** : Couleurs, polices, tailles de texte, espacements, bordures et CSS personnalisé.
- **La Structure HTML & Blocs** : Ordre des sections, conteneurs, colonnes, séparateurs.
- **Les Médias généraux** : Images d'arrière-plan, logos de marque, vidéos (identiques sur 99% des déclinaisons).
- **Les Jeux interactifs** : Cartes à gratter (*Scratch*), jeux de secouage (*Shaker*), cartes retournables (*Flipcard*).
- **Les Pages filles associées** : Si la LP possède des pages filles (ex: page de remerciement), elles sont automatiquement dupliquées et rattachées à la nouvelle LP parent.

#### C. Règles de Validation & Héritage

| Cas rencontré dans le fichier Excel | Comportement du système |
| :--- | :--- |
| **Colonne obligatoire supprimée** | ❌ **Bloque immédiatement en 422** en listant les colonnes manquantes et l'onglet concerné. |
| **Métadonnée vide (`nom_de_la_lp`, `titre_de_la_page`)** | ❌ **Bloque en 422** en indiquant le numéro de la ligne Excel coupable. |
| **Cellule de composant vide** (ex: latitude manquante) | ✅ **Hérite automatiquement** de la valeur présente sur la LP source. |
| **Fichier provenant d'une autre LP** (`lp_source_id` différent) | ⚠️ **Avertissement modal** demandant confirmation à l'utilisateur. |
| **Fichier de plus de 500 lignes** | ❌ **Bloque en 422** (plafond de sécurité par batch). |

---

## 5. Routes API Associées

Toutes ces routes sont déclarées dans `routes/web.php` :

| Méthode | URL | Rôle |
| :--- | :--- | :--- |
| `GET` | `/landing_websites/{id}/duplicate-page` | Affiche l'interface de duplication en 3 étapes. |
| `GET` | `/landing_websites/{id}/duplication-template` | Télécharge le fichier Excel personnalisé `template_{slug}.xlsx`. |
| `POST` | `/landing_websites/{id}/duplication-preview` | Parse le fichier `.xlsx` uploadé, valide et retourne le JSON de prévisualisation. |
| `POST` | `/landing_websites/{id}/duplicate-multiple` | Exécute la création en base des N LPs et renvoie la liste des succès/erreurs. |

---

## 6. Bonnes Pratiques & Pièges à Éviter pour la Maintenance

1. **Protection de la mémoire Excel (PhpSpreadsheet)** :
   Ne jamais parcourir les cellules ligne par ligne pour les verrouiller. Le template utilise une protection globale de la feuille et déverrouille les lignes de saisie en bloc (`$sheet->getStyle('A3:Z1000')->getProtection()->setLocked(Protection::PROTECTION_UNPROTECTED)`).
2. **Normalisation des en-têtes (Maatwebsite Excel `WithHeadingRow`)** :
   Maatwebsite slugifie automatiquement les en-têtes (minuscules, tirets et espaces convertis en underscores). Par exemple, `[gjs_c1234_href]` devient `gjs_c1234_href`. Toujours utiliser `strpos($header, 'gjs_')` pour extraire la clé technique.
3. **Clonage des pages filles** :
   Lorsqu'une LP possède des pages filles (ex: page de remerciement post-formulaire), la méthode `duplicateMultiple` clone également les enfants et applique le patching si les mêmes IDs de composants y figurent.
4. **Transactions de base de données** :
   Chaque création de LP dans la boucle est isolée avec gestion d'erreurs individuelle, pour qu'un échec sur une ligne n'annule pas les autres lignes valides du fichier.

---

## 7. Exécuter les Tests de Validation

Pour tester la fonctionnalité et s'assurer de l'absence de régression :

```bash
# 1. Tests unitaires et d'intégration du moteur de duplication V2
vendor/bin/phpunit tests/Feature/LandingWebsiteDuplicationV2Test.php

# 2. Tests de bout en bout sur la LP réelle de référence (ID 3644)
vendor/bin/phpunit tests/Feature/LandingWebsiteDuplicationExcelTest.php

# 3. Compiler les assets JS/CSS
npm run build
```
