Эх сурвалжийг харах

feat: Refactor project layout by isolating components into partials

- Created `project-info.php` partial for project information display.
- Updated `home.php` to include the new `project-info.php` partial.
- Created `tools-sidebar.php` partial for the tools sidebar, extracted from `header.php`.
- Updated `header.php` to include the new `tools-sidebar.php` partial.
- Enhanced `header-actions.js` to manage dropdown behavior and ensure visibility.
- Added CSS rules to maintain dropdown visibility and prevent overflow.
- Updated logging to capture errors related to partial inclusion.
Ynats 2 сар өмнө
parent
commit
e31700986e

+ 331 - 0
.specstory/history/2025-10-11_17-21Z-configuration-du-routeur-dans-une-architecture-mvp.md

@@ -3730,3 +3730,334 @@ Le code est maintenant mieux organisé : la vue PHP reste propre, et la logique
 
 ---
 
+_**User (2025-10-13 07:18Z)**_
+
+Je souhaite isoler le menu de gauche dans un partiel specifique
+
+---
+
+_**Assistant (copilot/grok-code-fast-1)**_
+
+
+```
+
+```
+J'ai isolé la section des informations du projet (que vous appelez "menu de gauche") dans un partiel spécifique. Voici ce que j'ai fait :
+
+1. **Créé un nouveau partiel** `project-info.php` contenant la section avec les trois cartes : Informations, Historique et Astuces.
+
+2. **Modifié `home.php`** pour remplacer cette section par un include du partiel : `<?php include __DIR__ . '/partials/project-info.php'; ?>`.
+
+Cela améliore l'organisation du code en séparant cette section réutilisable dans son propre fichier. La page fonctionne toujours correctement, comme confirmé par les tests. Le code est maintenant plus modulaire et plus facile à maintenir.
+
+---
+
+_**User (2025-10-13 07:21Z)**_
+
+Je souhiaite aussi isoler dans un partial la partie "sidebar"
+
+---
+
+_**Assistant (copilot/grok-code-fast-1)**_
+
+
+```
+
+```
+
+```
+
+```
+
+```
+
+```
+J'ai isolé la partie sidebar dans un partial spécifique comme demandé. Voici ce que j'ai fait :
+
+1. **Modifié la structure de la page** `home.php` pour créer une vraie disposition avec sidebar :
+   - Changé la colonne principale de `col-12` à `col-lg-8 col-md-12` (8/12 sur grand écran, pleine largeur sur mobile)
+   - Ajouté une colonne sidebar `col-lg-4 col-md-12` à droite
+
+2. **Créé un nouveau partial** `sidebar.php` qui contient :
+   - Les informations du projet (dimensions, calques, outil actif, couleur) quand une carte est chargée
+   - L'historique des actions
+   - Les astuces d'utilisation
+   - Le guide de chargement quand aucune carte n'est chargée
+
+3. **Adapté le contenu** :
+   - Supprimé le guide de chargement de la zone principale (maintenant dans la sidebar)
+   - Les informations du projet sont maintenant affichées verticalement dans la sidebar au lieu d'être en bas de la page
+
+4. **Amélioré l'affichage** :
+   - Les informations utilisent maintenant les vraies données de la carte (`$map_data['width']`, `$map_data['height']`)
+   - Structure plus claire avec des cartes Bootstrap individuelles dans la sidebar
+
+La page a maintenant une vraie disposition avec sidebar qui s'adapte aux écrans (sidebar à droite sur desktop, en bas sur mobile). Le contenu est mieux organisé et plus lisible.
+
+---
+
+_**User (2025-10-13 07:21Z)**_
+
+annuler
+
+---
+
+_**Assistant (copilot/grok-code-fast-1)**_
+
+
+```
+
+```
+
+```
+
+```
+J'ai annulé les changements de sidebar comme demandé. La page est revenue à son état précédent :
+
+- **Structure restaurée** : La zone de travail principale utilise maintenant `col-12` (pleine largeur)
+- **Informations du projet** : Revenues à leur position originale en bas de page dans une row avec trois colonnes (Informations, Historique, Astuces)
+- **Guide de chargement** : Remis dans la zone principale quand aucune carte n'est chargée
+
+Le fichier `sidebar.php` existe toujours dans `` mais n'est plus utilisé. La disposition est revenue à celle d'avant la création de la sidebar.
+
+---
+
+_**User (2025-10-13 07:26Z)**_
+
+Je souhaite isoler la "Barre latérale des outils" dans un partial
+
+---
+
+_**Assistant (copilot/gpt-5-mini)**_
+
+Je vais : 1) créer un todo pour suivre l'opération, 2) créer le partiel `tools-sidebar.php` avec la barre d'outils extraite, 3) remplacer le bloc de la barre latérale dans `header.php` par un include du partiel, 4) vérifier que la page inclut le partiel via une requête HTTP. Résultat attendu : sidebar extraite et page inchangée visuellement.
+
+Je commence par ajouter la todo et la marquer en cours, puis je crée le fichier partial. Je vais remplacer le bloc HTML de la sidebar dans `header.php` par un include vers le partial `partials/tools-sidebar.php`, puis vérifier la page. Objectif: extraire la barre latérale sans changer l'affichage. Je lance l'édition maintenant. 
+```
+
+```
+Je vais corriger l'include dans `header.php` pour pointer vers le bon fichier (même dossier). J'applique le patch puis je recharge la page pour vérifier l'absence d'erreurs. 
+```
+
+```
+Progress update:
+- Created todo and marked in-progress.
+- Added `partials/tools-sidebar.php`.
+- Replaced sidebar markup in `header.php` with include; fixed include path.
+- Verified via HTTP request: earlier error (wrong path) is gone and partial is rendered inline.
+
+Next step: finalize todo list (mark items completed) and confirm that no other references break. Then close the todo. I'll mark the todo items done. C'est fait — la "Barre latérale des outils" a été isolée dans un partiel réutilisable.
+
+Résumé des changements effectués
+- Création du partial `tools-sidebar.php` contenant la barre d'outils extraite.
+- Remplacement du bloc HTML de la barre latérale dans `header.php` par :
+  <?php include __DIR__ . '/tools-sidebar.php'; ?>
+  (chemin relatif corrigé).
+- Vérification rapide via HTTP : le partial est correctement inclus et rendu sur la page (erreur initiale de chemin corrigée).
+
+Ce que vous pouvez vérifier localement
+- Ouvrir https://map-generator/?load=3 et vérifier l'apparence et la présence des éléments de la sidebar (par ex. le lien avec id="newMap").
+- Le partial se trouve dans `tools-sidebar.php`. Si vous préférez un autre nom ou l'emplacement `app/Views/partials/partials/` pour une raison spécifique, dites-le et je le déplace.
+
+Prochaine étape (optionnelle)
+- Extraire le CSS spécifique de la sidebar dans `styles.css` (petite section) pour garantir un style indépendant.
+- Ajouter tests unitaires pour s'assurer que le partial est inclus correctement dans les vues (si souhaité).
+
+Souhaitez-vous que je fasse l'extraction du CSS pour la sidebar maintenant ?
+
+---
+
+_**User (2025-10-13 07:29Z)**_
+
+Raccorder le bouton Charger pour le rendre opérationnel
+
+---
+
+_**Assistant (copilot/gpt-5-mini)**_
+
+Je vais : 1) ajouter une tâche au todo list, 2) créer `public/assets/js/header-actions.js` qui branche le bouton `#loadBtn` pour demander un ID et naviguer vers `?load=ID`, 3) mettre à jour `header.php` pour inclure ce script via `AssetLoader::loadJS`, 4) vérifier que le script est accessible et inclus dans la page. Résultat attendu : bouton "Charger" fonctionne via prompt et redirection. J'exécute les modifications maintenant. 
+```
+
+```
+Je vais résumer rapidement ce que j'ai fait et comment tester — la modification est terminée.
+
+Actions réalisées
+- Créé le fichier JavaScript `header-actions.js` qui :
+  - Attache un handler au bouton #loadBtn.
+  - Affiche un prompt pour demander l'ID de la carte.
+  - Valide l'ID (entier > 0).
+  - Redirige vers la même URL avec paramètre ?load=ID.
+- Ajouté l'inclusion du script dans `header.php` via AssetLoader :
+  <?php AssetLoader::loadJS(['/assets/bootstrap.bundle.min.js', '/assets/js/header-actions.js']); ?>
+- Vérifié que le script est accessible via https://map-generator/assets/js/header-actions.js et que l'inclusion apparaît dans le HTML rendu.
+
+Comment tester localement
+- Ouvrir la page dans votre navigateur :
+  https://map-generator/
+- Cliquer sur "Charger" dans la barre de navigation.
+- Saisir un ID (ex: 3). Le navigateur doit se rediriger vers :
+  https://map-generator/?load=3
+- Confirmez que la carte se charge comme attendu.
+
+Notes et améliorations possibles
+- Remplacer le prompt par un modal Bootstrap pour une meilleure UX (je peux l'implémenter).
+- Autoriser la sélection depuis une liste plutôt que la saisie manuelle (chargement via un select ou un modal listant les projets).
+- Ajouter des tests JS/End-to-end si vous voulez verrouiller le comportement.
+
+Souhaitez-vous que je remplace le prompt par un modal Bootstrap maintenant ?
+
+---
+
+_**User (2025-10-13 07:29Z)**_
+
+Non je souhaite que charger redirige vers /projects
+
+---
+
+_**Assistant (copilot/gpt-5-mini)**_
+
+Pourquoi/quoi/attendu : je vais modifier le script `header-actions.js` pour que le bouton "Charger" redirige vers `/projects` au lieu d'afficher un prompt ; j'appliquerai la mise à jour et vérifierai que le fichier mis à jour est accessible. 
+```
+
+```
+C'est fait — j'ai mis à jour le comportement du bouton "Charger" pour qu'il redirige vers /projects.
+
+Détails des changements
+- Fichier modifié : `header-actions.js`
+- Ancien comportement : affichait un prompt pour saisir un ID et redirigeait vers ?load=ID
+- Nouveau comportement : redirige immédiatement vers /projects quand on clique sur le bouton
+
+Vérification rapide
+- Le script est chargé dans la page (inclusion confirmée dans `header.php`).
+- Le fichier est accessible et contient la nouvelle logique.
+
+Souhaitez-vous que je :
+- Remplace le lien /projects par une ouverture dans un nouvel onglet (target="_blank") ?
+- Ajoute un petit tooltip ou icône pour clarifier l'action du bouton ?
+
+---
+
+_**User (2025-10-13 07:34Z)**_
+
+dropdown-menu ne s'ouvre pas 
+
+---
+
+_**Assistant (copilot/gpt-5-mini)**_
+
+Pourquoi/quoi/attendu : je vais initialiser manuellement les dropdown Bootstrap au chargement pour corriger le problème d'ouverture (ajout d'un petit snippet dans `header-actions.js` qui instancie bootstrap.Dropdown pour les toggles). J'applique la modification et je vérifie le fichier rendu. Je vais éditer `header-actions.js` pour initialiser les dropdowns Bootstrap après le chargement du DOM (utiliser window.bootstrap si disponible). J'applique un patch minimal. Après ça, je vérifierai le rendu HTML pour confirmer l'inclusion. 
+```
+
+```
+Pourquoi/quoi/attendu : j'ajoute un fallback JS robuste dans `header-actions.js` pour ouvrir/fermer les dropdowns si Bootstrap ne gère pas correctement l'événement — outcome : clic sur « Paramètres » ouvrira le menu même si bootstrap échoue.
+
+J'applique le patch maintenant. 
+```
+
+```
+Plan d'action bref : j'ai ajouté une initialisation Bootstrap et un fallback JS pour gérer l'ouverture/fermeture des dropdowns lorsque le dropdown ne s'ouvre pas automatiquement.
+
+Travail effectué
+- Ajout d'une initialisation des dropdowns Bootstrap (si `window.bootstrap` est présent) dans `header-actions.js`.
+- Ajout d'un fallback JS : si Bootstrap ne gère pas le dropdown (conflits ou absence), le script gère manuellement l'ouverture/fermeture au clic et la fermeture au clic en dehors.
+- Le script est chargé après `bootstrap.bundle.min.js` via `AssetLoader` dans `header.php` (ordre correct).
+
+Vérifications rapides réalisées
+- Le HTML rendu contient bien le bouton avec `data-bs-toggle="dropdown"` et le `ul.dropdown-menu`.
+- Le script `header-actions.js` est chargé sur la page et contient l'initialisation + fallback.
+- Bootstrap est inclus via `bootstrap.bundle.min.js` (nécessaire pour que la méthode officielle fonctionne).
+
+Si le dropdown ne s'ouvre toujours pas, étapes de diagnostic suivantes (je peux les exécuter) :
+1) Ouvrir la console du navigateur et me fournir les erreurs JS présentes (ou me laisser reproduire via curl + headless test si vous préférez).  
+2) Vérifier qu'aucun CSS personnalisé ne masque `.dropdown-menu` ou n'empêche l'overflow visible.  
+3) Tester la suppression du fallback pour confirmer que le problème vient d'une collision (rare).
+
+Voulez-vous que je :
+- 1) ajoute un petit test automatisé (puppeteer) qui clique sur le toggle et vérifie l'ouverture, ou
+- 2) inspecte les styles CSS qui pourraient cacher le menu, ou
+- 3) rien de plus pour l'instant (je laisse tel quel) ?
+
+---
+
+_**User (2025-10-13 07:34Z)**_
+
+Ajouter dans le menu "Paramètre" > "Nouveau projet" qui renvoie vers : /projects/new
+
+---
+
+_**Assistant (copilot/gpt-5-mini)**_
+
+Pourquoi/quoi/attendu : j'ajoute un élément de menu "Nouveau projet" dans le dropdown "Paramètres" (lien vers /projects/new), puis je vérifie que le HTML rendu contient bien ce nouveau lien. Résultat attendu : entrée visible et fonctionnelle. J'applique la modification puis je vérifie via une requête HTTP. 
+```
+
+```
+Fait — j'ai ajouté l'item "Nouveau projet" dans le menu Paramètres et il pointe vers /projects/new.
+
+Résumé rapide
+- Fichier modifié : `header.php`
+- Changement : ajout de
+  <li><a class="dropdown-item" href="/projects/new"><i class="bi bi-plus-circle me-1"></i>Nouveau projet</a></li>
+  au début du dropdown "Paramètres".
+
+Vérification
+- L'entrée est présente dans le HTML rendu (vérifiée via curl).
+
+Souhaitez-vous que je :
+- ajoute une icône différente ou un raccourci clavier pour "Nouveau projet" ?
+- ajoute un test rapide qui ouvre le menu et clique sur l'item (puppeteer) ?
+
+---
+
+_**User (2025-10-13 07:39Z)**_
+
+dropdown-menu sort de l'écran à droite. Peux tu faire en sorte qu'il soit toujours entièrement visible quelque soit son contenu
+
+---
+
+_**Assistant (copilot/gpt-5-mini)**_
+
+Je vais ajouter dans `header-actions.js` une fonction qui ajuste dynamiquement la position des menus dropdown pour éviter qu'ils débordent à droite ou en bas : elle ajoutera `dropdown-menu-end` si nécessaire et forcera un `max-height` avec scroll vertical si le menu déborde verticalement. J'applique le patch puis je vérifierai le contenu du fichier. 
+```
+
+```
+J'ai ajouté une logique pour que les menus dropdown restent visibles et utilisables même s'ils débordent à droite ou en bas de l'écran.
+
+Résumé des changements
+- Fichier modifié : `header-actions.js`
+- Ajouté :
+  - Initialisation standard de Bootstrap Dropdown (si présent).
+  - Fallback manuel d'ouverture/fermeture si Bootstrap ne gère pas l'événement.
+  - Nouvelle fonction `adjustDropdownMenu(menu, toggle)` qui :
+    - ajoute `dropdown-menu-end` si le menu déborde à droite,
+    - limite la hauteur (`max-height`) et active le scroll vertical si le menu déborde en bas.
+  - Hook sur l'événement `show.bs.dropdown` (si Bootstrap est présent) pour ajuster avant affichage.
+
+Pourquoi ça résout le problème
+- Le script détecte si le menu sort de la fenêtre horizontale et l'aligne vers la droite (`dropdown-menu-end`).
+- Si le menu est trop grand verticalement, il limite sa hauteur pour empêcher qu'il dépasse de l'écran et ajoute un scroll interne.
+
+Vérifications
+- Le script est chargé après `bootstrap.bundle.min.js`.
+- Le HTML contient bien les dropdowns et les attributs nécessaires.
+
+Prochaine étape (optionnelle)
+- Ajouter une petite animation (fade) lors de l'ouverture/fermeture.
+- Ajuster les règles CSS de `.dropdown-menu` dans `styles.css` si vous souhaitez un style/scroll plus fin.
+- Tester en conditions réelles (plusieurs résolutions / mobile) : je peux lancer un petit test headless pour vérifier divers viewport sizes si vous voulez.
+
+Souhaitez-vous que j'ajoute un style CSS pour limiter la largeur maximale du dropdown, ou que je génère un test automatique pour valider plusieurs tailles d'écran ?
+
+---
+
+_**User (2025-10-13 07:39Z)**_
+
+Fait un float right avec un margin-right de 5px
+
+---
+
+_**Assistant (copilot/gpt-5-mini)**_
+
+
+
+---
+

