# Parc Machines — Améliorations UX

**Date** : 2026-03-23
**Scope** : 3 changements sur le frontend + 1 extension backend

---

## 1. Filtre sites multi-sélection par checkboxes

### Contexte
Le filtre site actuel est un `<select>` mono-sélection dans `machines/index.vue`.
L'utilisateur veut pouvoir sélectionner plusieurs sites simultanément.

### Design
- Remplacer le `<select>` par une rangée de checkboxes DaisyUI directement visibles dans la barre de filtre.
- Chaque site = une checkbox avec le nom du site.
- Quand **aucune** checkbox n'est cochée → toutes les machines s'affichent (équivalent "Tous les sites").
- Quand **une ou plusieurs** sont cochées → filtre sur ces sites uniquement.

### Changements techniques
**Fichier** : `frontend/app/pages/machines/index.vue`

- **Réactivité** : utiliser `reactive(new Set())` (Vue 3.4+ supporte nativement les mutations `add`/`delete`/`has` sur un Set réactif). Pas de `.value` nécessaire.
- **Note** : le fichier utilise `<script setup>` sans `lang="ts"` — ne pas utiliser d'annotations TypeScript comme `Set<string>`.
- Template : remplacer le `<select>` par un `div` flex-wrap avec des checkboxes DaisyUI (`checkbox checkbox-sm`) + label pour chaque site.
- Computed `filteredMachines` : remplacer `machine.siteId === selectedSite` par `selectedSites.size === 0 || selectedSites.has(machine.siteId)`.

---

## 2. Tri alphabétique croissant

### Contexte
Les machines s'affichent dans l'ordre retourné par l'API, sans tri. L'utilisateur veut un tri alphabétique croissant par nom.

### Design
Ajouter un `.sort()` avec `localeCompare('fr')` à la fin du computed `filteredMachines`.

### Changements techniques
**Fichier** : `frontend/app/pages/machines/index.vue`

- Dans le computed `filteredMachines`, ajouter avant le `return` :
  ```js
  filtered = [...filtered].sort((a, b) =>
    (a.name || '').localeCompare(b.name || '', 'fr')
  )
  ```

---

## 3. Recherche par référence dans les catalogues (Pièces, Composants, Produits)

### Contexte
Les placeholders des champs de recherche promettent "Nom ou référence…" mais le frontend n'envoie que `?name=xxx` à l'API. Le backend (API Platform SearchFilter) supporte `name` et `reference` en `ipartial`, mais combiner `?name=xxx&reference=xxx` produit un AND (les deux doivent matcher), pas un OR.

### Design
Créer une **Extension Doctrine** (`SearchByNameOrReferenceExtension`) qui intercepte un paramètre `?q=xxx` et ajoute une clause `WHERE name ILIKE %xxx% OR reference ILIKE %xxx%` à la requête. Côté frontend, remplacer `params.set('name', search)` par `params.set('q', search)`.

### Changements techniques

**Backend — Nouveau fichier** : `src/Doctrine/SearchByNameOrReferenceExtension.php`
- Implémente `QueryCollectionExtensionInterface`
- S'applique aux entités `Piece`, `Composant`, `Product`
- Lit le paramètre `q` depuis la requête HTTP
- Ajoute `LOWER(o.name) LIKE :searchQ OR LOWER(o.reference) LIKE :searchQ` avec paramètre `%{strtolower(q)}%`
- **Échappement LIKE** : les caractères `%` et `_` dans l'input utilisateur sont échappés via `addcslashes($q, '%_')` pour éviter des matchs trop larges
- **`reference` nullable** : les lignes avec `reference = NULL` ne matcheront pas (comportement SQL standard : `NULL LIKE x` = NULL = false), ce qui est le comportement attendu
- **Pas de conflit** avec le `SearchFilter` existant : le paramètre `q` n'est pas enregistré comme propriété de `SearchFilter`, donc il sera ignoré par celui-ci. Les filtres `name` et `reference` restent disponibles pour d'autres usages.

**Frontend — 3 fichiers** (dans la fonction `loadXxx`, remplacer l'appel `params.set('name', search.trim())`) :
- `frontend/app/composables/usePieces.ts` → `params.set('q', search.trim())`
- `frontend/app/composables/useComposants.ts` → idem
- `frontend/app/composables/useProducts.ts` → idem

---

## Fichiers impactés (résumé)

| Fichier | Changement |
|---------|-----------|
| `frontend/app/pages/machines/index.vue` | Checkboxes sites + tri alphabétique |
| `src/Doctrine/SearchByNameOrReferenceExtension.php` | **Nouveau** — Extension Doctrine OR search |
| `frontend/app/composables/usePieces.ts` | `name` → `q` |
| `frontend/app/composables/useComposants.ts` | `name` → `q` |
| `frontend/app/composables/useProducts.ts` | `name` → `q` |

## Hors scope
- La page Parc Machines cherche **déjà** sur nom ET référence côté frontend (filtrage client-side). Pas de changement nécessaire.
- Aucun changement de placeholder — ils affichent déjà "Nom ou référence…".
