refactor : merge Inventory_frontend submodule into frontend/ directory
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>
This commit is contained in:
316
frontend/app/shared/model/componentStructure.ts
Normal file
316
frontend/app/shared/model/componentStructure.ts
Normal file
@@ -0,0 +1,316 @@
|
||||
import {
|
||||
createEmptyComponentModelStructure,
|
||||
type ComponentModelCustomField,
|
||||
type ComponentModelStructure,
|
||||
type ComponentModelStructureNode,
|
||||
} from '../types/inventory'
|
||||
|
||||
// Import for internal use in this file
|
||||
import { sanitizeCustomFields, sanitizePieces, sanitizeProducts, sanitizeSubcomponents } from './componentStructureSanitize'
|
||||
import { hydrateCustomFields, hydratePieces, hydrateProducts, hydrateSubcomponents, mapComponentCustomFields, mapComponentPieces, mapComponentProducts, mapSubcomponents } from './componentStructureHydrate'
|
||||
|
||||
// Re-export sanitize functions so existing imports continue to work
|
||||
export {
|
||||
toStringArray,
|
||||
extractFieldValueObject,
|
||||
sanitizeCustomFields,
|
||||
sanitizePieces,
|
||||
sanitizeProducts,
|
||||
sanitizeSubcomponents,
|
||||
} from './componentStructureSanitize'
|
||||
|
||||
// Re-export hydrate functions so existing imports continue to work
|
||||
export {
|
||||
hydrateCustomFields,
|
||||
hydratePieces,
|
||||
hydrateProducts,
|
||||
hydrateSubcomponents,
|
||||
mapComponentCustomFields,
|
||||
mapComponentPieces,
|
||||
mapComponentProducts,
|
||||
mapSubcomponents,
|
||||
} from './componentStructureHydrate'
|
||||
|
||||
export const isPlainObject = (value: unknown): value is Record<string, unknown> => {
|
||||
return value !== null && typeof value === 'object' && !Array.isArray(value)
|
||||
}
|
||||
|
||||
export interface ModelStructurePreview {
|
||||
customFields: number
|
||||
pieces: number
|
||||
products: number
|
||||
subcomponents: number
|
||||
}
|
||||
|
||||
export const defaultStructure = (): ComponentModelStructure => ({
|
||||
...createEmptyComponentModelStructure(),
|
||||
})
|
||||
|
||||
const ensureStructureShape = (input: any): ComponentModelStructure => {
|
||||
const base = createEmptyComponentModelStructure()
|
||||
if (!isPlainObject(input)) {
|
||||
return base
|
||||
}
|
||||
|
||||
const clone: ComponentModelStructure = {
|
||||
...base,
|
||||
customFields: Array.isArray((input as any).customFields) ? (input as any).customFields : [],
|
||||
pieces: Array.isArray((input as any).pieces) ? (input as any).pieces : [],
|
||||
products: Array.isArray((input as any).products) ? (input as any).products : [],
|
||||
subcomponents: Array.isArray((input as any).subcomponents)
|
||||
? (input as any).subcomponents
|
||||
: Array.isArray((input as any).subComponents)
|
||||
? (input as any).subComponents
|
||||
: [],
|
||||
typeComposantId: typeof (input as any).typeComposantId === 'string' ? (input as any).typeComposantId : undefined,
|
||||
typeComposantLabel: typeof (input as any).typeComposantLabel === 'string'
|
||||
? (input as any).typeComposantLabel
|
||||
: undefined,
|
||||
modelId: typeof (input as any).modelId === 'string' ? (input as any).modelId : undefined,
|
||||
familyCode: typeof (input as any).familyCode === 'string' ? (input as any).familyCode : undefined,
|
||||
alias: typeof (input as any).alias === 'string' ? (input as any).alias : undefined,
|
||||
}
|
||||
|
||||
return clone
|
||||
}
|
||||
|
||||
export const cloneStructure = (input: any): ComponentModelStructure => {
|
||||
try {
|
||||
const cloned = JSON.parse(JSON.stringify(input ?? defaultStructure()))
|
||||
return ensureStructureShape(cloned)
|
||||
} catch (_error) {
|
||||
return defaultStructure()
|
||||
}
|
||||
}
|
||||
|
||||
export const normalizeStructureForEditor = (input: any): ComponentModelStructure => {
|
||||
const source = cloneStructure(input)
|
||||
|
||||
const sanitizedCustomFields = sanitizeCustomFields(source.customFields)
|
||||
const customFields = sanitizedCustomFields.map((field) => {
|
||||
const options = Array.isArray(field.options) ? [...field.options] : []
|
||||
const optionsText = options.length ? options.join('\n') : ''
|
||||
const defaultValue =
|
||||
field.defaultValue !== undefined && field.defaultValue !== null && field.defaultValue !== ''
|
||||
? String(field.defaultValue)
|
||||
: null
|
||||
const copy: ComponentModelCustomField = {
|
||||
name: field.name,
|
||||
type: field.type,
|
||||
required: field.required,
|
||||
options,
|
||||
defaultValue,
|
||||
optionsText,
|
||||
id: field.id,
|
||||
customFieldId: field.customFieldId,
|
||||
}
|
||||
return copy
|
||||
})
|
||||
|
||||
const result: ComponentModelStructure = {
|
||||
customFields: customFields as ComponentModelCustomField[],
|
||||
pieces: sanitizePieces(source.pieces),
|
||||
products: sanitizeProducts(source.products),
|
||||
subcomponents: hydrateSubcomponents(source.subcomponents),
|
||||
}
|
||||
|
||||
if (typeof source.typeComposantId === 'string' && source.typeComposantId.length > 0) {
|
||||
result.typeComposantId = source.typeComposantId
|
||||
}
|
||||
if (typeof source.typeComposantLabel === 'string' && source.typeComposantLabel.length > 0) {
|
||||
result.typeComposantLabel = source.typeComposantLabel
|
||||
}
|
||||
if (typeof source.modelId === 'string' && source.modelId.length > 0) {
|
||||
result.modelId = source.modelId
|
||||
}
|
||||
if (typeof source.familyCode === 'string' && source.familyCode.length > 0) {
|
||||
result.familyCode = source.familyCode
|
||||
}
|
||||
if (typeof source.alias === 'string' && source.alias.length > 0) {
|
||||
result.alias = source.alias
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export const normalizeStructureForSave = (input: any): any => {
|
||||
const source = cloneStructure(input)
|
||||
|
||||
const sanitizedCustomFields = sanitizeCustomFields(source.customFields)
|
||||
const backendCustomFields = sanitizedCustomFields.map((field) => {
|
||||
const value: Record<string, any> = {
|
||||
type: field.type,
|
||||
required: !!field.required,
|
||||
}
|
||||
if (field.options && field.options.length) {
|
||||
value.options = field.options
|
||||
}
|
||||
if (field.defaultValue !== undefined && field.defaultValue !== null && field.defaultValue !== '') {
|
||||
value.defaultValue = field.defaultValue
|
||||
}
|
||||
const payload: Record<string, any> = {
|
||||
key: field.name,
|
||||
value,
|
||||
}
|
||||
if (field.id) {
|
||||
payload.id = field.id
|
||||
}
|
||||
if (field.customFieldId) {
|
||||
payload.customFieldId = field.customFieldId
|
||||
}
|
||||
return payload
|
||||
}) as any
|
||||
|
||||
const backendPieces = sanitizePieces(source.pieces).map((piece) => {
|
||||
const payload: Record<string, any> = {}
|
||||
if ((piece as any).familyCode) {
|
||||
payload.familyCode = (piece as any).familyCode
|
||||
}
|
||||
if (piece.typePieceId) {
|
||||
payload.typePieceId = piece.typePieceId
|
||||
}
|
||||
if (piece.typePieceLabel) {
|
||||
payload.typePieceLabel = piece.typePieceLabel
|
||||
}
|
||||
if (piece.reference) {
|
||||
payload.reference = piece.reference
|
||||
}
|
||||
if ((piece as any).quantity !== undefined && (piece as any).quantity >= 1) {
|
||||
payload.quantity = (piece as any).quantity
|
||||
}
|
||||
return payload
|
||||
}) as any
|
||||
|
||||
const backendProducts = sanitizeProducts(source.products).map((product) => {
|
||||
const payload: Record<string, any> = {}
|
||||
if ((product as any).familyCode) {
|
||||
payload.familyCode = (product as any).familyCode
|
||||
}
|
||||
if (product.typeProductId) {
|
||||
payload.typeProductId = product.typeProductId
|
||||
}
|
||||
if (product.role) {
|
||||
payload.role = product.role
|
||||
}
|
||||
return payload
|
||||
}) as any
|
||||
|
||||
const mapSubcomponentForSave = (subcomponent: ComponentModelStructureNode): any => {
|
||||
const payload: Record<string, any> = {}
|
||||
if (subcomponent.typeComposantId) {
|
||||
payload.typeComposantId = subcomponent.typeComposantId
|
||||
}
|
||||
if (subcomponent.modelId) {
|
||||
payload.modelId = subcomponent.modelId
|
||||
}
|
||||
if (subcomponent.familyCode) {
|
||||
payload.familyCode = subcomponent.familyCode
|
||||
}
|
||||
if (subcomponent.alias) {
|
||||
payload.alias = subcomponent.alias
|
||||
}
|
||||
if (Array.isArray(subcomponent.subcomponents) && subcomponent.subcomponents.length) {
|
||||
payload.subcomponents = subcomponent.subcomponents.map(mapSubcomponentForSave)
|
||||
}
|
||||
return payload
|
||||
}
|
||||
|
||||
const backendSubcomponents = sanitizeSubcomponents(source.subcomponents).map(mapSubcomponentForSave) as any
|
||||
|
||||
const result: ComponentModelStructure = {
|
||||
customFields: backendCustomFields,
|
||||
pieces: backendPieces,
|
||||
products: backendProducts,
|
||||
subcomponents: backendSubcomponents,
|
||||
}
|
||||
|
||||
if (typeof source.typeComposantId === 'string' && source.typeComposantId.length > 0) {
|
||||
(result as any).typeComposantId = source.typeComposantId
|
||||
}
|
||||
if (typeof source.typeComposantLabel === 'string' && source.typeComposantLabel.length > 0) {
|
||||
(result as any).typeComposantLabel = source.typeComposantLabel
|
||||
}
|
||||
if (typeof source.modelId === 'string' && source.modelId.length > 0) {
|
||||
(result as any).modelId = source.modelId
|
||||
}
|
||||
if (typeof source.familyCode === 'string' && source.familyCode.length > 0) {
|
||||
(result as any).familyCode = source.familyCode
|
||||
}
|
||||
if (typeof source.alias === 'string' && source.alias.length > 0) {
|
||||
(result as any).alias = source.alias
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
export const hydrateStructureForEditor = (input: any): ComponentModelStructure => {
|
||||
const source = cloneStructure(input)
|
||||
return {
|
||||
customFields: hydrateCustomFields(source.customFields),
|
||||
pieces: hydratePieces(source.pieces),
|
||||
products: hydrateProducts(source.products),
|
||||
subcomponents: hydrateSubcomponents(
|
||||
Array.isArray(source.subcomponents) ? source.subcomponents : (source as any).subComponents,
|
||||
),
|
||||
typeComposantId: source.typeComposantId ?? '',
|
||||
typeComposantLabel: source.typeComposantLabel ?? '',
|
||||
modelId: source.modelId ?? '',
|
||||
familyCode: source.familyCode ?? '',
|
||||
alias: source.alias ?? '',
|
||||
}
|
||||
}
|
||||
|
||||
export const extractStructureFromComponent = (component: any) => {
|
||||
if (!component) {
|
||||
return defaultStructure()
|
||||
}
|
||||
|
||||
const raw = {
|
||||
customFields: mapComponentCustomFields(component.customFields),
|
||||
pieces: mapComponentPieces(component.pieces),
|
||||
products: mapComponentProducts(component.products),
|
||||
subcomponents: mapSubcomponents(
|
||||
Array.isArray(component?.subcomponents)
|
||||
? component.subcomponents
|
||||
: component?.subComponents,
|
||||
),
|
||||
typeComposantId: component?.typeComposantId ?? component?.typeComposant?.id ?? '',
|
||||
typeComposantLabel: component?.typeComposant?.name ?? component?.typeComposantLabel ?? '',
|
||||
modelId: component?.modelId ?? '',
|
||||
familyCode: component?.familyCode ?? component?.typeComposant?.code ?? '',
|
||||
alias: component?.alias ?? component?.name ?? '',
|
||||
}
|
||||
|
||||
return normalizeStructureForEditor(raw)
|
||||
}
|
||||
|
||||
export const computeStructureStats = (structure: any): ModelStructurePreview => {
|
||||
if (!structure || typeof structure !== 'object') {
|
||||
return { customFields: 0, pieces: 0, products: 0, subcomponents: 0 }
|
||||
}
|
||||
|
||||
return {
|
||||
customFields: Array.isArray(structure.customFields) ? structure.customFields.length : 0,
|
||||
pieces: Array.isArray(structure.pieces) ? structure.pieces.length : 0,
|
||||
products: Array.isArray(structure.products) ? structure.products.length : 0,
|
||||
subcomponents: Array.isArray(structure.subcomponents)
|
||||
? structure.subcomponents.length
|
||||
: Array.isArray(structure.subComponents)
|
||||
? structure.subComponents.length
|
||||
: 0,
|
||||
}
|
||||
}
|
||||
|
||||
export const formatStructurePreview = (structure: any) => {
|
||||
const stats = computeStructureStats(structure)
|
||||
if (!stats.customFields && !stats.pieces && !stats.products && !stats.subcomponents) {
|
||||
return 'Structure vide'
|
||||
}
|
||||
|
||||
const segments: string[] = []
|
||||
if (stats.customFields) segments.push(`${stats.customFields} champ(s)`)
|
||||
if (stats.pieces) segments.push(`${stats.pieces} pièce(s)`)
|
||||
if (stats.products) segments.push(`${stats.products} produit(s)`)
|
||||
if (stats.subcomponents) segments.push(`${stats.subcomponents} sous-composant(s)`)
|
||||
return segments.join(' • ')
|
||||
}
|
||||
Reference in New Issue
Block a user