perf(edit-pages) : reduce blocking API calls on edit pages
- Remove redundant getCustomFieldValuesByEntity() calls (use entity response) - Remove redundant refreshDocuments() from onMounted (docs already in entity) - Make loadHistory() non-blocking (fire-and-forget) - Defer bulk catalog loads on component edit (pieces/products/composants) - Use pieceTypes cache instead of separate getModelType() call on piece edit - Try embedded typeProduct from entity response on product edit Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -576,7 +576,7 @@ const { updateComposant, loadComposants, composants: componentCatalogRef } = use
|
||||
const { pieces, loadPieces } = usePieces()
|
||||
const { products, loadProducts } = useProducts()
|
||||
const { ensureConstructeurs } = useConstructeurs()
|
||||
const { upsertCustomFieldValue, updateCustomFieldValue, getCustomFieldValuesByEntity } = useCustomFields()
|
||||
const { upsertCustomFieldValue, updateCustomFieldValue } = useCustomFields()
|
||||
const toast = useToast()
|
||||
const { loadDocumentsByComponent, uploadDocuments, deleteDocument } = useDocuments()
|
||||
const {
|
||||
@@ -764,12 +764,10 @@ const fetchComponent = async () => {
|
||||
component.value = result.data
|
||||
componentDocuments.value = Array.isArray(result.data?.documents) ? result.data.documents : []
|
||||
|
||||
const customValues = await getCustomFieldValuesByEntity('composant', result.data.id)
|
||||
if (customValues.success && Array.isArray(customValues.data)) {
|
||||
component.value.customFieldValues = customValues.data
|
||||
refreshCustomFieldInputs(undefined, customValues.data)
|
||||
}
|
||||
await loadHistory(result.data.id)
|
||||
const customValues = Array.isArray(result.data?.customFieldValues) ? result.data.customFieldValues : []
|
||||
refreshCustomFieldInputs(undefined, customValues)
|
||||
|
||||
loadHistory(result.data.id).catch(() => {})
|
||||
} else {
|
||||
component.value = null
|
||||
componentDocuments.value = []
|
||||
@@ -1130,14 +1128,15 @@ onMounted(async () => {
|
||||
loadComponentTypes(),
|
||||
loadPieceTypes(),
|
||||
loadProductTypes(),
|
||||
loadPieces({ itemsPerPage: 500 }),
|
||||
loadProducts({ itemsPerPage: 500, force: true }),
|
||||
loadComposants({ itemsPerPage: 500 }),
|
||||
fetchComponent(),
|
||||
])
|
||||
loading.value = false
|
||||
if (component.value?.id) {
|
||||
await refreshDocuments()
|
||||
}
|
||||
|
||||
// Defer bulk catalog loads — not needed for initial render
|
||||
Promise.allSettled([
|
||||
loadPieces({ itemsPerPage: 500 }),
|
||||
loadProducts({ itemsPerPage: 500 }),
|
||||
loadComposants({ itemsPerPage: 500 }),
|
||||
]).catch(() => {})
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -516,7 +516,7 @@ const router = useRouter()
|
||||
const { get } = useApi()
|
||||
const { pieceTypes, loadPieceTypes } = usePieceTypes()
|
||||
const { updatePiece } = usePieces()
|
||||
const { upsertCustomFieldValue, updateCustomFieldValue, getCustomFieldValuesByEntity } = useCustomFields()
|
||||
const { upsertCustomFieldValue, updateCustomFieldValue } = useCustomFields()
|
||||
const toast = useToast()
|
||||
const { loadDocumentsByPiece, uploadDocuments, deleteDocument } = useDocuments()
|
||||
const { ensureConstructeurs } = useConstructeurs()
|
||||
@@ -750,20 +750,23 @@ const fetchPiece = async () => {
|
||||
if (result.success) {
|
||||
piece.value = result.data
|
||||
pieceDocuments.value = Array.isArray(result.data?.documents) ? result.data.documents : []
|
||||
const customValues = await getCustomFieldValuesByEntity('piece', result.data.id)
|
||||
if (customValues.success && Array.isArray(customValues.data)) {
|
||||
piece.value.customFieldValues = customValues.data
|
||||
refreshCustomFieldInputs(undefined, customValues.data)
|
||||
}
|
||||
await loadPieceTypeDetails(result.data)
|
||||
await loadHistory(result.data.id)
|
||||
|
||||
// Use customFieldValues from entity response (enriched with customField definitions via serialization groups)
|
||||
const customValues = Array.isArray(result.data?.customFieldValues) ? result.data.customFieldValues : []
|
||||
refreshCustomFieldInputs(undefined, customValues)
|
||||
|
||||
// Use cached type from loadPieceTypes() instead of separate getModelType() call
|
||||
loadPieceTypeDetailsFromCache(result.data)
|
||||
|
||||
// History is non-blocking — template handles its own loading state
|
||||
loadHistory(result.data.id).catch(() => {})
|
||||
} else {
|
||||
piece.value = null
|
||||
pieceDocuments.value = []
|
||||
}
|
||||
}
|
||||
|
||||
const loadPieceTypeDetails = async (currentPiece: any) => {
|
||||
const loadPieceTypeDetailsFromCache = (currentPiece: any) => {
|
||||
const typeId = currentPiece?.typePieceId
|
||||
|| extractRelationId(currentPiece?.typePiece)
|
||||
|| ''
|
||||
@@ -771,15 +774,22 @@ const loadPieceTypeDetails = async (currentPiece: any) => {
|
||||
pieceTypeDetails.value = null
|
||||
return
|
||||
}
|
||||
try {
|
||||
const type = await getModelType(typeId)
|
||||
// Look up in the already-loaded pieceTypes cache (from loadPieceTypes in onMounted)
|
||||
const cachedType = (pieceTypes.value || []).find((t: any) => t.id === typeId) ?? null
|
||||
if (cachedType) {
|
||||
pieceTypeDetails.value = cachedType
|
||||
refreshCustomFieldInputs((cachedType.structure as PieceModelStructure | null) ?? null, currentPiece?.customFieldValues ?? null)
|
||||
return
|
||||
}
|
||||
// Fallback: fetch if not in cache (edge case)
|
||||
getModelType(typeId).then((type) => {
|
||||
if (type && typeof type === 'object') {
|
||||
pieceTypeDetails.value = type
|
||||
refreshCustomFieldInputs((type.structure as PieceModelStructure | null) ?? null, currentPiece?.customFieldValues ?? null)
|
||||
}
|
||||
} catch (_error) {
|
||||
}).catch(() => {
|
||||
pieceTypeDetails.value = null
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
let initialized = false
|
||||
@@ -920,8 +930,5 @@ const submitEdition = async () => {
|
||||
onMounted(async () => {
|
||||
await Promise.allSettled([loadPieceTypes(), fetchPiece()])
|
||||
loading.value = false
|
||||
if (piece.value?.id) {
|
||||
await refreshDocuments()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -428,7 +428,7 @@ const route = useRoute()
|
||||
const router = useRouter()
|
||||
const toast = useToast()
|
||||
const { getProduct, updateProduct } = useProducts()
|
||||
const { upsertCustomFieldValue, updateCustomFieldValue, getCustomFieldValuesByEntity } = useCustomFields()
|
||||
const { upsertCustomFieldValue, updateCustomFieldValue } = useCustomFields()
|
||||
const {
|
||||
loadDocumentsByProduct,
|
||||
uploadDocuments: uploadProductDocuments,
|
||||
@@ -520,15 +520,17 @@ const loadProduct = async () => {
|
||||
if (result.success && result.data) {
|
||||
product.value = result.data
|
||||
productDocuments.value = Array.isArray(result.data.documents) ? result.data.documents : []
|
||||
|
||||
await loadProductType()
|
||||
const customValues = await getCustomFieldValuesByEntity('product', result.data.id)
|
||||
if (customValues.success && Array.isArray(customValues.data)) {
|
||||
product.value.customFieldValues = customValues.data
|
||||
refreshCustomFieldInputs(undefined, customValues.data)
|
||||
}
|
||||
await hydrateForm()
|
||||
await refreshDocuments()
|
||||
await loadHistory(result.data.id)
|
||||
|
||||
// Use customFieldValues from entity response (enriched with customField definitions via serialization groups)
|
||||
const customValues = Array.isArray(result.data?.customFieldValues) ? result.data.customFieldValues : []
|
||||
refreshCustomFieldInputs(undefined, customValues)
|
||||
|
||||
hydrateForm()
|
||||
|
||||
// History is non-blocking — template handles its own loading state
|
||||
loadHistory(result.data.id).catch(() => {})
|
||||
} else {
|
||||
product.value = null
|
||||
}
|
||||
@@ -587,9 +589,20 @@ const handleFilesAdded = async (files: File[]) => {
|
||||
}
|
||||
|
||||
const loadProductType = async () => {
|
||||
// Try using the expanded typeProduct from entity response first
|
||||
const embedded = product.value?.typeProduct
|
||||
if (embedded && typeof embedded === 'object' && embedded.id) {
|
||||
const embeddedStructure = embedded.structure ?? embedded.productSkeleton ?? null
|
||||
if (embeddedStructure) {
|
||||
productType.value = embedded
|
||||
structure.value = normalizeProductStructureForSave(embeddedStructure)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (!product.value?.typeProductId) {
|
||||
productType.value = product.value?.typeProduct ?? null
|
||||
structure.value = normalizeProductStructureForSave(productType.value?.structure ?? null)
|
||||
productType.value = embedded ?? null
|
||||
structure.value = normalizeProductStructureForSave(embedded?.structure ?? null)
|
||||
return
|
||||
}
|
||||
try {
|
||||
@@ -598,12 +611,12 @@ const loadProductType = async () => {
|
||||
structure.value = normalizeProductStructureForSave(type?.structure ?? type?.productSkeleton ?? null)
|
||||
} catch (error) {
|
||||
console.error('Erreur lors du chargement du type de produit:', error)
|
||||
productType.value = product.value?.typeProduct ?? null
|
||||
structure.value = normalizeProductStructureForSave(productType.value?.structure ?? null)
|
||||
productType.value = embedded ?? null
|
||||
structure.value = normalizeProductStructureForSave(embedded?.structure ?? null)
|
||||
}
|
||||
}
|
||||
|
||||
const hydrateForm = async () => {
|
||||
const hydrateForm = () => {
|
||||
if (!product.value) {
|
||||
return
|
||||
}
|
||||
@@ -618,7 +631,8 @@ const hydrateForm = async () => {
|
||||
: ''
|
||||
refreshCustomFieldInputs(structure.value, product.value.customFieldValues)
|
||||
if (editionForm.constructeurIds.length) {
|
||||
await ensureConstructeurs(editionForm.constructeurIds)
|
||||
// Smart-cached + deduped — fire-and-forget, ConstructeurSelect handles its own loading
|
||||
ensureConstructeurs(editionForm.constructeurIds).catch(() => {})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user