8.5 KiB
Executable File
8.5 KiB
Executable File
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)
<button class="photo-primary-toggle ${photo.is_primary ? 'active' : ''}"
onclick="setPrimaryPhoto(${photo.id})"
title="${photo.is_primary ? 'Photo principale' : 'Définir comme photo principale'}">
<i class="fas fa-${photo.is_primary ? 'check-circle' : 'circle'}"></i>
</button>
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)
/* 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)
// 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 :
- Appel API POST pour définir la photo comme principale
- Message de succès
- Rechargement de la galerie pour mettre à jour les icônes
4. Endpoint API Backend
Fichier : backend/app/api/endpoints/peripherals.py (lignes 370-396)
@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 :
- Vérifie que la photo existe et appartient au périphérique
- Retire le flag
is_primaryde toutes les autres photos - Définit
is_primary=Truepour la photo sélectionnée - 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
-
Une seule photo principale par périphérique
- Définie automatiquement lors de la sélection
- Les autres sont désélectionnées automatiquement
-
Photo principale = Vignette
- Utilisée dans les listes de périphériques
- Affichée comme aperçu principal
-
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
- Ouvrir page détail : Aller sur
/peripheral-detail.html?id=3 - Observer les icônes : Voir ⭕ ou ✅ en bas à gauche de chaque photo
- Hover sur icône : Vérifier effet scale + changement couleur
- Cliquer icône non cochée :
- Message "Photo principale définie"
- Icône devient ✅
- Autres icônes deviennent ⭕
- Vérifier badge : Badge "★ Principale" sur la photo cochée
Test API
# 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 :
{
"message": "Photo set as primary",
"photo_id": 5
}
Vérification base de données
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