diff --git a/dup-report.md b/dup-report.md
deleted file mode 100644
index 4b7eae0..0000000
--- a/dup-report.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# Rapport de déduplication
-
-## DUP-001 · Score 92 · Formulaire de contact site
-- **Motif** : duplication à l’identique du bloc de champs de contact (nom, téléphone, adresse…) entre les modales de création et d’édition de site.
-- **Occurrences détectées** :
- - `app/components/sites/SiteCreateModal.vue` — lignes 1-52 (bloc de formulaire remplacé par ``).
- - `app/components/sites/SiteEditModal.vue` — lignes 1-155 (même bloc de formulaire remplacé par ``).
-- **Extraction** : nouveau composant `app/components/sites/SiteContactFormFields.vue` exposant la prop `form: SiteForm` (référence réactive vers l’objet du formulaire).
-- **Plan / Statut** : les deux modales importent désormais le composant partagé (``), supprimant l’ancienne duplication. Aucun changement d’API publique côté modale.
-
-## DUP-002 · Score 95 · Éditeur de contraintes (composants/pièces)
-- **Motif** : logique et template identiques pour la gestion des groupes requis dans `TypeEditComponentRequirementsSection` et `TypeEditPieceRequirementsSection` (ajout/suppression, formulaires, cases à cocher).
-- **Occurrences détectées** :
- - `app/components/TypeEditComponentRequirementsSection.vue` — lignes 1-94 (ancien template remplacé par ``).
- - `app/components/TypeEditPieceRequirementsSection.vue` — lignes 1-94 (même duplication remplacée).
-- **Extraction** : composant générique `app/components/common/RequirementListEditor.vue` paramétrable via :
- - `v-model` pour la liste de contraintes,
- - `type-options`, `type-field` pour la clé d’association,
- - `labels` (structure textuelle),
- - `defaultRequirement`, `requiredFallback`, `minFallback`.
-- **Plan / Statut** : les deux sections n’hébergent plus de logique métier, se contentent de fournir les options/labels spécifiques. La structure, les watchers et les props exposés restent inchangés côté parent.
-
-## DUP-003 · Score 88 · Formatage de dates UI
-- **Motif** : fonctions utilitaires de formatage (`toLocaleDateString`/`Intl.DateTimeFormat`) recopiées dans plusieurs pages (catalogues modèles et documents).
-- **Occurrences détectées** :
- - `app/pages/component-catalog.vue` — lignes 70-311 (affichage de la colonne « Modifié »).
- - `app/pages/pieces-catalog.vue` — lignes 70-310.
- - `app/pages/documents.vue` — lignes 90-188.
-- **Extraction** : utilitaire commun `app/utils/date.ts` exposant `formatFrenchDate(value: Date | string | number | null | undefined): string` avec gestion des valeurs nulles/invalides.
-- **Plan / Statut** : toutes les pages importent `formatFrenchDate` et l’utilisent directement en template. Plus de fonction locale dupliquée.
-
-## Couverture & suites
-- Les trois duplications les plus impactantes repérées ont été factorisées (>= 80 % du volume ciblé).
-- Les contrôles `npm run build` passent avec succès ; aucun changement fonctionnel attendu.
-- Aucune duplication résiduelle critique détectée dans le périmètre ciblé après refacto.
diff --git a/micro-dup-report.md b/micro-dup-report.md
deleted file mode 100644
index 3a596bd..0000000
--- a/micro-dup-report.md
+++ /dev/null
@@ -1,100 +0,0 @@
-# Micro duplication report
-
-## MDUP-001 · Score 92 · Type form-field
-- **Pattern**: Champ téléphone complet (label, input `tel`, placeholder "Ex: 06 00 00 00 00", règles de validation implicites).
-- **Occurrences**:
- - `app/components/ConstructeurSelect.vue` L70-L86 — modal de création de constructeur. 【F:app/components/ConstructeurSelect.vue†L66-L88】
- - `app/pages/constructeurs.vue` L82-L92 — formulaire de création/édition. 【F:app/pages/constructeurs.vue†L80-L92】
- - `app/components/sites/SiteContactFormFields.vue` L1-L57 — bloc de formulaire de contact. 【F:app/components/sites/SiteContactFormFields.vue†L1-L58】
- - `app/pages/index.vue` L200-L224 — création rapide d’un site. 【F:app/pages/index.vue†L200-L224】
-- **Extraction**: ✅ `app/components/form/FieldPhone.vue` (props : `modelValue`, `label`, `required`, `error`, `help`, `placeholder`, `disabled`, `normalizeOnBlur`, `validateOnBlur`). 【F:app/components/form/FieldPhone.vue†L1-L113】
-- **Plan de remplacement**: Remplacer chaque bloc par ``. Call-sites déjà migrés ci-dessus.
-
-## MDUP-002 · Score 90 · Type form-field
-- **Pattern**: Champ email (label « Email », input `type="email"`, placeholder d’exemple, aucune validation mutualisée).
-- **Occurrences**:
- - `app/components/ConstructeurSelect.vue` L66-L84. 【F:app/components/ConstructeurSelect.vue†L66-L86】
- - `app/pages/constructeurs.vue` L82-L90. 【F:app/pages/constructeurs.vue†L80-L92】
-- **Extraction**: ✅ `app/components/form/FieldEmail.vue` avec normalisation et validation partagée. 【F:app/components/form/FieldEmail.vue†L1-L112】
-- **Plan de remplacement**: Blocs remplacés par `` sur les deux formulaires.
-
-## MDUP-003 · Score 88 · Type form-field
-- **Pattern**: Groupe « informations de contact site » (Nom du contact, Téléphone, Adresse, Code postal, Ville) répliqué.
-- **Occurrences**:
- - `app/components/sites/SiteContactFormFields.vue` (composant existant). 【F:app/components/sites/SiteContactFormFields.vue†L1-L58】
- - `app/pages/index.vue` L200-L223 — doublait le bloc dans le modal rapide. 【F:app/pages/index.vue†L200-L223】
-- **Extraction**: ✅ Réutilisation directe du composant `SiteContactFormFields` sur la page index. Aucun changement d’API.
-- **Plan de remplacement**: Remplacer le bloc du modal par `` (effectué).
-
-## MDUP-004 · Score 86 · Type tiny-logic
-- **Pattern**: Liaisons `computed({ get, set })` pour faire transiter `v-model` entre props et emits.
-- **Occurrences**:
- - `app/components/TypeEditComponentRequirementsSection.vue` L45-L59. 【F:app/components/TypeEditComponentRequirementsSection.vue†L45-L59】
- - `app/components/TypeEditPieceRequirementsSection.vue` L45-L59. 【F:app/components/TypeEditPieceRequirementsSection.vue†L45-L59】
- - `app/components/common/RequirementListEditor.vue` L198-L203. 【F:app/components/common/RequirementListEditor.vue†L198-L204】
- - `app/components/TypeEditCustomFieldsSection.vue` L163-L168. 【F:app/components/TypeEditCustomFieldsSection.vue†L163-L168】
- - `app/components/TypeEditBaseInfoSection.vue` L82-L102. 【F:app/components/TypeEditBaseInfoSection.vue†L82-L102】
- - `app/components/sites/SiteEditModal.vue` L140-L154. 【F:app/components/sites/SiteEditModal.vue†L140-L154】
-- **Extraction proposée**: `app/composables/useControlledModel.ts` retournant `{ model }` via `useVModel` maison (prop name configurable, options pour defaultValue et transform).
-- **Plan**: 1) Introduire le composable, 2) remapper les computed existantes, 3) supprimer le code duplicatif.
-
-## MDUP-005 · Score 82 · Type tiny-logic
-- **Pattern**: Fonctions `createDefaultRequirement` quasi identiques (seuls champs `minCount`, `required` et `type*Id` changent).
-- **Occurrences**:
- - `app/components/TypeEditComponentRequirementsSection.vue` L61-L69. 【F:app/components/TypeEditComponentRequirementsSection.vue†L61-L69】
- - `app/components/TypeEditPieceRequirementsSection.vue` L61-L69. 【F:app/components/TypeEditPieceRequirementsSection.vue†L61-L69】
-- **Extraction proposée**: `app/shared/requirements/defaults.ts` exportant `createRequirementDefaults({ min, required, typeKey })`.
-- **Plan**: Mutualiser la fonction, la paramétrer par options, adapter les deux sections.
-
-## MDUP-006 · Score 80 · Type tiny-logic
-- **Pattern**: Effet `onMounted` identique qui teste la liste et déclenche `loadX` si vide.
-- **Occurrences**:
- - `app/components/TypeEditComponentRequirementsSection.vue` L89-L93. 【F:app/components/TypeEditComponentRequirementsSection.vue†L89-L93】
- - `app/components/TypeEditPieceRequirementsSection.vue` L89-L93. 【F:app/components/TypeEditPieceRequirementsSection.vue†L89-L93】
-- **Extraction proposée**: `useEnsureOptionsLoaded(optionsRef, loader)` dans `app/composables/` pour encapsuler le check + chargement (support async/await, options pour refetch forcé).
-- **Plan**: Appeler le composable dans les deux sections et supprimer le code inline.
-
-## MDUP-007 · Score 78 · Type ui-fragment
-- **Pattern**: Pieds de modale avec boutons « Annuler » + primaire + spinner optionnel.
-- **Occurrences**:
- - `app/components/ConstructeurSelect.vue` L80-L86. 【F:app/components/ConstructeurSelect.vue†L80-L86】
- - `app/pages/constructeurs.vue` L86-L91. 【F:app/pages/constructeurs.vue†L86-L91】
- - `app/components/sites/SiteCreateModal.vue` L21-L27. 【F:app/components/sites/SiteCreateModal.vue†L21-L27】
- - `app/components/sites/SiteEditModal.vue` L82-L89. 【F:app/components/sites/SiteEditModal.vue†L82-L89】
- - `app/pages/index.vue` L217-L223 & L306-L312. 【F:app/pages/index.vue†L215-L313】
-- **Extraction proposée**: `app/components/common/ModalActions.vue` avec props `primaryLabel`, `primaryLoading`, `onCancel`, slots secondaires.
-- **Plan**: Introduire le composant, refactorer chaque modal pour l’utiliser, garantir les mêmes classes Tailwind.
-
-## MDUP-008 · Score 76 · Type ui-fragment
-- **Pattern**: Gabarit de modale (div `.modal` + `.modal-box`, titre `
`, formulaire, actions).
-- **Occurrences**:
- - `app/components/ConstructeurSelect.vue` L58-L89. 【F:app/components/ConstructeurSelect.vue†L58-L89】
- - `app/components/sites/SiteCreateModal.vue` L1-L31. 【F:app/components/sites/SiteCreateModal.vue†L1-L31】
- - `app/components/sites/SiteEditModal.vue` L1-L94. 【F:app/components/sites/SiteEditModal.vue†L1-L94】
- - `app/pages/index.vue` L192-L315 (modales site/machine). 【F:app/pages/index.vue†L192-L315】
-- **Extraction proposée**: `app/components/common/ModalShell.vue` gérant l’ouverture, le titre, le footer via slots (`header`, `default`, `footer`).
-- **Plan**: Remplacer chaque squelette par le nouveau composant tout en conservant la structure DOM requise par DaisyUI.
-
-## MDUP-009 · Score 74 · Type form-field
-- **Pattern**: Champ texte simple (label, input type="text", `required`) pour les « Nom » & co.
-- **Occurrences**:
- - `app/components/ConstructeurSelect.vue` L62-L65. 【F:app/components/ConstructeurSelect.vue†L62-L66】
- - `app/pages/constructeurs.vue` L78-L81. 【F:app/pages/constructeurs.vue†L78-L81】
- - `app/components/sites/SiteCreateModal.vue` L6-L17. 【F:app/components/sites/SiteCreateModal.vue†L5-L17】
- - `app/components/sites/SiteEditModal.vue` L8-L20. 【F:app/components/sites/SiteEditModal.vue†L8-L20】
- - `app/components/TypeEditBaseInfoSection.vue` L8-L48. 【F:app/components/TypeEditBaseInfoSection.vue†L8-L48】
-- **Extraction proposée**: `app/components/form/FieldText.vue` avec props `type`, `label`, `required`, `maxlength`, `placeholder`, support `modelModifiers`.
-- **Plan**: Introduire le composant, migrer progressivement les champs texte, ajouter un paramètre pour afficher l’étoile obligatoire.
-
-## MDUP-010 · Score 72 · Type ui-fragment
-- **Pattern**: Bouton primaire avec indicateur de chargement inline (``).
-- **Occurrences**:
- - `app/components/ConstructeurSelect.vue` L82-L84. 【F:app/components/ConstructeurSelect.vue†L82-L84】
- - `app/pages/constructeurs.vue` L88-L89. 【F:app/pages/constructeurs.vue†L88-L90】
- - `app/components/sites/SiteEditModal.vue` L86-L88. 【F:app/components/sites/SiteEditModal.vue†L86-L88】
-- **Extraction proposée**: `app/components/common/LoadingButton.vue` gérant les variantes (`primary`, `outline`), le spinner et le label via slots.
-- **Plan**: Remplacer les boutons concernés par le composant, propager `loading` & `disabled` automatiquement.
-
-## Annexes
-- **Validations centralisées**: `app/shared/validation/phone.ts` & `app/shared/validation/email.ts` fournissent désormais des schémas communs. 【F:app/shared/validation/phone.ts†L1-L36】【F:app/shared/validation/email.ts†L1-L34】
-- **Formatters communs**: `app/utils/formatters/phone.ts` et `app/utils/formatters/email.ts` proposent les helpers associés. 【F:app/utils/formatters/phone.ts†L1-L67】【F:app/utils/formatters/email.ts†L1-L37】
diff --git a/migration.md b/migration.md
deleted file mode 100644
index 61b527c..0000000
--- a/migration.md
+++ /dev/null
@@ -1,229 +0,0 @@
-# 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
-```