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>
This commit is contained in:
Matthieu
2026-02-09 11:19:40 +01:00
parent e1594cab76
commit 519fa3a8f4
15 changed files with 1047 additions and 1883 deletions

View File

@@ -42,6 +42,7 @@ import { computed, onMounted, ref } from 'vue'
import { useHead, useRoute, useRouter } from '#imports'
import ModelTypeForm from '~/components/model-types/ModelTypeForm.vue'
import { getModelType, updateModelType, type ModelTypePayload } from '~/services/modelTypes'
import type { ComponentModelStructure } from '~/shared/types/inventory'
import { useCategoryEditGuard } from '~/composables/useCategoryEditGuard'
import { useToast } from '~/composables/useToast'
@@ -108,7 +109,7 @@ const loadCategory = async () => {
code: response.code,
category: response.category,
notes: response.notes ?? response.description ?? '',
structure: response.structure ?? undefined,
structure: (response.structure as ComponentModelStructure | null) ?? undefined,
}
await loadLinkedCount(id)

View File

@@ -558,7 +558,6 @@ import {
import {
historyActionLabel,
formatHistoryDate,
formatHistoryValue,
historyDiffEntries as _historyDiffEntries,
} from '~/shared/utils/historyDisplayUtils'
@@ -709,7 +708,7 @@ const refreshDocuments = async () => {
try {
const result = await loadDocumentsByComponent(component.value.id, { updateStore: false })
if (result.success) {
componentDocuments.value = result.data || []
componentDocuments.value = Array.isArray(result.data) ? result.data : result.data ? [result.data] : []
}
} finally {
loadingDocuments.value = false
@@ -851,8 +850,8 @@ const submitEdition = async () => {
saving.value = true
try {
const result = await updateComposant(component.value.id, payload)
if (result.success) {
const updatedComponent = result.data
if (result.success && result.data) {
const updatedComponent = result.data as Record<string, any>
await _saveCustomFieldValues(
'composant',
updatedComponent.id,
@@ -925,13 +924,14 @@ const fetchPieceTypeNames = async (ids: string[]) => {
)
const next = { ...fetchedPieceTypeMap.value }
results.forEach((result, index) => {
if (result.status !== 'fulfilled') {
const key = missing[index]
if (!key || result.status !== 'fulfilled') {
return
}
const data = result.value?.data
const name = data?.name || data?.code
if (name) {
next[missing[index]] = name
next[key] = name
}
})
fetchedPieceTypeMap.value = next
@@ -968,13 +968,14 @@ const fetchProductTypeNames = async (ids: string[]) => {
)
const next = { ...fetchedProductTypeMap.value }
results.forEach((result, index) => {
if (result.status !== 'fulfilled') {
const key = missing[index]
if (!key || result.status !== 'fulfilled') {
return
}
const data = result.value?.data
const name = data?.name || data?.code
if (name) {
next[missing[index]] = name
next[key] = name
}
})
fetchedProductTypeMap.value = next

View File

@@ -813,13 +813,14 @@ const fetchPieceTypeNames = async (ids: string[]) => {
)
const next = { ...fetchedPieceTypeMap.value }
results.forEach((result, index) => {
if (result.status !== 'fulfilled') {
const key = missing[index]
if (!key || result.status !== 'fulfilled') {
return
}
const data = result.value?.data
const name = data?.name || data?.code
if (name) {
next[missing[index]] = name
next[key] = name
}
})
fetchedPieceTypeMap.value = next
@@ -1144,7 +1145,7 @@ const resolveOptions = (field: any): string[] => {
return option.trim()
}
if (typeof option === 'object') {
const record = option || {}
const record = option as Record<string, unknown>
const keys = ['value', 'label', 'name']
for (const key of keys) {
const candidate = record[key]

View File

@@ -42,6 +42,7 @@ import { computed, onMounted, ref } from 'vue'
import { useHead, useRoute, useRouter } from '#imports'
import ModelTypeForm from '~/components/model-types/ModelTypeForm.vue'
import { getModelType, updateModelType, type ModelTypePayload } from '~/services/modelTypes'
import type { PieceModelStructure } from '~/shared/types/inventory'
import { useCategoryEditGuard } from '~/composables/useCategoryEditGuard'
import { useToast } from '~/composables/useToast'
@@ -106,7 +107,7 @@ const loadCategory = async () => {
code: response.code,
category: response.category,
notes: response.notes ?? response.description ?? '',
structure: response.structure ?? undefined,
structure: (response.structure as PieceModelStructure | null) ?? undefined,
}
await loadLinkedCount(id)

View File

@@ -503,7 +503,6 @@ import {
import {
historyActionLabel,
formatHistoryDate,
formatHistoryValue,
historyDiffEntries as _historyDiffEntries,
} from '~/shared/utils/historyDisplayUtils'
@@ -626,7 +625,7 @@ const refreshDocuments = async () => {
try {
const result = await loadDocumentsByPiece(piece.value.id, { updateStore: false })
if (result.success) {
pieceDocuments.value = result.data || []
pieceDocuments.value = Array.isArray(result.data) ? result.data : result.data ? [result.data] : []
}
} finally {
loadingDocuments.value = false
@@ -776,9 +775,9 @@ const loadPieceTypeDetails = async (currentPiece: any) => {
const type = await getModelType(typeId)
if (type && typeof type === 'object') {
pieceTypeDetails.value = type
refreshCustomFieldInputs(type.structure ?? null, currentPiece?.customFieldValues ?? null)
refreshCustomFieldInputs((type.structure as PieceModelStructure | null) ?? null, currentPiece?.customFieldValues ?? null)
}
} catch (error) {
} catch (_error) {
pieceTypeDetails.value = null
}
}
@@ -898,8 +897,8 @@ const submitEdition = async () => {
saving.value = true
try {
const result = await updatePiece(piece.value.id, payload)
if (result.success) {
const updatedPiece = result.data
if (result.success && result.data) {
const updatedPiece = result.data as Record<string, any>
await _saveCustomFieldValues(
'piece',
updatedPiece.id,

View File

@@ -543,22 +543,23 @@ const submitCreation = async () => {
submitting.value = true
try {
const result = await createPiece(payload)
if (result.success) {
if (result.success && result.data) {
const createdPiece = result.data as Record<string, any>
await _saveCustomFieldValues(
'piece',
result.data.id,
createdPiece.id,
[
result.data?.typePiece?.pieceCustomFields,
result.data?.typeMachinePieceRequirement?.typePiece?.pieceCustomFields,
createdPiece?.typePiece?.pieceCustomFields,
createdPiece?.typeMachinePieceRequirement?.typePiece?.pieceCustomFields,
],
{ customFieldInputs, upsertCustomFieldValue, updateCustomFieldValue, toast },
)
if (selectedDocuments.value.length && result.data?.id) {
if (selectedDocuments.value.length && createdPiece.id) {
uploadingDocuments.value = true
const uploadResult = await uploadDocuments(
{
files: selectedDocuments.value,
context: { pieceId: result.data.id },
context: { pieceId: createdPiece.id },
},
{ updateStore: false },
)

View File

@@ -42,6 +42,7 @@ import { computed, onMounted, ref } from 'vue'
import { useHead, useRoute, useRouter } from '#imports'
import ModelTypeForm from '~/components/model-types/ModelTypeForm.vue'
import { getModelType, updateModelType, type ModelTypePayload } from '~/services/modelTypes'
import type { ProductModelStructure } from '~/shared/types/inventory'
import { useCategoryEditGuard } from '~/composables/useCategoryEditGuard'
import { useToast } from '~/composables/useToast'
@@ -106,7 +107,7 @@ const loadCategory = async () => {
code: response.code,
category: response.category,
notes: response.notes ?? response.description ?? '',
structure: response.structure ?? undefined,
structure: (response.structure as ProductModelStructure | null) ?? undefined,
}
await loadLinkedCount(id)

View File

@@ -421,7 +421,6 @@ import {
import {
historyActionLabel,
formatHistoryDate,
formatHistoryValue,
historyDiffEntries as _historyDiffEntries,
} from '~/shared/utils/historyDisplayUtils'
@@ -518,9 +517,9 @@ const loadProduct = async () => {
return
}
const result = await getProduct(id)
if (result.success) {
if (result.success && result.data) {
product.value = result.data
productDocuments.value = Array.isArray(result.data?.documents) ? result.data.documents : []
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)) {