Files
serv_benchmark/docs/FEATURE_PRIMARY_PHOTO_TOGGLE.md
Gilles Soulier c67befc549 addon
2026-01-05 16:08:01 +01:00

295 lines
8.5 KiB
Markdown
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
<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)
```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