- Add usePermissions composable (isAdmin, canEdit, canView) - Password-protected profile login with modal on profiles page - Disable all form fields for ROLE_VIEWER across edit/create pages - Show navigation buttons (Modifier/Consulter) for all roles, hide delete for viewers - Add readonly prop to ModelTypeForm for category pages - Disable modal fields (sites, constructeurs) for viewers - Guard /admin routes in middleware Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
10 KiB
10 KiB
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-composantsStatut 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) etproduct-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 :
- Créer
app/components/common/CatalogPage.vueacceptant :columns: ColumnDef[](nom, clé, slot optionnel)fetchFn: (params) => Promise<PaginatedResult>deleteFn: (id) => Promise<Result>deleteGuard?: (item) => string | null(message bloquant ou null)entityLabel: string,createRoute: string- Slots nommés pour colonnes custom (
#col-supplier, etc.)
- Extraire
supplierDisplayUtils.ts(patternMAX_VISIBLE_SUPPLIERSdupliqué dans pieces-catalog et product-catalog). - Réduire chaque page catalogue à ~80 LOC (config + slots custom).
- Créer
- Gain estimé : ~850 LOC
- Statut :
[ ]
Phase 2 — Composables CRUD génériques (~1 170 LOC → ~400 LOC)
M2.1 · Factory useEntityCRUD<T>(config)
- Motif :
usePieces.ts(240),useProducts.ts(305),useComposants.ts(231),useSites.ts(124) suivent le même pattern CRUD : refsloading/loaded/error,loadItems()paginé,create/update/deleteavec mise à jour cache + toast. - Différences isolées : endpoint, normaliseur, enrichissement constructeurs, champs de tri.
- Plan :
- Créer
app/composables/useEntityCRUD.ts:interface EntityCRUDConfig { endpoint: string label: string normalizer?: (item: any) => any enricher?: (item: any) => Promise<any> defaultSort?: { field: string; dir: 'asc' | 'desc' } } export function useEntityCRUD(config: EntityCRUDConfig) - Extraire
extractTotal()dansapiHelpers.ts(dupliqué 3×, ~10 LOC chacun). - Extraire
buildPaginatedQuery(options)dansapiHelpers.ts(dupliqué 3×, ~15 LOC chacun). - Extraire pattern
withResolvedConstructeurs()dansuseEntityEnricher.ts(dupliqué 3× dans pieces/products/composants, ~50 LOC chacun). - Réduire chaque composable à un appel de factory + méthodes spécifiques.
- Garder
useMachines.tsséparé (méthodes spéciales :reconfigureSkeleton,createMachineFromType).
- Créer
- 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.tsexportant :async function withLoadingState<T>(loading: Ref<boolean>, fn: () => Promise<T>): Promise<T> - Gain estimé : ~100 LOC
- Statut :
[ ]
M2.3 · Fusion usePersistedValue + usePersistedSort
- Motif : même pattern
useCookie()+watch()+ JSON parse/stringify. - Plan : fusionner en
usePersistedState<T>(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.vueavec propsentries,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.vueavec propsdocuments,entityId,entityTypeet eventsupload,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 :
- Créer
app/composables/useEntityEditForm.tsgérant le cycle de vie commun (load, save, custom fields sync). - Chaque page edit ne garde que ses spécificités.
- Créer
- Gain estimé : ~500 LOC
- Statut :
[ ]
M3.4 · Réutilisation customFieldFormUtils.ts dans component/create.vue
- Motif :
component/create.vue(1 266 LOC) réimplémenteresolveFieldName,resolveFieldType,resolveDefaultValuedéjà danscustomFieldFormUtils.ts. Aussi 3 fonctionsresolveXxxLabelquasi-identiques (~18 LOC × 3). - Plan :
- Remplacer les fonctions locales par les imports de
customFieldFormUtils.ts. - Créer
resolveTypeLabel(entity, typeField, labelField, fallback)générique.
- Remplacer les fonctions locales par les imports de
- 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<T>(items: Ref<T[]>)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 transiterv-modelentre 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 +
onMountedidentiques 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é
- P2 (CRUD generics) — fondation pour P1 et P3
- P1 (catalogues) — dépend de P2 pour les fetch functions
- P3 (pages edit) — plus gros gain absolu, dépend partiellement de P2
- P5 (drag-drop) — indépendant, quick win
- P4 (machine detail) — complexe mais fort impact
- P6 (micro-dup) — petits gains, faible risque
- P7 (custom fields) — délicat, à faire en dernier
Vérification après chaque phase
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