addon
This commit is contained in:
294
docs/FEATURE_PRIMARY_PHOTO_TOGGLE.md
Executable file
294
docs/FEATURE_PRIMARY_PHOTO_TOGGLE.md
Executable file
@@ -0,0 +1,294 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user