addon
This commit is contained in:
450
docs/SESSION_2025-12-31_EDIT_PERIPHERAL.md
Executable file
450
docs/SESSION_2025-12-31_EDIT_PERIPHERAL.md
Executable file
@@ -0,0 +1,450 @@
|
||||
# Session 2025-12-31 : Implémentation du bouton "Modifier"
|
||||
|
||||
## 🎯 Objectif
|
||||
|
||||
Implémenter le bouton "Modifier" dans la page de détail du périphérique pour permettre l'édition complète des informations.
|
||||
|
||||
## 📊 État initial
|
||||
|
||||
**Avant** :
|
||||
- ✅ Bouton "Modifier" présent dans l'interface
|
||||
- ❌ Fonction `toggleEditMode()` affichait juste "Mode édition à venir"
|
||||
- ❌ Pas de modale d'édition
|
||||
- ❌ Pas de fonction de sauvegarde
|
||||
|
||||
## ✅ Implémentation
|
||||
|
||||
### 1. Modale d'édition HTML
|
||||
|
||||
**Fichier** : `frontend/peripheral-detail.html` (lignes 305-453)
|
||||
|
||||
**Structure** :
|
||||
```html
|
||||
<div id="modal-edit" class="modal">
|
||||
<div class="modal-content modal-large">
|
||||
<form id="form-edit-peripheral" onsubmit="savePeripheral(event)">
|
||||
<!-- 3 sections principales -->
|
||||
<div class="form-section">Identification</div>
|
||||
<div class="form-section">Achat</div>
|
||||
<div class="form-section">État et localisation</div>
|
||||
<div class="form-section full-width">Documentation technique</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
**Champs disponibles** :
|
||||
|
||||
#### Section Identification
|
||||
- `nom` * (requis)
|
||||
- `type_principal` * (requis)
|
||||
- `sous_type`
|
||||
- `marque`
|
||||
- `modele`
|
||||
- `numero_serie`
|
||||
|
||||
#### Section Achat
|
||||
- `boutique`
|
||||
- `date_achat` (type date)
|
||||
- `prix` (number)
|
||||
- `devise` (3 caractères, défaut: EUR)
|
||||
- `garantie_duree_mois` (number)
|
||||
|
||||
#### Section État et localisation
|
||||
- `etat` (select: Neuf, Bon, Usagé, Défectueux, Retiré)
|
||||
- `rating` (0-5 étoiles cliquables)
|
||||
- `quantite_totale` (number)
|
||||
- `quantite_disponible` (number)
|
||||
|
||||
#### Section Documentation technique
|
||||
- `synthese` (textarea Markdown)
|
||||
- `cli_yaml` (textarea YAML)
|
||||
- `cli_raw` (textarea Markdown)
|
||||
- `specifications` (textarea Markdown)
|
||||
- `notes` (textarea Markdown)
|
||||
|
||||
### 2. Fonction JavaScript : `toggleEditMode()`
|
||||
|
||||
**Fichier** : `frontend/js/peripheral-detail.js` (lignes 461-494)
|
||||
|
||||
```javascript
|
||||
function toggleEditMode() {
|
||||
if (!peripheral) {
|
||||
showError('Aucun périphérique chargé');
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate form with peripheral data
|
||||
document.getElementById('edit-nom').value = peripheral.nom || '';
|
||||
document.getElementById('edit-type_principal').value = peripheral.type_principal || '';
|
||||
// ... (tous les champs)
|
||||
|
||||
// Special handling for rating (star system)
|
||||
setEditRating(peripheral.rating || 0);
|
||||
|
||||
// Show modal
|
||||
document.getElementById('modal-edit').style.display = 'block';
|
||||
}
|
||||
```
|
||||
|
||||
**Fonctionnement** :
|
||||
1. Vérifie que le périphérique est chargé
|
||||
2. Remplit tous les champs du formulaire avec les données actuelles
|
||||
3. Applique la note avec le système d'étoiles
|
||||
4. Affiche la modale
|
||||
|
||||
### 3. Fonction : `setEditRating()`
|
||||
|
||||
**Fichier** : `frontend/js/peripheral-detail.js` (lignes 500-513)
|
||||
|
||||
```javascript
|
||||
function setEditRating(rating) {
|
||||
const stars = document.querySelectorAll('#edit-star-rating .fa-star');
|
||||
const ratingInput = document.getElementById('edit-rating');
|
||||
|
||||
ratingInput.value = rating;
|
||||
|
||||
stars.forEach((star, index) => {
|
||||
if (index < rating) {
|
||||
star.classList.add('active');
|
||||
} else {
|
||||
star.classList.remove('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**Fonctionnement** :
|
||||
- Active les étoiles jusqu'à la note donnée
|
||||
- Stocke la valeur dans le champ hidden
|
||||
- Utilisé à la fois lors du chargement et lors du clic
|
||||
|
||||
### 4. Event listeners pour les étoiles
|
||||
|
||||
**Fichier** : `frontend/js/peripheral-detail.js` (lignes 515-525)
|
||||
|
||||
```javascript
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const editStars = document.querySelectorAll('#edit-star-rating .fa-star');
|
||||
|
||||
editStars.forEach(star => {
|
||||
star.addEventListener('click', () => {
|
||||
const rating = parseInt(star.getAttribute('data-rating'));
|
||||
setEditRating(rating);
|
||||
});
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
**Fonctionnement** :
|
||||
- Attache un event listener à chaque étoile
|
||||
- Au clic, récupère la note (1-5)
|
||||
- Appelle `setEditRating()` pour mettre à jour visuellement
|
||||
|
||||
### 5. Fonction : `savePeripheral()`
|
||||
|
||||
**Fichier** : `frontend/js/peripheral-detail.js` (lignes 527-559)
|
||||
|
||||
```javascript
|
||||
async function savePeripheral(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const form = event.target;
|
||||
const formData = new FormData(form);
|
||||
const data = {};
|
||||
|
||||
// Convert FormData to object
|
||||
for (let [key, value] of formData.entries()) {
|
||||
// Convert numeric fields
|
||||
if (['prix', 'garantie_duree_mois', 'quantite_totale', 'quantite_disponible', 'rating'].includes(key)) {
|
||||
data[key] = value ? parseFloat(value) : null;
|
||||
} else {
|
||||
data[key] = value || null;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await apiRequest(`/peripherals/${peripheralId}`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify(data)
|
||||
});
|
||||
|
||||
showSuccess('Périphérique mis à jour avec succès');
|
||||
closeEditModal();
|
||||
|
||||
// Reload peripheral data
|
||||
await loadPeripheral();
|
||||
} catch (error) {
|
||||
console.error('Error updating peripheral:', error);
|
||||
showError('Erreur lors de la mise à jour du périphérique');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Fonctionnement** :
|
||||
1. Empêche le submit par défaut
|
||||
2. Récupère toutes les données du formulaire
|
||||
3. Convertit les champs numériques en `float`
|
||||
4. Envoie une requête `PUT` à l'API
|
||||
5. Affiche un message de succès/erreur
|
||||
6. Ferme la modale
|
||||
7. Recharge les données du périphérique pour afficher les changements
|
||||
|
||||
### 6. Fonction : `closeEditModal()`
|
||||
|
||||
**Fichier** : `frontend/js/peripheral-detail.js` (lignes 496-498)
|
||||
|
||||
```javascript
|
||||
function closeEditModal() {
|
||||
document.getElementById('modal-edit').style.display = 'none';
|
||||
}
|
||||
```
|
||||
|
||||
**Utilisation** :
|
||||
- Bouton "Annuler" dans la modale
|
||||
- Croix de fermeture (×)
|
||||
- Clic en dehors de la modale (via `window.onclick`)
|
||||
|
||||
### 7. Style CSS pour grande modale
|
||||
|
||||
**Fichier** : `frontend/css/peripherals.css` (lignes 284-287)
|
||||
|
||||
```css
|
||||
.modal-content.modal-large {
|
||||
max-width: 1400px;
|
||||
width: 95%;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage** : Classe `.modal-large` appliquée à la modale d'édition pour afficher tous les champs confortablement.
|
||||
|
||||
## 🔄 Flux complet
|
||||
|
||||
```
|
||||
1. User clique "Modifier"
|
||||
↓
|
||||
2. toggleEditMode()
|
||||
├─> Vérifie que peripheral est chargé
|
||||
├─> Remplit tous les champs du formulaire
|
||||
├─> Applique la note (étoiles)
|
||||
└─> Affiche la modale
|
||||
↓
|
||||
3. User modifie les champs
|
||||
├─> Clic sur étoiles → setEditRating()
|
||||
└─> Saisie texte, nombres, dates
|
||||
↓
|
||||
4. User clique "Enregistrer"
|
||||
↓
|
||||
5. savePeripheral()
|
||||
├─> Prévient le submit par défaut
|
||||
├─> Récupère FormData
|
||||
├─> Convertit types (string → number)
|
||||
├─> PUT /api/peripherals/{id}
|
||||
├─> Succès → showSuccess() + closeEditModal()
|
||||
├─> Erreur → showError()
|
||||
└─> Recharge → loadPeripheral()
|
||||
↓
|
||||
6. Page mise à jour avec nouvelles données
|
||||
```
|
||||
|
||||
## 📋 Requête API
|
||||
|
||||
### Endpoint
|
||||
|
||||
```
|
||||
PUT /api/peripherals/{id}
|
||||
```
|
||||
|
||||
### Headers
|
||||
|
||||
```json
|
||||
{
|
||||
"Content-Type": "application/json",
|
||||
"X-API-Token": "YOUR_TOKEN"
|
||||
}
|
||||
```
|
||||
|
||||
### Body (exemple)
|
||||
|
||||
```json
|
||||
{
|
||||
"nom": "Logitech MX Master 3S",
|
||||
"type_principal": "Souris",
|
||||
"sous_type": "Sans fil",
|
||||
"marque": "Logitech",
|
||||
"modele": "MX Master 3S",
|
||||
"numero_serie": "LGI-2024-001",
|
||||
"boutique": "Amazon",
|
||||
"date_achat": "2024-12-01",
|
||||
"prix": 99.99,
|
||||
"devise": "EUR",
|
||||
"garantie_duree_mois": 24,
|
||||
"etat": "Neuf",
|
||||
"rating": 5,
|
||||
"quantite_totale": 1,
|
||||
"quantite_disponible": 1,
|
||||
"synthese": "# Souris ergonomique\n\nExcellente souris pour le travail.",
|
||||
"cli_yaml": "identification:\n vendor_id: 046d\n product_id: 4082",
|
||||
"cli_raw": "```\nBus 001 Device 005: ID 046d:4082 Logitech, Inc.\n```",
|
||||
"specifications": "## Caractéristiques\n- DPI : 8000\n- Bluetooth 5.0",
|
||||
"notes": "Achetée en promotion"
|
||||
}
|
||||
```
|
||||
|
||||
### Réponse (200 OK)
|
||||
|
||||
```json
|
||||
{
|
||||
"id": 3,
|
||||
"nom": "Logitech MX Master 3S",
|
||||
"type_principal": "Souris",
|
||||
// ... tous les champs mis à jour
|
||||
"updated_at": "2025-12-31T12:00:00"
|
||||
}
|
||||
```
|
||||
|
||||
## 🧪 Test de validation
|
||||
|
||||
### 1. Ouvrir la page de détail
|
||||
|
||||
```
|
||||
http://10.0.0.50:8087/peripheral-detail.html?id=3
|
||||
```
|
||||
|
||||
### 2. Cliquer sur "Modifier"
|
||||
|
||||
**Vérifications** :
|
||||
- ✅ La modale s'affiche
|
||||
- ✅ Tous les champs sont pré-remplis avec les données actuelles
|
||||
- ✅ Les étoiles correspondent à la note actuelle
|
||||
|
||||
### 3. Modifier des champs
|
||||
|
||||
**Test** :
|
||||
- Modifier le nom
|
||||
- Changer la note (clic sur étoiles)
|
||||
- Modifier le prix
|
||||
- Ajouter des notes
|
||||
|
||||
**Vérifications** :
|
||||
- ✅ Les étoiles s'activent au clic
|
||||
- ✅ Les champs acceptent la saisie
|
||||
- ✅ La validation fonctionne (champs requis)
|
||||
|
||||
### 4. Enregistrer
|
||||
|
||||
**Vérifications** :
|
||||
- ✅ Message "Périphérique mis à jour avec succès"
|
||||
- ✅ Modale se ferme
|
||||
- ✅ Données affichées sont mises à jour
|
||||
- ✅ Pas d'erreur console
|
||||
|
||||
### 5. Vérifier la persistance
|
||||
|
||||
**Test** :
|
||||
- Rafraîchir la page (F5)
|
||||
- Vérifier que les modifications sont conservées
|
||||
|
||||
**Vérifications** :
|
||||
- ✅ Les données modifiées sont affichées
|
||||
- ✅ La base de données a bien été mise à jour
|
||||
|
||||
## 🎨 Interface utilisateur
|
||||
|
||||
### Bouton "Modifier"
|
||||
|
||||
**Position** : En haut à droite de la carte "Informations générales"
|
||||
|
||||
```html
|
||||
<button class="btn btn-primary" onclick="toggleEditMode()" id="btn-edit">
|
||||
<i class="fas fa-edit"></i> Modifier
|
||||
</button>
|
||||
```
|
||||
|
||||
### Modale d'édition
|
||||
|
||||
**Largeur** : 95% (max 1400px) grâce à `.modal-large`
|
||||
|
||||
**Layout** : Grid responsive avec 3 colonnes sur desktop
|
||||
|
||||
**Sections** :
|
||||
1. Identification (colonne 1)
|
||||
2. Achat (colonne 2)
|
||||
3. État et localisation (colonne 3)
|
||||
4. Documentation technique (pleine largeur)
|
||||
|
||||
### Boutons d'action
|
||||
|
||||
```html
|
||||
<div class="form-actions">
|
||||
<button type="button" class="btn btn-secondary" onclick="closeEditModal()">
|
||||
Annuler
|
||||
</button>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="fas fa-save"></i> Enregistrer
|
||||
</button>
|
||||
</div>
|
||||
```
|
||||
|
||||
## 📝 Notes techniques
|
||||
|
||||
### Gestion des types de données
|
||||
|
||||
**String vers Number** :
|
||||
```javascript
|
||||
if (['prix', 'garantie_duree_mois', 'quantite_totale', 'quantite_disponible', 'rating'].includes(key)) {
|
||||
data[key] = value ? parseFloat(value) : null;
|
||||
}
|
||||
```
|
||||
|
||||
**Date** : Format `YYYY-MM-DD` (HTML5 input type="date")
|
||||
|
||||
**Null values** : Les champs vides sont envoyés comme `null` au lieu de chaînes vides
|
||||
|
||||
### Validation
|
||||
|
||||
**Côté client** :
|
||||
- Champs requis : `nom`, `type_principal`
|
||||
- Type number : min="0" pour prix et quantités
|
||||
- Type date : format ISO 8601
|
||||
|
||||
**Côté serveur** : Validation Pydantic dans le backend FastAPI
|
||||
|
||||
### Système d'étoiles
|
||||
|
||||
**État actif** : Classe CSS `.active` ajoutée aux étoiles
|
||||
|
||||
**HTML structure** :
|
||||
```html
|
||||
<div class="star-rating" id="edit-star-rating">
|
||||
<input type="hidden" id="edit-rating" name="rating" value="0">
|
||||
<i class="fas fa-star" data-rating="1"></i>
|
||||
<i class="fas fa-star" data-rating="2"></i>
|
||||
<!-- ... -->
|
||||
</div>
|
||||
```
|
||||
|
||||
**CSS** :
|
||||
```css
|
||||
.star-rating .fa-star.active {
|
||||
color: #f1c40f;
|
||||
text-shadow: 0 0 3px rgba(241, 196, 15, 0.5);
|
||||
}
|
||||
```
|
||||
|
||||
## 🔧 Améliorations futures possibles
|
||||
|
||||
- [ ] Validation en temps réel des champs
|
||||
- [ ] Preview Markdown pour les textareas
|
||||
- [ ] Auto-save (brouillon local)
|
||||
- [ ] Historique des modifications (qui/quand)
|
||||
- [ ] Undo/Redo
|
||||
- [ ] Dropdowns pour type_principal/sous_type (au lieu de input text)
|
||||
- [ ] Upload photo directement depuis la modale d'édition
|
||||
- [ ] Confirmation avant fermeture si modifications non sauvegardées
|
||||
|
||||
---
|
||||
|
||||
**Date** : 31 décembre 2025
|
||||
**Statut** : ✅ Implémenté et testé
|
||||
**Impact** : Édition complète des périphériques depuis la page de détail
|
||||
Reference in New Issue
Block a user