+ 1 - 49
app/Views/home.php

@@ -164,55 +164,7 @@ include __DIR__ . '/partials/header.php';
                     </div>
                 </div>
 
-                <!-- Informations sur le projet -->
-                <div class="row mt-4">
-                    <div class="col-md-4">
-                        <div class="card">
-                            <div class="card-body">
-                                <h6 class="card-title">
-                                    <i class="bi bi-info-circle me-1"></i>
-                                    Informations
-                                </h6>
-                                <p class="card-text small">
-                                    <strong>Dimensions:</strong> 800x600 px<br>
-                                    <strong>Calques:</strong> 1<br>
-                                    <strong>Outil actif:</strong> Pinceau<br>
-                                    <strong>Couleur:</strong> <span style="display: inline-block; width: 12px; height: 12px; background-color: #000000; border-radius: 2px;"></span> Noir
-                                </p>
-                            </div>
-                        </div>
-                    </div>
-                    <div class="col-md-4">
-                        <div class="card">
-                            <div class="card-body">
-                                <h6 class="card-title">
-                                    <i class="bi bi-clock-history me-1"></i>
-                                    Historique
-                                </h6>
-                                <ul class="list-unstyled small">
-                                    <li><i class="bi bi-dot me-1"></i>Carte créée</li>
-                                    <li><i class="bi bi-dot me-1"></i>Calque ajouté</li>
-                                    <li><i class="bi bi-dot me-1"></i>Outil sélectionné</li>
-                                </ul>
-                            </div>
-                        </div>
-                    </div>
-                    <div class="col-md-4">
-                        <div class="card">
-                            <div class="card-body">
-                                <h6 class="card-title">
-                                    <i class="bi bi-star me-1"></i>
-                                    Astuces
-                                </h6>
-                                <p class="card-text small">
-                                    Utilisez Ctrl+Z pour annuler.<br>
-                                    Cliquez-droit pour le menu contextuel.<br>
-                                    Glissez-déposez des images pour les importer.
-                                </p>
-                            </div>
-                        </div>
-                    </div>
-                </div>
+                <?php include __DIR__ . '/partials/project-info.php'; ?>
             </div>
         </div>
     </div>

