Files
Inventory/app/composables/useEntityProductDisplay.ts
Matthieu 519fa3a8f4 refactor(components): extract shared entity utilities and simplify item components (F1.3, F1.4)
Extract 3 entity composables (useEntityCustomFields, useEntityDocuments,
useEntityProductDisplay) and entityCustomFieldLogic utility shared across
ComponentItem (1336→585 LOC) and PieceItem (1588→740 LOC).
Improve type safety in edit/create pages with explicit casts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-09 11:19:40 +01:00

104 lines
3.5 KiB
TypeScript

/**
* Reactive product display for entity items (ComponentItem, PieceItem).
*
* Resolves product information from entity.product, entity.__productDisplay,
* or a selectedProduct ref, and produces display-ready computed properties.
*/
import { computed, type Ref } from 'vue'
const currencyFormatter = new Intl.NumberFormat('fr-FR', {
style: 'currency',
currency: 'EUR',
currencyDisplay: 'narrowSymbol',
})
function buildProductDisplay(product: any) {
if (!product || typeof product !== 'object') return null
const suppliers = Array.isArray(product.constructeurs)
? product.constructeurs
.map((c: any) => c?.name)
.filter((name: any) => typeof name === 'string' && name.trim().length > 0)
.join(', ')
: product.supplierLabel || null
const priceValue = product.supplierPrice ?? product.price ?? product.priceLabel ?? product.priceDisplay ?? null
let price: string | null = null
if (priceValue !== null && priceValue !== undefined) {
const parsed = Number(priceValue)
if (!Number.isNaN(parsed)) {
price = currencyFormatter.format(parsed)
} else if (typeof priceValue === 'string' && priceValue.trim().length > 0) {
price = priceValue
}
}
return {
name: product.name || product.label || product.reference || product.productName || null,
reference: product.reference || null,
category: product.typeProduct?.name || product.category || null,
suppliers,
price,
}
}
export interface EntityProductDisplayDeps {
entity: () => any
selectedProduct?: Ref<any>
}
export function useEntityProductDisplay(deps: EntityProductDisplayDeps) {
const { entity, selectedProduct } = deps
const displayProduct = computed(() => {
// Priority: selectedProduct (for PieceItem) → entity.product → entity.__productDisplay
if (selectedProduct?.value) {
const normalized = buildProductDisplay(selectedProduct.value)
if (normalized) return normalized
}
const explicit = entity().product || null
const normalized = buildProductDisplay(explicit)
if (normalized) return normalized
const fallback = entity().__productDisplay
if (fallback) {
return {
name: fallback.name || null,
reference: fallback.reference || null,
category: fallback.category || null,
suppliers: fallback.suppliers || null,
price: fallback.price || null,
}
}
return null
})
const displayProductName = computed(() => {
if (displayProduct.value?.name) return displayProduct.value.name
const e = entity()
return e.product?.name || e.productName || e.productLabel || null
})
const productInfoRows = computed(() => {
if (!displayProduct.value) return []
const rows: { label: string; value: string }[] = []
if (displayProduct.value.reference) rows.push({ label: 'Référence', value: displayProduct.value.reference })
if (displayProduct.value.price) rows.push({ label: 'Prix indicatif', value: displayProduct.value.price })
if (displayProduct.value.suppliers) rows.push({ label: 'Fournisseur(s)', value: displayProduct.value.suppliers })
if (displayProduct.value.category) rows.push({ label: 'Catégorie', value: displayProduct.value.category })
return rows
})
const productDocuments = computed(() => {
const product = selectedProduct?.value || entity().product || null
return Array.isArray(product?.documents) ? product.documents : []
})
return {
displayProduct,
displayProductName,
productInfoRows,
productDocuments,
}
}