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 pieceSlotEntries = computed(() => {
|
||||||
const structure = component.value?.structure
|
const structure = component.value?.structure
|
||||||
if (!structure?.pieces) return []
|
if (!structure?.pieces) return []
|
||||||
return (structure.pieces as any[]).map((slot: any, i: number) => ({
|
return (structure.pieces as any[]).map((slot: any, i: number) => {
|
||||||
slotId: slot.slotId,
|
const edits = slotEdits.pieces[slot.slotId]
|
||||||
typePieceId: slot.typePieceId,
|
return {
|
||||||
selectedPieceId: slot.selectedPieceId ?? null,
|
slotId: slot.slotId,
|
||||||
quantity: slot.quantity ?? 1,
|
typePieceId: slot.typePieceId,
|
||||||
position: slot.position ?? i,
|
selectedPieceId: edits && 'selectedPieceId' in edits ? edits.selectedPieceId : (slot.selectedPieceId ?? null),
|
||||||
label: pieceTypeLabelMap.value[slot.typePieceId] || `Pièce #${i + 1}`,
|
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 productSlotEntries = computed(() => {
|
||||||
const structure = component.value?.structure
|
const structure = component.value?.structure
|
||||||
if (!structure?.products) return []
|
if (!structure?.products) return []
|
||||||
return (structure.products as any[]).map((slot: any, i: number) => ({
|
return (structure.products as any[]).map((slot: any, i: number) => {
|
||||||
slotId: slot.slotId,
|
const edits = slotEdits.products[slot.slotId]
|
||||||
typeProductId: slot.typeProductId,
|
return {
|
||||||
selectedProductId: slot.selectedProductId ?? null,
|
slotId: slot.slotId,
|
||||||
familyCode: slot.familyCode,
|
typeProductId: slot.typeProductId,
|
||||||
position: slot.position ?? i,
|
selectedProductId: edits && 'selectedProductId' in edits ? edits.selectedProductId : (slot.selectedProductId ?? null),
|
||||||
label: productTypeLabelMap.value[slot.typeProductId] || `Produit #${i + 1}`,
|
familyCode: slot.familyCode,
|
||||||
}))
|
position: slot.position ?? i,
|
||||||
|
label: productTypeLabelMap.value[slot.typeProductId] || `Produit #${i + 1}`,
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const subcomponentSlotEntries = computed(() => {
|
const subcomponentSlotEntries = computed(() => {
|
||||||
const structure = component.value?.structure
|
const structure = component.value?.structure
|
||||||
if (!structure?.subcomponents) return []
|
if (!structure?.subcomponents) return []
|
||||||
return (structure.subcomponents as any[]).map((slot: any, i: number) => ({
|
return (structure.subcomponents as any[]).map((slot: any, i: number) => {
|
||||||
slotId: slot.slotId,
|
const edits = slotEdits.subcomponents[slot.slotId]
|
||||||
typeComposantId: slot.typeComposantId,
|
return {
|
||||||
selectedComponentId: slot.selectedComponentId ?? null,
|
slotId: slot.slotId,
|
||||||
alias: slot.alias,
|
typeComposantId: slot.typeComposantId,
|
||||||
familyCode: slot.familyCode,
|
selectedComponentId: edits && 'selectedComposantId' in edits ? edits.selectedComposantId : (slot.selectedComponentId ?? null),
|
||||||
position: slot.position ?? i,
|
alias: slot.alias,
|
||||||
label: slot.alias || `Sous-composant #${i + 1}`,
|
familyCode: slot.familyCode,
|
||||||
}))
|
position: slot.position ?? i,
|
||||||
|
label: slot.alias || `Sous-composant #${i + 1}`,
|
||||||
|
}
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const savePieceSlotSelection = async (slotId: string, selectedPieceId: string | null) => {
|
const setPieceSlotSelection = (slotId: string, selectedPieceId: string | null) => {
|
||||||
const result = await patch(`/composant-piece-slots/${slotId}`, { selectedPieceId })
|
slotEdits.pieces[slotId] = { ...slotEdits.pieces[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 saveProductSlotSelection = async (slotId: string, selectedProductId: string | null) => {
|
const setProductSlotSelection = (slotId: string, selectedProductId: string | null) => {
|
||||||
const result = await patch(`/composant-product-slots/${slotId}`, { selectedProductId })
|
slotEdits.products[slotId] = { ...slotEdits.products[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 saveSubcomponentSlotSelection = async (slotId: string, selectedComposantId: string | null) => {
|
const setSubcomponentSlotSelection = (slotId: string, selectedComposantId: string | null) => {
|
||||||
const result = await patch(`/composant-subcomponent-slots/${slotId}`, { selectedComposantId })
|
slotEdits.subcomponents[slotId] = { ...slotEdits.subcomponents[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 saveSlotQuantity = async (slotId: string, quantity: number) => {
|
const setSlotQuantity = (slotId: string, quantity: number) => {
|
||||||
if (!slotId || quantity < 1) return
|
if (!slotId || quantity < 1) return
|
||||||
const result = await patch(`/composant-piece-slots/${slotId}`, { quantity: Math.max(1, quantity) })
|
slotEdits.pieces[slotId] = { ...slotEdits.pieces[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')
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const submitEdition = async () => {
|
const submitEdition = async () => {
|
||||||
@@ -403,6 +386,54 @@ export function useComponentEdit(componentId: string) {
|
|||||||
],
|
],
|
||||||
{ customFieldInputs, upsertCustomFieldValue, updateCustomFieldValue, toast },
|
{ 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.')
|
toast.showSuccess('Composant mis à jour avec succès.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -538,10 +569,10 @@ export function useComponentEdit(componentId: string) {
|
|||||||
handleFilesAdded,
|
handleFilesAdded,
|
||||||
refreshDocuments,
|
refreshDocuments,
|
||||||
submitEdition,
|
submitEdition,
|
||||||
saveSlotQuantity,
|
setSlotQuantity,
|
||||||
savePieceSlotSelection,
|
setPieceSlotSelection,
|
||||||
saveProductSlotSelection,
|
setProductSlotSelection,
|
||||||
saveSubcomponentSlotSelection,
|
setSubcomponentSlotSelection,
|
||||||
resolvePieceLabel,
|
resolvePieceLabel,
|
||||||
resolveProductLabel,
|
resolveProductLabel,
|
||||||
resolveSubcomponentLabel,
|
resolveSubcomponentLabel,
|
||||||
|
|||||||
@@ -198,7 +198,7 @@
|
|||||||
:model-value="slot.selectedPieceId"
|
:model-value="slot.selectedPieceId"
|
||||||
:disabled="!canEdit || saving"
|
:disabled="!canEdit || saving"
|
||||||
:type-piece-id="slot.typePieceId"
|
:type-piece-id="slot.typePieceId"
|
||||||
@update:model-value="(value) => savePieceSlotSelection(slot.slotId, value)"
|
@update:model-value="(value) => setPieceSlotSelection(slot.slotId, value)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-20 shrink-0">
|
<div class="w-20 shrink-0">
|
||||||
@@ -209,7 +209,7 @@
|
|||||||
class="input input-bordered input-sm w-full text-center"
|
class="input input-bordered input-sm w-full text-center"
|
||||||
:disabled="!canEdit || saving"
|
:disabled="!canEdit || saving"
|
||||||
title="Quantité"
|
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>
|
||||||
</div>
|
</div>
|
||||||
@@ -232,7 +232,7 @@
|
|||||||
:model-value="slot.selectedProductId"
|
:model-value="slot.selectedProductId"
|
||||||
:disabled="!canEdit || saving"
|
:disabled="!canEdit || saving"
|
||||||
:type-product-id="slot.typeProductId"
|
:type-product-id="slot.typeProductId"
|
||||||
@update:model-value="(value) => saveProductSlotSelection(slot.slotId, value)"
|
@update:model-value="(value) => setProductSlotSelection(slot.slotId, value)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -253,7 +253,7 @@
|
|||||||
:model-value="slot.selectedComponentId"
|
:model-value="slot.selectedComponentId"
|
||||||
:disabled="!canEdit || saving"
|
:disabled="!canEdit || saving"
|
||||||
:type-composant-id="slot.typeComposantId"
|
:type-composant-id="slot.typeComposantId"
|
||||||
@update:model-value="(value) => saveSubcomponentSlotSelection(slot.slotId, value)"
|
@update:model-value="(value) => setSubcomponentSlotSelection(slot.slotId, value)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -381,10 +381,10 @@ const {
|
|||||||
removeDocument,
|
removeDocument,
|
||||||
handleFilesAdded,
|
handleFilesAdded,
|
||||||
submitEdition,
|
submitEdition,
|
||||||
saveSlotQuantity,
|
setSlotQuantity,
|
||||||
savePieceSlotSelection,
|
setPieceSlotSelection,
|
||||||
saveProductSlotSelection,
|
setProductSlotSelection,
|
||||||
saveSubcomponentSlotSelection,
|
setSubcomponentSlotSelection,
|
||||||
resolvePieceLabel,
|
resolvePieceLabel,
|
||||||
resolveProductLabel,
|
resolveProductLabel,
|
||||||
resolveSubcomponentLabel,
|
resolveSubcomponentLabel,
|
||||||
|
|||||||
Reference in New Issue
Block a user