diff --git a/app/composables/useComponentEdit.ts b/app/composables/useComponentEdit.ts index 9906274..48dafc6 100644 --- a/app/composables/useComponentEdit.ts +++ b/app/composables/useComponentEdit.ts @@ -269,95 +269,78 @@ export function useComponentEdit(componentId: string) { } }) - // --- Slot selection entries (for selectors) --- + // --- Slot local edits (saved on submit, not auto-saved) --- + + const slotEdits = reactive<{ + pieces: Record + products: Record + subcomponents: Record + }>({ pieces: {}, products: {}, subcomponents: {} }) const pieceSlotEntries = computed(() => { const structure = component.value?.structure if (!structure?.pieces) return [] - return (structure.pieces as any[]).map((slot: any, i: number) => ({ - slotId: slot.slotId, - typePieceId: slot.typePieceId, - selectedPieceId: slot.selectedPieceId ?? null, - quantity: slot.quantity ?? 1, - position: slot.position ?? i, - label: pieceTypeLabelMap.value[slot.typePieceId] || `Pièce #${i + 1}`, - })) + return (structure.pieces as any[]).map((slot: any, i: number) => { + const edits = slotEdits.pieces[slot.slotId] + return { + slotId: slot.slotId, + typePieceId: slot.typePieceId, + selectedPieceId: edits && 'selectedPieceId' in edits ? edits.selectedPieceId : (slot.selectedPieceId ?? null), + quantity: edits && 'quantity' in edits ? edits.quantity! : (slot.quantity ?? 1), + position: slot.position ?? i, + label: pieceTypeLabelMap.value[slot.typePieceId] || `Pièce #${i + 1}`, + } + }) }) const productSlotEntries = computed(() => { const structure = component.value?.structure if (!structure?.products) return [] - return (structure.products as any[]).map((slot: any, i: number) => ({ - slotId: slot.slotId, - typeProductId: slot.typeProductId, - selectedProductId: slot.selectedProductId ?? null, - familyCode: slot.familyCode, - position: slot.position ?? i, - label: productTypeLabelMap.value[slot.typeProductId] || `Produit #${i + 1}`, - })) + return (structure.products as any[]).map((slot: any, i: number) => { + const edits = slotEdits.products[slot.slotId] + return { + slotId: slot.slotId, + typeProductId: slot.typeProductId, + selectedProductId: edits && 'selectedProductId' in edits ? edits.selectedProductId : (slot.selectedProductId ?? null), + familyCode: slot.familyCode, + position: slot.position ?? i, + label: productTypeLabelMap.value[slot.typeProductId] || `Produit #${i + 1}`, + } + }) }) const subcomponentSlotEntries = computed(() => { const structure = component.value?.structure if (!structure?.subcomponents) return [] - return (structure.subcomponents as any[]).map((slot: any, i: number) => ({ - slotId: slot.slotId, - typeComposantId: slot.typeComposantId, - selectedComponentId: slot.selectedComponentId ?? null, - alias: slot.alias, - familyCode: slot.familyCode, - position: slot.position ?? i, - label: slot.alias || `Sous-composant #${i + 1}`, - })) + return (structure.subcomponents as any[]).map((slot: any, i: number) => { + const edits = slotEdits.subcomponents[slot.slotId] + return { + slotId: slot.slotId, + typeComposantId: slot.typeComposantId, + selectedComponentId: edits && 'selectedComposantId' in edits ? edits.selectedComposantId : (slot.selectedComponentId ?? null), + alias: slot.alias, + familyCode: slot.familyCode, + position: slot.position ?? i, + label: slot.alias || `Sous-composant #${i + 1}`, + } + }) }) - const savePieceSlotSelection = async (slotId: string, selectedPieceId: string | null) => { - const result = await patch(`/composant-piece-slots/${slotId}`, { selectedPieceId }) - if (result.success) { - const structure = component.value?.structure - if (structure?.pieces) { - const slot = (structure.pieces as any[]).find((s: any) => s.slotId === slotId) - if (slot) slot.selectedPieceId = selectedPieceId - } - toast.showSuccess('Pièce mise à jour') - } + const setPieceSlotSelection = (slotId: string, selectedPieceId: string | null) => { + slotEdits.pieces[slotId] = { ...slotEdits.pieces[slotId], selectedPieceId } } - const saveProductSlotSelection = async (slotId: string, selectedProductId: string | null) => { - const result = await patch(`/composant-product-slots/${slotId}`, { selectedProductId }) - if (result.success) { - const structure = component.value?.structure - if (structure?.products) { - const slot = (structure.products as any[]).find((s: any) => s.slotId === slotId) - if (slot) slot.selectedProductId = selectedProductId - } - toast.showSuccess('Produit mis à jour') - } + const setProductSlotSelection = (slotId: string, selectedProductId: string | null) => { + slotEdits.products[slotId] = { ...slotEdits.products[slotId], selectedProductId } } - const saveSubcomponentSlotSelection = async (slotId: string, selectedComposantId: string | null) => { - const result = await patch(`/composant-subcomponent-slots/${slotId}`, { selectedComposantId }) - if (result.success) { - const structure = component.value?.structure - if (structure?.subcomponents) { - const slot = (structure.subcomponents as any[]).find((s: any) => s.slotId === slotId) - if (slot) slot.selectedComponentId = selectedComposantId - } - toast.showSuccess('Sous-composant mis à jour') - } + const setSubcomponentSlotSelection = (slotId: string, selectedComposantId: string | null) => { + slotEdits.subcomponents[slotId] = { ...slotEdits.subcomponents[slotId], selectedComposantId } } - const saveSlotQuantity = async (slotId: string, quantity: number) => { + const setSlotQuantity = (slotId: string, quantity: number) => { if (!slotId || quantity < 1) return - const result = await patch(`/composant-piece-slots/${slotId}`, { quantity: Math.max(1, quantity) }) - if (result.success) { - const structure = component.value?.structure - if (structure?.pieces) { - const slot = (structure.pieces as any[]).find((s: any) => s.slotId === slotId) - if (slot) slot.quantity = quantity - } - toast.showSuccess('Quantité mise à jour') - } + slotEdits.pieces[slotId] = { ...slotEdits.pieces[slotId], quantity: Math.max(1, quantity) } } const submitEdition = async () => { @@ -403,6 +386,54 @@ export function useComponentEdit(componentId: string) { ], { customFieldInputs, upsertCustomFieldValue, updateCustomFieldValue, toast }, ) + + // Save slot edits + const slotPromises: Promise[] = [] + for (const [slotId, edits] of Object.entries(slotEdits.pieces)) { + if (Object.keys(edits).length) { + slotPromises.push(patch(`/composant-piece-slots/${slotId}`, { + ...'selectedPieceId' in edits ? { selectedPieceId: edits.selectedPieceId } : {}, + ...'quantity' in edits ? { quantity: edits.quantity } : {}, + })) + } + } + for (const [slotId, edits] of Object.entries(slotEdits.products)) { + if ('selectedProductId' in edits) { + slotPromises.push(patch(`/composant-product-slots/${slotId}`, { selectedProductId: edits.selectedProductId })) + } + } + for (const [slotId, edits] of Object.entries(slotEdits.subcomponents)) { + if ('selectedComposantId' in edits) { + slotPromises.push(patch(`/composant-subcomponent-slots/${slotId}`, { selectedComposantId: edits.selectedComposantId })) + } + } + await Promise.all(slotPromises) + + // Apply slot edits to local structure so UI reflects saved values + const structure = component.value?.structure + if (structure) { + for (const [slotId, edits] of Object.entries(slotEdits.pieces)) { + const slot = (structure.pieces as any[])?.find((s: any) => s.slotId === slotId) + if (slot) { + if ('selectedPieceId' in edits) slot.selectedPieceId = edits.selectedPieceId + if ('quantity' in edits) slot.quantity = edits.quantity + } + } + for (const [slotId, edits] of Object.entries(slotEdits.products)) { + const slot = (structure.products as any[])?.find((s: any) => s.slotId === slotId) + if (slot && 'selectedProductId' in edits) slot.selectedProductId = edits.selectedProductId + } + for (const [slotId, edits] of Object.entries(slotEdits.subcomponents)) { + const slot = (structure.subcomponents as any[])?.find((s: any) => s.slotId === slotId) + if (slot && 'selectedComposantId' in edits) slot.selectedComponentId = edits.selectedComposantId + } + } + + // Reset local slot edits + slotEdits.pieces = {} + slotEdits.products = {} + slotEdits.subcomponents = {} + toast.showSuccess('Composant mis à jour avec succès.') } } @@ -538,10 +569,10 @@ export function useComponentEdit(componentId: string) { handleFilesAdded, refreshDocuments, submitEdition, - saveSlotQuantity, - savePieceSlotSelection, - saveProductSlotSelection, - saveSubcomponentSlotSelection, + setSlotQuantity, + setPieceSlotSelection, + setProductSlotSelection, + setSubcomponentSlotSelection, resolvePieceLabel, resolveProductLabel, resolveSubcomponentLabel, diff --git a/app/pages/component/[id]/edit.vue b/app/pages/component/[id]/edit.vue index 3014a27..dad8e9c 100644 --- a/app/pages/component/[id]/edit.vue +++ b/app/pages/component/[id]/edit.vue @@ -198,7 +198,7 @@ :model-value="slot.selectedPieceId" :disabled="!canEdit || saving" :type-piece-id="slot.typePieceId" - @update:model-value="(value) => savePieceSlotSelection(slot.slotId, value)" + @update:model-value="(value) => setPieceSlotSelection(slot.slotId, value)" />
@@ -209,7 +209,7 @@ class="input input-bordered input-sm w-full text-center" :disabled="!canEdit || saving" title="Quantité" - @change="(e) => saveSlotQuantity(slot.slotId, Number((e.target as HTMLInputElement).value))" + @change="(e) => setSlotQuantity(slot.slotId, Number((e.target as HTMLInputElement).value))" >
@@ -232,7 +232,7 @@ :model-value="slot.selectedProductId" :disabled="!canEdit || saving" :type-product-id="slot.typeProductId" - @update:model-value="(value) => saveProductSlotSelection(slot.slotId, value)" + @update:model-value="(value) => setProductSlotSelection(slot.slotId, value)" /> @@ -253,7 +253,7 @@ :model-value="slot.selectedComponentId" :disabled="!canEdit || saving" :type-composant-id="slot.typeComposantId" - @update:model-value="(value) => saveSubcomponentSlotSelection(slot.slotId, value)" + @update:model-value="(value) => setSubcomponentSlotSelection(slot.slotId, value)" /> @@ -381,10 +381,10 @@ const { removeDocument, handleFilesAdded, submitEdition, - saveSlotQuantity, - savePieceSlotSelection, - saveProductSlotSelection, - saveSubcomponentSlotSelection, + setSlotQuantity, + setPieceSlotSelection, + setProductSlotSelection, + setSubcomponentSlotSelection, resolvePieceLabel, resolveProductLabel, resolveSubcomponentLabel,