Split 1017 LOC monolith into: - shared/model/componentStructure.ts (~590 LOC) - shared/model/pieceProductStructure.ts (~155 LOC) - shared/model/definitionOverrides.ts (~50 LOC) Rewrite modelUtils.ts as 37 LOC barrel re-export for backward compat. All 11 consumer files unchanged. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
178 lines
5.5 KiB
TypeScript
178 lines
5.5 KiB
TypeScript
import {
|
|
createEmptyPieceModelStructure,
|
|
createEmptyProductModelStructure,
|
|
type PieceModelCustomField,
|
|
type PieceModelProduct,
|
|
type PieceModelStructure,
|
|
type PieceModelStructureEditorField,
|
|
type PieceModelStructureForEditor,
|
|
type ProductModelStructure,
|
|
} from '../types/inventory'
|
|
import { isPlainObject, sanitizeProducts, hydrateProducts } from './componentStructure'
|
|
|
|
export const defaultPieceStructure = (): PieceModelStructure => ({
|
|
...createEmptyPieceModelStructure(),
|
|
})
|
|
|
|
export const defaultProductStructure = (): ProductModelStructure => ({
|
|
...createEmptyProductModelStructure(),
|
|
})
|
|
|
|
const ensurePieceStructureShape = (input: any): PieceModelStructure => {
|
|
const base = createEmptyPieceModelStructure()
|
|
if (!isPlainObject(input)) {
|
|
return base
|
|
}
|
|
|
|
const clone: PieceModelStructure = {
|
|
...base,
|
|
customFields: Array.isArray((input as any).customFields) ? (input as any).customFields : [],
|
|
products: Array.isArray((input as any).products) ? (input as any).products : [],
|
|
}
|
|
|
|
for (const [key, value] of Object.entries(input as Record<string, unknown>)) {
|
|
if (key === 'customFields' || key === 'products') {
|
|
continue
|
|
}
|
|
clone[key] = value
|
|
}
|
|
|
|
return clone
|
|
}
|
|
|
|
export const clonePieceStructure = (input: any): PieceModelStructure => {
|
|
try {
|
|
const cloned = JSON.parse(JSON.stringify(input ?? defaultPieceStructure()))
|
|
return ensurePieceStructureShape(cloned)
|
|
} catch (_error) {
|
|
return defaultPieceStructure()
|
|
}
|
|
}
|
|
|
|
export const cloneProductStructure = (input: any): ProductModelStructure => {
|
|
return clonePieceStructure(input)
|
|
}
|
|
|
|
const sanitizePieceCustomFields = (fields: any[]): PieceModelCustomField[] => {
|
|
if (!Array.isArray(fields)) {
|
|
return []
|
|
}
|
|
|
|
return fields
|
|
.map((field, index) => {
|
|
const name = typeof field?.name === 'string' ? field.name.trim() : ''
|
|
if (!name) {
|
|
return null
|
|
}
|
|
|
|
const type = typeof field?.type === 'string' && field.type ? field.type : 'text'
|
|
const required = !!field?.required
|
|
|
|
let options: string[] | undefined
|
|
if (type === 'select') {
|
|
const rawOptions = typeof field?.optionsText === 'string'
|
|
? field.optionsText
|
|
: Array.isArray(field?.options)
|
|
? field.options.join('\n')
|
|
: ''
|
|
const parsed = rawOptions
|
|
.split(/\r?\n/)
|
|
.map((option: string) => option.trim())
|
|
.filter((option: string) => option.length > 0)
|
|
options = parsed.length > 0 ? parsed : undefined
|
|
}
|
|
|
|
const result: PieceModelCustomField = { name, type, required }
|
|
if (options) {
|
|
result.options = options
|
|
}
|
|
const orderIndex = typeof field?.orderIndex === 'number' ? field.orderIndex : index
|
|
result.orderIndex = orderIndex
|
|
return result
|
|
})
|
|
.filter((field): field is PieceModelCustomField => !!field)
|
|
}
|
|
|
|
const sanitizePieceProducts = (products: any[]): PieceModelProduct[] => {
|
|
return sanitizeProducts(products) as PieceModelProduct[]
|
|
}
|
|
|
|
export const normalizePieceStructureForSave = (input: any): PieceModelStructure => {
|
|
const source = clonePieceStructure(input)
|
|
const restEntries = Object.entries(source).filter(
|
|
([key]) => key !== 'customFields' && key !== 'products',
|
|
)
|
|
return {
|
|
...Object.fromEntries(restEntries),
|
|
products: sanitizePieceProducts(source.products || []),
|
|
customFields: sanitizePieceCustomFields(source.customFields),
|
|
}
|
|
}
|
|
|
|
const hydratePieceCustomFields = (fields: any[]): PieceModelStructureEditorField[] => {
|
|
if (!Array.isArray(fields)) {
|
|
return []
|
|
}
|
|
|
|
return fields.map((field, index) => ({
|
|
name: field?.name ?? '',
|
|
type: field?.type ?? 'text',
|
|
required: !!field?.required,
|
|
options: Array.isArray(field?.options) ? field.options : undefined,
|
|
optionsText: typeof field?.optionsText === 'string'
|
|
? field.optionsText
|
|
: Array.isArray(field?.options)
|
|
? field.options.join('\n')
|
|
: '',
|
|
orderIndex: typeof field?.orderIndex === 'number' ? field.orderIndex : index,
|
|
}))
|
|
}
|
|
|
|
export const hydratePieceStructureForEditor = (input: any): PieceModelStructureForEditor => {
|
|
const source = clonePieceStructure(input)
|
|
const payload: PieceModelStructureForEditor = {
|
|
...Object.fromEntries(
|
|
Object.entries(source).filter(([key]) => key !== 'customFields' && key !== 'products'),
|
|
),
|
|
products: hydrateProducts(source.products || []) as PieceModelProduct[],
|
|
customFields: hydratePieceCustomFields(source.customFields),
|
|
}
|
|
return payload
|
|
}
|
|
|
|
export const formatPieceStructurePreview = (structure: any) => {
|
|
if (!structure || typeof structure !== 'object') {
|
|
return 'Aucun champ personnalisé'
|
|
}
|
|
|
|
const customFields = Array.isArray((structure as any).customFields)
|
|
? (structure as any).customFields.length
|
|
: 0
|
|
const products = Array.isArray((structure as any).products)
|
|
? (structure as any).products.length
|
|
: 0
|
|
|
|
if (!customFields && !products) {
|
|
return 'Aucun produit ni champ personnalisé'
|
|
}
|
|
|
|
const segments: string[] = []
|
|
if (products) {
|
|
segments.push(`${products} produit(s)`)
|
|
}
|
|
if (customFields) {
|
|
segments.push(`${customFields} champ(s) personnalisé(s)`)
|
|
}
|
|
|
|
return segments.join(' · ')
|
|
}
|
|
|
|
export const normalizeProductStructureForSave = (input: any): ProductModelStructure =>
|
|
normalizePieceStructureForSave(input)
|
|
|
|
export const hydrateProductStructureForEditor = (input: any) =>
|
|
hydratePieceStructureForEditor(input)
|
|
|
|
export const formatProductStructurePreview = (structure: any) =>
|
|
formatPieceStructurePreview(structure)
|