Files
Inventory_frontend/app/shared/utils/productDisplayUtils.ts

334 lines
11 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 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<string, unknown>
// ---------------------------------------------------------------------------
// 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.definition as AnyRecord)?.products,
((source.definition as AnyRecord)?.structure as AnyRecord)?.products,
(source.structure as AnyRecord)?.products,
(source.requirement as AnyRecord)?.products,
((source.requirement as AnyRecord)?.structure as AnyRecord)?.products,
(source.typeComposant as AnyRecord)?.products,
((source.typeComposant as AnyRecord)?.structure as AnyRecord)?.products,
(source.originalComposant as AnyRecord)?.products,
((source.originalComposant as AnyRecord)?.definition as AnyRecord)?.products,
(((source.originalComposant as AnyRecord)?.definition as AnyRecord)?.structure as AnyRecord)?.products,
(source.originalComponent as AnyRecord)?.products,
((source.originalComponent as AnyRecord)?.definition as AnyRecord)?.products,
(((source.originalComponent as AnyRecord)?.definition as AnyRecord)?.structure as AnyRecord)?.products,
]
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
}