addon
This commit is contained in:
310
docs/FIXES_UI_IMPROVEMENTS.md
Executable file
310
docs/FIXES_UI_IMPROVEMENTS.md
Executable file
@@ -0,0 +1,310 @@
|
||||
# Corrections UI - Icônes par type, Localisation, Boutons
|
||||
|
||||
## 🎯 Problèmes corrigés
|
||||
|
||||
1. **Icônes génériques** → Icônes spécifiques selon le type de périphérique
|
||||
2. **Localisation manquante** dans la modale d'édition
|
||||
3. **Boutons Annuler/Enregistrer** s'affichaient en haut au lieu d'en bas
|
||||
|
||||
---
|
||||
|
||||
## ✅ 1. Icônes spécifiques par type
|
||||
|
||||
### Problème
|
||||
|
||||
Tous les périphériques sans photo affichaient l'icône générique `fa-microchip`.
|
||||
|
||||
### Solution
|
||||
|
||||
**Fichier** : `frontend/js/peripherals.js` (lignes 973-1011)
|
||||
|
||||
**Nouvelle fonction `getTypeIcon(type)`** :
|
||||
|
||||
```javascript
|
||||
// Get icon based on peripheral type
|
||||
function getTypeIcon(type) {
|
||||
if (!type) return 'fa-microchip';
|
||||
|
||||
const typeUpper = type.toUpperCase();
|
||||
|
||||
// USB devices
|
||||
if (typeUpper.includes('USB')) return 'fa-usb';
|
||||
|
||||
// Storage devices
|
||||
if (typeUpper.includes('STOCKAGE') || typeUpper.includes('DISK') ||
|
||||
typeUpper.includes('SSD') || typeUpper.includes('HDD') ||
|
||||
typeUpper.includes('FLASH')) return 'fa-hdd';
|
||||
|
||||
// Network devices
|
||||
if (typeUpper.includes('RÉSEAU') || typeUpper.includes('RESEAU') ||
|
||||
typeUpper.includes('NETWORK') || typeUpper.includes('WIFI') ||
|
||||
typeUpper.includes('ETHERNET')) return 'fa-network-wired';
|
||||
|
||||
// Audio devices
|
||||
if (typeUpper.includes('AUDIO') || typeUpper.includes('SOUND') ||
|
||||
typeUpper.includes('SPEAKER') || typeUpper.includes('HEADPHONE')) return 'fa-volume-up';
|
||||
|
||||
// Video devices
|
||||
if (typeUpper.includes('VIDEO') || typeUpper.includes('VIDÉO') ||
|
||||
typeUpper.includes('WEBCAM') || typeUpper.includes('CAMERA')) return 'fa-video';
|
||||
|
||||
// Input devices
|
||||
if (typeUpper.includes('CLAVIER') || typeUpper.includes('KEYBOARD')) return 'fa-keyboard';
|
||||
if (typeUpper.includes('SOURIS') || typeUpper.includes('MOUSE')) return 'fa-mouse';
|
||||
|
||||
// Other devices
|
||||
if (typeUpper.includes('BLUETOOTH')) return 'fa-bluetooth';
|
||||
if (typeUpper.includes('HUB')) return 'fa-project-diagram';
|
||||
if (typeUpper.includes('ADAPTATEUR') || typeUpper.includes('ADAPTER')) return 'fa-plug';
|
||||
|
||||
// Default
|
||||
return 'fa-microchip';
|
||||
}
|
||||
```
|
||||
|
||||
**Logique** :
|
||||
- Détection par mots-clés dans le type (insensible à la casse)
|
||||
- Support français et anglais
|
||||
- Fallback vers `fa-microchip` si aucun match
|
||||
|
||||
**Utilisation dans le tableau** (lignes 327-328) :
|
||||
|
||||
```javascript
|
||||
${p.thumbnail_url
|
||||
? `<img src="${escapeHtml(p.thumbnail_url)}" alt="${escapeHtml(p.nom)}" onerror="this.style.display='none'; this.nextElementSibling.style.display='flex';">
|
||||
<i class="fas ${getTypeIcon(p.type_principal)}" style="display:none;"></i>`
|
||||
: `<i class="fas ${getTypeIcon(p.type_principal)}"></i>`
|
||||
}
|
||||
```
|
||||
|
||||
### Mapping des icônes
|
||||
|
||||
| Type | Mots-clés | Icône Font Awesome | Rendu |
|
||||
|------|-----------|-------------------|-------|
|
||||
| **USB** | USB | `fa-usb` | 🔌 |
|
||||
| **Stockage** | STOCKAGE, DISK, SSD, HDD, FLASH | `fa-hdd` | 💾 |
|
||||
| **Réseau** | RÉSEAU, NETWORK, WIFI, ETHERNET | `fa-network-wired` | 🌐 |
|
||||
| **Audio** | AUDIO, SOUND, SPEAKER, HEADPHONE | `fa-volume-up` | 🔊 |
|
||||
| **Vidéo** | VIDEO, VIDÉO, WEBCAM, CAMERA | `fa-video` | 📹 |
|
||||
| **Clavier** | CLAVIER, KEYBOARD | `fa-keyboard` | ⌨️ |
|
||||
| **Souris** | SOURIS, MOUSE | `fa-mouse` | 🖱️ |
|
||||
| **Bluetooth** | BLUETOOTH | `fa-bluetooth` | 🔵 |
|
||||
| **Hub** | HUB | `fa-project-diagram` | 🔀 |
|
||||
| **Adaptateur** | ADAPTATEUR, ADAPTER | `fa-plug` | 🔌 |
|
||||
| **Défaut** | Autre | `fa-microchip` | 💻 |
|
||||
|
||||
### Exemples
|
||||
|
||||
```javascript
|
||||
getTypeIcon('USB') // → 'fa-usb'
|
||||
getTypeIcon('Stockage SSD') // → 'fa-hdd'
|
||||
getTypeIcon('Réseau WiFi') // → 'fa-network-wired'
|
||||
getTypeIcon('Webcam') // → 'fa-video'
|
||||
getTypeIcon('Clavier') // → 'fa-keyboard'
|
||||
getTypeIcon('Bluetooth') // → 'fa-bluetooth'
|
||||
getTypeIcon('Hub USB') // → 'fa-usb' (USB détecté en premier)
|
||||
getTypeIcon('Unknown Type') // → 'fa-microchip' (fallback)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ 2. Champ Localisation ajouté
|
||||
|
||||
### Problème
|
||||
|
||||
Le champ "Localisation" était absent de la section "État et localisation" dans la modale d'édition.
|
||||
|
||||
### Solution HTML
|
||||
|
||||
**Fichier** : `frontend/peripheral-detail.html` (lignes 393-398)
|
||||
|
||||
**Ajout du champ** :
|
||||
|
||||
```html
|
||||
<div class="form-group">
|
||||
<label for="edit-location_id">Localisation</label>
|
||||
<select id="edit-location_id" name="location_id">
|
||||
<option value="">Non définie</option>
|
||||
</select>
|
||||
</div>
|
||||
```
|
||||
|
||||
**Position** : Entre "Note" et "Quantité totale"
|
||||
|
||||
### Solution JavaScript
|
||||
|
||||
**Fichier** : `frontend/js/peripheral-detail.js` (lignes 512-539)
|
||||
|
||||
**1. Appel dans `toggleEditMode()` (ligne 513)** :
|
||||
|
||||
```javascript
|
||||
// Load and set location
|
||||
loadEditLocations(peripheral.location_id);
|
||||
```
|
||||
|
||||
**2. Nouvelle fonction `loadEditLocations()`** :
|
||||
|
||||
```javascript
|
||||
// Load locations for edit modal
|
||||
async function loadEditLocations(selectedLocationId) {
|
||||
try {
|
||||
const locations = await apiRequest('/locations/');
|
||||
const select = document.getElementById('edit-location_id');
|
||||
|
||||
select.innerHTML = '<option value="">Non définie</option>';
|
||||
|
||||
locations.forEach(location => {
|
||||
const option = document.createElement('option');
|
||||
option.value = location.id;
|
||||
option.textContent = location.nom;
|
||||
if (location.id === selectedLocationId) {
|
||||
option.selected = true;
|
||||
}
|
||||
select.appendChild(option);
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error loading locations:', error);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Fonctionnement** :
|
||||
1. Appel API `GET /locations/` pour récupérer toutes les localisations
|
||||
2. Peuplement du `<select>` avec les options
|
||||
3. Pré-sélection de la localisation actuelle du périphérique (si définie)
|
||||
4. Gestion des erreurs avec console.error
|
||||
|
||||
**Résultat** :
|
||||
- L'utilisateur peut maintenant modifier la localisation lors de l'édition
|
||||
- La localisation actuelle est pré-sélectionnée
|
||||
- Option "Non définie" disponible pour retirer la localisation
|
||||
|
||||
---
|
||||
|
||||
## ✅ 3. Positionnement des boutons en bas
|
||||
|
||||
### Problème
|
||||
|
||||
Les boutons "Annuler" et "Enregistrer" s'affichaient en haut de la modale au lieu d'en bas.
|
||||
|
||||
### Cause
|
||||
|
||||
La div `.form-actions` était dans un conteneur `.form-grid` avec `display: grid`. Sans instruction spécifique, elle ne prenait pas toute la largeur et pouvait s'afficher dans une colonne du grid.
|
||||
|
||||
### Solution CSS
|
||||
|
||||
**Fichier** : `frontend/css/peripherals.css` (ligne 446)
|
||||
|
||||
**Modification** :
|
||||
|
||||
```css
|
||||
.form-actions {
|
||||
grid-column: 1 / -1; /* Take full width of grid */
|
||||
margin-top: 2rem;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 1rem;
|
||||
padding-top: 1.5rem;
|
||||
border-top: 1px solid #3e3d32;
|
||||
}
|
||||
```
|
||||
|
||||
**Ajout de `grid-column: 1 / -1`** :
|
||||
- Force `.form-actions` à occuper toutes les colonnes du grid
|
||||
- `-1` = dernière colonne (quelle que soit la taille du grid)
|
||||
- Assure que les boutons soient toujours en bas, sur toute la largeur
|
||||
|
||||
**Résultat** :
|
||||
```
|
||||
┌─────────────────────────────────────────────────────┐
|
||||
│ [Identification] [Achat] [État et localisation] │
|
||||
│ │
|
||||
│ [Documentation technique - pleine largeur] │
|
||||
│ │
|
||||
├─────────────────────────────────────────────────────┤
|
||||
│ [Annuler] [Enregistrer] │
|
||||
└─────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 Résumé des modifications
|
||||
|
||||
### Fichiers modifiés
|
||||
|
||||
| Fichier | Lignes | Modification |
|
||||
|---------|--------|--------------|
|
||||
| `frontend/js/peripherals.js` | 973-1011 | Fonction `getTypeIcon()` |
|
||||
| `frontend/js/peripherals.js` | 327-328 | Utilisation de `getTypeIcon()` |
|
||||
| `frontend/peripheral-detail.html` | 393-398 | Champ localisation ajouté |
|
||||
| `frontend/js/peripheral-detail.js` | 513 | Appel `loadEditLocations()` |
|
||||
| `frontend/js/peripheral-detail.js` | 519-539 | Fonction `loadEditLocations()` |
|
||||
| `frontend/css/peripherals.css` | 446 | `grid-column: 1 / -1` |
|
||||
|
||||
### Nouveaux éléments
|
||||
|
||||
**Fonctions JavaScript** :
|
||||
- ✅ `getTypeIcon(type)` - Retourne l'icône selon le type
|
||||
- ✅ `loadEditLocations(selectedLocationId)` - Charge les localisations
|
||||
|
||||
**Champs HTML** :
|
||||
- ✅ `<select id="edit-location_id">` - Sélecteur de localisation
|
||||
|
||||
---
|
||||
|
||||
## 🧪 Tests
|
||||
|
||||
### Test 1 : Icônes spécifiques
|
||||
|
||||
1. Ouvrir `http://10.0.0.50:8087/peripherals.html`
|
||||
2. Observer la colonne "Photo"
|
||||
3. Vérifier que les périphériques sans photo affichent des icônes différentes :
|
||||
- ✅ USB → Icône USB
|
||||
- ✅ Stockage → Icône disque dur
|
||||
- ✅ Réseau → Icône réseau
|
||||
- ✅ Clavier → Icône clavier
|
||||
- ✅ Souris → Icône souris
|
||||
|
||||
### Test 2 : Localisation dans édition
|
||||
|
||||
1. Ouvrir un périphérique : `http://10.0.0.50:8087/peripheral-detail.html?id=3`
|
||||
2. Cliquer sur "Modifier"
|
||||
3. Vérifier la section "État et localisation"
|
||||
4. ✅ Le champ "Localisation" est présent
|
||||
5. ✅ Le select est pré-rempli avec les localisations disponibles
|
||||
6. ✅ La localisation actuelle est sélectionnée
|
||||
7. Modifier la localisation et enregistrer
|
||||
8. ✅ La modification est sauvegardée
|
||||
|
||||
### Test 3 : Boutons en bas
|
||||
|
||||
1. Ouvrir un périphérique
|
||||
2. Cliquer sur "Modifier"
|
||||
3. ✅ Les boutons "Annuler" et "Enregistrer" sont en bas de la modale
|
||||
4. ✅ Ils occupent toute la largeur (ligne séparatrice visible)
|
||||
5. ✅ Boutons alignés à droite
|
||||
|
||||
---
|
||||
|
||||
## 💡 Améliorations futures possibles
|
||||
|
||||
### Icônes
|
||||
- [ ] Icônes personnalisées pour plus de types (Scanner, Imprimante, etc.)
|
||||
- [ ] Couleurs différentes selon l'état (Neuf = vert, Défectueux = rouge)
|
||||
- [ ] Possibilité de définir une icône personnalisée par périphérique
|
||||
|
||||
### Localisation
|
||||
- [ ] Création rapide de localisation depuis la modale
|
||||
- [ ] Affichage du chemin complet (location parent → enfant)
|
||||
- [ ] Icône de localisation à côté du nom
|
||||
|
||||
### Boutons
|
||||
- [ ] Raccourci clavier (Ctrl+S pour sauvegarder)
|
||||
- [ ] Confirmation avant fermeture si modifications non sauvegardées
|
||||
- [ ] Bouton "Appliquer" qui sauvegarde sans fermer la modale
|
||||
|
||||
---
|
||||
|
||||
**Date** : 31 décembre 2025
|
||||
**Statut** : ✅ Toutes les corrections appliquées et testées
|
||||
**Impact** : Interface plus intuitive avec icônes contextuelles, localisation éditable, et boutons correctement positionnés
|
||||
Reference in New Issue
Block a user