From 0049638e3c51d94ca4d23d03ab83c702ffe22425 Mon Sep 17 00:00:00 2001 From: r-dev Date: Fri, 3 Apr 2026 13:46:39 +0200 Subject: [PATCH] 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) --- frontend/app/components/ComponentItem.vue | 69 +++++++-------- frontend/app/components/PieceItem.vue | 68 +++++++-------- .../components/common/CustomFieldDisplay.vue | 35 ++++++-- .../machine/MachineComponentsCard.vue | 4 +- .../components/machine/MachinePiecesCard.vue | 2 + .../useMachineDetailCustomFields.ts | 83 +++++++++++++++++++ .../app/composables/useMachineDetailData.ts | 18 +++- .../composables/useMachineDetailUpdates.ts | 10 +-- .../app/composables/useMachineHierarchy.ts | 4 + frontend/app/pages/machine/[id].vue | 4 +- .../app/shared/model/componentStructure.ts | 1 + src/Controller/MaintenanceController.php | 2 +- src/Entity/Machine.php | 4 +- src/EventListener/MaintenanceModeListener.php | 4 +- 14 files changed, 217 insertions(+), 91 deletions(-) diff --git a/frontend/app/components/ComponentItem.vue b/frontend/app/components/ComponentItem.vue index cef9d28..d21fed9 100644 --- a/frontend/app/components/ComponentItem.vue +++ b/frontend/app/components/ComponentItem.vue @@ -200,21 +200,26 @@ :fields="displayedCustomFields" :is-edit-mode="isEditMode" :columns="2" + title="Champs personnalisés item" + :editable="false" @field-blur="updateComponentCustomField" /> - -
-

- Champs contextuels -

- +
@@ -323,7 +328,6 @@ import { parseConstructeurLinksFromApi, } from '~/shared/constructeurUtils' import { - formatSize, shouldInlinePdf, documentPreviewSrc, documentIcon, @@ -332,9 +336,12 @@ import { import { useEntityDocuments } from '~/composables/useEntityDocuments' import { useEntityProductDisplay } from '~/composables/useEntityProductDisplay' import { useEntityCustomFields } from '~/composables/useEntityCustomFields' -import { mergeFieldDefinitionsWithValues, dedupeMergedFields } from '~/shared/utils/entityCustomFieldLogic' -import { useCustomFields } from '~/composables/useCustomFields' -import { useToast } from '~/composables/useToast' +import { + mergeFieldDefinitionsWithValues, + dedupeMergedFields, + resolveCustomFieldId, + resolveFieldId, +} from '~/shared/utils/entityCustomFieldLogic' const props = defineProps({ component: { type: Object, required: true }, @@ -374,9 +381,6 @@ const { updateCustomField: updateComponentCustomField, } = useEntityCustomFields({ entity: () => props.component, entityType: 'composant' }) -const { upsertCustomFieldValue } = useCustomFields() -const { showSuccess, showError } = useToast() - const mergedContextFields = computed(() => { const definitions = props.component?.contextCustomFields ?? [] const values = props.component?.contextCustomFieldValues ?? [] @@ -386,26 +390,23 @@ const mergedContextFields = computed(() => { ) }) -const updateContextCustomField = async (field) => { +const queueContextCustomFieldUpdate = (field, value) => { const linkId = props.component?.linkId if (!linkId || !field) return - const customFieldId = field.customFieldId || field.customField?.id - if (!customFieldId) return + const customFieldId = resolveCustomFieldId(field) + const customFieldValueId = resolveFieldId(field) + if (!customFieldId && !customFieldValueId) return - const result = await upsertCustomFieldValue( - customFieldId, - 'machineComponentLink', - linkId, - field.value ?? '', - ) - - if (result.success) { - showSuccess(`Champ contextuel "${field.name || field.customField?.name}" mis à jour`) - } - else { - showError(`Erreur lors de la mise à jour du champ contextuel`) - } + field.value = value + emit('custom-field-update', { + entityType: 'machineComponentLink', + entityId: linkId, + fieldId: customFieldId, + customFieldValueId, + value: value ?? '', + fieldName: field.name || field.customField?.name || 'Champ contextuel', + }) } // --- Document edit modal --- diff --git a/frontend/app/components/PieceItem.vue b/frontend/app/components/PieceItem.vue index e79ba80..c715deb 100644 --- a/frontend/app/components/PieceItem.vue +++ b/frontend/app/components/PieceItem.vue @@ -241,22 +241,27 @@ - -
-

- Champs contextuels -

- -
+
@@ -316,13 +321,14 @@ import { } from '~/shared/constructeurUtils' import { resolveFieldId, - resolveFieldReadOnly, mergeFieldDefinitionsWithValues, dedupeMergedFields + resolveFieldReadOnly, + resolveCustomFieldId, + mergeFieldDefinitionsWithValues, + dedupeMergedFields, } from '~/shared/utils/entityCustomFieldLogic' import { useEntityDocuments } from '~/composables/useEntityDocuments' import { useEntityProductDisplay } from '~/composables/useEntityProductDisplay' import { useEntityCustomFields } from '~/composables/useEntityCustomFields' -import { useCustomFields } from '~/composables/useCustomFields' -import { useToast } from '~/composables/useToast' const props = defineProps({ piece: { type: Object, required: true }, @@ -390,9 +396,6 @@ const { updateCustomField, } = useEntityCustomFields({ entity: () => props.piece, entityType: 'piece' }) -const { upsertCustomFieldValue } = useCustomFields() -const { showSuccess, showError } = useToast() - const mergedContextFields = computed(() => { const definitions = props.piece?.contextCustomFields ?? [] const values = props.piece?.contextCustomFieldValues ?? [] @@ -402,26 +405,23 @@ const mergedContextFields = computed(() => { ) }) -const updateContextCustomField = async (field) => { +const queueContextCustomFieldUpdate = (field, value) => { const linkId = props.piece?.linkId if (!linkId || !field) return - const customFieldId = field.customFieldId || field.customField?.id - if (!customFieldId) return + const customFieldId = resolveCustomFieldId(field) + const customFieldValueId = resolveFieldId(field) + if (!customFieldId && !customFieldValueId) return - const result = await upsertCustomFieldValue( - customFieldId, - 'machinePieceLink', - linkId, - field.value ?? '', - ) - - if (result.success) { - showSuccess(`Champ contextuel "${field.name || field.customField?.name}" mis à jour`) - } - else { - showError(`Erreur lors de la mise à jour du champ contextuel`) - } + field.value = value + emit('custom-field-update', { + entityType: 'machinePieceLink', + entityId: linkId, + fieldId: customFieldId, + customFieldValueId, + value: value ?? '', + fieldName: field.name || field.customField?.name || 'Champ contextuel', + }) } // --- Document edit modal --- diff --git a/frontend/app/components/common/CustomFieldDisplay.vue b/frontend/app/components/common/CustomFieldDisplay.vue index 30e04c7..5dcab65 100644 --- a/frontend/app/components/common/CustomFieldDisplay.vue +++ b/frontend/app/components/common/CustomFieldDisplay.vue @@ -1,10 +1,10 @@