/** * Product resolution and display utilities. * * Extracted from pages/machine/[id].vue – these functions resolve product * references from deeply nested API payloads and build display objects. */ // --------------------------------------------------------------------------- // Types // --------------------------------------------------------------------------- export interface ProductDisplay { name: string reference: string | null category: string | null suppliers: string | null price: string | null } type AnyRecord = Record // --------------------------------------------------------------------------- // Helpers // --------------------------------------------------------------------------- const isPlainObject = (value: unknown): value is AnyRecord => Object.prototype.toString.call(value) === '[object Object]' export const resolveIdentifier = (...candidates: unknown[]): string | null => { for (const candidate of candidates) { if (candidate !== undefined && candidate !== null && candidate !== '') { return candidate as string } } return null } // --------------------------------------------------------------------------- // Supplier / price labels // --------------------------------------------------------------------------- export const getProductSuppliersLabel = (product: AnyRecord | null): string | null => { if (!product) return null const suppliers = Array.isArray(product.constructeurs) ? (product.constructeurs as AnyRecord[]).map((c) => c?.name as string).filter(Boolean) : [] return suppliers.length > 0 ? suppliers.join(', ') : null } export const getProductPriceLabel = (product: AnyRecord | null): string | null => { if (!product) return null const priceValue = (product.supplierPrice ?? product.prix ?? product.price ?? null) as string | number | null if (priceValue === undefined || priceValue === null) return null const numeric = Number(priceValue) if (Number.isNaN(numeric)) return null return `${numeric.toFixed(2)} €` } // --------------------------------------------------------------------------- // resolveProductReference // --------------------------------------------------------------------------- export const resolveProductReference = ( source: AnyRecord | null | undefined, findProductById: (id: string) => AnyRecord | null, ): { product: AnyRecord | null; productId: string | null } => { if (!source || typeof source !== 'object') { return { product: null, productId: null } } const candidateKeys: (string | null)[] = [ null, 'productLink', 'machinePieceLink', 'machineComponentLink', 'machineProductLink', 'originalPiece', 'originalComposant', 'link', 'overrides', 'machineComponentLinkOverrides', 'requirement', 'selection', 'entry', ] let product: AnyRecord | null = null let productId: string | null = null const inspect = (container: unknown) => { if (!container || typeof container !== 'object') return const c = container as AnyRecord if (!product && c.product && typeof c.product === 'object') { product = c.product as AnyRecord } if (!productId) { const candidate = (c.productId as string) || (c.product && typeof c.product === 'object' ? ((c.product as AnyRecord).id as string) || ((c.product as AnyRecord).productId as string) : null) || null if (candidate) productId = candidate } } candidateKeys.forEach((key) => { if (key === null) inspect(source) else inspect((source as AnyRecord)[key]) }) if (!product && productId) { product = findProductById(productId) || null } if (!product && !productId && source.productName) { const suppliersLabel = typeof source.constructeursLabel === 'string' ? source.constructeursLabel : typeof source.productSuppliers === 'string' ? source.productSuppliers : null return { product: { name: source.productName, reference: source.productReference || null, typeProduct: source.productCategory ? { name: source.productCategory } : null, constructeurs: suppliersLabel ? (suppliersLabel as string) .split(',') .map((name: string) => name.trim()) .filter((name: string) => name.length > 0) .map((name: string) => ({ name })) : undefined, supplierPrice: source.productPrice ?? source.productPriceLabel ?? source.price ?? null, } as AnyRecord, productId: null, } } if (productId && product && product.id && product.id !== productId) { const resolved = findProductById(productId) if (resolved) product = resolved } return { product: product || null, productId: productId || null } } // --------------------------------------------------------------------------- // getProductDisplay // --------------------------------------------------------------------------- export const getProductDisplay = ( source: AnyRecord | null | undefined, findProductById: (id: string) => AnyRecord | null, ): ProductDisplay | null => { if (!source || typeof source !== 'object') return null const { product, productId } = resolveProductReference(source, findProductById) if (product) { return { name: (product.name as string) || (product.reference as string) || 'Produit catalogue', reference: (product.reference as string) || null, category: (product.typeProduct as AnyRecord)?.name as string || null, suppliers: getProductSuppliersLabel(product), price: getProductPriceLabel(product), } } let fallbackName = (source.productName || source.productLabel || source.typeProductLabel || (source.typeProduct as AnyRecord)?.name || (productId ? `Produit ${productId}` : null)) as string | null let fallbackReference = (source.productReference || source.reference || null) as string | null let fallbackCategory = (source.productCategory || source.typeProductLabel || (source.typeProduct as AnyRecord)?.name || null) as string | null let fallbackSuppliers = (source.productSuppliers || source.constructeursLabel || source.supplierLabel || null) as string | null let fallbackPrice = (source.productPriceLabel || source.productPrice || source.priceLabel || source.price || null) as string | number | null const structuralCandidates = [ source.products, source.productSkeleton, (source.definition as AnyRecord)?.products, (source.definition as AnyRecord)?.productSkeleton, ((source.definition as AnyRecord)?.structure as AnyRecord)?.products, ((source.definition as AnyRecord)?.structure as AnyRecord)?.productSkeleton, (source.structure as AnyRecord)?.products, (source.structure as AnyRecord)?.productSkeleton, (source.requirement as AnyRecord)?.products, (source.requirement as AnyRecord)?.productSkeleton, ((source.requirement as AnyRecord)?.structure as AnyRecord)?.products, ((source.requirement as AnyRecord)?.structure as AnyRecord)?.productSkeleton, ((source.requirement as AnyRecord)?.componentSkeleton as AnyRecord)?.products, (source.typeMachineComponentRequirement as AnyRecord)?.products, (source.typeMachineComponentRequirement as AnyRecord)?.productSkeleton, ((source.typeMachineComponentRequirement as AnyRecord)?.structure as AnyRecord)?.products, ((source.typeMachineComponentRequirement as AnyRecord)?.structure as AnyRecord)?.productSkeleton, ((source.typeMachineComponentRequirement as AnyRecord)?.componentSkeleton as AnyRecord)?.products, (source.typeComposant as AnyRecord)?.products, (source.typeComposant as AnyRecord)?.productSkeleton, ((source.typeComposant as AnyRecord)?.structure as AnyRecord)?.products, ((source.typeComposant as AnyRecord)?.structure as AnyRecord)?.productSkeleton, (source.originalComposant as AnyRecord)?.products, (source.originalComposant as AnyRecord)?.productSkeleton, ((source.originalComposant as AnyRecord)?.definition as AnyRecord)?.products, ((source.originalComposant as AnyRecord)?.definition as AnyRecord)?.productSkeleton, (((source.originalComposant as AnyRecord)?.definition as AnyRecord)?.structure as AnyRecord)?.products, (((source.originalComposant as AnyRecord)?.definition as AnyRecord)?.structure as AnyRecord)?.productSkeleton, (source.originalComponent as AnyRecord)?.products, (source.originalComponent as AnyRecord)?.productSkeleton, ((source.originalComponent as AnyRecord)?.definition as AnyRecord)?.products, ((source.originalComponent as AnyRecord)?.definition as AnyRecord)?.productSkeleton, (((source.originalComponent as AnyRecord)?.definition as AnyRecord)?.structure as AnyRecord)?.products, (((source.originalComponent as AnyRecord)?.definition as AnyRecord)?.structure as AnyRecord)?.productSkeleton, ] const structuralProducts = structuralCandidates .flatMap((candidate) => { if (Array.isArray(candidate)) return candidate if (candidate && typeof candidate === 'object' && Array.isArray((candidate as AnyRecord).products)) { return (candidate as AnyRecord).products as unknown[] } return [] }) .filter((entry) => entry && typeof entry === 'object') const structuralProduct = structuralProducts.length ? (structuralProducts[0] as AnyRecord) : null const structuralFamilyCode = (structuralProduct && typeof structuralProduct.familyCode === 'string' ? structuralProduct.familyCode : null) || (typeof source.familyCode === 'string' ? source.familyCode : null) if (!fallbackName && structuralProduct) { fallbackName = (structuralProduct.typeProductLabel as string) || ((structuralProduct.typeProduct as AnyRecord)?.name as string) || (structuralProduct.reference as string) || (structuralFamilyCode ? `Famille ${structuralFamilyCode}` : null) || null } if (!fallbackReference && structuralProduct?.reference) { fallbackReference = structuralProduct.reference as string } if (!fallbackCategory) { fallbackCategory = (structuralProduct?.typeProductLabel as string) || ((structuralProduct?.typeProduct as AnyRecord)?.name as string) || (structuralFamilyCode ? `Famille ${structuralFamilyCode}` : null) || null } if (!fallbackSuppliers && structuralProduct?.supplierLabel) { fallbackSuppliers = structuralProduct.supplierLabel as string } if (!fallbackSuppliers && Array.isArray(structuralProduct?.constructeurs)) { const supplierNames = (structuralProduct!.constructeurs as AnyRecord[]) .map((c) => c?.name as string) .filter((name) => typeof name === 'string' && name.trim().length > 0) if (supplierNames.length) fallbackSuppliers = supplierNames.join(', ') } if (!fallbackPrice && structuralProduct?.priceLabel) fallbackPrice = structuralProduct.priceLabel as string if (!fallbackPrice && structuralProduct?.price) fallbackPrice = structuralProduct.price as string | number if (fallbackName || fallbackReference || fallbackCategory || fallbackSuppliers || fallbackPrice) { return { name: fallbackName || 'Produit catalogue', reference: fallbackReference, category: fallbackCategory, suppliers: fallbackSuppliers, price: typeof fallbackPrice === 'number' ? `${fallbackPrice.toFixed(2)} €` : (fallbackPrice as string) || null, } } return null } // --------------------------------------------------------------------------- // Parent link identifiers // --------------------------------------------------------------------------- export const extractParentLinkIdentifiers = (source: AnyRecord | null | undefined): AnyRecord => { if (!source || typeof source !== 'object') return {} const identifiers: AnyRecord = {} const idKeys = [ 'parentRequirementId', 'parentComponentRequirementId', 'parentPieceRequirementId', 'parentMachineComponentRequirementId', 'parentMachinePieceRequirementId', 'parentLinkId', 'parentComponentLinkId', 'parentPieceLinkId', 'parentComponentId', 'parentPieceId', ] idKeys.forEach((key) => { if (Object.prototype.hasOwnProperty.call(source, key)) { const value = source[key] if (value !== undefined && value !== null && value !== '') { identifiers[key] = value } } }) const objectKeys = [ 'parentRequirement', 'parentComponentRequirement', 'parentPieceRequirement', 'parentMachineComponentRequirement', 'parentMachinePieceRequirement', ] objectKeys.forEach((key) => { const value = source[key] if (isPlainObject(value) && value.id !== undefined && value.id !== null && value.id !== '') { const idKey = `${key}Id` if (!Object.prototype.hasOwnProperty.call(identifiers, idKey)) { identifiers[idKey] = value.id } } }) return identifiers }