feat: improve machine component hierarchy handling

This commit is contained in:
Matthieu
2025-10-13 09:01:19 +02:00
parent 95c2a82689
commit 06ae0ca7aa
9 changed files with 1184 additions and 408 deletions

View File

@@ -2108,27 +2108,83 @@ const mergeCustomFieldValuesWithDefinitions = (valueEntries = [], ...definitionS
return result
}
const dedupeCustomFieldEntries = (fields) => {
if (!Array.isArray(fields) || fields.length <= 1) {
return Array.isArray(fields) ? fields : []
}
const seen = new Set()
const result = []
for (const field of fields) {
if (!field) {
continue
}
field.type = field.type || 'text'
let normalizedName =
typeof field.name === 'string' ? field.name.trim() : ''
if (!normalizedName && field.customField?.name) {
normalizedName = String(field.customField.name).trim()
field.name = normalizedName
} else if (typeof field.name === 'string') {
field.name = normalizedName
}
const key =
field.customFieldId ||
field.id ||
(normalizedName ? `${normalizedName}::${field.type || 'text'}` : null)
if (!key && !normalizedName) {
continue
}
if (key && seen.has(key)) {
continue
}
if (!normalizedName) {
continue
}
if (key) {
seen.add(key)
}
if (normalizedName) {
seen.add(`${normalizedName}::${field.type || 'text'}`)
}
result.push(field)
}
return result
}
const transformCustomFields = (pieces) => {
return (pieces || []).map((piece) => {
const requirement = piece.typeMachinePieceRequirement || {}
const typePiece = requirement.typePiece || piece.typePiece || {}
const customFields = mergeCustomFieldValuesWithDefinitions(
piece.customFieldValues,
piece.customFields,
piece.definition?.customFields,
piece.typePiece?.customFields,
typePiece.customFields,
requirement.typePiece?.customFields,
requirement.customFields,
requirement.definition?.customFields,
getStructureCustomFields(piece.definition?.structure),
getStructureCustomFields(piece.typePiece?.structure),
getStructureCustomFields(typePiece.structure),
getStructureCustomFields(typePiece.pieceSkeleton),
getStructureCustomFields(piece.typePiece?.pieceSkeleton),
getStructureCustomFields(requirement.structure),
getStructureCustomFields(requirement.pieceSkeleton),
const customFields = dedupeCustomFieldEntries(
mergeCustomFieldValuesWithDefinitions(
piece.customFieldValues,
piece.customFields,
piece.definition?.customFields,
piece.typePiece?.customFields,
typePiece.customFields,
requirement.typePiece?.customFields,
requirement.customFields,
requirement.definition?.customFields,
getStructureCustomFields(piece.definition?.structure),
getStructureCustomFields(piece.typePiece?.structure),
getStructureCustomFields(typePiece.structure),
getStructureCustomFields(typePiece.pieceSkeleton),
getStructureCustomFields(piece.typePiece?.pieceSkeleton),
getStructureCustomFields(requirement.structure),
getStructureCustomFields(requirement.pieceSkeleton),
),
)
return {
@@ -2151,21 +2207,23 @@ const transformComponentCustomFields = (componentsData) => {
const requirement = component.typeMachineComponentRequirement || {}
const type = requirement.typeComposant || component.typeComposant || {}
const customFields = mergeCustomFieldValuesWithDefinitions(
component.customFieldValues,
component.customFields,
component.definition?.customFields,
component.typeComposant?.customFields,
type.customFields,
requirement.typeComposant?.customFields,
requirement.customFields,
requirement.definition?.customFields,
getStructureCustomFields(component.definition?.structure),
getStructureCustomFields(component.typeComposant?.structure),
getStructureCustomFields(type.structure),
getStructureCustomFields(type.componentSkeleton),
getStructureCustomFields(requirement.structure),
getStructureCustomFields(requirement.componentSkeleton),
const customFields = dedupeCustomFieldEntries(
mergeCustomFieldValuesWithDefinitions(
component.customFieldValues,
component.customFields,
component.definition?.customFields,
component.typeComposant?.customFields,
type.customFields,
requirement.typeComposant?.customFields,
requirement.customFields,
requirement.definition?.customFields,
getStructureCustomFields(component.definition?.structure),
getStructureCustomFields(component.typeComposant?.structure),
getStructureCustomFields(type.structure),
getStructureCustomFields(type.componentSkeleton),
getStructureCustomFields(requirement.structure),
getStructureCustomFields(requirement.componentSkeleton),
),
)
const pieces = component.pieces
@@ -2198,14 +2256,14 @@ const transformComponentCustomFields = (componentsData) => {
const syncMachineCustomFields = () => {
if (!machine.value) {
machineCustomFields.value = []
return
return
}
const merged = mergeCustomFieldValuesWithDefinitions(
const merged = dedupeCustomFieldEntries(mergeCustomFieldValuesWithDefinitions(
machine.value.customFieldValues,
machine.value.customFields,
machine.value.typeMachine?.customFields,
).map((field) => ({
)).map((field) => ({
...field,
readOnly: false,
}))
@@ -2276,210 +2334,271 @@ function mergeComponentTrees(existing = [], updates = []) {
}
const buildMachineHierarchyFromLinks = (componentLinks = [], pieceLinks = []) => {
const componentMap = new Map()
const componentRoots = []
const normalizeComponentLinkId = (link) =>
resolveIdentifier(link?.id, link?.linkId, link?.machineComponentLinkId)
componentLinks.forEach((link, index) => {
if (!isPlainObject(link)) {
return
const normalizePieceLinkId = (link) =>
resolveIdentifier(link?.id, link?.linkId, link?.machinePieceLinkId)
const createPieceNode = (link, parentComponentName = null) => {
if (!link || typeof link !== 'object') {
return null
}
const baseComponent = isPlainObject(link.composant)
? link.composant
: isPlainObject(link.component)
? link.component
: isPlainObject(link.targetComponent)
? link.targetComponent
: {}
const appliedPiece =
(link.piece && typeof link.piece === 'object' && link.piece) || {}
const originalPiece =
(link.originalPiece && typeof link.originalPiece === 'object' && link.originalPiece) || null
const linkId = resolveIdentifier(link.id, link.linkId, link.machineComponentLinkId)
const requirement =
link.typeMachinePieceRequirement ||
appliedPiece.typeMachinePieceRequirement ||
originalPiece?.typeMachinePieceRequirement ||
null
const node = {
...baseComponent,
machineComponentLink: link,
machineComponentLinkId: linkId,
linkId,
componentLinkId: linkId,
composantId: resolveIdentifier(
baseComponent.composantId,
baseComponent.componentId,
link.composantId,
link.componentId,
baseComponent.id,
),
const machinePieceLinkId = normalizePieceLinkId(link)
const pieceId = resolveIdentifier(appliedPiece.id, appliedPiece.pieceId, link.pieceId)
const basePiece = {
...appliedPiece,
id: appliedPiece.id || pieceId || machinePieceLinkId || `piece-${machinePieceLinkId}`,
pieceId,
name:
link.overrides?.name ||
appliedPiece.name ||
appliedPiece.definition?.name ||
appliedPiece.definition?.role ||
originalPiece?.name ||
'Pièce',
reference:
link.overrides?.reference ||
appliedPiece.reference ||
appliedPiece.definition?.reference ||
originalPiece?.reference ||
null,
prix:
link.overrides?.prix ??
appliedPiece.prix ??
originalPiece?.prix ??
null,
constructeur:
appliedPiece.constructeur ||
originalPiece?.constructeur ||
null,
constructeurId:
appliedPiece.constructeurId ||
appliedPiece.constructeur?.id ||
originalPiece?.constructeurId ||
null,
documents:
Array.isArray(appliedPiece.documents)
? appliedPiece.documents
: Array.isArray(originalPiece?.documents)
? originalPiece.documents
: [],
typePiece: appliedPiece.typePiece || requirement?.typePiece || null,
typePieceId:
appliedPiece.typePieceId ||
appliedPiece.typePiece?.id ||
requirement?.typePieceId ||
requirement?.typePiece?.id ||
null,
typeMachinePieceRequirement: requirement,
typeMachinePieceRequirementId: requirement?.id || null,
requirementId: requirement?.id || null,
overrides: link.overrides || null,
originalPiece,
machinePieceLink: link,
machinePieceLinkId,
linkId: machinePieceLinkId,
parentComponentLinkId: resolveIdentifier(
link.parentComponentLinkId,
link.parentLinkId,
link.parentMachineComponentLinkId,
baseComponent.parentComponentLinkId,
baseComponent.parentLinkId,
appliedPiece.parentComponentLinkId,
),
parentComposantId: resolveIdentifier(
baseComponent.parentComposantId,
parentComponentId: resolveIdentifier(
appliedPiece.parentComponentId,
link.parentComponentId,
),
parentRequirementId: resolveIdentifier(
baseComponent.parentRequirementId,
link.parentRequirementId,
),
parentMachineComponentRequirementId: resolveIdentifier(
baseComponent.parentMachineComponentRequirementId,
link.parentMachineComponentRequirementId,
),
parentMachinePieceRequirementId: resolveIdentifier(
baseComponent.parentMachinePieceRequirementId,
link.parentMachinePieceRequirementId,
),
typeMachineComponentRequirement:
link.requirement
|| link.typeMachineComponentRequirement
|| baseComponent.typeMachineComponentRequirement
|| null,
typeMachineComponentRequirementId: resolveIdentifier(
link.requirementId,
link.typeMachineComponentRequirementId,
(link.requirement || link.typeMachineComponentRequirement)?.id,
baseComponent.typeMachineComponentRequirementId,
),
definition: baseComponent.definition || {},
pieces: [],
subComponents: [],
sousComposants: [],
}
if (!node.id) {
node.id = resolveIdentifier(
baseComponent.id,
node.composantId,
link.composantId,
link.componentId,
`component-${index}`,
)
}
node.requirementId = node.typeMachineComponentRequirementId
node.machineComponentLinkOverrides = link.overrides || null
node.overrides = link.overrides || null
node.definitionOverrides = link.overrides || null
node.subcomponents = node.subComponents
componentMap.set(node.machineComponentLinkId || node.id, node)
})
componentMap.forEach((node) => {
const parentLinkId = resolveIdentifier(node.parentComponentLinkId)
if (parentLinkId && componentMap.has(parentLinkId)) {
const parent = componentMap.get(parentLinkId)
parent.subComponents.push(node)
parent.sousComposants = parent.subComponents
parent.subcomponents = parent.subComponents
node.parentComposantId = resolveIdentifier(
node.parentComposantId,
parent.composantId,
parent.id,
)
} else {
componentRoots.push(node)
}
})
const machinePieces = []
pieceLinks.forEach((link, index) => {
if (!isPlainObject(link)) {
return
}
const basePiece = isPlainObject(link.piece)
? link.piece
: isPlainObject(link.targetPiece)
? link.targetPiece
: isPlainObject(link.pieceModel)
? link.pieceModel
: {}
const linkId = resolveIdentifier(link.id, link.linkId, link.machinePieceLinkId)
const parentComponentLinkId = resolveIdentifier(
link.parentComponentLinkId,
link.parentLinkId,
link.parentMachineComponentLinkId,
basePiece.parentComponentLinkId,
)
const pieceEntry = {
...basePiece,
id: resolveIdentifier(basePiece.id, link.pieceId, linkId, `piece-${index}`),
pieceId: resolveIdentifier(basePiece.id, link.pieceId),
machinePieceLink: link,
machinePieceLinkId: linkId,
linkId,
parentComponentLinkId,
parentComponentName,
parentLinkId: resolveIdentifier(
link.parentLinkId,
link.parentMachinePieceLinkId,
basePiece.parentLinkId,
appliedPiece.parentLinkId,
),
parentPieceLinkId: resolveIdentifier(
link.parentPieceLinkId,
basePiece.parentPieceLinkId,
appliedPiece.parentPieceLinkId,
),
parentPieceId: resolveIdentifier(
basePiece.parentPieceId,
appliedPiece.parentPieceId,
link.parentPieceId,
),
parentComponentId: resolveIdentifier(
basePiece.parentComponentId,
parentMachineComponentRequirementId: resolveIdentifier(
appliedPiece.parentMachineComponentRequirementId,
link.parentMachineComponentRequirementId,
),
parentMachinePieceRequirementId: resolveIdentifier(
appliedPiece.parentMachinePieceRequirementId,
link.parentMachinePieceRequirementId,
),
definition: appliedPiece.definition || originalPiece?.definition || {},
customFields: appliedPiece.customFields || [],
skeletonOnly: !pieceId,
}
return basePiece
}
const createComponentNode = (link) => {
if (!link || typeof link !== 'object') {
return null
}
const appliedComponent =
(link.composant && typeof link.composant === 'object' && link.composant) || {}
const originalComponent =
(link.originalComposant && typeof link.originalComposant === 'object' && link.originalComposant) || null
const requirement =
link.typeMachineComponentRequirement ||
appliedComponent.typeMachineComponentRequirement ||
originalComponent?.typeMachineComponentRequirement ||
null
const machineComponentLinkId = normalizeComponentLinkId(link)
const composantId = resolveIdentifier(
appliedComponent.id,
appliedComponent.composantId,
link.composantId,
)
const componentName =
link.overrides?.name ||
appliedComponent.name ||
appliedComponent.definition?.alias ||
appliedComponent.definition?.name ||
originalComponent?.name ||
'Composant'
const pieces = Array.isArray(link.pieceLinks)
? link.pieceLinks.map((pieceLink) => createPieceNode(pieceLink, componentName)).filter(Boolean)
: []
const subComponents = Array.isArray(link.childLinks)
? link.childLinks.map(createComponentNode).filter(Boolean)
: []
const baseComponent = {
...appliedComponent,
id: appliedComponent.id || composantId || machineComponentLinkId || `component-${machineComponentLinkId}`,
composantId,
name: componentName,
reference:
link.overrides?.reference ||
appliedComponent.reference ||
appliedComponent.definition?.reference ||
originalComponent?.reference ||
null,
prix:
link.overrides?.prix ??
appliedComponent.prix ??
originalComponent?.prix ??
null,
constructeur:
appliedComponent.constructeur ||
originalComponent?.constructeur ||
null,
constructeurId:
appliedComponent.constructeurId ||
appliedComponent.constructeur?.id ||
originalComponent?.constructeurId ||
null,
documents:
Array.isArray(appliedComponent.documents)
? appliedComponent.documents
: Array.isArray(originalComponent?.documents)
? originalComponent.documents
: [],
typeComposant:
appliedComponent.typeComposant ||
requirement?.typeComposant ||
null,
typeComposantId:
appliedComponent.typeComposantId ||
appliedComponent.typeComposant?.id ||
requirement?.typeComposantId ||
requirement?.typeComposant?.id ||
null,
typeMachineComponentRequirement: requirement,
typeMachineComponentRequirementId: requirement?.id || null,
requirementId: requirement?.id || null,
overrides: link.overrides || null,
machineComponentLinkOverrides: link.overrides || null,
definitionOverrides: link.overrides || null,
originalComposant: originalComponent,
machineComponentLink: link,
machineComponentLinkId,
componentLinkId: machineComponentLinkId,
parentComponentLinkId: resolveIdentifier(
link.parentComponentLinkId,
link.parentLinkId,
link.parentMachineComponentLinkId,
appliedComponent.parentComponentLinkId,
),
parentComposantId: resolveIdentifier(
appliedComponent.parentComposantId,
link.parentComponentId,
),
composantId: resolveIdentifier(
basePiece.composantId,
basePiece.componentId,
link.composantId,
link.componentId,
parentRequirementId: resolveIdentifier(
appliedComponent.parentRequirementId,
link.parentRequirementId,
),
typeMachinePieceRequirement:
link.requirement
|| link.typeMachinePieceRequirement
|| basePiece.typeMachinePieceRequirement
|| null,
parentMachineComponentRequirementId: resolveIdentifier(
appliedComponent.parentMachineComponentRequirementId,
link.parentMachineComponentRequirementId,
),
parentMachinePieceRequirementId: resolveIdentifier(
appliedComponent.parentMachinePieceRequirementId,
link.parentMachinePieceRequirementId,
),
definition: appliedComponent.definition || originalComponent?.definition || {},
customFields: appliedComponent.customFields || [],
pieces,
subComponents,
subcomponents: subComponents,
sousComposants: subComponents,
skeletonOnly: !composantId,
}
pieceEntry.typeMachinePieceRequirementId = resolveIdentifier(
link.requirementId,
link.typeMachinePieceRequirementId,
pieceEntry.typeMachinePieceRequirement?.id,
basePiece.typeMachinePieceRequirementId,
)
pieceEntry.parentMachineComponentRequirementId = resolveIdentifier(
basePiece.parentMachineComponentRequirementId,
link.parentMachineComponentRequirementId,
)
pieceEntry.parentMachinePieceRequirementId = resolveIdentifier(
basePiece.parentMachinePieceRequirementId,
link.parentMachinePieceRequirementId,
)
pieceEntry.definition = basePiece.definition || {}
pieceEntry.overrides = link.overrides || null
if (!pieceEntry.name && link.overrides?.name) {
pieceEntry.name = link.overrides.name
}
return baseComponent
}
if (parentComponentLinkId && componentMap.has(parentComponentLinkId)) {
const parent = componentMap.get(parentComponentLinkId)
parent.pieces.push(pieceEntry)
pieceEntry.parentComponentName = parent.name || parent.nom || null
} else {
machinePieces.push(pieceEntry)
}
})
const rootComponents = (Array.isArray(componentLinks) ? componentLinks : [])
.filter((link) =>
!resolveIdentifier(
link?.parentComponentLinkId,
link?.parentLinkId,
link?.parentMachineComponentLinkId,
),
)
.map(createComponentNode)
.filter(Boolean)
componentMap.forEach((node) => {
node.sousComposants = node.subComponents
node.subcomponents = node.subComponents
})
const machinePieces = (Array.isArray(pieceLinks) ? pieceLinks : [])
.filter((link) =>
!resolveIdentifier(
link?.parentComponentLinkId,
link?.parentLinkId,
link?.parentMachineComponentLinkId,
),
)
.map((link) => createPieceNode(link, null))
.filter(Boolean)
return {
components: componentRoots,
components: rootComponents,
machinePieces,
}
}