/** * Reactive custom field management for entity items (ComponentItem, PieceItem). * * Wraps the pure logic from entityCustomFieldLogic.ts with Vue reactivity, * watchers, and API calls for updating/upserting custom field values. */ import { computed, watch } from 'vue' import { useCustomFields } from '~/composables/useCustomFields' import { useToast } from '~/composables/useToast' import { buildDefinitionSources, buildCandidateCustomFields, mergeFieldDefinitionsWithValues, dedupeMergedFields, ensureCustomFieldId, resolveFieldId, resolveFieldName, resolveFieldType, resolveFieldReadOnly, resolveCustomFieldId, buildCustomFieldMetadata, } from '~/shared/utils/entityCustomFieldLogic' export interface EntityCustomFieldsDeps { entity: () => any entityType: 'composant' | 'piece' } export function useEntityCustomFields(deps: EntityCustomFieldsDeps) { const { entity, entityType } = deps const { updateCustomFieldValue: updateCustomFieldValueApi, upsertCustomFieldValue, } = useCustomFields() const { showSuccess, showError } = useToast() const definitionSources = computed(() => buildDefinitionSources(entity(), entityType), ) const displayedCustomFields = computed(() => dedupeMergedFields( mergeFieldDefinitionsWithValues( definitionSources.value, entity().customFieldValues, ), ), ) const candidateCustomFields = computed(() => buildCandidateCustomFields(entity(), definitionSources.value), ) // Watchers to ensure field IDs are resolved watch( candidateCustomFields, () => { const candidates = candidateCustomFields.value ;(displayedCustomFields.value || []).forEach((field: any) => { if (field) ensureCustomFieldId(field, candidates) }) }, { immediate: true, deep: true }, ) watch( displayedCustomFields, (fields) => { const candidates = candidateCustomFields.value ;(fields || []).forEach((field: any) => { if (field) ensureCustomFieldId(field, candidates) }) }, { immediate: true, deep: true }, ) const updateCustomField = async (field: any) => { if (!field || resolveFieldReadOnly(field)) return const e = entity() const fieldValueId = resolveFieldId(field) // Update existing field value if (fieldValueId) { const result: any = await updateCustomFieldValueApi(fieldValueId, { value: field.value ?? '' }) if (result.success) { const existingValue = e.customFieldValues?.find((v: any) => v.id === fieldValueId) if (existingValue?.customField?.id) { field.customFieldId = existingValue.customField.id field.customField = existingValue.customField } showSuccess(`Champ "${resolveFieldName(field)}" mis à jour avec succès`) } else { showError(`Erreur lors de la mise à jour du champ "${resolveFieldName(field)}"`) } return } // Create new field value const customFieldId = ensureCustomFieldId(field, candidateCustomFields.value) const fieldName = resolveFieldName(field) if (!e?.id) { showError(`Impossible de créer la valeur pour ce champ`) return } if (!customFieldId && (!fieldName || fieldName === 'Champ')) { showError(`Impossible de créer la valeur pour ce champ`) return } const metadata = customFieldId ? undefined : buildCustomFieldMetadata(field) const result: any = await upsertCustomFieldValue( customFieldId, entityType, 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 field.customField = newValue.customField } if (Array.isArray(e.customFieldValues)) { const index = e.customFieldValues.findIndex((v: any) => v.id === newValue.id) if (index !== -1) { e.customFieldValues.splice(index, 1, newValue) } else { e.customFieldValues.push(newValue) } } else { e.customFieldValues = [newValue] } } showSuccess(`Champ "${resolveFieldName(field)}" créé avec succès`) // Update definitions list const definitions = Array.isArray(e.customFields) ? [...e.customFields] : [] const fieldIdentifier = ensureCustomFieldId(field, candidateCustomFields.value) const existingIndex = definitions.findIndex((definition: any) => { const definitionId = resolveCustomFieldId(definition) if (fieldIdentifier && definitionId) return definitionId === fieldIdentifier return definition?.name === resolveFieldName(field) }) const updatedDefinition = { ...(existingIndex !== -1 ? definitions[existingIndex] : {}), customFieldValueId: field.customFieldValueId, customFieldId: fieldIdentifier, name: resolveFieldName(field), type: resolveFieldType(field), required: field.required ?? false, options: field.options ?? [], value: field.value ?? '', customField: field.customField ?? null, } if (existingIndex !== -1) { definitions.splice(existingIndex, 1, updatedDefinition) } else { definitions.push(updatedDefinition) } e.customFields = definitions } else { showError(`Erreur lors de la sauvegarde du champ "${resolveFieldName(field)}"`) } } return { displayedCustomFields, candidateCustomFields, updateCustomField, } }