Files
Inventory_frontend/app/shared/model/componentStructureSanitize.ts
2026-03-12 12:08:43 +01:00

318 lines
10 KiB
TypeScript

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<string, unknown> => {
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<string, any> => {
if (isPlainObject(field?.value)) {
return field.value as Record<string, any>
}
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 result: ComponentModelCustomField = { name, type, required }
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<string, any>)) {
return (defaultCandidate as Record<string, any>).defaultValue
}
if ('value' in (defaultCandidate as Record<string, any>)) {
return (defaultCandidate as Record<string, any>).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)
}