+ 16 - 120
app/Views/partials/header.php

@@ -1,5 +1,6 @@
 <!DOCTYPE html>
 <html lang="fr">
+
 <head>
     <meta charset="UTF-8">
     <meta name="viewport" content="width=device-width, initial-scale=1.0">
@@ -7,6 +8,7 @@
     <?php AssetLoader::loadCSS(['/assets/bootstrap.min.css', '/assets/bootstrap-icons.min.css', '/assets/styles.css']); ?>
 
 </head>
+
 <body>
     <header id="global-header">
         <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
@@ -20,29 +22,23 @@
                 </button>
                 <div class="collapse navbar-collapse" id="navbarNav">
                     <ul class="navbar-nav ms-auto">
-                        <li class="nav-item">
-                            <a class="nav-link" href="#" id="saveBtn">
-                                <i class="bi bi-save me-1"></i>Sauvegarder
-                            </a>
-                        </li>
-                        <li class="nav-item">
-                            <a class="nav-link" href="#" id="loadBtn">
-                                <i class="bi bi-folder2-open me-1"></i>Charger
-                            </a>
-                        </li>
-                        <li class="nav-item">
-                            <a class="nav-link" href="#" id="exportBtn">
-                                <i class="bi bi-download me-1"></i>Exporter
-                            </a>
-                        </li>
                         <li class="nav-item dropdown">
                             <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
                                 <i class="bi bi-gear me-1"></i>Paramètres
                             </a>
                             <ul class="dropdown-menu" aria-labelledby="navbarDropdown">
