Files
Inventory_frontend/app/shared/utils/structureSelectionUtils.ts
Matthieu 5912216a89 fix(piece) : persist slot quantity on blur and send prix as string
- Save composant piece slot quantity via PATCH on blur
- Pass slotId through hierarchy and selection entries
- Send prix as string (not number) to match backend expectation
- Show quantity in view mode when > 1
- Allow quantity edit for all pieces (not just root-level)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 11:19:09 +01:00

114 lines
4.0 KiB
TypeScript

export type SelectionEntry = {
id: string
path: string
requirementLabel: string
resolvedName: string
quantity?: number
slotId?: string
_definition?: Record<string, any>
}
export type StructureSelectionResult = {
pieces: SelectionEntry[]
products: SelectionEntry[]
components: SelectionEntry[]
}
type CatalogMap = Map<string, { name?: string, [key: string]: any }>
type LabelResolvers = {
resolvePieceLabel: (definition: Record<string, any>) => string
resolveProductLabel: (definition: Record<string, any>) => string
resolveSubcomponentLabel: (definition: Record<string, any>) => string
}
const isNonEmptyString = (value: unknown): value is string =>
typeof value === 'string' && value.trim().length > 0
export function collectStructureSelections(
root: any,
catalogs: {
pieceCatalogMap: CatalogMap
productCatalogMap: CatalogMap
componentCatalogMap: CatalogMap
},
resolvers: LabelResolvers,
): StructureSelectionResult {
const piecesSelected: SelectionEntry[] = []
const productsSelected: SelectionEntry[] = []
const componentsSelected: SelectionEntry[] = []
if (!root || typeof root !== 'object') {
return { pieces: piecesSelected, products: productsSelected, components: componentsSelected }
}
const visitNode = (node: any, fallbackPath = 'racine') => {
if (!node || typeof node !== 'object') {
return
}
const nodePath = isNonEmptyString(node.path) ? node.path : fallbackPath
const nodePieces = Array.isArray(node.pieces) ? node.pieces : []
nodePieces.forEach((entry: any, index: number) => {
const selectedId = entry?.selectedPieceId
if (!isNonEmptyString(selectedId)) {
return
}
const definition = entry?.definition ?? entry
const catalogPiece = catalogs.pieceCatalogMap.get(selectedId)
piecesSelected.push({
id: selectedId,
path: isNonEmptyString(entry?.path) ? entry.path : `${nodePath}:piece-${index + 1}`,
requirementLabel: resolvers.resolvePieceLabel(definition),
resolvedName: catalogPiece?.name || selectedId,
quantity: typeof definition?.quantity === 'number' ? definition.quantity : undefined,
slotId: isNonEmptyString(entry?.slotId) ? entry.slotId : undefined,
_definition: definition,
})
})
const nodeProducts = Array.isArray(node.products) ? node.products : []
nodeProducts.forEach((entry: any, index: number) => {
const selectedId = entry?.selectedProductId
if (!isNonEmptyString(selectedId)) {
return
}
const definition = entry?.definition ?? entry
const catalogProduct = catalogs.productCatalogMap.get(selectedId)
productsSelected.push({
id: selectedId,
path: isNonEmptyString(entry?.path) ? entry.path : `${nodePath}:product-${index + 1}`,
requirementLabel: resolvers.resolveProductLabel(definition),
resolvedName: catalogProduct?.name || selectedId,
})
})
const nodeChildren = Array.isArray(node.subcomponents)
? node.subcomponents
: Array.isArray(node.subComponents)
? node.subComponents
: []
nodeChildren.forEach((child: any, index: number) => {
const selectedId = child?.selectedComponentId
if (isNonEmptyString(selectedId)) {
const definition = child?.definition ?? child
const catalogComponent = catalogs.componentCatalogMap.get(selectedId)
componentsSelected.push({
id: selectedId,
path: isNonEmptyString(child?.path) ? child.path : `${nodePath}:subcomponent-${index + 1}`,
requirementLabel: resolvers.resolveSubcomponentLabel(definition),
resolvedName: catalogComponent?.name || selectedId,
})
}
visitNode(child, isNonEmptyString(child?.path) ? child.path : `${nodePath}:subcomponent-${index + 1}`)
})
}
visitNode(root, isNonEmptyString(root?.path) ? root.path : 'racine')
return { pieces: piecesSelected, products: productsSelected, components: componentsSelected }
}