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

@@ -8,14 +8,12 @@
import { ref, computed } from 'vue'
import { useCustomFields } from '~/composables/useCustomFields'
import { useToast } from '~/composables/useToast'
import { normalizeStructureForEditor } from '~/shared/modelUtils'
import {
shouldDisplayCustomField,
normalizeExistingCustomFieldDefinitions,
normalizeCustomFieldValueEntry,
mergeCustomFieldValuesWithDefinitions,
dedupeCustomFieldEntries,
} from '~/shared/utils/customFieldUtils'
mergeDefinitionsWithValues,
filterByContext,
hasDisplayableValue,
type CustomFieldInput,
} from '~/shared/utils/customFields'
import {
resolveConstructeurs,
uniqueConstructeurIds,
@@ -53,56 +51,23 @@ export function useMachineDetailCustomFields(deps: MachineDetailCustomFieldsDeps
const visibleMachineCustomFields = computed(() => {
const fields = Array.isArray(machineCustomFields.value) ? machineCustomFields.value : []
if (isEditMode.value) return fields
return fields.filter((field) => shouldDisplayCustomField(field))
return fields.filter((field) => hasDisplayableValue(field as unknown as CustomFieldInput))
})
// ---------------------------------------------------------------------------
// Transform helpers
// ---------------------------------------------------------------------------
const getStructureCustomFields = (structure: unknown): AnyRecord[] => {
if (!structure || typeof structure !== 'object') return []
const normalized = normalizeStructureForEditor(structure as any) as any
return Array.isArray(normalized?.customFields)
? (normalized.customFields as AnyRecord[])
: []
}
const transformCustomFields = (piecesData: AnyRecord[]): AnyRecord[] => {
return (piecesData || []).map((piece) => {
const typePiece = (piece.typePiece as AnyRecord) || {}
const normalizeStructureDefs = (structure: unknown) =>
structure ? normalizeStructureForEditor(structure as AnyRecord) : null
const normalizedStructureDefs = [
normalizeStructureDefs((piece.definition as AnyRecord)?.structure),
normalizeStructureDefs(typePiece.structure),
]
const valueEntries = [
...(Array.isArray(piece.customFieldValues) ? piece.customFieldValues : []),
...(Array.isArray(piece.customFields)
? (piece.customFields as AnyRecord[])
.map(normalizeCustomFieldValueEntry)
.filter((e) => e !== null)
: []),
...(Array.isArray(typePiece.customFieldValues)
? (typePiece.customFieldValues as AnyRecord[])
.map(normalizeCustomFieldValueEntry)
.filter((e) => e !== null)
: []),
]
const customFields = dedupeCustomFieldEntries(
mergeCustomFieldValuesWithDefinitions(
valueEntries,
normalizeExistingCustomFieldDefinitions(piece.customFields),
normalizeExistingCustomFieldDefinitions((piece.definition as AnyRecord)?.customFields),
normalizeExistingCustomFieldDefinitions((piece.typePiece as AnyRecord)?.customFields),
normalizeExistingCustomFieldDefinitions(typePiece.customFields),
...normalizedStructureDefs.map((def) => getStructureCustomFields(def)),
const customFields = filterByContext(
mergeDefinitionsWithValues(
typePiece.customFields ?? (piece.typePiece as AnyRecord)?.customFields ?? [],
piece.customFieldValues ?? [],
),
'standalone',
)
const constructeurIds = uniqueConstructeurIds(
@@ -159,43 +124,16 @@ export function useMachineDetailCustomFields(deps: MachineDetailCustomFieldsDeps
}
const transformComponentCustomFields = (componentsData: AnyRecord[]): AnyRecord[] => {
const normalizeStructureDefs = (structure: unknown) =>
structure ? normalizeStructureForEditor(structure as AnyRecord) : null
return (componentsData || []).map((component) => {
const type = (component.typeComposant as AnyRecord) || {}
const normalizedStructureDefs = [
normalizeStructureDefs((component.definition as AnyRecord)?.structure),
normalizeStructureDefs(type.structure),
]
const actualComponent = (component.originalComposant as AnyRecord) || component
const valueEntries = [
...(Array.isArray(component.customFieldValues) ? component.customFieldValues : []),
...(Array.isArray(component.customFields)
? (component.customFields as AnyRecord[])
.map(normalizeCustomFieldValueEntry)
.filter((e) => e !== null)
: []),
...(Array.isArray(actualComponent?.customFields)
? (actualComponent.customFields as AnyRecord[])
.map(normalizeCustomFieldValueEntry)
.filter((e) => e !== null)
: []),
]
const customFields = dedupeCustomFieldEntries(
mergeCustomFieldValuesWithDefinitions(
valueEntries,
normalizeExistingCustomFieldDefinitions(component.customFields),
normalizeExistingCustomFieldDefinitions((component.definition as AnyRecord)?.customFields),
normalizeExistingCustomFieldDefinitions((component.typeComposant as AnyRecord)?.customFields),
normalizeExistingCustomFieldDefinitions(type.customFields),
normalizeExistingCustomFieldDefinitions(actualComponent?.customFields),
...normalizedStructureDefs.map((def) => getStructureCustomFields(def)),
const customFields = filterByContext(
mergeDefinitionsWithValues(
type.customFields ?? [],
component.customFieldValues ?? actualComponent?.customFieldValues ?? [],
),
'standalone',
)
const piecesTransformed = component.pieces
@@ -271,21 +209,11 @@ export function useMachineDetailCustomFields(deps: MachineDetailCustomFieldsDeps
machineCustomFields.value = []
return
}
const valueEntries = [
...(Array.isArray(machine.value.customFieldValues) ? machine.value.customFieldValues : []),
...(Array.isArray(machine.value.customFields)
? (machine.value.customFields as AnyRecord[])
.map(normalizeCustomFieldValueEntry)
.filter((e) => e !== null)
: []),
]
const merged = dedupeCustomFieldEntries(
mergeCustomFieldValuesWithDefinitions(
valueEntries,
normalizeExistingCustomFieldDefinitions(machine.value.customFields),
),
).map((field: AnyRecord) => ({ ...field, readOnly: false }))
machineCustomFields.value = merged
const merged = mergeDefinitionsWithValues(
machine.value?.customFields ?? [],
machine.value?.customFieldValues ?? [],
)
machineCustomFields.value = merged.map(f => ({ ...f, readOnly: false }))
}
const setMachineCustomFieldValue = (field: AnyRecord, value: unknown) => {
@@ -302,7 +230,8 @@ export function useMachineDetailCustomFields(deps: MachineDetailCustomFieldsDeps
const updateMachineCustomField = async (field: AnyRecord) => {
if (!machine.value || !field) return
const { id: customFieldId, customFieldValueId } = field
const customFieldId = (field.customFieldId ?? field.id) as string | undefined
const customFieldValueId = field.customFieldValueId as string | undefined
const fieldLabel = (field.name as string) || 'Champ personnalisé'
try {
@@ -467,7 +396,8 @@ export function useMachineDetailCustomFields(deps: MachineDetailCustomFieldsDeps
)
for (const field of fieldsToSave) {
const { id: customFieldId, customFieldValueId } = field
const customFieldId = (field.customFieldId ?? field.id) as string | undefined
const customFieldValueId = field.customFieldValueId as string | undefined
try {
if (customFieldValueId) {