# Plan de migration — Réduction de code frontend > Objectif : réduire ~5 700 LOC sans modifier le fonctionnel. > Branche : à partir de `refacto/F1-decoupage-mega-composants` > Statut global : **EN ATTENTE** --- ## Phase 1 — Pages catalogue (3 pages, ~1 200 LOC → ~350 LOC) ### M1.1 · Composant générique `CatalogPage.vue` - **Motif** : `component-catalog.vue` (348 LOC), `pieces-catalog.vue` (463 LOC) et `product-catalog.vue` (408 LOC) partagent 95 % de structure (recherche, tri, pagination, tableau, suppression, états vides/loading). - **Différences isolées** : colonnes du tableau, garde de suppression, extraction fournisseur. - **Plan** : 1. Créer `app/components/common/CatalogPage.vue` acceptant : - `columns: ColumnDef[]` (nom, clé, slot optionnel) - `fetchFn: (params) => Promise` - `deleteFn: (id) => Promise` - `deleteGuard?: (item) => string | null` (message bloquant ou null) - `entityLabel: string`, `createRoute: string` - Slots nommés pour colonnes custom (`#col-supplier`, etc.) 2. Extraire `supplierDisplayUtils.ts` (pattern `MAX_VISIBLE_SUPPLIERS` dupliqué dans pieces-catalog et product-catalog). 3. Réduire chaque page catalogue à ~80 LOC (config + slots custom). - **Gain estimé** : ~850 LOC - **Statut** : `[ ]` --- ## Phase 2 — Composables CRUD génériques (~1 170 LOC → ~400 LOC) ### M2.1 · Factory `useEntityCRUD(config)` - **Motif** : `usePieces.ts` (240), `useProducts.ts` (305), `useComposants.ts` (231), `useSites.ts` (124) suivent le même pattern CRUD : refs `loading/loaded/error`, `loadItems()` paginé, `create/update/delete` avec mise à jour cache + toast. - **Différences isolées** : endpoint, normaliseur, enrichissement constructeurs, champs de tri. - **Plan** : 1. Créer `app/composables/useEntityCRUD.ts` : ```ts interface EntityCRUDConfig { endpoint: string label: string normalizer?: (item: any) => any enricher?: (item: any) => Promise defaultSort?: { field: string; dir: 'asc' | 'desc' } } export function useEntityCRUD(config: EntityCRUDConfig) ``` 2. Extraire `extractTotal()` dans `apiHelpers.ts` (dupliqué 3×, ~10 LOC chacun). 3. Extraire `buildPaginatedQuery(options)` dans `apiHelpers.ts` (dupliqué 3×, ~15 LOC chacun). 4. Extraire pattern `withResolvedConstructeurs()` dans `useEntityEnricher.ts` (dupliqué 3× dans pieces/products/composants, ~50 LOC chacun). 5. Réduire chaque composable à un appel de factory + méthodes spécifiques. 6. Garder `useMachines.ts` séparé (méthodes spéciales : `reconfigureSkeleton`, `createMachineFromType`). - **Gain estimé** : ~770 LOC - **Statut** : `[ ]` ### M2.2 · Helper `withLoadingState()` - **Motif** : pattern `loading.value = true; try { ... } finally { loading.value = false }` répété 10+ fois dans les composables CRUD. - **Plan** : créer `app/composables/useLoadingHelper.ts` exportant : ```ts async function withLoadingState(loading: Ref, fn: () => Promise): Promise ``` - **Gain estimé** : ~100 LOC - **Statut** : `[ ]` ### M2.3 · Fusion `usePersistedValue` + `usePersistedSort` - **Motif** : même pattern `useCookie()` + `watch()` + JSON parse/stringify. - **Plan** : fusionner en `usePersistedState(key, fallback, prefix?)`. - **Gain estimé** : ~30 LOC - **Statut** : `[ ]` --- ## Phase 3 — Pages edit entités (~2 750 LOC → ~1 200 LOC) ### M3.1 · Composant `HistorySection.vue` - **Motif** : bloc historique identique (loading/error/empty + itération entries) dans `component/[id]/edit.vue` (L437-503), `pieces/[id]/edit.vue` (L384-450), `product/[id]/edit.vue` (L304-370) — ~67 LOC × 3. - **Plan** : créer `app/components/common/HistorySection.vue` avec props `entries`, `loading`, `error`. - **Gain estimé** : ~130 LOC - **Statut** : `[ ]` ### M3.2 · Composant `DocumentsSection.vue` - **Motif** : bloc document (upload, liste, preview, download, delete) dupliqué dans les 3 pages edit + `MachineDocumentsCard.vue` + `SiteEditModal.vue` — ~70-180 LOC × 5. - **Plan** : créer `app/components/common/DocumentsSection.vue` avec props `documents`, `entityId`, `entityType` et events `upload`, `delete`, `preview`. - **Gain estimé** : ~400 LOC - **Statut** : `[ ]` ### M3.3 · Composable `useEntityEditForm(config)` - **Motif** : les 3 pages edit partagent : chargement entité + types + constructeurs, gestion champs custom, normalisation payload, sauvegarde, gestion erreur. - **Différences** : component a structure display, piece a product selection, product est plus simple. - **Plan** : 1. Créer `app/composables/useEntityEditForm.ts` gérant le cycle de vie commun (load, save, custom fields sync). 2. Chaque page edit ne garde que ses spécificités. - **Gain estimé** : ~500 LOC - **Statut** : `[ ]` ### M3.4 · Réutilisation `customFieldFormUtils.ts` dans `component/create.vue` - **Motif** : `component/create.vue` (1 266 LOC) réimplémente `resolveFieldName`, `resolveFieldType`, `resolveDefaultValue` déjà dans `customFieldFormUtils.ts`. Aussi 3 fonctions `resolveXxxLabel` quasi-identiques (~18 LOC × 3). - **Plan** : 1. Remplacer les fonctions locales par les imports de `customFieldFormUtils.ts`. 2. Créer `resolveTypeLabel(entity, typeField, labelField, fallback)` générique. - **Gain estimé** : ~120 LOC - **Statut** : `[ ]` --- ## Phase 4 — Décomposition `useMachineDetailData.ts` (1 410 LOC → ~500 LOC) ### M4.1 · Extraire `useMachineDocuments.ts` - **Motif** : gestion documents (upload, delete, preview, refresh) = ~200 LOC dans le composable monolithique. - **Gain estimé** : ~150 LOC (après factorisation avec DocumentsSection) - **Statut** : `[ ]` ### M4.2 · Extraire `useMachineConstructeurs.ts` - **Motif** : résolution constructeurs avec chaînes de fallback 4 niveaux, `uniqueConstructeurIds`, `resolveConstructeurs` = ~80 LOC. - **Gain estimé** : ~60 LOC - **Statut** : `[ ]` ### M4.3 · Fusionner `transformCustomFields` et `transformComponentCustomFields` - **Motif** : L303-405 et L407-514 — logique quasi-identique de transformation des champs custom, seule la source (machine vs composant) diffère. - **Plan** : créer `transformEntityCustomFields(entity, fieldSource, config)` paramétrable. - **Gain estimé** : ~100 LOC - **Statut** : `[ ]` ### M4.4 · Extraire groupement de requirements - **Motif** : `componentRequirementGroups`, `pieceRequirementGroups` = computed complexes avec construction de maps et filtres répétitifs. - **Gain estimé** : ~80 LOC - **Statut** : `[ ]` --- ## Phase 5 — `StructureNodeEditor.vue` (1 167 LOC → ~600 LOC) ### M5.1 · Composable `useDragDrop.ts` - **Motif** : 4 handlers drag-drop quasi-identiques (custom fields, pièces, produits, sous-composants) avec chacun `draggingIndex`, `dropTargetIndex`, `reorderClass()`, `handleDragStart/Over/End`. - **Plan** : créer `useDragDrop(items: Ref)` retournant `{ dragging, target, reorderClass, onDragStart, onDragOver, onDragEnd, onDrop }`. - **Gain estimé** : ~350 LOC - **Statut** : `[ ]` ### M5.2 · Extraire validation noeud - **Motif** : `isAssignmentNodeComplete` + logique de validation dispersée. - **Plan** : déplacer vers `app/shared/utils/structureValidation.ts`. - **Gain estimé** : ~40 LOC - **Statut** : `[ ]` --- ## Phase 6 — Micro-duplications restantes (du `micro-dup-report.md`) ### M6.1 · `useControlledModel.ts` (MDUP-004) - **Motif** : `computed({ get, set })` pour transiter `v-model` entre props et emits — dupliqué dans 6 composants. - **Gain estimé** : ~60 LOC - **Statut** : `[ ]` ### M6.2 · `ModalShell.vue` (MDUP-008) + `ModalActions.vue` (MDUP-007) - **Motif** : squelette de modale DaisyUI (`.modal` + `.modal-box` + titre + footer) dupliqué dans 4+ composants. Pieds de modale « Annuler + Primaire + spinner » dupliqués 5×. - **Gain estimé** : ~120 LOC - **Statut** : `[ ]` ### M6.3 · `LoadingButton.vue` (MDUP-010) + `FieldText.vue` (MDUP-009) - **Motif** : bouton primaire avec spinner (3 occurrences), champ texte simple label+input (5 occurrences). - **Gain estimé** : ~80 LOC - **Statut** : `[ ]` ### M6.4 · `createRequirementDefaults` + `useEnsureOptionsLoaded` (MDUP-005, MDUP-006) - **Motif** : factory de requirement par défaut + `onMounted` identiques dans les sections composant/pièce. - **Gain estimé** : ~30 LOC - **Statut** : `[ ]` --- ## Phase 7 — Consolidation custom fields (~1 150 LOC → ~800 LOC) ### M7.1 · Fusionner logique de résolution dans `customFieldUtils.ts` - **Motif** : `customFieldUtils.ts` (440), `entityCustomFieldLogic.ts` (349), `customFieldFormUtils.ts` (367) contiennent des fonctions de résolution de champs qui se chevauchent (`resolveFieldId`, `resolveFieldName`, génération de clé, déduplication). - **Plan** : consolider les fonctions dupliquées en gardant la séparation thématique (utils / form / entity) mais en partageant les primitives. - **Gain estimé** : ~150 LOC - **Statut** : `[ ]` --- ## Récapitulatif | Phase | Cible | LOC avant | Gain estimé | Priorité | |-------|-------|-----------|-------------|----------| | **P1** | Pages catalogue | ~1 220 | ~850 | Haute | | **P2** | Composables CRUD | ~1 170 | ~900 | Haute | | **P3** | Pages edit entités | ~2 750 | ~1 150 | Haute | | **P4** | useMachineDetailData | ~1 410 | ~390 | Moyenne | | **P5** | StructureNodeEditor | ~1 167 | ~390 | Moyenne | | **P6** | Micro-duplications | ~400 | ~290 | Basse | | **P7** | Custom fields utils | ~1 150 | ~150 | Basse | | | **Total** | | **~4 120 LOC** | | ### Ordre recommandé 1. **P2** (CRUD generics) — fondation pour P1 et P3 2. **P1** (catalogues) — dépend de P2 pour les fetch functions 3. **P3** (pages edit) — plus gros gain absolu, dépend partiellement de P2 4. **P5** (drag-drop) — indépendant, quick win 5. **P4** (machine detail) — complexe mais fort impact 6. **P6** (micro-dup) — petits gains, faible risque 7. **P7** (custom fields) — délicat, à faire en dernier ### Vérification après chaque phase ```bash cd Inventory_frontend npx nuxi typecheck # 0 erreurs npm run lint:fix # 0 erreurs npm run build # succès npx vitest run # 54+ tests pass ```