# Fonctionnalité : Icône cliquable pour photo principale ## 🎯 Objectif Ajouter une icône cliquable en bas à gauche de chaque photo dans la galerie pour permettre de définir facilement quelle photo sera utilisée comme vignette principale (thumbnail). ## ✅ Implémentation ### 1. Interface utilisateur **Fichier** : `frontend/js/peripheral-detail.js` (lignes 108-112) ```javascript ``` **Icônes** : - ⭕ `circle` (non cochée) - Photo normale - ✅ `check-circle` (cochée) - Photo principale ### 2. Style CSS **Fichier** : `frontend/css/peripherals.css` (lignes 764-803) ```css /* Photo Primary Toggle */ .photo-primary-toggle { position: absolute; bottom: 8px; left: 8px; background: rgba(0, 0, 0, 0.7); border: 2px solid #666; border-radius: 50%; width: 32px; height: 32px; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.2s ease; color: #999; font-size: 16px; z-index: 10; } .photo-primary-toggle:hover { background: rgba(0, 0, 0, 0.85); border-color: #66d9ef; color: #66d9ef; transform: scale(1.1); } .photo-primary-toggle.active { background: rgba(102, 217, 239, 0.2); border-color: #66d9ef; color: #66d9ef; } .photo-primary-toggle.active:hover { background: rgba(102, 217, 239, 0.3); } .photo-item { position: relative; } ``` **Caractéristiques** : - Position : Coin inférieur gauche de chaque photo - Taille : 32×32px, bouton rond - États : normal (gris), hover (bleu), active (bleu clair) - Effet : Scale 1.1 au hover - Z-index élevé pour rester au-dessus de l'image ### 3. Fonction JavaScript **Fichier** : `frontend/js/peripheral-detail.js` (lignes 239-252) ```javascript // Set photo as primary async function setPrimaryPhoto(photoId) { try { await apiRequest(`/peripherals/${peripheralId}/photos/${photoId}/set-primary`, { method: 'POST' }); showSuccess('Photo principale définie'); loadPhotos(); // Reload to update icons } catch (error) { console.error('Error setting primary photo:', error); showError('Erreur lors de la définition de la photo principale'); } } ``` **Processus** : 1. Appel API POST pour définir la photo comme principale 2. Message de succès 3. Rechargement de la galerie pour mettre à jour les icônes ### 4. Endpoint API Backend **Fichier** : `backend/app/api/endpoints/peripherals.py` (lignes 370-396) ```python @router.post("/{peripheral_id}/photos/{photo_id}/set-primary", status_code=200) def set_primary_photo( peripheral_id: int, photo_id: int, db: Session = Depends(get_peripherals_db) ): """Set a photo as primary (thumbnail)""" # Get the photo photo = db.query(PeripheralPhoto).filter( PeripheralPhoto.id == photo_id, PeripheralPhoto.peripheral_id == peripheral_id ).first() if not photo: raise HTTPException(status_code=404, detail="Photo not found") # Unset all other primary photos for this peripheral db.query(PeripheralPhoto).filter( PeripheralPhoto.peripheral_id == peripheral_id, PeripheralPhoto.id != photo_id ).update({"is_primary": False}) # Set this photo as primary photo.is_primary = True db.commit() return {"message": "Photo set as primary", "photo_id": photo_id} ``` **Logique** : 1. Vérifie que la photo existe et appartient au périphérique 2. Retire le flag `is_primary` de toutes les autres photos 3. Définit `is_primary=True` pour la photo sélectionnée 4. Garantit qu'une seule photo est principale à la fois ## 🎨 Rendu visuel ### Galerie de photos ``` ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ │ │ │ │ │ Photo 1 │ │ Photo 2 │ │ Photo 3 │ │ │ │ │ │ │ │ ⭕ │ │ ✅ │ │ ⭕ │ │ [🗑️] │ │ [🗑️] │ │ [🗑️] │ │ ★ Principale│ │ │ │ │ └─────────────┘ └─────────────┘ └─────────────┘ ``` **Légende** : - ⭕ = Icône ronde grise (non sélectionnée) - ✅ = Icône check bleu cyan (sélectionnée) - ★ = Badge "Principale" (affiché en haut) - 🗑️ = Bouton supprimer (en haut à droite) ### États de l'icône | État | Apparence | Couleur | Comportement | |------|-----------|---------|--------------| | **Normal** | ⭕ Circle | Gris #999 | Cliquable | | **Hover** | ⭕ Circle agrandie | Bleu #66d9ef | Scale 1.1 | | **Active** | ✅ Check-circle | Bleu #66d9ef | Fond bleu clair | | **Active + Hover** | ✅ Check-circle | Bleu plus clair | Rétroaction visuelle | ## 🔄 Flux d'utilisation ``` 1. User voit la galerie de photos ↓ 2. Chaque photo affiche une icône ⭕/✅ en bas à gauche ↓ 3. User clique sur une icône ⭕ (non sélectionnée) ↓ 4. setPrimaryPhoto(photoId) appelé │ ├─> POST /api/peripherals/{id}/photos/{photo_id}/set-primary │ └─> Backend met à jour is_primary ↓ 5. Base de données mise à jour │ ├─> Ancienne photo principale : is_primary = false │ └─> Nouvelle photo : is_primary = true ↓ 6. Success │ ├─> Message "Photo principale définie" │ ├─> Galerie rechargée │ └─> Icônes mises à jour (✅ sur la nouvelle, ⭕ sur les autres) ``` ## 📊 Règles métier ### Contraintes 1. **Une seule photo principale** par périphérique - Définie automatiquement lors de la sélection - Les autres sont désélectionnées automatiquement 2. **Photo principale = Vignette** - Utilisée dans les listes de périphériques - Affichée comme aperçu principal 3. **Upload avec is_primary** - Possibilité de cocher lors de l'upload - Si cochée, retire le flag des autres ### Avantages - ✅ **Interface intuitive** : Un clic pour changer - ✅ **Visuel clair** : États bien différenciés - ✅ **Feedback immédiat** : Message + rechargement - ✅ **Cohérence** : Une seule photo principale garantie ## 🧪 Tests ### Test manuel 1. **Ouvrir page détail** : Aller sur `/peripheral-detail.html?id=3` 2. **Observer les icônes** : Voir ⭕ ou ✅ en bas à gauche de chaque photo 3. **Hover sur icône** : Vérifier effet scale + changement couleur 4. **Cliquer icône non cochée** : - Message "Photo principale définie" - Icône devient ✅ - Autres icônes deviennent ⭕ 5. **Vérifier badge** : Badge "★ Principale" sur la photo cochée ### Test API ```bash # Définir photo ID 5 comme principale pour périphérique 3 curl -X POST "http://10.0.0.50:8007/api/peripherals/3/photos/5/set-primary" \ -H "X-API-Token: YOUR_TOKEN" ``` **Résultat attendu** : ```json { "message": "Photo set as primary", "photo_id": 5 } ``` ### Vérification base de données ```bash docker exec linux_benchtools_backend python3 -c " import sqlite3 conn = sqlite3.connect('/app/data/peripherals.db') cursor = conn.cursor() cursor.execute('SELECT id, filename, is_primary FROM peripheral_photos WHERE peripheral_id = 3') for row in cursor.fetchall(): print(f'Photo {row[0]}: {row[1]} - Primary: {row[2]}') " ``` **Résultat attendu** : ``` Photo 4: image1.png - Primary: 0 Photo 5: image2.png - Primary: 1 ← Une seule Photo 6: image3.png - Primary: 0 ``` ## 📝 Fichiers modifiés ### Frontend - ✅ `frontend/js/peripheral-detail.js` - Ajout bouton + fonction setPrimaryPhoto() - ✅ `frontend/css/peripherals.css` - Style .photo-primary-toggle ### Backend - ✅ `backend/app/api/endpoints/peripherals.py` - Endpoint POST set-primary ### Documentation - ✅ `docs/FEATURE_PRIMARY_PHOTO_TOGGLE.md` - Cette documentation ## 💡 Améliorations futures possibles - [ ] Drag & drop pour réorganiser les photos - [ ] Double-clic sur photo pour la définir comme principale - [ ] Raccourci clavier (ex: P pour Primary) - [ ] Animation de transition entre photos principales - [ ] Preview de la vignette avant validation --- **Date** : 31 décembre 2025 **Statut** : ✅ Implémenté et fonctionnel **Impact** : Interface intuitive pour choisir la photo principale