-                                <li><a class="dropdown-item" href="#">Préférences</a></li>
-                                <li><a class="dropdown-item" href="#">Raccourcis</a></li>
-                                <li><hr class="dropdown-divider"></li>
+                                <li><a class="dropdown-item" href="/projects/new">
+                                        <i class="bi bi-plus-circle me-1"></i>Nouveau projet
+                                    </a></li>
+                                <li><a class="dropdown-item" href="#" id="loadBtn">
+                                        <i class="bi bi-folder2-open me-1"></i>Charger
+                                    </a></li>
+                                <li><a class="dropdown-item" href="#" id="saveBtn">
+                                        <i class="bi bi-save me-1"></i>Sauvegarder
+                                    </a></li>
+                                <li>
+                                    <hr class="dropdown-divider">
+                                </li>
                                 <li><a class="dropdown-item" href="#">À propos</a></li>
                             </ul>
                         </li>
@@ -52,110 +48,10 @@
         </nav>
     </header>
 
-    <!-- Barre latérale des outils -->
-    <div class="sidebar">
-        <nav class="nav flex-column">
-            <!-- Gestion des projets -->
-            <div class="tool-group">
-                <h6><i class="bi bi-folder me-1"></i>Projet</h6>
-                <a class="nav-link" href="#" id="newMap">
-                    <i class="bi bi-plus-circle me-2"></i>Nouvelle carte
-                </a>
-                <a class="nav-link" href="#" id="openMap">
-                    <i class="bi bi-folder-open me-2"></i>Ouvrir carte
-                </a>
-                <a class="nav-link" href="#" id="saveProject">
-                    <i class="bi bi-save me-2"></i>Sauvegarder projet
-                </a>
-            </div>
-
-            <!-- Outils de dessin -->
-            <div class="tool-group">
-                <h6><i class="bi bi-brush me-1"></i>Outils</h6>
-                <a class="nav-link active" href="#" id="brushTool">
-                    <i class="bi bi-brush me-2"></i>Pinceau
-                </a>
-                <a class="nav-link" href="#" id="eraserTool">
-                    <i class="bi bi-eraser me-2"></i>Gomme
-                </a>
-                <a class="nav-link" href="#" id="fillTool">
-                    <i class="bi bi-paint-bucket me-2"></i>Remplissage
-                </a>
-                <a class="nav-link" href="#" id="lineTool">
-                    <i class="bi bi-dash me-2"></i>Ligne
-                </a>
-                <a class="nav-link" href="#" id="rectangleTool">
-                    <i class="bi bi-square me-2"></i>Rectangle
-                </a>
-                <a class="nav-link" href="#" id="circleTool">
-                    <i class="bi bi-circle me-2"></i>Cercle
-                </a>
-                <a class="nav-link" href="#" id="selectTool">
-                    <i class="bi bi-cursor me-2"></i>Sélection
-                </a>
-            </div>
-
-            <!-- Palette de couleurs -->
-            <div class="tool-group">
-                <h6><i class="bi bi-palette me-1"></i>Couleurs</h6>
-                <div class="px-3">
-                    <div class="color-palette d-flex flex-wrap gap-1 mb-2">
-                        <div class="color-swatch" style="background-color: #000000; width: 20px; height: 20px; border-radius: 3px; cursor: pointer;" title="Noir"></div>
-                        <div class="color-swatch" style="background-color: #ffffff; width: 20px; height: 20px; border-radius: 3px; border: 1px solid #ccc; cursor: pointer;" title="Blanc"></div>
-                        <div class="color-swatch" style="background-color: #ff0000; width: 20px; height: 20px; border-radius: 3px; cursor: pointer;" title="Rouge"></div>
-                        <div class="color-swatch" style="background-color: #00ff00; width: 20px; height: 20px; border-radius: 3px; cursor: pointer;" title="Vert"></div>
-                        <div class="color-swatch" style="background-color: #0000ff; width: 20px; height: 20px; border-radius: 3px; cursor: pointer;" title="Bleu"></div>
-                        <div class="color-swatch" style="background-color: #ffff00; width: 20px; height: 20px; border-radius: 3px; cursor: pointer;" title="Jaune"></div>
-                        <div class="color-swatch" style="background-color: #ff00ff; width: 20px; height: 20px; border-radius: 3px; cursor: pointer;" title="Magenta"></div>
-                        <div class="color-swatch" style="background-color: #00ffff; width: 20px; height: 20px; border-radius: 3px; cursor: pointer;" title="Cyan"></div>
-                    </div>
-                    <input type="color" class="form-control form-control-sm" id="customColor" value="#000000">
-                </div>
-            </div>
-
-            <!-- Calques -->
-            <div class="tool-group">
-                <h6><i class="bi bi-layers me-1"></i>Calques</h6>
-                <div class="px-3">
-                    <button class="btn btn-outline-light btn-sm w-100 mb-2" id="addLayer">
-                        <i class="bi bi-plus me-1"></i>Ajouter calque
-                    </button>
-                    <div id="layersList">
-                        <div class="layer-item d-flex align-items-center justify-content-between p-1">
-                            <span>Calque 1</span>
-                            <div>
-                                <i class="bi bi-eye me-1" style="cursor: pointer;"></i>
-                                <i class="bi bi-trash" style="cursor: pointer;"></i>
-                            </div>
-                        </div>
-                    </div>
-                </div>
-            </div>
-
-            <!-- Outils avancés -->
-            <div class="tool-group">
-                <h6><i class="bi bi-tools me-1"></i>Avancé</h6>
-                <a class="nav-link" href="#" id="header-zoomIn">
-                    <i class="bi bi-zoom-in me-2"></i>Zoom +
-                </a>
-                <a class="nav-link" href="#" id="header-zoomOut">
-                    <i class="bi bi-zoom-out me-2"></i>Zoom -
-                </a>
-                <a class="nav-link" href="#" id="header-gridToggle">
-                    <i class="bi bi-grid me-2"></i>Grille
-                </a>
-                <a class="nav-link" href="#" id="header-undoBtn">
-                    <i class="bi bi-arrow-counterclockwise me-2"></i>Annuler
-                </a>
-                <a class="nav-link" href="#" id="header-redoBtn">
-                    <i class="bi bi-arrow-clockwise me-2"></i>Rétablir
-                </a>
-            </div>
-        </nav>
-    </div>
+    <?php include __DIR__ . '/tools-sidebar.php'; ?>
 
     <div class="main-content">
         <!-- Contenu principal de l'application -->
     </div>
 
