454 lines
17 KiB
TypeScript
454 lines
17 KiB
TypeScript
/**
|
|
* 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<string, unknown>
|
|
|
|
interface MachineDetailCustomFieldsDeps {
|
|
machine: Ref<AnyRecord | null>
|
|
isEditMode: Ref<boolean>
|
|
constructeurs: Ref<unknown[]>
|
|
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<AnyRecord[]>([])
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// 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: customFields.filter((f: any) => !f.machineContextOnly && !f.customField?.machineContextOnly),
|
|
contextCustomFields: piece.contextCustomFields ?? [],
|
|
contextCustomFieldValues: piece.contextCustomFieldValues ?? [],
|
|
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: customFields.filter((f: any) => !f.machineContextOnly && !f.customField?.machineContextOnly),
|
|
contextCustomFields: component.contextCustomFields ?? [],
|
|
contextCustomFieldValues: component.contextCustomFieldValues ?? [],
|
|
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)
|
|
}
|
|
}
|
|
|
|
const saveAllMachineCustomFields = async () => {
|
|
if (!machine.value) return
|
|
|
|
const fields = Array.isArray(machineCustomFields.value) ? machineCustomFields.value : []
|
|
const fieldsToSave = fields.filter(
|
|
(field) => field.value !== undefined && field.value !== null && String(field.value).trim() !== '',
|
|
)
|
|
|
|
for (const field of fieldsToSave) {
|
|
const { id: customFieldId, customFieldValueId } = field
|
|
|
|
try {
|
|
if (customFieldValueId) {
|
|
await updateCustomFieldValueApi(customFieldValueId as string, {
|
|
value: field.value ?? '',
|
|
} as any)
|
|
} else if (customFieldId) {
|
|
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
|
|
if (createdValue?.id) {
|
|
field.customFieldValueId = createdValue.id
|
|
if (!createdValue.customField) {
|
|
createdValue.customField = {
|
|
id: customFieldId,
|
|
name: field.name,
|
|
type: field.type,
|
|
required: field.required,
|
|
options: field.options,
|
|
}
|
|
}
|
|
const existingValues = Array.isArray(machine.value.customFieldValues)
|
|
? (machine.value.customFieldValues as AnyRecord[]).filter(
|
|
(item) => item.id !== createdValue.id,
|
|
)
|
|
: []
|
|
machine.value.customFieldValues = [...existingValues, createdValue]
|
|
}
|
|
}
|
|
}
|
|
} catch (error) {
|
|
console.error('Erreur lors de la sauvegarde du champ personnalisé:', error)
|
|
throw error
|
|
}
|
|
}
|
|
}
|
|
|
|
return {
|
|
// State
|
|
machineCustomFields,
|
|
|
|
// Computed
|
|
visibleMachineCustomFields,
|
|
|
|
// Transform functions
|
|
transformCustomFields,
|
|
transformComponentCustomFields,
|
|
|
|
// Methods
|
|
syncMachineCustomFields,
|
|
setMachineCustomFieldValue,
|
|
updateMachineCustomField,
|
|
updatePieceCustomField,
|
|
saveAllMachineCustomFields,
|
|
}
|
|
}
|