/** * Machine detail — custom field management sub-composable. * * Handles custom field resolution, display filtering, sync and updates * for machines, components and pieces. */ import { ref, computed } from 'vue' import { useCustomFields } from '~/composables/useCustomFields' import { useToast } from '~/composables/useToast' import { normalizeStructureForEditor } from '~/shared/modelUtils' import { shouldDisplayCustomField, normalizeExistingCustomFieldDefinitions, normalizeCustomFieldValueEntry, mergeCustomFieldValuesWithDefinitions, dedupeCustomFieldEntries, } from '~/shared/utils/customFieldUtils' import { resolveConstructeurs, uniqueConstructeurIds, } from '~/shared/constructeurUtils' type AnyRecord = Record interface MachineDetailCustomFieldsDeps { machine: Ref isEditMode: Ref constructeurs: Ref resolveProductReference: (source: AnyRecord) => { product: unknown; productId: string | null } getProductDisplay: (source: AnyRecord) => unknown } export function useMachineDetailCustomFields(deps: MachineDetailCustomFieldsDeps) { const { machine, isEditMode, constructeurs, resolveProductReference, getProductDisplay } = deps const { upsertCustomFieldValue, updateCustomFieldValue: updateCustomFieldValueApi, } = useCustomFields() const toast = useToast() // --------------------------------------------------------------------------- // State // --------------------------------------------------------------------------- const machineCustomFields = ref([]) // --------------------------------------------------------------------------- // Computed // --------------------------------------------------------------------------- const visibleMachineCustomFields = computed(() => { const fields = Array.isArray(machineCustomFields.value) ? machineCustomFields.value : [] if (isEditMode.value) return fields return fields.filter((field) => shouldDisplayCustomField(field)) }) // --------------------------------------------------------------------------- // Transform helpers // --------------------------------------------------------------------------- const getStructureCustomFields = (structure: unknown): AnyRecord[] => { if (!structure || typeof structure !== 'object') return [] const normalized = normalizeStructureForEditor(structure as any) as any return Array.isArray(normalized?.customFields) ? (normalized.customFields as AnyRecord[]) : [] } const transformCustomFields = (piecesData: AnyRecord[]): AnyRecord[] => { return (piecesData || []).map((piece) => { const typePiece = (piece.typePiece as AnyRecord) || {} const normalizeStructureDefs = (structure: unknown) => structure ? normalizeStructureForEditor(structure as AnyRecord) : null const normalizedStructureDefs = [ normalizeStructureDefs((piece.definition as AnyRecord)?.structure), normalizeStructureDefs(typePiece.structure), ] const valueEntries = [ ...(Array.isArray(piece.customFieldValues) ? piece.customFieldValues : []), ...(Array.isArray(piece.customFields) ? (piece.customFields as AnyRecord[]) .map(normalizeCustomFieldValueEntry) .filter((e) => e !== null) : []), ...(Array.isArray(typePiece.customFieldValues) ? (typePiece.customFieldValues as AnyRecord[]) .map(normalizeCustomFieldValueEntry) .filter((e) => e !== null) : []), ] const customFields = dedupeCustomFieldEntries( mergeCustomFieldValuesWithDefinitions( valueEntries, normalizeExistingCustomFieldDefinitions(piece.customFields), normalizeExistingCustomFieldDefinitions((piece.definition as AnyRecord)?.customFields), normalizeExistingCustomFieldDefinitions((piece.typePiece as AnyRecord)?.customFields), normalizeExistingCustomFieldDefinitions(typePiece.customFields), ...normalizedStructureDefs.map((def) => getStructureCustomFields(def)), ), ) const constructeurIds = uniqueConstructeurIds( piece.constructeurs, piece.constructeurIds, piece.constructeurId, piece.constructeur, (piece.originalPiece as AnyRecord)?.constructeurs, (piece.originalPiece as AnyRecord)?.constructeurIds, (piece.originalPiece as AnyRecord)?.constructeurId, (piece.originalPiece as AnyRecord)?.constructeur, ) const { product: resolvedProduct, productId: resolvedProductId } = resolveProductReference(piece) const constructeursList = resolveConstructeurs( constructeurIds, Array.isArray(piece.constructeurs) ? (piece.constructeurs as any[]) : [], piece.constructeur ? [piece.constructeur as any] : [], Array.isArray((piece.originalPiece as AnyRecord)?.constructeurs) ? ((piece.originalPiece as AnyRecord).constructeurs as any[]) : [], (piece.originalPiece as AnyRecord)?.constructeur ? [(piece.originalPiece as AnyRecord).constructeur as any] : [], constructeurs.value as any, ) as any[] const normalizedPiece = { ...piece, product: resolvedProduct || piece.product || null, productId: resolvedProductId || piece.productId || (piece.product as AnyRecord)?.id || null, } const productDisplay = getProductDisplay(normalizedPiece) return { ...normalizedPiece, customFields, documents: piece.documents || [], constructeurs: constructeursList, constructeur: constructeursList[0] || piece.constructeur || null, constructeurIds, constructeurId: constructeurIds[0] || null, typePieceId: piece.typePieceId || (piece.typePiece as AnyRecord)?.id || null, __productDisplay: productDisplay, } }) } const transformComponentCustomFields = (componentsData: AnyRecord[]): AnyRecord[] => { const normalizeStructureDefs = (structure: unknown) => structure ? normalizeStructureForEditor(structure as AnyRecord) : null return (componentsData || []).map((component) => { const type = (component.typeComposant as AnyRecord) || {} const normalizedStructureDefs = [ normalizeStructureDefs((component.definition as AnyRecord)?.structure), normalizeStructureDefs(type.structure), ] const actualComponent = (component.originalComposant as AnyRecord) || component const valueEntries = [ ...(Array.isArray(component.customFieldValues) ? component.customFieldValues : []), ...(Array.isArray(component.customFields) ? (component.customFields as AnyRecord[]) .map(normalizeCustomFieldValueEntry) .filter((e) => e !== null) : []), ...(Array.isArray(actualComponent?.customFields) ? (actualComponent.customFields as AnyRecord[]) .map(normalizeCustomFieldValueEntry) .filter((e) => e !== null) : []), ] const customFields = dedupeCustomFieldEntries( mergeCustomFieldValuesWithDefinitions( valueEntries, normalizeExistingCustomFieldDefinitions(component.customFields), normalizeExistingCustomFieldDefinitions((component.definition as AnyRecord)?.customFields), normalizeExistingCustomFieldDefinitions((component.typeComposant as AnyRecord)?.customFields), normalizeExistingCustomFieldDefinitions(type.customFields), normalizeExistingCustomFieldDefinitions(actualComponent?.customFields), ...normalizedStructureDefs.map((def) => getStructureCustomFields(def)), ), ) const piecesTransformed = component.pieces ? transformCustomFields(component.pieces as AnyRecord[]).map((p) => ({ ...p, parentComponentName: component.name, })) : [] const subComponents = component.sousComposants ? transformComponentCustomFields(component.sousComposants as AnyRecord[]) : [] const constructeurIds = uniqueConstructeurIds( component.constructeurs, component.constructeurIds, component.constructeurId, component.constructeur, actualComponent?.constructeurs, actualComponent?.constructeurIds, actualComponent?.constructeurId, actualComponent?.constructeur, ) const constructeursList = resolveConstructeurs( constructeurIds, Array.isArray(component.constructeurs) ? (component.constructeurs as any[]) : [], component.constructeur ? [component.constructeur as any] : [], Array.isArray(actualComponent?.constructeurs) ? (actualComponent.constructeurs as any[]) : [], actualComponent?.constructeur ? [actualComponent.constructeur as any] : [], constructeurs.value as any, ) as any[] const { product: resolvedProduct, productId: resolvedProductId } = resolveProductReference(component) const normalizedComponent = { ...component, product: resolvedProduct || component.product || null, productId: resolvedProductId || component.productId || (component.product as AnyRecord)?.id || null, } const productDisplay = getProductDisplay(normalizedComponent) return { ...normalizedComponent, customFields, pieces: piecesTransformed, subComponents, documents: component.documents || [], constructeurs: constructeursList, constructeur: constructeursList[0] || component.constructeur || null, constructeurIds, constructeurId: constructeurIds[0] || null, typeComposantId: component.typeComposantId || (component.typeComposant as AnyRecord)?.id || null, __productDisplay: productDisplay, } }) } // --------------------------------------------------------------------------- // Machine custom field methods // --------------------------------------------------------------------------- const syncMachineCustomFields = () => { if (!machine.value) { machineCustomFields.value = [] return } const valueEntries = [ ...(Array.isArray(machine.value.customFieldValues) ? machine.value.customFieldValues : []), ...(Array.isArray(machine.value.customFields) ? (machine.value.customFields as AnyRecord[]) .map(normalizeCustomFieldValueEntry) .filter((e) => e !== null) : []), ] const merged = dedupeCustomFieldEntries( mergeCustomFieldValuesWithDefinitions( valueEntries, normalizeExistingCustomFieldDefinitions(machine.value.customFields), ), ).map((field: AnyRecord) => ({ ...field, readOnly: false })) machineCustomFields.value = merged } const setMachineCustomFieldValue = (field: AnyRecord, value: unknown) => { if (!field) return field.value = value if (field.customFieldValueId && (machine.value as AnyRecord)?.customFieldValues) { const stored = ((machine.value as AnyRecord).customFieldValues as AnyRecord[]).find( (fv) => fv.id === field.customFieldValueId, ) if (stored) stored.value = value } } const updateMachineCustomField = async (field: AnyRecord) => { if (!machine.value || !field) return const { id: customFieldId, customFieldValueId } = field const fieldLabel = (field.name as string) || 'Champ personnalisé' try { if (customFieldValueId) { const result: any = await updateCustomFieldValueApi(customFieldValueId as string, { value: field.value ?? '', } as any) if (result.success) { toast.showSuccess(`Champ "${fieldLabel}" de la machine mis à jour avec succès`) syncMachineCustomFields() } else { toast.showError(`Erreur lors de la mise à jour du champ "${fieldLabel}"`) } return } if (!customFieldId) { toast.showError( 'Impossible de mettre à jour ce champ personnalisé (identifiant manquant).', ) return } const result: any = await upsertCustomFieldValue( customFieldId as string, 'machine', machine.value.id as string, field.value ?? '', ) if (result.success) { const createdValue = result.data as AnyRecord toast.showSuccess(`Champ "${fieldLabel}" de la machine mis à jour avec succès`) if (createdValue?.id) { if (!createdValue.customField) { createdValue.customField = { id: customFieldId, name: field.name, type: field.type, required: field.required, options: field.options, } } field.customFieldValueId = createdValue.id field.readOnly = false const existingValues = Array.isArray(machine.value.customFieldValues) ? (machine.value.customFieldValues as AnyRecord[]).filter( (item) => item.id !== createdValue.id, ) : [] machine.value.customFieldValues = [...existingValues, createdValue] } syncMachineCustomFields() } else { toast.showError(`Erreur lors de la mise à jour du champ "${fieldLabel}"`) } } catch (error) { console.error('Erreur lors de la mise à jour du champ personnalisé de la machine:', error) toast.showError(`Erreur lors de la mise à jour du champ "${fieldLabel}"`) } } const updatePieceCustomField = async (fieldUpdate: AnyRecord) => { try { const result: any = await upsertCustomFieldValue( fieldUpdate.fieldId as string, 'piece', fieldUpdate.pieceId as string, fieldUpdate.value, ) if (result.success) { toast.showSuccess('Champ personnalisé mis à jour avec succès') } else { toast.showError('Erreur lors de la mise à jour du champ personnalisé') } } catch (error) { toast.showError('Erreur lors de la mise à jour du champ personnalisé') console.error('Erreur lors de la mise à jour du champ personnalisé:', error) } } return { // State machineCustomFields, // Computed visibleMachineCustomFields, // Transform functions transformCustomFields, transformComponentCustomFields, // Methods syncMachineCustomFields, setMachineCustomFieldValue, updateMachineCustomField, updatePieceCustomField, } }