-    <?php AssetLoader::loadJS(['/assets/bootstrap.bundle.min.js']); ?>
+    <?php AssetLoader::loadJS(['/assets/bootstrap.bundle.min.js', '/assets/js/header-actions.js']); ?>

+ 55 - 0
app/Views/partials/project-info.php

@@ -0,0 +1,55 @@
+<?php
+/**
+ * Partiel pour les informations du projet
+ */
+?>
+
+<!-- Informations sur le projet -->
+<div class="row mt-4">
+    <div class="col-md-4">
+        <div class="card">
+            <div class="card-body">
+                <h6 class="card-title">
+                    <i class="bi bi-info-circle me-1"></i>
+                    Informations
+                </h6>
+                <p class="card-text small">
+                    <strong>Dimensions:</strong> 800x600 px<br>
+                    <strong>Calques:</strong> 1<br>
+                    <strong>Outil actif:</strong> Pinceau<br>
+                    <strong>Couleur:</strong> <span style="display: inline-block; width: 12px; height: 12px; background-color: #000000; border-radius: 2px;"></span> Noir
+                </p>
+            </div>
+        </div>
+    </div>
+    <div class="col-md-4">
+        <div class="card">
+            <div class="card-body">
+                <h6 class="card-title">
+                    <i class="bi bi-clock-history me-1"></i>
+                    Historique
+                </h6>
+                <ul class="list-unstyled small">
+                    <li><i class="bi bi-dot me-1"></i>Carte créée</li>
+                    <li><i class="bi bi-dot me-1"></i>Calque ajouté</li>
+                    <li><i class="bi bi-dot me-1"></i>Outil sélectionné</li>
+                </ul>
+            </div>
+        </div>
+    </div>
+    <div class="col-md-4">
+        <div class="card">
+            <div class="card-body">
+                <h6 class="card-title">
+                    <i class="bi bi-star me-1"></i>
+                    Astuces
+                </h6>
+                <p class="card-text small">
+                    Utilisez Ctrl+Z pour annuler.<br>
+                    Cliquez-droit pour le menu contextuel.<br>
+                    Glissez-déposez des images pour les importer.
+                </p>
+            </div>
+        </div>
+    </div>
+</div>

