Files
Inventory_frontend/migration.md
Matthieu cc70fe2b29 feat(permissions) : add role-based UI guards and readonly mode for viewers
- 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>
2026-02-26 13:36:42 +01:00

10 KiB
Raw Blame History

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<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.)
    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<T>(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 :
      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)
      
    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 :
    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.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<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 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

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