refactor(component-edit) : replace slot auto-save with deferred save on submit
Slot selections (piece, product, subcomponent, quantity) are no longer saved immediately on change. Instead, edits are stored locally and persisted together with base fields and custom fields when the user clicks "Enregistrer les modifications". Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -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<string, { selectedPieceId?: string | null, quantity?: number }>
|
||||
products: Record<string, { selectedProductId?: string | null }>
|
||||
subcomponents: Record<string, { selectedComposantId?: string | null }>
|
||||
}>({ 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<any>[] = []
|
||||
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,
|
||||
|
||||
@@ -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)"
|
||||
/>
|
||||
</div>
|
||||
<div class="w-20 shrink-0">
|
||||
@@ -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))"
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -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)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -381,10 +381,10 @@ const {
|
||||
removeDocument,
|
||||
handleFilesAdded,
|
||||
submitEdition,
|
||||
saveSlotQuantity,
|
||||
savePieceSlotSelection,
|
||||
saveProductSlotSelection,
|
||||
saveSubcomponentSlotSelection,
|
||||
setSlotQuantity,
|
||||
setPieceSlotSelection,
|
||||
setProductSlotSelection,
|
||||
setSubcomponentSlotSelection,
|
||||
resolvePieceLabel,
|
||||
resolveProductLabel,
|
||||
resolveSubcomponentLabel,
|
||||
|
||||
Reference in New Issue
Block a user