/** * Machine creation page – orchestration composable. * * Consolidates entity lookup maps, option filters, label helpers, * template wrappers, and the finalization logic that were previously * inlined in pages/machines/new.vue. */ import { ref, reactive, computed, watch, onMounted } from 'vue' import { useMachines } from '~/composables/useMachines' import { useSites } from '~/composables/useSites' import { useMachineTypesApi } from '~/composables/useMachineTypesApi' import { useComposants } from '~/composables/useComposants' import { usePieces } from '~/composables/usePieces' import { useProducts } from '~/composables/useProducts' import { useApi } from '~/composables/useApi' import { useToast } from '~/composables/useToast' import { useMachineCreateSelections } from '~/composables/useMachineCreateSelections' import { useMachineCreatePreview, validateRequirementSelections as _validateRequirementSelections, resolveComponentRequirementTypeLabel as _resolveComponentRequirementTypeLabel, resolvePieceRequirementTypeLabel as _resolvePieceRequirementTypeLabel, } from '~/composables/useMachineCreatePreview' import { getComponentMachineAssignments, getPieceMachineAssignments, getPieceComponentAssignments, formatAssignmentList, } from '~/shared/utils/assignmentUtils' export function useMachineCreatePage() { // --------------------------------------------------------------------------- // Composable calls // --------------------------------------------------------------------------- const { createMachine, createMachineFromType, reconfigureSkeleton } = useMachines() const { sites, loadSites } = useSites() const { machineTypes, loadMachineTypes, loading: machineTypesLoading } = useMachineTypesApi() const { composants, loadComposants, loading: composantsLoading } = useComposants() const { pieces, loadPieces, loading: piecesLoading } = usePieces() const { products, loadProducts, loading: productsLoading } = useProducts() const { get } = useApi() const toast = useToast() // --------------------------------------------------------------------------- // Local state // --------------------------------------------------------------------------- const submitting = ref(false) const newMachine = reactive({ name: '', siteId: '', typeMachineId: '', reference: '', }) const selectedMachineType = computed(() => { if (!newMachine.typeMachineId) return null return (machineTypes as any).value.find((type: any) => type.id === newMachine.typeMachineId) || null }) // --------------------------------------------------------------------------- // Entity lookup maps // --------------------------------------------------------------------------- const componentById = computed(() => { const map = new Map() ;((composants as any).value || []).forEach((component: any) => { if (component?.id) map.set(component.id, component) }) return map }) const pieceById = computed(() => { const map = new Map() ;((pieces as any).value || []).forEach((piece: any) => { if (piece?.id) map.set(piece.id, piece) }) return map }) const componentInventory = computed(() => (composants as any).value || []) const pieceInventory = computed(() => (pieces as any).value || []) const productInventory = computed(() => (products as any).value || []) const productById = computed(() => { const map = new Map() ;(productInventory.value || []).forEach((product: any) => { if (product?.id) map.set(product.id, product) }) return map }) // --------------------------------------------------------------------------- // Entity finders // --------------------------------------------------------------------------- const findComponentById = (id: string) => { if (!id) return null return componentById.value.get(id) || null } const findPieceById = (id: string): any => { if (!id) return null return pieceById.value.get(id) || findPieceInCachedOptions(id) || null } const findProductById = (id: string) => { if (!id) return null return productById.value.get(id) || null } // --------------------------------------------------------------------------- // Selection state (from composable) // --------------------------------------------------------------------------- const { pieceOptionsByKey, pieceLoadingByKey, selectedPieceIds, getPieceKey, findPieceInCachedOptions, fetchPieceOptions, getComponentRequirementEntries, getPieceRequirementEntries, getProductRequirementEntries, addComponentSelectionEntry, removeComponentSelectionEntry, addPieceSelectionEntry, removePieceSelectionEntry, addProductSelectionEntry, removeProductSelectionEntry, setComponentRequirementComponent, setPieceRequirementPiece, setProductRequirementProduct: _setProductRequirementProduct, clearRequirementSelections, initializeRequirementSelections, } = useMachineCreateSelections({ findComponentById, findPieceById, pieces: pieces as any, get: get as any, toast, }) // --------------------------------------------------------------------------- // Preview / validation (from composable) // --------------------------------------------------------------------------- const { machinePreview, blockingPreviewIssues, canCreateMachine } = useMachineCreatePreview({ newMachine, sites: sites as any, selectedMachineType, findComponentById, findPieceById, findProductById, getComponentRequirementEntries, getPieceRequirementEntries, getProductRequirementEntries, }) // --------------------------------------------------------------------------- // Template wrappers // --------------------------------------------------------------------------- const resolveComponentRequirementTypeLabel = (requirement: any, entry: any) => _resolveComponentRequirementTypeLabel(requirement, entry, findComponentById) const resolvePieceRequirementTypeLabel = (requirement: any, entry: any) => _resolvePieceRequirementTypeLabel(requirement, entry, findPieceById) const setProductRequirementProduct = (requirement: any, index: number, productId: string) => _setProductRequirementProduct(requirement, index, productId, findProductById) const validateRequirementSelections = (type: any) => _validateRequirementSelections(type, { newMachine, sites: sites as any, selectedMachineType, findComponentById, findPieceById, findProductById, getComponentRequirementEntries, getPieceRequirementEntries, getProductRequirementEntries, }) // --------------------------------------------------------------------------- // Machine type helpers // --------------------------------------------------------------------------- const machineTypeLabel = (type: any) => { if (!type) return '' return type.name || 'Type de machine' } const machineTypeDescription = (type: any) => { if (!type) return '' const parts: string[] = [] if (type.category) parts.push(`Catégorie : ${type.category}`) const componentCount = type.componentRequirements?.length ?? 0 const pieceCount = type.pieceRequirements?.length ?? 0 const productCount = type.productRequirements?.length ?? 0 parts.push( `${componentCount} composant(s)`, `${pieceCount} pièce(s)`, `${productCount} produit(s)`, ) return parts.join(' • ') } // --------------------------------------------------------------------------- // Option filters // --------------------------------------------------------------------------- const getComponentOptions = (requirement: any, currentEntry: any) => { const requirementTypeId = requirement?.typeComposantId || requirement?.typeComposant?.id || null return componentInventory.value.filter((component: any) => { if (!component?.id) return false if (requirementTypeId && component.typeComposantId !== requirementTypeId) { return currentEntry?.composantId === component.id } return true }) } const getPieceOptions = (requirement: any, currentEntry: any, entryIndex: number) => { const key = getPieceKey(requirement, entryIndex) const cached = pieceOptionsByKey.value[key] if (cached) return cached const requirementTypeId = requirement?.typePieceId || requirement?.typePiece?.id || null const usedIds = new Set( selectedPieceIds.value.filter((id: any) => id && (!currentEntry || id !== currentEntry.pieceId)), ) return pieceInventory.value.filter((piece: any) => { if (requirementTypeId && piece.typePieceId !== requirementTypeId) return false if (!piece.id) return false if (currentEntry?.pieceId === piece.id) return true return !usedIds.has(piece.id) }) } const getProductOptions = (requirement: any) => { const requirementTypeId = requirement?.typeProductId || requirement?.typeProduct?.id || null return productInventory.value.filter((product: any) => { if (!product?.id) return false if (!requirementTypeId) return true const productTypeId = product.typeProductId || product.typeProduct?.id || null return productTypeId === requirementTypeId }) } // --------------------------------------------------------------------------- // Option label / description helpers // --------------------------------------------------------------------------- const componentOptionLabel = (component: any) => component?.name || 'Composant' const componentOptionDescription = (component: any) => { if (!component) return '' const parts: string[] = [] if (component.reference) parts.push(`Réf. ${component.reference}`) const constructeurName = component.constructeur?.name || component.constructeurName if (constructeurName) parts.push(constructeurName) const machineAssignments = getComponentMachineAssignments(component) if (machineAssignments.length) parts.push(`Machines: ${formatAssignmentList(machineAssignments)}`) const productTypeName = component.product?.typeProduct?.name const productLabel = component.product?.name || component.product?.reference if (productTypeName || productLabel) parts.push(`Produit: ${productTypeName || productLabel}`) return parts.join(' • ') } const pieceOptionLabel = (piece: any) => piece?.name || 'Pièce' const pieceOptionDescription = (piece: any) => { if (!piece) return '' const parts: string[] = [] if (piece.reference) parts.push(`Réf. ${piece.reference}`) const constructeurName = piece.constructeur?.name || piece.constructeurName if (constructeurName) parts.push(constructeurName) const machineAssignments = getPieceMachineAssignments(piece) if (machineAssignments.length) parts.push(`Machines: ${formatAssignmentList(machineAssignments)}`) const componentAssignments = getPieceComponentAssignments(piece) if (componentAssignments.length) parts.push(`Composants: ${formatAssignmentList(componentAssignments)}`) const productTypeName = piece.product?.typeProduct?.name const productLabel = piece.product?.name || piece.product?.reference if (productTypeName || productLabel) parts.push(`Produit: ${productTypeName || productLabel}`) return parts.join(' • ') } // --------------------------------------------------------------------------- // Machine creation // --------------------------------------------------------------------------- const finalizeMachineCreation = async () => { if (submitting.value) return const type = selectedMachineType.value if (!type) { toast.showError('Merci de sélectionner un type de machine') return } if (!canCreateMachine.value) { toast.showError('Compléter les informations obligatoires avant de créer la machine') return } submitting.value = true try { const baseMachineData = { name: newMachine.name, siteId: newMachine.siteId, reference: newMachine.reference, typeMachineId: type.id, } const hasRequirements = (type.componentRequirements?.length || 0) > 0 || (type.pieceRequirements?.length || 0) > 0 || (type.productRequirements?.length || 0) > 0 let componentLinks: any[] = [] let pieceLinks: any[] = [] let productLinks: any[] = [] if (hasRequirements) { const validationResult = validateRequirementSelections(type) if (!validationResult.valid) { toast.showError(validationResult.error as string) return } componentLinks = validationResult.componentLinks as any[] pieceLinks = validationResult.pieceLinks as any[] productLinks = validationResult.productLinks as any[] } const result: any = hasRequirements ? await createMachine(baseMachineData as any) : await createMachineFromType(baseMachineData as any, type) if (result.success) { if (hasRequirements && result.data?.id) { const skeletonResult: any = await reconfigureSkeleton(result.data.id, { componentLinks, pieceLinks, productLinks, } as any) if (!skeletonResult.success) { toast.showError(skeletonResult.error || 'Impossible d\'enregistrer les pièces/composants') return } } newMachine.name = '' newMachine.siteId = '' newMachine.typeMachineId = '' newMachine.reference = '' clearRequirementSelections() await navigateTo('/machines') } else if (result.error) { toast.showError(`Impossible de créer la machine: ${result.error}`) } } catch (error: any) { toast.showError(`Erreur lors de la création: ${error.message}`) } finally { submitting.value = false } } // --------------------------------------------------------------------------- // Watchers & lifecycle // --------------------------------------------------------------------------- watch( () => newMachine.typeMachineId, (typeId) => { clearRequirementSelections() if (!typeId) return const type = (machineTypes as any).value.find((item: any) => item.id === typeId) if (!type) return initializeRequirementSelections(type) }, ) onMounted(async () => { await Promise.all([ loadSites(), loadMachineTypes(), loadComposants(), loadPieces(), loadProducts(), ]) }) // --------------------------------------------------------------------------- // Public API // --------------------------------------------------------------------------- return { // State submitting, newMachine, sites, machineTypes, machineTypesLoading, composantsLoading, piecesLoading, productsLoading, selectedMachineType, // Selection state pieceLoadingByKey, getPieceKey, fetchPieceOptions, getComponentRequirementEntries, getPieceRequirementEntries, getProductRequirementEntries, addComponentSelectionEntry, removeComponentSelectionEntry, addPieceSelectionEntry, removePieceSelectionEntry, addProductSelectionEntry, removeProductSelectionEntry, setComponentRequirementComponent, setPieceRequirementPiece, setProductRequirementProduct, // Preview machinePreview, blockingPreviewIssues, canCreateMachine, // Entity finders findComponentById, findPieceById, findProductById, // Options getComponentOptions, getPieceOptions, getProductOptions, // Label helpers machineTypeLabel, machineTypeDescription, componentOptionLabel, componentOptionDescription, pieceOptionLabel, pieceOptionDescription, // Type label resolvers resolveComponentRequirementTypeLabel, resolvePieceRequirementTypeLabel, // Actions finalizeMachineCreation, } }