refactor(frontend) : split useMachineDetailData into focused composables
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
306
app/composables/useMachineDetailHierarchy.ts
Normal file
306
app/composables/useMachineDetailHierarchy.ts
Normal file
@@ -0,0 +1,306 @@
|
||||
/**
|
||||
* Machine detail — hierarchy & link management sub-composable.
|
||||
*
|
||||
* Handles machine hierarchy building, component/piece tree resolution,
|
||||
* flatten helpers, find-by-id utilities, and structure link CRUD.
|
||||
*/
|
||||
|
||||
import { ref, computed } from 'vue'
|
||||
import { useApi } from '~/composables/useApi'
|
||||
import { useToast } from '~/composables/useToast'
|
||||
import {
|
||||
resolveIdentifier,
|
||||
} from '~/shared/utils/productDisplayUtils'
|
||||
import {
|
||||
buildMachineHierarchyFromLinks,
|
||||
resolveLinkArray,
|
||||
} from '~/composables/useMachineHierarchy'
|
||||
|
||||
type AnyRecord = Record<string, unknown>
|
||||
|
||||
interface MachineDetailHierarchyDeps {
|
||||
machineId: string
|
||||
machine: Ref<AnyRecord | null>
|
||||
constructeurs: Ref<unknown[]>
|
||||
findProductById: (id: string | null | undefined) => AnyRecord | null
|
||||
transformComponentCustomFields: (data: AnyRecord[]) => AnyRecord[]
|
||||
transformCustomFields: (data: AnyRecord[]) => AnyRecord[]
|
||||
syncMachineCustomFields: () => void
|
||||
}
|
||||
|
||||
export function useMachineDetailHierarchy(deps: MachineDetailHierarchyDeps) {
|
||||
const {
|
||||
machineId,
|
||||
machine,
|
||||
constructeurs,
|
||||
findProductById,
|
||||
transformComponentCustomFields,
|
||||
transformCustomFields,
|
||||
syncMachineCustomFields,
|
||||
} = deps
|
||||
|
||||
const { get, post: apiPost, delete: apiDel } = useApi()
|
||||
const toast = useToast()
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// State
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const components = ref<AnyRecord[]>([])
|
||||
const pieces = ref<AnyRecord[]>([])
|
||||
const machineComponentLinks = ref<AnyRecord[]>([])
|
||||
const machinePieceLinks = ref<AnyRecord[]>([])
|
||||
const machineProductLinks = ref<AnyRecord[]>([])
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const flattenComponents = (list: AnyRecord[] = []): AnyRecord[] => {
|
||||
const result: AnyRecord[] = []
|
||||
const traverse = (items: AnyRecord[]) => {
|
||||
items.forEach((item) => {
|
||||
result.push(item)
|
||||
if (Array.isArray(item.subComponents) && item.subComponents.length) {
|
||||
traverse(item.subComponents as AnyRecord[])
|
||||
}
|
||||
})
|
||||
}
|
||||
traverse(list)
|
||||
return result
|
||||
}
|
||||
|
||||
const findComponentById = (items: AnyRecord[] | undefined, id: string): AnyRecord | null => {
|
||||
for (const item of items || []) {
|
||||
if (item.id === id) return item
|
||||
const found = findComponentById(item.subComponents as AnyRecord[] | undefined, id)
|
||||
if (found) return found
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const findPieceById = (pieceId: string): AnyRecord | null => {
|
||||
const direct = pieces.value.find((p) => p.id === pieceId)
|
||||
if (direct) return direct
|
||||
|
||||
const searchInComponents = (items: AnyRecord[]): AnyRecord | null => {
|
||||
for (const item of items || []) {
|
||||
const match = ((item.pieces as AnyRecord[]) || []).find((p) => p.id === pieceId)
|
||||
if (match) return match
|
||||
const nested = searchInComponents((item.subComponents as AnyRecord[]) || [])
|
||||
if (nested) return nested
|
||||
}
|
||||
return null
|
||||
}
|
||||
return searchInComponents(components.value)
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Hierarchy & links
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const applyMachineLinks = (source: AnyRecord): boolean => {
|
||||
const container = (source?.machine as AnyRecord) ?? null
|
||||
const componentLinksData =
|
||||
resolveLinkArray(source, ['componentLinks', 'machineComponentLinks']) ??
|
||||
resolveLinkArray(container, ['componentLinks', 'machineComponentLinks'])
|
||||
const pieceLinksData =
|
||||
resolveLinkArray(source, ['pieceLinks', 'machinePieceLinks']) ??
|
||||
resolveLinkArray(container, ['pieceLinks', 'machinePieceLinks'])
|
||||
const productLinksData =
|
||||
resolveLinkArray(source, ['productLinks', 'machineProductLinks']) ??
|
||||
resolveLinkArray(container, ['productLinks', 'machineProductLinks'])
|
||||
|
||||
if (componentLinksData === null && pieceLinksData === null && productLinksData === null) {
|
||||
return false
|
||||
}
|
||||
|
||||
const normalizedComponentLinks = (componentLinksData ?? []) as AnyRecord[]
|
||||
const normalizedPieceLinks = (pieceLinksData ?? []) as AnyRecord[]
|
||||
const normalizedProductLinks = (productLinksData ?? []) as AnyRecord[]
|
||||
|
||||
machineComponentLinks.value = normalizedComponentLinks
|
||||
machinePieceLinks.value = normalizedPieceLinks
|
||||
machineProductLinks.value = normalizedProductLinks
|
||||
|
||||
const { components: hierarchy, machinePieces: machineLevelPieces } =
|
||||
buildMachineHierarchyFromLinks(
|
||||
normalizedComponentLinks,
|
||||
normalizedPieceLinks,
|
||||
findProductById as any,
|
||||
constructeurs.value as any,
|
||||
)
|
||||
|
||||
components.value = transformComponentCustomFields(hierarchy as AnyRecord[])
|
||||
pieces.value = transformCustomFields(machineLevelPieces as AnyRecord[])
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Computed
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const flattenedComponents = computed(() => flattenComponents(components.value))
|
||||
|
||||
const machinePieces = computed(() => {
|
||||
return pieces.value.filter((piece) => {
|
||||
const parentLinkId = resolveIdentifier(
|
||||
piece.parentComponentLinkId,
|
||||
(piece.machinePieceLink as AnyRecord)?.parentComponentLinkId,
|
||||
piece.parentLinkId,
|
||||
)
|
||||
if (parentLinkId) return false
|
||||
return !piece.composantId
|
||||
})
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Structure reload
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const reloadMachineStructure = async () => {
|
||||
const result: any = await get(`/machines/${machineId}/structure`)
|
||||
if (result.success) {
|
||||
const machinePayload =
|
||||
result.data?.machine && typeof result.data.machine === 'object'
|
||||
? result.data.machine
|
||||
: result.data
|
||||
if (machinePayload && typeof machinePayload === 'object') {
|
||||
machine.value = {
|
||||
...machine.value,
|
||||
...machinePayload,
|
||||
documents: machinePayload.documents || (machine.value as AnyRecord)?.documents || [],
|
||||
customFieldValues: machinePayload.customFieldValues || (machine.value as AnyRecord)?.customFieldValues || [],
|
||||
}
|
||||
const linksApplied = applyMachineLinks(result.data)
|
||||
if (linksApplied && machine.value) {
|
||||
machine.value.componentLinks = machineComponentLinks.value
|
||||
machine.value.pieceLinks = machinePieceLinks.value
|
||||
machine.value.productLinks = machineProductLinks.value
|
||||
}
|
||||
syncMachineCustomFields()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Structure link CRUD
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const addComponentLink = async (composantId: string) => {
|
||||
const result: any = await apiPost('/machine_component_links', {
|
||||
machine: `/api/machines/${machineId}`,
|
||||
composant: `/api/composants/${composantId}`,
|
||||
})
|
||||
if (result.success) {
|
||||
toast.showSuccess('Composant ajouté à la machine')
|
||||
await reloadMachineStructure()
|
||||
} else {
|
||||
toast.showError('Erreur lors de l\'ajout du composant')
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
const removeComponentLink = async (linkId: string) => {
|
||||
const result: any = await apiDel(`/machine_component_links/${linkId}`)
|
||||
if (result.success) {
|
||||
toast.showSuccess('Composant retiré de la machine')
|
||||
await reloadMachineStructure()
|
||||
} else {
|
||||
toast.showError('Erreur lors de la suppression du composant')
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
const addPieceLink = async (pieceId: string, parentComponentLinkId?: string) => {
|
||||
const payload: any = {
|
||||
machine: `/api/machines/${machineId}`,
|
||||
piece: `/api/pieces/${pieceId}`,
|
||||
}
|
||||
if (parentComponentLinkId) {
|
||||
payload.parentLink = `/api/machine_component_links/${parentComponentLinkId}`
|
||||
}
|
||||
const result: any = await apiPost('/machine_piece_links', payload)
|
||||
if (result.success) {
|
||||
toast.showSuccess('Pièce ajoutée à la machine')
|
||||
await reloadMachineStructure()
|
||||
} else {
|
||||
toast.showError('Erreur lors de l\'ajout de la pièce')
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
const removePieceLink = async (linkId: string) => {
|
||||
const result: any = await apiDel(`/machine_piece_links/${linkId}`)
|
||||
if (result.success) {
|
||||
toast.showSuccess('Pièce retirée de la machine')
|
||||
await reloadMachineStructure()
|
||||
} else {
|
||||
toast.showError('Erreur lors de la suppression de la pièce')
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
const addProductLink = async (productId: string, parentComponentLinkId?: string, parentPieceLinkId?: string) => {
|
||||
const payload: any = {
|
||||
machine: `/api/machines/${machineId}`,
|
||||
product: `/api/products/${productId}`,
|
||||
}
|
||||
if (parentComponentLinkId) {
|
||||
payload.parentComponentLink = `/api/machine_component_links/${parentComponentLinkId}`
|
||||
}
|
||||
if (parentPieceLinkId) {
|
||||
payload.parentPieceLink = `/api/machine_piece_links/${parentPieceLinkId}`
|
||||
}
|
||||
const result: any = await apiPost('/machine_product_links', payload)
|
||||
if (result.success) {
|
||||
toast.showSuccess('Produit ajouté à la machine')
|
||||
await reloadMachineStructure()
|
||||
} else {
|
||||
toast.showError('Erreur lors de l\'ajout du produit')
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
const removeProductLink = async (linkId: string) => {
|
||||
const result: any = await apiDel(`/machine_product_links/${linkId}`)
|
||||
if (result.success) {
|
||||
toast.showSuccess('Produit retiré de la machine')
|
||||
await reloadMachineStructure()
|
||||
} else {
|
||||
toast.showError('Erreur lors de la suppression du produit')
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
return {
|
||||
// State
|
||||
components,
|
||||
pieces,
|
||||
machineComponentLinks,
|
||||
machinePieceLinks,
|
||||
machineProductLinks,
|
||||
|
||||
// Computed
|
||||
flattenedComponents,
|
||||
machinePieces,
|
||||
|
||||
// Helpers
|
||||
flattenComponents,
|
||||
findComponentById,
|
||||
findPieceById,
|
||||
|
||||
// Hierarchy
|
||||
applyMachineLinks,
|
||||
|
||||
// Structure link management
|
||||
reloadMachineStructure,
|
||||
addComponentLink,
|
||||
removeComponentLink,
|
||||
addPieceLink,
|
||||
removePieceLink,
|
||||
addProductLink,
|
||||
removeProductLink,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user