feat: auto complete missing custom fields on machine page
This commit is contained in:
@@ -97,6 +97,34 @@ export function useMachines() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const addMissingCustomFields = async (machineId, { showToast: shouldShowToast = true } = {}) => {
|
||||||
|
if (!machineId) {
|
||||||
|
const error = 'Identifiant de machine manquant'
|
||||||
|
if (shouldShowToast) {
|
||||||
|
showError(error)
|
||||||
|
}
|
||||||
|
return { success: false, error }
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await post(`/machines/${machineId}/add-custom-fields`)
|
||||||
|
if (result.success) {
|
||||||
|
if (shouldShowToast) {
|
||||||
|
showSuccess('Champs personnalisés complétés avec succès')
|
||||||
|
}
|
||||||
|
} else if (shouldShowToast && result.error) {
|
||||||
|
showError(result.error)
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur lors de l’ajout des champs personnalisés manquants:', error)
|
||||||
|
if (shouldShowToast) {
|
||||||
|
showError('Erreur lors de la complétion des champs personnalisés')
|
||||||
|
}
|
||||||
|
return { success: false, error: error.message }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const deleteMachine = async (id) => {
|
const deleteMachine = async (id) => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
@@ -143,6 +171,7 @@ export function useMachines() {
|
|||||||
getMachinesBySite,
|
getMachinesBySite,
|
||||||
getMachinesByType,
|
getMachinesByType,
|
||||||
getMachines,
|
getMachines,
|
||||||
isLoading
|
isLoading,
|
||||||
|
addMissingCustomFields
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,20 @@
|
|||||||
>
|
>
|
||||||
Modifier les éléments du squelette
|
Modifier les éléments du squelette
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="btn btn-outline"
|
||||||
|
:class="{ 'btn-primary': hasMissingRequiredCustomFields }"
|
||||||
|
:disabled="completingCustomFields"
|
||||||
|
@click="handleCompleteCustomFieldsClick"
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
v-if="completingCustomFields"
|
||||||
|
class="loading loading-spinner loading-sm mr-2"
|
||||||
|
aria-hidden="true"
|
||||||
|
/>
|
||||||
|
Compléter les champs personnalisés
|
||||||
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="!isEditMode"
|
v-if="!isEditMode"
|
||||||
@click="openPrintModal"
|
@click="openPrintModal"
|
||||||
@@ -918,7 +932,11 @@ if (!machineId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Composables
|
// Composables
|
||||||
const { updateMachine: updateMachineApi, reconfigureSkeleton: reconfigureMachineSkeleton } = useMachines()
|
const {
|
||||||
|
updateMachine: updateMachineApi,
|
||||||
|
reconfigureSkeleton: reconfigureMachineSkeleton,
|
||||||
|
addMissingCustomFields: addMissingCustomFieldsApi,
|
||||||
|
} = useMachines()
|
||||||
const {
|
const {
|
||||||
getComposantsByMachine,
|
getComposantsByMachine,
|
||||||
updateComposant: updateComposantApi
|
updateComposant: updateComposantApi
|
||||||
@@ -954,6 +972,7 @@ const loading = ref(true)
|
|||||||
const machine = ref(null)
|
const machine = ref(null)
|
||||||
const components = ref([])
|
const components = ref([])
|
||||||
const pieces = ref([])
|
const pieces = ref([])
|
||||||
|
const completingCustomFields = ref(false)
|
||||||
const printAreaRef = ref(null)
|
const printAreaRef = ref(null)
|
||||||
|
|
||||||
const { constructeurs, loadConstructeurs } = useConstructeurs()
|
const { constructeurs, loadConstructeurs } = useConstructeurs()
|
||||||
@@ -1350,6 +1369,7 @@ const applySkeletonReconfigurationResult = async (data) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
await ensureModelsForExistingEntities()
|
await ensureModelsForExistingEntities()
|
||||||
|
await autoCompleteMissingCustomFieldsIfNeeded()
|
||||||
}
|
}
|
||||||
|
|
||||||
const saveSkeletonConfiguration = async () => {
|
const saveSkeletonConfiguration = async () => {
|
||||||
@@ -1489,6 +1509,26 @@ const flattenComponents = (list = []) => {
|
|||||||
|
|
||||||
const flattenedComponents = computed(() => flattenComponents(components.value))
|
const flattenedComponents = computed(() => flattenComponents(components.value))
|
||||||
|
|
||||||
|
const hasMissingRequiredCustomFields = computed(() => {
|
||||||
|
if (!machine.value) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasMissingRequiredCustomFieldsInMachine(machine.value)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (components.value.some(component => hasMissingRequiredCustomFieldsInComponent(component))) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pieces.value.some(piece => hasMissingRequiredCustomFieldsInPiece(piece))) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
|
||||||
const preloadModelsForTypeMachine = async (typeMachine) => {
|
const preloadModelsForTypeMachine = async (typeMachine) => {
|
||||||
if (!typeMachine) return
|
if (!typeMachine) return
|
||||||
const componentTypeIds = new Set(
|
const componentTypeIds = new Set(
|
||||||
@@ -1895,6 +1935,281 @@ const transformComponentCustomFields = (componentsData) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getCustomFieldIdentifier(field) {
|
||||||
|
if (!field) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return field.customFieldId ?? field.customField?.id ?? field.id ?? null
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeCustomFieldEntries(entries = []) {
|
||||||
|
return entries
|
||||||
|
.map(entry => ({
|
||||||
|
id: getCustomFieldIdentifier(entry),
|
||||||
|
required: entry?.customField?.required ?? entry?.required ?? false,
|
||||||
|
value: entry?.value ?? null,
|
||||||
|
}))
|
||||||
|
.filter(entry => entry.id !== null)
|
||||||
|
}
|
||||||
|
|
||||||
|
function collectCustomFieldDefinitions(...sources) {
|
||||||
|
return sources.flatMap(source => (Array.isArray(source) ? source : []))
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEmptyCustomFieldValue(value) {
|
||||||
|
if (value === null || value === undefined) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
return value.trim() === ''
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasMissingCustomFieldValues(values = []) {
|
||||||
|
return values.some(entry => entry.required && isEmptyCustomFieldValue(entry.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasMissingCustomFieldDefinitions(definitions = [], values = []) {
|
||||||
|
if (!definitions.length) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const valueIds = new Set(values.map(entry => entry.id))
|
||||||
|
return definitions.some(definition => {
|
||||||
|
if (!definition?.required) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
const id = getCustomFieldIdentifier(definition)
|
||||||
|
return id !== null && !valueIds.has(id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasMissingRequiredCustomFieldsInMachine(machineData) {
|
||||||
|
if (!machineData) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const values = normalizeCustomFieldEntries(machineData.customFieldValues || [])
|
||||||
|
const definitions = collectCustomFieldDefinitions(machineData.typeMachine?.customFields)
|
||||||
|
|
||||||
|
return (
|
||||||
|
hasMissingCustomFieldDefinitions(definitions, values)
|
||||||
|
|| hasMissingCustomFieldValues(values)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasMissingRequiredCustomFieldsInComponent(component) {
|
||||||
|
if (!component) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceValues = component.customFields?.length
|
||||||
|
? component.customFields
|
||||||
|
: component.customFieldValues || []
|
||||||
|
const values = normalizeCustomFieldEntries(sourceValues)
|
||||||
|
|
||||||
|
const definitions = collectCustomFieldDefinitions(
|
||||||
|
component.typeComposant?.customFields,
|
||||||
|
component.composantModel?.customFields,
|
||||||
|
component.typeMachineComponentRequirement?.customFields,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (
|
||||||
|
hasMissingCustomFieldDefinitions(definitions, values)
|
||||||
|
|| hasMissingCustomFieldValues(values)
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
Array.isArray(component.pieces)
|
||||||
|
&& component.pieces.some(piece => hasMissingRequiredCustomFieldsInPiece(piece))
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
Array.isArray(component.subComponents)
|
||||||
|
&& component.subComponents.some(sub => hasMissingRequiredCustomFieldsInComponent(sub))
|
||||||
|
) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasMissingRequiredCustomFieldsInPiece(piece) {
|
||||||
|
if (!piece) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceValues = piece.customFields?.length
|
||||||
|
? piece.customFields
|
||||||
|
: piece.customFieldValues || []
|
||||||
|
const values = normalizeCustomFieldEntries(sourceValues)
|
||||||
|
|
||||||
|
const definitions = collectCustomFieldDefinitions(
|
||||||
|
piece.typePiece?.customFields,
|
||||||
|
piece.pieceModel?.customFields,
|
||||||
|
piece.typeMachinePieceRequirement?.customFields,
|
||||||
|
)
|
||||||
|
|
||||||
|
return (
|
||||||
|
hasMissingCustomFieldDefinitions(definitions, values)
|
||||||
|
|| hasMissingCustomFieldValues(values)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergePieceLists(existing = [], updates = []) {
|
||||||
|
if (!existing.length) {
|
||||||
|
return updates
|
||||||
|
}
|
||||||
|
if (!updates.length) {
|
||||||
|
return existing
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateMap = new Map(updates.map(piece => [piece.id, piece]))
|
||||||
|
const merged = existing.map(piece => {
|
||||||
|
const update = updateMap.get(piece.id)
|
||||||
|
if (!update) {
|
||||||
|
return piece
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...piece,
|
||||||
|
...update,
|
||||||
|
customFields: update.customFields ?? piece.customFields,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
updates.forEach(update => {
|
||||||
|
if (!existing.some(piece => piece.id === update.id)) {
|
||||||
|
merged.push(update)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return merged
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeComponentTrees(existing = [], updates = []) {
|
||||||
|
if (!existing.length) {
|
||||||
|
return updates
|
||||||
|
}
|
||||||
|
if (!updates.length) {
|
||||||
|
return existing
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateMap = new Map(updates.map(component => [component.id, component]))
|
||||||
|
const merged = existing.map(component => {
|
||||||
|
const update = updateMap.get(component.id)
|
||||||
|
if (!update) {
|
||||||
|
return component
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...component,
|
||||||
|
...update,
|
||||||
|
customFields: update.customFields ?? component.customFields,
|
||||||
|
pieces: mergePieceLists(component.pieces || [], update.pieces || []),
|
||||||
|
subComponents: mergeComponentTrees(component.subComponents || [], update.subComponents || []),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
updates.forEach(update => {
|
||||||
|
if (!existing.some(component => component.id === update.id)) {
|
||||||
|
merged.push(update)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return merged
|
||||||
|
}
|
||||||
|
|
||||||
|
async function applyCustomFieldsCompletionResult(payload) {
|
||||||
|
if (!payload) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedMachine = payload.machine || payload
|
||||||
|
if (machine.value && updatedMachine?.customFieldValues) {
|
||||||
|
machine.value = {
|
||||||
|
...machine.value,
|
||||||
|
customFieldValues: updatedMachine.customFieldValues,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedComponentsRaw = payload.components ?? updatedMachine?.components ?? null
|
||||||
|
if (Array.isArray(updatedComponentsRaw)) {
|
||||||
|
const transformedComponents = transformComponentCustomFields(updatedComponentsRaw)
|
||||||
|
components.value = mergeComponentTrees(components.value, transformedComponents)
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedPiecesRaw = payload.pieces ?? updatedMachine?.pieces ?? null
|
||||||
|
if (Array.isArray(updatedPiecesRaw)) {
|
||||||
|
const transformedPieces = transformCustomFields(updatedPiecesRaw)
|
||||||
|
pieces.value = mergePieceLists(pieces.value, transformedPieces)
|
||||||
|
}
|
||||||
|
|
||||||
|
await ensureModelsForExistingEntities()
|
||||||
|
}
|
||||||
|
|
||||||
|
async function completeMissingCustomFields({ silent = false, checkMissing = true } = {}) {
|
||||||
|
if (!machine.value?.id) {
|
||||||
|
const error = 'Machine introuvable'
|
||||||
|
if (!silent) {
|
||||||
|
toast.showError(error)
|
||||||
|
}
|
||||||
|
return { success: false, error }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkMissing && !hasMissingRequiredCustomFields.value) {
|
||||||
|
return { success: true, skipped: true }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (completingCustomFields.value) {
|
||||||
|
if (!silent) {
|
||||||
|
toast.showInfo('Complétion des champs personnalisés déjà en cours')
|
||||||
|
}
|
||||||
|
return { success: false, error: 'Complétion déjà en cours' }
|
||||||
|
}
|
||||||
|
|
||||||
|
completingCustomFields.value = true
|
||||||
|
try {
|
||||||
|
const result = await addMissingCustomFieldsApi(machine.value.id, { showToast: false })
|
||||||
|
if (result.success) {
|
||||||
|
await applyCustomFieldsCompletionResult(result.data)
|
||||||
|
if (!silent) {
|
||||||
|
toast.showSuccess('Champs personnalisés complétés avec succès')
|
||||||
|
}
|
||||||
|
} else if (!silent) {
|
||||||
|
toast.showError(result.error || 'Impossible de compléter les champs personnalisés')
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Erreur lors de la complétion des champs personnalisés:', error)
|
||||||
|
if (!silent) {
|
||||||
|
toast.showError('Impossible de compléter les champs personnalisés')
|
||||||
|
}
|
||||||
|
return { success: false, error: error?.message || 'Erreur inconnue' }
|
||||||
|
} finally {
|
||||||
|
completingCustomFields.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function autoCompleteMissingCustomFieldsIfNeeded() {
|
||||||
|
await nextTick()
|
||||||
|
if (completingCustomFields.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!machine.value?.id) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!hasMissingRequiredCustomFields.value) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
await completeMissingCustomFields({ silent: true, checkMissing: true })
|
||||||
|
}
|
||||||
|
|
||||||
// Methods
|
// Methods
|
||||||
const loadMachineData = async () => {
|
const loadMachineData = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
@@ -1970,6 +2285,8 @@ const loadMachineData = async () => {
|
|||||||
console.log('Aucune pièce trouvée dans la réponse de la machine')
|
console.log('Aucune pièce trouvée dans la réponse de la machine')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await autoCompleteMissingCustomFieldsIfNeeded()
|
||||||
|
|
||||||
if (!machineDocumentsLoaded.value) {
|
if (!machineDocumentsLoaded.value) {
|
||||||
await refreshMachineDocuments()
|
await refreshMachineDocuments()
|
||||||
}
|
}
|
||||||
@@ -2230,6 +2547,10 @@ const updatePieceCustomField = async (fieldUpdate) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleCompleteCustomFieldsClick = async () => {
|
||||||
|
await completeMissingCustomFields({ silent: false, checkMissing: false })
|
||||||
|
}
|
||||||
|
|
||||||
const editComponent = (component) => {
|
const editComponent = (component) => {
|
||||||
// TODO: Implement edit modal
|
// TODO: Implement edit modal
|
||||||
console.log('Edit component:', component)
|
console.log('Edit component:', component)
|
||||||
|
|||||||
Reference in New Issue
Block a user