451 lines
11 KiB
Markdown
Executable File
451 lines
11 KiB
Markdown
Executable File
# 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
|