import type { ComponentModelCustomField, ComponentModelCustomFieldType, ComponentModelPiece, ComponentModelProduct, ComponentModelStructureNode, } from '../types/inventory' // Inline helper to avoid circular dependency with componentStructure.ts const isPlainObject = (value: unknown): value is Record => { return value !== null && typeof value === 'object' && !Array.isArray(value) } export const toStringArray = (input: unknown): string[] | undefined => { if (!Array.isArray(input)) { return undefined } const parsed = input .map((value) => { if (typeof value === 'string') { return value.trim() } if (value === null || value === undefined) { return '' } return String(value).trim() }) .filter((value) => value.length > 0) return parsed.length ? parsed : undefined } export const extractFieldValueObject = (field: any): Record => { if (isPlainObject(field?.value)) { return field.value as Record } return {} } export const sanitizeCustomFields = (fields: any[]): ComponentModelCustomField[] => { if (!Array.isArray(fields)) { return [] } return fields .map((field, index) => { const rawName = typeof field?.name === 'string' ? field.name : typeof field?.key === 'string' ? field.key : '' const name = rawName.trim() if (!name) { return null } const valueObject = extractFieldValueObject(field) const candidateType = typeof field?.type === 'string' && field.type ? field.type : typeof valueObject?.type === 'string' ? valueObject.type : '' const allowedTypes: ComponentModelCustomFieldType[] = ['text', 'number', 'select', 'boolean', 'date'] const type = allowedTypes.includes(candidateType as ComponentModelCustomFieldType) ? (candidateType as ComponentModelCustomFieldType) : 'text' const required = typeof valueObject?.required === 'boolean' ? valueObject.required : !!field?.required let options: string[] | undefined if (type === 'select') { options = toStringArray(valueObject?.options) || toStringArray((valueObject as any)?.choices) || toStringArray(field?.options) if (!options && typeof field?.optionsText === 'string') { const parsedFromText = field.optionsText .split(/\r?\n/) .map((option: string) => option.trim()) .filter((option: string) => option.length > 0) options = parsedFromText.length ? parsedFromText : undefined } } const machineContextOnly = typeof valueObject?.machineContextOnly === 'boolean' ? valueObject.machineContextOnly : !!field?.machineContextOnly const result: ComponentModelCustomField = { name, type, required, machineContextOnly } if (options) { result.options = options } const defaultCandidate = field?.defaultValue ?? valueObject?.defaultValue ?? field?.value ?? field?.default ?? null const resolvedDefault = (() => { if (defaultCandidate === undefined || defaultCandidate === null) { return undefined } if (typeof defaultCandidate === 'object') { if (defaultCandidate === null) { return undefined } if ('defaultValue' in (defaultCandidate as Record)) { return (defaultCandidate as Record).defaultValue } if ('value' in (defaultCandidate as Record)) { return (defaultCandidate as Record).value } return undefined } return defaultCandidate })() if (resolvedDefault !== undefined && resolvedDefault !== null && resolvedDefault !== '') { result.defaultValue = String(resolvedDefault) } const id = typeof field?.id === 'string' ? field.id : undefined if (id) { result.id = id } const customFieldId = typeof field?.customFieldId === 'string' ? field.customFieldId : undefined if (customFieldId) { result.customFieldId = customFieldId } const orderIndex = typeof field?.orderIndex === 'number' ? field.orderIndex : index result.orderIndex = orderIndex return result }) .filter((field): field is ComponentModelCustomField => !!field) } export const sanitizePieces = (pieces: any[]): ComponentModelPiece[] => { if (!Array.isArray(pieces)) { return [] } return pieces .map((piece) => { const rawTypePieceId = typeof piece?.typePieceId === 'string' ? piece.typePieceId.trim() : typeof piece?.typePiece?.id === 'string' ? piece.typePiece.id.trim() : '' const typePieceId = rawTypePieceId.length > 0 ? rawTypePieceId : undefined const rawTypePieceLabel = typeof piece?.typePieceLabel === 'string' ? piece.typePieceLabel.trim() : typeof piece?.typePiece?.name === 'string' ? piece.typePiece.name.trim() : '' const typePieceLabel = rawTypePieceLabel.length > 0 ? rawTypePieceLabel : undefined const reference = typeof piece?.reference === 'string' && piece.reference.trim().length > 0 ? piece.reference.trim() : undefined const rawFamilyCode = typeof piece?.familyCode === 'string' ? piece.familyCode.trim() : typeof piece?.typePiece?.code === 'string' ? piece.typePiece.code.trim() : '' const familyCode = rawFamilyCode.length > 0 ? rawFamilyCode : undefined const rawRole = typeof piece?.role === 'string' ? piece.role.trim() : '' const role = rawRole.length > 0 ? rawRole : undefined const quantity = typeof piece?.quantity === 'number' && piece.quantity >= 1 ? piece.quantity : undefined if (!typePieceId && !typePieceLabel && !reference && !familyCode) { return null } const result: ComponentModelPiece = {} if (role) { result.role = role } if (familyCode) { result.familyCode = familyCode } if (reference !== undefined) { result.reference = reference } if (typePieceId) { result.typePieceId = typePieceId } if (typePieceLabel) { result.typePieceLabel = typePieceLabel } if (quantity !== undefined) { result.quantity = quantity } return result }) .filter((piece): piece is ComponentModelPiece => !!piece) } export const sanitizeProducts = (products: any[]): ComponentModelProduct[] => { if (!Array.isArray(products)) { return [] } return products .map((product) => { const rawTypeProductId = typeof product?.typeProductId === 'string' ? product.typeProductId.trim() : typeof product?.typeProduct?.id === 'string' ? product.typeProduct.id.trim() : '' const typeProductId = rawTypeProductId.length > 0 ? rawTypeProductId : undefined const rawTypeProductLabel = typeof product?.typeProductLabel === 'string' ? product.typeProductLabel.trim() : typeof product?.typeProduct?.name === 'string' ? product.typeProduct.name.trim() : '' const typeProductLabel = rawTypeProductLabel.length > 0 ? rawTypeProductLabel : undefined const reference = typeof product?.reference === 'string' && product.reference.trim().length > 0 ? product.reference.trim() : undefined const rawFamilyCode = typeof product?.familyCode === 'string' ? product.familyCode.trim() : typeof product?.typeProduct?.code === 'string' ? product.typeProduct.code.trim() : '' const familyCode = rawFamilyCode.length > 0 ? rawFamilyCode : undefined const rawRole = typeof product?.role === 'string' ? product.role.trim() : '' const role = rawRole.length > 0 ? rawRole : undefined if (!typeProductId && !typeProductLabel && !reference && !familyCode) { return null } const result: ComponentModelProduct = {} if (role) { result.role = role } if (familyCode) { result.familyCode = familyCode } if (reference !== undefined) { result.reference = reference } if (typeProductId) { result.typeProductId = typeProductId } if (typeProductLabel) { result.typeProductLabel = typeProductLabel } return result }) .filter((product): product is ComponentModelProduct => !!product) } export const sanitizeSubcomponents = (components: any[]): ComponentModelStructureNode[] => { if (!Array.isArray(components)) { return [] } return components .map((component) => { const rawTypeComposantId = typeof component?.typeComposantId === 'string' ? component.typeComposantId.trim() : typeof component?.typeComposant?.id === 'string' ? component.typeComposant.id.trim() : '' const typeComposantId = rawTypeComposantId.length > 0 ? rawTypeComposantId : undefined const modelId = typeof component?.modelId === 'string' && component.modelId.trim().length > 0 ? component.modelId.trim() : undefined const familyCode = typeof component?.familyCode === 'string' && component.familyCode.trim().length > 0 ? component.familyCode.trim() : undefined const alias = typeof component?.alias === 'string' && component.alias.trim().length > 0 ? component.alias.trim() : undefined if (!typeComposantId && !modelId && !familyCode) { return null } const result: ComponentModelStructureNode = { subcomponents: sanitizeSubcomponents( Array.isArray(component?.subcomponents) ? component.subcomponents : component?.subComponents, ), } if (typeComposantId) { result.typeComposantId = typeComposantId } const typeComposantLabel = typeof component?.typeComposantLabel === 'string' ? component.typeComposantLabel.trim() : typeof component?.typeComposant?.name === 'string' ? component.typeComposant.name.trim() : '' if (typeComposantLabel) { result.typeComposantLabel = typeComposantLabel } if (modelId) { result.modelId = modelId } if (familyCode) { result.familyCode = familyCode } if (alias) { result.alias = alias } return result }) .filter((component): component is ComponentModelStructureNode => !!component) }