Merges the full git history of Inventory_frontend into the monorepo under frontend/. Removes the submodule in favor of a unified repo. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
259 lines
7.8 KiB
TypeScript
259 lines
7.8 KiB
TypeScript
/**
|
|
* Pure helper functions for building, validating and serializing
|
|
* component structure assignment trees.
|
|
*
|
|
* Extracted from useComponentCreate composable to keep file sizes manageable.
|
|
*/
|
|
|
|
import type { StructureAssignmentNode } from '~/components/ComponentStructureAssignmentNode.vue'
|
|
import type {
|
|
ComponentModelPiece,
|
|
ComponentModelProduct,
|
|
ComponentModelStructure,
|
|
ComponentModelStructureNode,
|
|
} from '~/shared/types/inventory'
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Extraction helpers
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export function extractSubcomponents(
|
|
definition: ComponentModelStructure | ComponentModelStructureNode | null | undefined,
|
|
): ComponentModelStructureNode[] {
|
|
if (!definition || typeof definition !== 'object') {
|
|
return []
|
|
}
|
|
const raw = Array.isArray((definition as any).subcomponents)
|
|
? (definition as any).subcomponents
|
|
: Array.isArray((definition as any).subComponents)
|
|
? (definition as any).subComponents
|
|
: []
|
|
return raw.filter(
|
|
(item: unknown): item is ComponentModelStructureNode =>
|
|
!!item && typeof item === 'object',
|
|
)
|
|
}
|
|
|
|
export function extractPiecesFromNode(
|
|
definition: ComponentModelStructure | ComponentModelStructureNode | null | undefined,
|
|
): ComponentModelPiece[] {
|
|
if (!definition || typeof definition !== 'object') {
|
|
return []
|
|
}
|
|
const raw = Array.isArray((definition as any).pieces)
|
|
? (definition as any).pieces
|
|
: []
|
|
return raw.filter(
|
|
(item: unknown): item is ComponentModelPiece =>
|
|
!!item && typeof item === 'object',
|
|
)
|
|
}
|
|
|
|
export function extractProductsFromNode(
|
|
definition: ComponentModelStructure | ComponentModelStructureNode | null | undefined,
|
|
): ComponentModelProduct[] {
|
|
if (!definition || typeof definition !== 'object') {
|
|
return []
|
|
}
|
|
const raw = Array.isArray((definition as any).products)
|
|
? (definition as any).products
|
|
: []
|
|
return raw.filter(
|
|
(item: unknown): item is ComponentModelProduct =>
|
|
!!item && typeof item === 'object',
|
|
)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Assignment tree building
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export function buildAssignmentNode(
|
|
definition: ComponentModelStructureNode | ComponentModelStructure,
|
|
path: string,
|
|
): StructureAssignmentNode {
|
|
const pieces = extractPiecesFromNode(definition).map((piece, index) => ({
|
|
path: `${path}:piece-${index}`,
|
|
definition: piece,
|
|
selectedPieceId: '',
|
|
}))
|
|
|
|
const products = extractProductsFromNode(definition).map((product, index) => ({
|
|
path: `${path}:product-${index}`,
|
|
definition: product,
|
|
selectedProductId: '',
|
|
}))
|
|
|
|
const subcomponents = extractSubcomponents(definition).map(
|
|
(child, index) => buildAssignmentNode(child, `${path}:sub-${index}`),
|
|
)
|
|
|
|
return {
|
|
path,
|
|
definition,
|
|
selectedComponentId: '',
|
|
pieces,
|
|
products,
|
|
subcomponents,
|
|
}
|
|
}
|
|
|
|
export function initializeStructureAssignments(
|
|
structure: ComponentModelStructure | null,
|
|
): StructureAssignmentNode | null {
|
|
if (!structure || typeof structure !== 'object') {
|
|
return null
|
|
}
|
|
return buildAssignmentNode(structure, 'root')
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Validation
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export function hasAssignments(node: StructureAssignmentNode | null): boolean {
|
|
if (!node) {
|
|
return false
|
|
}
|
|
if (node.pieces.length > 0 || node.products.length > 0 || node.subcomponents.length > 0) {
|
|
return true
|
|
}
|
|
return node.subcomponents.some((child) => hasAssignments(child))
|
|
}
|
|
|
|
export function isAssignmentNodeComplete(
|
|
node: StructureAssignmentNode,
|
|
isRootNode = false,
|
|
): boolean {
|
|
const piecesComplete = node.pieces.every(
|
|
(piece) => !!piece.selectedPieceId && piece.selectedPieceId.length > 0,
|
|
)
|
|
const productsComplete = node.products.every(
|
|
(product) => !!product.selectedProductId && product.selectedProductId.length > 0,
|
|
)
|
|
const subcomponentsComplete = node.subcomponents.every(
|
|
(child) =>
|
|
!!child.selectedComponentId
|
|
&& child.selectedComponentId.length > 0
|
|
&& isAssignmentNodeComplete(child, false),
|
|
)
|
|
return (
|
|
piecesComplete
|
|
&& productsComplete
|
|
&& subcomponentsComplete
|
|
&& (isRootNode || !!node.selectedComponentId)
|
|
)
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Serialization
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export function stripNullish(input: Record<string, any>) {
|
|
return Object.fromEntries(
|
|
Object.entries(input).filter(
|
|
([, value]) => value !== null && value !== undefined && value !== '',
|
|
),
|
|
)
|
|
}
|
|
|
|
export function sanitizeStructureDefinition(
|
|
definition: ComponentModelStructureNode,
|
|
) {
|
|
return stripNullish({
|
|
alias: definition.alias ?? null,
|
|
typeComposantId: definition.typeComposantId ?? null,
|
|
typeComposantLabel: definition.typeComposantLabel ?? null,
|
|
modelId: definition.modelId ?? null,
|
|
familyCode: (definition as any).familyCode ?? null,
|
|
})
|
|
}
|
|
|
|
export function sanitizePieceDefinition(definition: ComponentModelPiece) {
|
|
return stripNullish({
|
|
role: (definition as any).role ?? null,
|
|
typePieceId: definition.typePieceId ?? null,
|
|
typePieceLabel: definition.typePieceLabel ?? null,
|
|
reference: definition.reference ?? null,
|
|
familyCode: (definition as any).familyCode ?? null,
|
|
quantity: typeof (definition as any).quantity === 'number' ? (definition as any).quantity : null,
|
|
})
|
|
}
|
|
|
|
export function sanitizeProductDefinition(definition: ComponentModelProduct) {
|
|
return stripNullish({
|
|
role: (definition as any).role ?? null,
|
|
typeProductId: definition.typeProductId ?? null,
|
|
typeProductLabel: (definition as any).typeProductLabel ?? null,
|
|
reference: (definition as any).reference ?? null,
|
|
familyCode: (definition as any).familyCode ?? null,
|
|
})
|
|
}
|
|
|
|
export function serializeStructureAssignments(
|
|
root: StructureAssignmentNode | null,
|
|
) {
|
|
if (!root) {
|
|
return null
|
|
}
|
|
|
|
const serializeNode = (
|
|
assignment: StructureAssignmentNode,
|
|
isRootNode = false,
|
|
): Record<string, any> => {
|
|
const serializedPieces = assignment.pieces
|
|
.filter((piece) => !!piece.selectedPieceId)
|
|
.map((piece) =>
|
|
stripNullish({
|
|
path: piece.path,
|
|
definition: sanitizePieceDefinition(piece.definition),
|
|
selectedPieceId: piece.selectedPieceId,
|
|
}),
|
|
)
|
|
|
|
const serializedProducts = assignment.products
|
|
.filter((product) => !!product.selectedProductId)
|
|
.map((product) =>
|
|
stripNullish({
|
|
path: product.path,
|
|
definition: sanitizeProductDefinition(product.definition),
|
|
selectedProductId: product.selectedProductId,
|
|
}),
|
|
)
|
|
|
|
const serializedSubcomponents = assignment.subcomponents
|
|
.map((child) => serializeNode(child, false))
|
|
.filter((child) => Object.keys(child).length > 0)
|
|
|
|
const base: Record<string, any> = {
|
|
path: assignment.path,
|
|
definition: sanitizeStructureDefinition(assignment.definition),
|
|
}
|
|
|
|
if (!isRootNode) {
|
|
base.selectedComponentId = assignment.selectedComponentId
|
|
}
|
|
if (serializedPieces.length) {
|
|
base.pieces = serializedPieces
|
|
}
|
|
if (serializedProducts.length) {
|
|
base.products = serializedProducts
|
|
}
|
|
if (serializedSubcomponents.length) {
|
|
base.subcomponents = serializedSubcomponents
|
|
}
|
|
|
|
return stripNullish(base)
|
|
}
|
|
|
|
const serializedRoot = serializeNode(root, true)
|
|
if (
|
|
(!serializedRoot.pieces || serializedRoot.pieces.length === 0)
|
|
&& (!serializedRoot.products || serializedRoot.products.length === 0)
|
|
&& (!serializedRoot.subcomponents || serializedRoot.subcomponents.length === 0)
|
|
) {
|
|
return null
|
|
}
|
|
return serializedRoot
|
|
}
|