diff --git a/frontend/app/components/ComponentItem.vue b/frontend/app/components/ComponentItem.vue index d21fed9..cd324bf 100644 --- a/frontend/app/components/ComponentItem.vue +++ b/frontend/app/components/ComponentItem.vue @@ -335,13 +335,8 @@ import { } from '~/shared/utils/documentDisplayUtils' import { useEntityDocuments } from '~/composables/useEntityDocuments' import { useEntityProductDisplay } from '~/composables/useEntityProductDisplay' -import { useEntityCustomFields } from '~/composables/useEntityCustomFields' -import { - mergeFieldDefinitionsWithValues, - dedupeMergedFields, - resolveCustomFieldId, - resolveFieldId, -} from '~/shared/utils/entityCustomFieldLogic' +import { useCustomFields } from '~/composables/useCustomFields' +import { mergeDefinitionsWithValues } from '~/shared/utils/customFields' const props = defineProps({ component: { type: Object, required: true }, @@ -377,25 +372,81 @@ const { } = useEntityProductDisplay({ entity: () => props.component }) const { - displayedCustomFields, - updateCustomField: updateComponentCustomField, -} = useEntityCustomFields({ entity: () => props.component, entityType: 'composant' }) + updateCustomFieldValue: updateCustomFieldValueApi, + upsertCustomFieldValue, +} = useCustomFields() +const { showSuccess, showError } = useToast() +// Parent already pre-merges standalone custom fields into props.component.customFields +const displayedCustomFields = computed(() => { + const fields = props.component?.customFields + return Array.isArray(fields) ? fields.filter((f) => !f.machineContextOnly) : [] +}) + +const updateComponentCustomField = async (field) => { + if (!field || field.readOnly) return + + const e = props.component + const fieldValueId = field.customFieldValueId + + if (fieldValueId) { + const result = await updateCustomFieldValueApi(fieldValueId, { value: field.value ?? '' }) + if (result.success) { + showSuccess(`Champ "${field.name}" mis à jour avec succès`) + } else { + showError(`Erreur lors de la mise à jour du champ "${field.name}"`) + } + return + } + + if (!e?.id) { + showError('Impossible de créer la valeur pour ce champ') + return + } + + const metadata = field.customFieldId ? undefined : { + customFieldName: field.name, + customFieldType: field.type, + customFieldRequired: field.required, + customFieldOptions: field.options, + } + const result = await upsertCustomFieldValue( + field.customFieldId, + 'composant', + e.id, + field.value ?? '', + metadata, + ) + + if (result.success) { + const newValue = result.data + if (newValue?.id) { + field.customFieldValueId = newValue.id + field.value = newValue.value ?? field.value ?? '' + if (newValue.customField?.id) { + field.customFieldId = newValue.customField.id + } + } + showSuccess(`Champ "${field.name}" créé avec succès`) + } else { + showError(`Erreur lors de la sauvegarde du champ "${field.name}"`) + } +} + +// Context fields are NOT pre-merged — merge locally const mergedContextFields = computed(() => { const definitions = props.component?.contextCustomFields ?? [] const values = props.component?.contextCustomFieldValues ?? [] if (!definitions.length && !values.length) return [] - return dedupeMergedFields( - mergeFieldDefinitionsWithValues(definitions, values), - ) + return mergeDefinitionsWithValues(definitions, values) }) const queueContextCustomFieldUpdate = (field, value) => { const linkId = props.component?.linkId if (!linkId || !field) return - const customFieldId = resolveCustomFieldId(field) - const customFieldValueId = resolveFieldId(field) + const customFieldId = field.customFieldId + const customFieldValueId = field.customFieldValueId if (!customFieldId && !customFieldValueId) return field.value = value @@ -405,7 +456,7 @@ const queueContextCustomFieldUpdate = (field, value) => { fieldId: customFieldId, customFieldValueId, value: value ?? '', - fieldName: field.name || field.customField?.name || 'Champ contextuel', + fieldName: field.name || 'Champ contextuel', }) } diff --git a/frontend/app/components/PieceItem.vue b/frontend/app/components/PieceItem.vue index c715deb..1bb461d 100644 --- a/frontend/app/components/PieceItem.vue +++ b/frontend/app/components/PieceItem.vue @@ -319,16 +319,10 @@ import { uniqueConstructeurIds, parseConstructeurLinksFromApi, } from '~/shared/constructeurUtils' -import { - resolveFieldId, - resolveFieldReadOnly, - resolveCustomFieldId, - mergeFieldDefinitionsWithValues, - dedupeMergedFields, -} from '~/shared/utils/entityCustomFieldLogic' +import { mergeDefinitionsWithValues } from '~/shared/utils/customFields' +import { useCustomFields } from '~/composables/useCustomFields' import { useEntityDocuments } from '~/composables/useEntityDocuments' import { useEntityProductDisplay } from '~/composables/useEntityProductDisplay' -import { useEntityCustomFields } from '~/composables/useEntityCustomFields' const props = defineProps({ piece: { type: Object, required: true }, @@ -392,25 +386,81 @@ const { } = useEntityProductDisplay({ entity: () => props.piece, selectedProduct }) const { - displayedCustomFields, - updateCustomField, -} = useEntityCustomFields({ entity: () => props.piece, entityType: 'piece' }) + updateCustomFieldValue: updateCustomFieldValueApi, + upsertCustomFieldValue, +} = useCustomFields() +const { showSuccess, showError } = useToast() +// Parent already pre-merges standalone custom fields into props.piece.customFields +const displayedCustomFields = computed(() => { + const fields = props.piece?.customFields + return Array.isArray(fields) ? fields.filter((f) => !f.machineContextOnly) : [] +}) + +const updateCustomField = async (field) => { + if (!field || field.readOnly) return + + const e = props.piece + const fieldValueId = field.customFieldValueId + + if (fieldValueId) { + const result = await updateCustomFieldValueApi(fieldValueId, { value: field.value ?? '' }) + if (result.success) { + showSuccess(`Champ "${field.name}" mis à jour avec succès`) + } else { + showError(`Erreur lors de la mise à jour du champ "${field.name}"`) + } + return + } + + if (!e?.id) { + showError('Impossible de créer la valeur pour ce champ') + return + } + + const metadata = field.customFieldId ? undefined : { + customFieldName: field.name, + customFieldType: field.type, + customFieldRequired: field.required, + customFieldOptions: field.options, + } + const result = await upsertCustomFieldValue( + field.customFieldId, + 'piece', + e.id, + field.value ?? '', + metadata, + ) + + if (result.success) { + const newValue = result.data + if (newValue?.id) { + field.customFieldValueId = newValue.id + field.value = newValue.value ?? field.value ?? '' + if (newValue.customField?.id) { + field.customFieldId = newValue.customField.id + } + } + showSuccess(`Champ "${field.name}" créé avec succès`) + } else { + showError(`Erreur lors de la sauvegarde du champ "${field.name}"`) + } +} + +// Context fields are NOT pre-merged — merge locally const mergedContextFields = computed(() => { const definitions = props.piece?.contextCustomFields ?? [] const values = props.piece?.contextCustomFieldValues ?? [] if (!definitions.length && !values.length) return [] - return dedupeMergedFields( - mergeFieldDefinitionsWithValues(definitions, values), - ) + return mergeDefinitionsWithValues(definitions, values) }) const queueContextCustomFieldUpdate = (field, value) => { const linkId = props.piece?.linkId if (!linkId || !field) return - const customFieldId = resolveCustomFieldId(field) - const customFieldValueId = resolveFieldId(field) + const customFieldId = field.customFieldId + const customFieldValueId = field.customFieldValueId if (!customFieldId && !customFieldValueId) return field.value = value @@ -420,7 +470,7 @@ const queueContextCustomFieldUpdate = (field, value) => { fieldId: customFieldId, customFieldValueId, value: value ?? '', - fieldName: field.name || field.customField?.name || 'Champ contextuel', + fieldName: field.name || 'Champ contextuel', }) } @@ -544,8 +594,8 @@ const handleProductChange = async (value) => { // --- Custom field event handlers --- const handleCustomFieldInput = (field, value) => { - if (resolveFieldReadOnly(field)) return - const fieldValueId = resolveFieldId(field) + if (field.readOnly) return + const fieldValueId = field.customFieldValueId if (!fieldValueId) return const fieldValue = props.piece.customFieldValues?.find((fv) => fv.id === fieldValueId) if (fieldValue) fieldValue.value = value @@ -553,7 +603,7 @@ const handleCustomFieldInput = (field, value) => { const handleCustomFieldBlur = async (field) => { await updateCustomField(field) - const cfId = field?.customFieldId || field?.customField?.id || null + const cfId = field?.customFieldId || null if (cfId || field?.customFieldValueId) { emit('custom-field-update', { fieldId: cfId, diff --git a/frontend/app/components/common/CustomFieldDisplay.vue b/frontend/app/components/common/CustomFieldDisplay.vue index 5dcab65..ba6166f 100644 --- a/frontend/app/components/common/CustomFieldDisplay.vue +++ b/frontend/app/components/common/CustomFieldDisplay.vue @@ -9,15 +9,15 @@
@@ -26,32 +26,32 @@