feat(component-edit) : add interactive slot selectors for pieces, products and subcomponents

Replace read-only selections display with PieceSelect, ProductSelect, ComposantSelect
components that allow changing the assigned item in each slot directly from the edit page.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matthieu
2026-03-13 16:40:11 +01:00
parent 07cad19988
commit 271844efb1
4 changed files with 397 additions and 48 deletions

View File

@@ -270,6 +270,94 @@ export function useComponentEdit(componentId: string) {
}
})
// --- Slot selection entries (for selectors) ---
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}`,
}))
})
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}`,
}))
})
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}`,
}))
})
const savePieceSlotSelection = async (slotId: string, selectedPieceId: string | null) => {
try {
await patch(`/composant-piece-slots/${slotId}`, { selectedPieceId })
// Update local structure
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')
}
catch (error: any) {
toast.showError(error?.message || 'Erreur lors de la mise à jour')
}
}
const saveProductSlotSelection = async (slotId: string, selectedProductId: string | null) => {
try {
await patch(`/composant-product-slots/${slotId}`, { selectedProductId })
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')
}
catch (error: any) {
toast.showError(error?.message || 'Erreur lors de la mise à jour')
}
}
const saveSubcomponentSlotSelection = async (slotId: string, selectedComposantId: string | null) => {
try {
await patch(`/composant-subcomponent-slots/${slotId}`, { selectedComposantId })
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')
}
catch (error: any) {
toast.showError(error?.message || 'Erreur lors de la mise à jour')
}
}
const saveSlotQuantity = async (entry: SelectionEntry) => {
const slotId = entry.slotId
const quantity = typeof entry._definition?.quantity === 'number'
@@ -423,14 +511,12 @@ export function useComponentEdit(componentId: string) {
])
loading.value = false
// Defer bulk catalog loads — only needed when component has structure selections
if (component.value?.structure) {
Promise.allSettled([
loadPieces({ itemsPerPage: 200 }),
loadProducts({ itemsPerPage: 200 }),
loadComposants({ itemsPerPage: 200 }),
]).catch(() => {})
}
// Load catalogs for slot selectors
Promise.allSettled([
loadPieces({ itemsPerPage: 200 }),
loadProducts({ itemsPerPage: 200 }),
loadComposants({ itemsPerPage: 200 }),
]).catch(() => {})
})
return {
@@ -456,6 +542,9 @@ export function useComponentEdit(componentId: string) {
selectedType,
selectedTypeStructure,
structureSelections,
pieceSlotEntries,
productSlotEntries,
subcomponentSlotEntries,
// History
history,
@@ -470,6 +559,9 @@ export function useComponentEdit(componentId: string) {
refreshDocuments,
submitEdition,
saveSlotQuantity,
savePieceSlotSelection,
saveProductSlotSelection,
saveSubcomponentSlotSelection,
resolvePieceLabel,
resolveProductLabel,
resolveSubcomponentLabel,