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:
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -151,13 +151,18 @@ export function useMachineDetailData(machineId: string) {
|
||||
const {
|
||||
machineCustomFields,
|
||||
visibleMachineCustomFields,
|
||||
pendingContextFieldUpdates,
|
||||
transformCustomFields,
|
||||
transformComponentCustomFields,
|
||||
syncMachineCustomFields,
|
||||
setMachineCustomFieldValue,
|
||||
updateMachineCustomField,
|
||||
updatePieceCustomField,
|
||||
handleCustomFieldUpdate,
|
||||
queueContextFieldUpdate,
|
||||
clearPendingContextFieldUpdates,
|
||||
saveAllMachineCustomFields,
|
||||
saveAllContextCustomFields,
|
||||
} = useMachineDetailCustomFields({
|
||||
machine,
|
||||
isEditMode,
|
||||
@@ -333,10 +338,13 @@ export function useMachineDetailData(machineId: string) {
|
||||
// 2. Save all custom field values
|
||||
await saveAllMachineCustomFields()
|
||||
|
||||
// 3. Reload machine data to get fresh state
|
||||
// 3. Save contextual custom field values queued from piece/component inputs
|
||||
await saveAllContextCustomFields()
|
||||
|
||||
// 4. Reload machine data to get fresh state
|
||||
await loadMachineData()
|
||||
|
||||
// 4. Exit edit mode
|
||||
// 5. Exit edit mode
|
||||
isEditMode.value = false
|
||||
toast.showSuccess('Machine mise à jour avec succès')
|
||||
} catch (error) {
|
||||
@@ -350,6 +358,7 @@ export function useMachineDetailData(machineId: string) {
|
||||
const cancelEdition = () => {
|
||||
initMachineFields()
|
||||
syncMachineCustomFields()
|
||||
clearPendingContextFieldUpdates()
|
||||
constructeurLinks.value = originalConstructeurLinks.value.map(l => ({ ...l }))
|
||||
machineConstructeurIds.value = constructeurIdsFromLinks(constructeurLinks.value)
|
||||
isEditMode.value = false
|
||||
@@ -486,7 +495,7 @@ export function useMachineDetailData(machineId: string) {
|
||||
|
||||
// UI state
|
||||
machineDocumentFiles, machineDocumentsUploading, machineDocumentsLoaded,
|
||||
machineCustomFields, previewDocument, previewVisible,
|
||||
machineCustomFields, pendingContextFieldUpdates, previewDocument, previewVisible,
|
||||
isEditMode, debug,
|
||||
componentsCollapsed, collapseToggleToken, piecesCollapsed, pieceCollapseToggleToken,
|
||||
|
||||
@@ -499,7 +508,8 @@ export function useMachineDetailData(machineId: string) {
|
||||
findProductById, resolveProductReference, getProductDisplay,
|
||||
initMachineFields, getMachineFieldId,
|
||||
syncMachineCustomFields, setMachineCustomFieldValue,
|
||||
updateMachineCustomField, updatePieceCustomField,
|
||||
updateMachineCustomField, updatePieceCustomField, handleCustomFieldUpdate,
|
||||
queueContextFieldUpdate, clearPendingContextFieldUpdates, saveAllContextCustomFields,
|
||||
refreshMachineDocuments, handleMachineFilesAdded, removeMachineDocument,
|
||||
openPreview, closePreview,
|
||||
updateMachineInfo, updateComponent, updatePieceFromComponent,
|
||||
|
||||
@@ -73,11 +73,11 @@ export function useMachineDetailUpdates(deps: UseMachineDetailUpdatesDeps) {
|
||||
const updateMachineInfo = async () => {
|
||||
if (!machine.value) return
|
||||
try {
|
||||
const result: any = await updateMachineApi(machine.value.id as string, {
|
||||
name: machineName.value,
|
||||
reference: machineReference.value,
|
||||
siteId: machineSiteId.value || undefined,
|
||||
} as any)
|
||||
const payload: Record<string, unknown> = {}
|
||||
if (machineName.value !== machine.value.name) payload.name = machineName.value
|
||||
if (machineReference.value !== machine.value.reference) payload.reference = machineReference.value
|
||||
if ((machineSiteId.value || undefined) !== ((machine.value.siteId as string) || (machine.value.site as any)?.id || undefined)) payload.siteId = machineSiteId.value || undefined
|
||||
const result: any = await updateMachineApi(machine.value.id as string, payload as any)
|
||||
if (result.success) {
|
||||
const machinePayload =
|
||||
result.data?.machine && typeof result.data.machine === 'object'
|
||||
|
||||
@@ -208,6 +208,8 @@ export const buildMachineHierarchyFromLinks = (
|
||||
quantity: typeof link.quantity === 'number' ? link.quantity : 1,
|
||||
definition: appliedPiece.definition || originalPiece?.definition || {},
|
||||
customFields: appliedPiece.customFields || [],
|
||||
contextCustomFields: link.contextCustomFields || [],
|
||||
contextCustomFieldValues: link.contextCustomFieldValues || [],
|
||||
}
|
||||
|
||||
const resolvedProductId = resolveIdentifier(appliedPiece.productId, (appliedPiece.product as AnyRecord)?.id, link.productId, (link.product as AnyRecord)?.id, originalPiece?.productId, (originalPiece?.product as AnyRecord)?.id)
|
||||
@@ -335,6 +337,8 @@ export const buildMachineHierarchyFromLinks = (
|
||||
parentComposantId: resolveIdentifier(appliedComponent.parentComposantId, link.parentComponentId),
|
||||
definition: appliedComponent.definition || originalComponent?.definition || {},
|
||||
customFields: appliedComponent.customFields || [],
|
||||
contextCustomFields: link.contextCustomFields || [],
|
||||
contextCustomFieldValues: link.contextCustomFieldValues || [],
|
||||
pieces,
|
||||
subComponents,
|
||||
subcomponents: subComponents,
|
||||
|
||||
Reference in New Issue
Block a user