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>
104 lines
3.5 KiB
TypeScript
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,
|
|
}
|
|
}
|