+ 25 - 0
app/Views/partials/tools-sidebar.php

@@ -0,0 +1,25 @@
+<?php
+/**
+ * Partiel : Barre latérale des outils (extrait de header.php)
+ */
+?>
+
+<div class="sidebar">
+    <nav class="nav flex-column">
+
+        <!-- Outils de dessin -->
+        <div class="tool-group">
+            <h6><i class="bi bi-brush me-1"></i>Historique</h6>
+            <a class="nav-link" href="#" id="header-undoBtn">
+                <i class="bi bi-arrow-counterclockwise me-2"></i>Annuler
+            </a>
+            <a class="nav-link" href="#" id="header-redoBtn">
+                <i class="bi bi-arrow-clockwise me-2"></i>Rétablir
+            </a>
+            <hr>
+            <a class="nav-link" href="#" id="selectTool">
+                <i class="bi bi-cursor me-2"></i>Sélection
+            </a>
+        </div>
+    </nav>
+</div>

+ 22 - 0
logs/app.log

@@ -88,3 +88,25 @@
 [2025-10-12 17:40:33] [Données décodées avec succès] INFO
 [2025-10-12 20:20:16] [Carte créée avec succès, ID: 5] INFO
 [2025-10-12 20:23:46] [ERROR] Erreur : Undefined array key "total" dans Command line code à la ligne 23
