refactor(custom-fields) : unify 3 parallel implementations into 1 module

Replace ~2900 lines across 9 files with ~400 lines in 2 files:
- shared/utils/customFields.ts (types + pure helpers)
- composables/useCustomFieldInputs.ts (reactive composable)

Migrated all consumers:
- Backend: add defaultValue to API Platform serialization groups
- Standalone pages: component edit/create, piece edit/create, product edit/create/detail
- Machine page: MachineCustomFieldsCard, MachineInfoCard, useMachineDetailCustomFields
- Hierarchy: ComponentItem, PieceItem
- Shared: CustomFieldDisplay, CustomFieldInputGrid
- Category editor: componentStructure.ts

Deleted:
- entityCustomFieldLogic.ts (335 lines)
- customFieldUtils.ts (440 lines)
- customFieldFormUtils.ts (404 lines)
- useEntityCustomFields.ts (181 lines)
- customFieldFormUtils.test.ts

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-04 13:09:27 +02:00
parent f2eff89e00
commit 894d522036
25 changed files with 861 additions and 2279 deletions

View File

@@ -225,7 +225,6 @@ import { usePieceTypes } from '~/composables/usePieceTypes'
import { usePieces } from '~/composables/usePieces'
import { useToast } from '~/composables/useToast'
import { humanizeError } from '~/shared/utils/errorMessages'
import { useCustomFields } from '~/composables/useCustomFields'
import { useDocuments } from '~/composables/useDocuments'
import { useConstructeurLinks } from '~/composables/useConstructeurLinks'
import { useConstructeurs } from '~/composables/useConstructeurs'
@@ -243,12 +242,7 @@ import {
applyProductSelection,
collectNormalizedProductIds,
} from '~/shared/utils/pieceProductSelectionUtils'
import {
type CustomFieldInput,
normalizeCustomFieldInputs,
requiredCustomFieldsFilled as _requiredCustomFieldsFilled,
saveCustomFieldValues as _saveCustomFieldValues,
} from '~/shared/utils/customFieldFormUtils'
import { useCustomFieldInputs, type CustomFieldEntityType } from '~/composables/useCustomFieldInputs'
interface PieceCatalogType extends ModelType {
structure: PieceModelStructure | null
@@ -261,7 +255,6 @@ const router = useRouter()
const { pieceTypes, loadPieceTypes, loadingPieceTypes: loadingTypes } = usePieceTypes()
const { createPiece } = usePieces()
const toast = useToast()
const { upsertCustomFieldValue, updateCustomFieldValue } = useCustomFields()
const { uploadDocuments } = useDocuments()
const { syncLinks } = useConstructeurLinks()
const { getConstructeurById } = useConstructeurs()
@@ -281,7 +274,14 @@ const constructeurLinks = ref<ConstructeurLinkEntry[]>([])
const productSelections = ref<(string | null)[]>([])
const lastSuggestedName = ref('')
const customFieldInputs = ref<CustomFieldInput[]>([])
const cfDefinitions = ref<any[]>([])
const createdEntityId = ref<string | null>(null)
const { fields: customFieldInputs, requiredFilled: requiredCustomFieldsFilled, saveAll: saveAllCustomFields } = useCustomFieldInputs({
definitions: cfDefinitions,
values: [] as any[],
entityType: 'piece' as CustomFieldEntityType,
entityId: createdEntityId,
})
const selectedDocuments = ref<File[]>([])
const uploadingDocuments = ref(false)
@@ -360,21 +360,17 @@ watch(structureProducts, (products) => {
watch(selectedType, (type) => {
if (!type) {
clearCreationForm()
customFieldInputs.value = []
cfDefinitions.value = []
return
}
if (!creationForm.name || creationForm.name === lastSuggestedName.value) {
creationForm.name = type.name
}
lastSuggestedName.value = creationForm.name
customFieldInputs.value = normalizeCustomFieldInputs(type.structure)
cfDefinitions.value = type.structure?.customFields ?? []
productSelections.value = Array.from({ length: structureProducts.value.length }, () => null)
})
const requiredCustomFieldsFilled = computed(() =>
_requiredCustomFieldsFilled(customFieldInputs.value),
)
const canSubmit = computed(() =>
Boolean(
canEdit.value &&
@@ -450,14 +446,11 @@ const submitCreation = async () => {
const result = await createPiece(payload)
if (result.success && result.data) {
const createdPiece = result.data as Record<string, any>
await _saveCustomFieldValues(
'piece',
createdPiece.id,
[
createdPiece?.typePiece?.structure?.customFields,
],
{ customFieldInputs, upsertCustomFieldValue, updateCustomFieldValue, toast },
)
createdEntityId.value = createdPiece.id
const failedFields = await saveAllCustomFields()
if (failedFields.length) {
toast.showError(`Pièce créée, mais impossible d'enregistrer ${failedFields.length} champ(s): ${failedFields.join(', ')}`)
}
// Sync constructeur links after creation
if (constructeurLinks.value.length) {
await syncLinks('piece', createdPiece.id, [], constructeurLinks.value)