fix(custom-fields) : context fields display, batch save, hierarchy propagation and UniqueEntity fix

- ComponentItem/PieceItem: DaisyUI divider, emit context-field-update for batch save
- CustomFieldDisplay: support editable/emit-blur/title/show-header props
- MachineComponentsCard/MachinePiecesCard: propagate custom-field-update events
- useMachineDetailCustomFields: pendingContextFieldUpdates + saveAllContextCustomFields
- useMachineDetailData: wire context field save into submitEdition
- useMachineDetailUpdates: only PATCH changed machine fields
- useMachineHierarchy: propagate contextCustomFields/Values from link to nodes
- componentStructure: include machineContextOnly in normalizeStructureForEditor
- Machine entity: convert empty reference to null, ignoreNull on UniqueEntity

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-03 13:46:39 +02:00
parent c54e5c33f2
commit 0049638e3c
14 changed files with 217 additions and 91 deletions

View File

@@ -44,6 +44,7 @@ export function useMachineDetailCustomFields(deps: MachineDetailCustomFieldsDeps
// ---------------------------------------------------------------------------
const machineCustomFields = ref<AnyRecord[]>([])
const pendingContextFieldUpdates = ref<AnyRecord[]>([])
// ---------------------------------------------------------------------------
// Computed
@@ -380,6 +381,83 @@ export function useMachineDetailCustomFields(deps: MachineDetailCustomFieldsDeps
}
}
const handleCustomFieldUpdate = async (fieldUpdate: AnyRecord) => {
if (fieldUpdate?.entityType && fieldUpdate?.entityId) {
queueContextFieldUpdate(fieldUpdate)
return
}
await updatePieceCustomField(fieldUpdate)
}
const queueContextFieldUpdate = (fieldUpdate: AnyRecord) => {
const entityType = fieldUpdate.entityType as string | undefined
const entityId = fieldUpdate.entityId as string | undefined
const fieldId = fieldUpdate.fieldId as string | undefined
const customFieldValueId = fieldUpdate.customFieldValueId as string | undefined
if (!entityType || !entityId || (!fieldId && !customFieldValueId)) return
const nextUpdate = {
entityType,
entityId,
fieldId,
customFieldValueId,
value: fieldUpdate.value ?? '',
fieldName: fieldUpdate.fieldName ?? 'Champ contextuel',
}
const existingIndex = pendingContextFieldUpdates.value.findIndex(
(item) =>
item.entityType === entityType &&
item.entityId === entityId &&
item.fieldId === fieldId &&
item.customFieldValueId === customFieldValueId,
)
if (existingIndex >= 0) {
pendingContextFieldUpdates.value[existingIndex] = nextUpdate
return
}
pendingContextFieldUpdates.value.push(nextUpdate)
}
const clearPendingContextFieldUpdates = () => {
pendingContextFieldUpdates.value = []
}
const saveAllContextCustomFields = async () => {
const updates = pendingContextFieldUpdates.value.slice()
if (!updates.length) return
try {
for (const update of updates) {
if (update.customFieldValueId) {
await updateCustomFieldValueApi(update.customFieldValueId as string, {
value: update.value ?? '',
} as any)
continue
}
if (!update.fieldId) {
continue
}
await upsertCustomFieldValue(
update.fieldId as string,
update.entityType as string,
update.entityId as string,
update.value ?? '',
)
}
clearPendingContextFieldUpdates()
} catch (error) {
console.error('Erreur lors de la sauvegarde batch des champs contextuels:', error)
throw error
}
}
const saveAllMachineCustomFields = async () => {
if (!machine.value) return
@@ -435,6 +513,7 @@ export function useMachineDetailCustomFields(deps: MachineDetailCustomFieldsDeps
return {
// State
machineCustomFields,
pendingContextFieldUpdates,
// Computed
visibleMachineCustomFields,
@@ -448,6 +527,10 @@ export function useMachineDetailCustomFields(deps: MachineDetailCustomFieldsDeps
setMachineCustomFieldValue,
updateMachineCustomField,
updatePieceCustomField,
handleCustomFieldUpdate,
queueContextFieldUpdate,
clearPendingContextFieldUpdates,
saveAllMachineCustomFields,
saveAllContextCustomFields,
}
}