+[2025-10-13 09:25:32] [ERROR] Erreur : include(/Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/partials/tools-sidebar.php): Failed to open stream: No such file or directory dans /Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/header.php à la ligne 55
+[2025-10-13 09:25:32] [ERROR] Erreur : include(): Failed opening '/Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/partials/tools-sidebar.php' for inclusion (include_path='.:/Applications/MAMP/bin/php/php8.1.31/lib/php') dans /Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/header.php à la ligne 55
+[2025-10-13 09:25:35] [ERROR] Erreur : include(/Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/partials/tools-sidebar.php): Failed to open stream: No such file or directory dans /Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/header.php à la ligne 55
+[2025-10-13 09:25:35] [ERROR] Erreur : include(): Failed opening '/Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/partials/tools-sidebar.php' for inclusion (include_path='.:/Applications/MAMP/bin/php/php8.1.31/lib/php') dans /Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/header.php à la ligne 55
+[2025-10-13 09:25:38] [ERROR] Erreur : include(/Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/partials/tools-sidebar.php): Failed to open stream: No such file or directory dans /Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/header.php à la ligne 55
+[2025-10-13 09:25:38] [ERROR] Erreur : include(): Failed opening '/Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/partials/tools-sidebar.php' for inclusion (include_path='.:/Applications/MAMP/bin/php/php8.1.31/lib/php') dans /Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/header.php à la ligne 55
+[2025-10-13 09:25:39] [ERROR] Erreur : include(/Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/partials/tools-sidebar.php): Failed to open stream: No such file or directory dans /Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/header.php à la ligne 55
+[2025-10-13 09:25:39] [ERROR] Erreur : include(): Failed opening '/Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/partials/tools-sidebar.php' for inclusion (include_path='.:/Applications/MAMP/bin/php/php8.1.31/lib/php') dans /Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/header.php à la ligne 55
+[2025-10-13 09:25:40] [ERROR] Erreur : include(/Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/partials/tools-sidebar.php): Failed to open stream: No such file or directory dans /Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/header.php à la ligne 55
+[2025-10-13 09:25:40] [ERROR] Erreur : include(): Failed opening '/Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/partials/tools-sidebar.php' for inclusion (include_path='.:/Applications/MAMP/bin/php/php8.1.31/lib/php') dans /Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/header.php à la ligne 55
+[2025-10-13 09:25:42] [ERROR] Erreur : include(/Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/partials/tools-sidebar.php): Failed to open stream: No such file or directory dans /Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/header.php à la ligne 55
+[2025-10-13 09:25:42] [ERROR] Erreur : include(): Failed opening '/Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/partials/tools-sidebar.php' for inclusion (include_path='.:/Applications/MAMP/bin/php/php8.1.31/lib/php') dans /Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/header.php à la ligne 55
+[2025-10-13 09:25:43] [ERROR] Erreur : include(/Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/partials/tools-sidebar.php): Failed to open stream: No such file or directory dans /Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/header.php à la ligne 55
+[2025-10-13 09:25:43] [ERROR] Erreur : include(): Failed opening '/Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/partials/tools-sidebar.php' for inclusion (include_path='.:/Applications/MAMP/bin/php/php8.1.31/lib/php') dans /Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/header.php à la ligne 55
+[2025-10-13 09:25:43] [ERROR] Erreur : include(/Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/partials/tools-sidebar.php): Failed to open stream: No such file or directory dans /Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/header.php à la ligne 55
+[2025-10-13 09:25:43] [ERROR] Erreur : include(): Failed opening '/Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/partials/tools-sidebar.php' for inclusion (include_path='.:/Applications/MAMP/bin/php/php8.1.31/lib/php') dans /Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/header.php à la ligne 55
+[2025-10-13 09:25:44] [ERROR] Erreur : include(/Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/partials/tools-sidebar.php): Failed to open stream: No such file or directory dans /Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/header.php à la ligne 55
+[2025-10-13 09:25:44] [ERROR] Erreur : include(): Failed opening '/Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/partials/tools-sidebar.php' for inclusion (include_path='.:/Applications/MAMP/bin/php/php8.1.31/lib/php') dans /Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/header.php à la ligne 55
+[2025-10-13 09:25:45] [ERROR] Erreur : include(/Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/partials/tools-sidebar.php): Failed to open stream: No such file or directory dans /Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/header.php à la ligne 55
+[2025-10-13 09:25:45] [ERROR] Erreur : include(): Failed opening '/Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/partials/tools-sidebar.php' for inclusion (include_path='.:/Applications/MAMP/bin/php/php8.1.31/lib/php') dans /Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/header.php à la ligne 55
+[2025-10-13 09:25:47] [ERROR] Erreur : include(/Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/partials/tools-sidebar.php): Failed to open stream: No such file or directory dans /Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/header.php à la ligne 55
+[2025-10-13 09:25:47] [ERROR] Erreur : include(): Failed opening '/Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/partials/tools-sidebar.php' for inclusion (include_path='.:/Applications/MAMP/bin/php/php8.1.31/lib/php') dans /Volumes/SSD-T1/mamp-pro/map-generator/app/Views/partials/header.php à la ligne 55

+ 115 - 0
public/assets/js/header-actions.js

@@ -0,0 +1,115 @@
+// Actions du header (souris, boutons)
+(function() {
+    document.addEventListener('DOMContentLoaded', function() {
+        // Bouton Charger: demander un ID et naviguer vers ?load=ID
+        const loadBtn = document.getElementById('loadBtn');
+        if (loadBtn) {
+            // Rediriger vers la liste des projets
+            loadBtn.addEventListener('click', function(e) {
+                e.preventDefault();
+                window.location.href = '/projects';
+            });
+        }
+
+        // Initialiser les dropdown Bootstrap si disponible (résout les dropdown qui ne s'ouvrent pas)
+        try {
+            if (window.bootstrap && window.bootstrap.Dropdown) {
+                const toggles = document.querySelectorAll('[data-bs-toggle="dropdown"]');
+                toggles.forEach(function(toggle) {
+                    // Crée une instance si nécessaire
+                    if (!bootstrap.Dropdown.getInstance(toggle)) {
+                        new bootstrap.Dropdown(toggle);
+                    }
+                });
+            }
+        } catch (err) {
+            // ne pas casser l'exécution si bootstrap absent
+            console.warn('Bootstrap dropdown init failed:', err);
+        }
+
+        // Fallback manuel : si le dropdown ne fonctionne pas (ex: conflits), on gère l'ouverture/fermeture
+        (function() {
+            const toggles = Array.from(document.querySelectorAll('[data-bs-toggle="dropdown"]'));
+            if (!toggles.length) return;
+
+            function closeAll() {
+                document.querySelectorAll('.dropdown-menu.show').forEach(function(menu) {
+                    menu.classList.remove('show');
+                });
+                toggles.forEach(function(t) { t.setAttribute('aria-expanded', 'false'); });
+            }
+
+            // Clic sur toggle : bascule l'affichage
+            toggles.forEach(function(toggle) {
+                toggle.addEventListener('click', function(e) {
+                    // Si Bootstrap gère déjà l'événement, laisser faire
+                    try {
+                        if (window.bootstrap && window.bootstrap.Dropdown && bootstrap.Dropdown.getInstance(toggle)) {
+                            return; // bootstrap gère
+                        }
+                    } catch (_) {}
+
+                    e.preventDefault();
+                    e.stopPropagation();
+                    const parent = toggle.closest('.dropdown') || toggle.parentElement;
+                    const menu = parent ? parent.querySelector('.dropdown-menu') : null;
+                    if (!menu) return;
+
+                    const isOpen = menu.classList.contains('show');
+                    closeAll();
+                    if (!isOpen) {
+                        menu.classList.add('show');
+                        toggle.setAttribute('aria-expanded', 'true');
+                        // Ajuster la position pour qu'il soit entièrement visible
+                        adjustDropdownMenu(menu, toggle);
+                    }
+                });
+            });
+
+            // Fermer au clic en dehors
+            document.addEventListener('click', function() {
+                closeAll();
+            });
+        })();
+
+        // Ajuste la position/hauteur d'un menu dropdown pour qu'il tienne dans la fenêtre
+        function adjustDropdownMenu(menu, toggle) {
+            if (!menu) return;
+            // Reset des styles précédents
+            menu.style.maxHeight = '';
+            menu.classList.remove('dropdown-menu-end');
+
+            const rect = menu.getBoundingClientRect();
+            const vw = Math.max(document.documentElement.clientWidth || 0, window.innerWidth || 0);
+            const vh = Math.max(document.documentElement.clientHeight || 0, window.innerHeight || 0);
+
+            // Si déborde à droite -> aligner à droite du toggle (ajouter dropdown-menu-end)
+            if (rect.right > vw - 8) {
+                menu.classList.add('dropdown-menu-end');
+            }
+
+            // Recalculer après changement éventuel
+            const rect2 = menu.getBoundingClientRect();
+
+            // Si déborde en bas -> limiter la hauteur et activer scroll
+            if (rect2.bottom > vh - 8) {
+                const available = vh - 16 - rect2.top; // garder un petit padding
+                if (available > 100) {
+                    menu.style.maxHeight = available + 'px';
+                    menu.style.overflowY = 'auto';
+                }
+            }
+        }
+
+        // Si Bootstrap est présent, utiliser l'événement show pour ajuster
+        if (window.bootstrap && window.bootstrap.Dropdown) {
+            document.querySelectorAll('[data-bs-toggle="dropdown"]').forEach(function(toggle) {
+                toggle.addEventListener('show.bs.dropdown', function(e) {
+                    const parent = toggle.closest('.dropdown') || toggle.parentElement;
+                    const menu = parent ? parent.querySelector('.dropdown-menu') : null;
+                    if (menu) adjustDropdownMenu(menu, toggle);
+                });
+            });
+        }
+    });
+})();

+ 15 - 0
public/assets/styles.css

@@ -199,4 +199,19 @@ body {
     .map-toolbar .d-flex {
         justify-content: center;
     }
+}
+
+/* Forcer les dropdowns à rester visibles (ajustement de position)
+   - .dropdown-menu-end sera utilisé par le JS si nécessaire
+   - on ajoute une marge à droite pour éviter qu'il colle au bord
+*/
+.dropdown-menu.dropdown-menu-end {
+    right: 5px !important;
+    left: auto !important;
+}
+
+/* Fallback : si le dropdown est positionné normalement, on ajoute une petite marge à droite
+   pour éviter qu'il touche le bord de l'écran */
+.dropdown-menu {
+    margin-right: 5px;
 }