feat(skeleton) : remove skeleton JSON field references — use structure API field directly
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -390,7 +390,7 @@ const loadProductType = async () => {
|
||||
// Try using the expanded typeProduct from entity response first
|
||||
const embedded = product.value?.typeProduct
|
||||
if (embedded && typeof embedded === 'object' && embedded.id) {
|
||||
const embeddedStructure = embedded.structure ?? embedded.productSkeleton ?? null
|
||||
const embeddedStructure = embedded.structure ?? null
|
||||
if (embeddedStructure) {
|
||||
productType.value = embedded
|
||||
structure.value = normalizeProductStructureForSave(embeddedStructure)
|
||||
@@ -406,7 +406,7 @@ const loadProductType = async () => {
|
||||
try {
|
||||
const type = await getModelType(product.value.typeProductId)
|
||||
productType.value = type
|
||||
structure.value = normalizeProductStructureForSave(type?.structure ?? type?.productSkeleton ?? null)
|
||||
structure.value = normalizeProductStructureForSave(type?.structure ?? null)
|
||||
} catch (error) {
|
||||
console.error('Erreur lors du chargement du type de produit:', error)
|
||||
productType.value = embedded ?? null
|
||||
|
||||
@@ -46,9 +46,6 @@ export interface ModelType extends BaseModelTypePayload {
|
||||
updatedAt: string;
|
||||
category: ModelCategory;
|
||||
structure: ModelTypeStructure;
|
||||
componentSkeleton?: ComponentModelStructure | null;
|
||||
pieceSkeleton?: PieceModelStructure | null;
|
||||
productSkeleton?: ProductModelStructure | null;
|
||||
}
|
||||
|
||||
export interface ModelTypeListParams {
|
||||
@@ -86,42 +83,9 @@ const normalizeModelType = (item: any): ModelType => {
|
||||
if (!item || typeof item !== 'object') {
|
||||
return item as ModelType;
|
||||
}
|
||||
if (!item.structure) {
|
||||
if (item.category === 'COMPONENT' && item.componentSkeleton) {
|
||||
item.structure = item.componentSkeleton;
|
||||
} else if (item.category === 'PIECE' && item.pieceSkeleton) {
|
||||
item.structure = item.pieceSkeleton;
|
||||
} else if (item.category === 'PRODUCT' && item.productSkeleton) {
|
||||
item.structure = item.productSkeleton;
|
||||
}
|
||||
}
|
||||
return item as ModelType;
|
||||
};
|
||||
|
||||
const mapStructureToSkeleton = <T extends Record<string, any>>(payload: T): T => {
|
||||
if (!payload || typeof payload !== 'object') {
|
||||
return payload;
|
||||
}
|
||||
if (!('structure' in payload)) {
|
||||
return payload;
|
||||
}
|
||||
const structure = (payload as any).structure;
|
||||
if (!structure) {
|
||||
return payload;
|
||||
}
|
||||
const category = (payload as any).category;
|
||||
const next = { ...payload } as Record<string, any>;
|
||||
if (category === 'COMPONENT') {
|
||||
next.componentSkeleton = structure;
|
||||
} else if (category === 'PIECE') {
|
||||
next.pieceSkeleton = structure;
|
||||
} else if (category === 'PRODUCT') {
|
||||
next.productSkeleton = structure;
|
||||
}
|
||||
delete next.structure;
|
||||
return next as T;
|
||||
};
|
||||
|
||||
export async function listModelTypes(params: ModelTypeListParams = {}, opts: { signal?: AbortSignal } = {}) {
|
||||
const requestFetch = useRequestFetch();
|
||||
const query: Record<string, string | number> = {};
|
||||
@@ -178,28 +142,26 @@ export async function listModelTypes(params: ModelTypeListParams = {}, opts: { s
|
||||
|
||||
export function createModelType(payload: ModelTypePayload, opts: { signal?: AbortSignal } = {}) {
|
||||
const requestFetch = useRequestFetch();
|
||||
const mappedPayload = mapStructureToSkeleton(payload);
|
||||
return requestFetch<ModelType>(ENDPOINT, createOptions({
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/ld+json',
|
||||
Accept: 'application/ld+json',
|
||||
},
|
||||
body: mappedPayload,
|
||||
body: payload,
|
||||
signal: opts.signal,
|
||||
})).then(normalizeModelType);
|
||||
}
|
||||
|
||||
export function updateModelType(id: string, payload: Partial<ModelTypePayload>, opts: { signal?: AbortSignal } = {}) {
|
||||
const requestFetch = useRequestFetch();
|
||||
const mappedPayload = mapStructureToSkeleton(payload);
|
||||
return requestFetch<ModelType>(`${ENDPOINT}/${id}`, createOptions({
|
||||
method: 'PATCH',
|
||||
headers: {
|
||||
'Content-Type': 'application/merge-patch+json',
|
||||
Accept: 'application/ld+json',
|
||||
},
|
||||
body: mappedPayload,
|
||||
body: payload,
|
||||
signal: opts.signal,
|
||||
})).then(normalizeModelType);
|
||||
}
|
||||
|
||||
@@ -196,34 +196,19 @@ export const getProductDisplay = (
|
||||
|
||||
const structuralCandidates = [
|
||||
source.products,
|
||||
source.productSkeleton,
|
||||
(source.definition as AnyRecord)?.products,
|
||||
(source.definition as AnyRecord)?.productSkeleton,
|
||||
((source.definition as AnyRecord)?.structure as AnyRecord)?.products,
|
||||
((source.definition as AnyRecord)?.structure as AnyRecord)?.productSkeleton,
|
||||
(source.structure as AnyRecord)?.products,
|
||||
(source.structure as AnyRecord)?.productSkeleton,
|
||||
(source.requirement as AnyRecord)?.products,
|
||||
(source.requirement as AnyRecord)?.productSkeleton,
|
||||
((source.requirement as AnyRecord)?.structure as AnyRecord)?.products,
|
||||
((source.requirement as AnyRecord)?.structure as AnyRecord)?.productSkeleton,
|
||||
((source.requirement as AnyRecord)?.componentSkeleton as AnyRecord)?.products,
|
||||
(source.typeComposant as AnyRecord)?.products,
|
||||
(source.typeComposant as AnyRecord)?.productSkeleton,
|
||||
((source.typeComposant as AnyRecord)?.structure as AnyRecord)?.products,
|
||||
((source.typeComposant as AnyRecord)?.structure as AnyRecord)?.productSkeleton,
|
||||
(source.originalComposant as AnyRecord)?.products,
|
||||
(source.originalComposant as AnyRecord)?.productSkeleton,
|
||||
((source.originalComposant as AnyRecord)?.definition as AnyRecord)?.products,
|
||||
((source.originalComposant as AnyRecord)?.definition as AnyRecord)?.productSkeleton,
|
||||
(((source.originalComposant as AnyRecord)?.definition as AnyRecord)?.structure as AnyRecord)?.products,
|
||||
(((source.originalComposant as AnyRecord)?.definition as AnyRecord)?.structure as AnyRecord)?.productSkeleton,
|
||||
(source.originalComponent as AnyRecord)?.products,
|
||||
(source.originalComponent as AnyRecord)?.productSkeleton,
|
||||
((source.originalComponent as AnyRecord)?.definition as AnyRecord)?.products,
|
||||
((source.originalComponent as AnyRecord)?.definition as AnyRecord)?.productSkeleton,
|
||||
(((source.originalComponent as AnyRecord)?.definition as AnyRecord)?.structure as AnyRecord)?.products,
|
||||
(((source.originalComponent as AnyRecord)?.definition as AnyRecord)?.structure as AnyRecord)?.productSkeleton,
|
||||
]
|
||||
|
||||
const structuralProducts = structuralCandidates
|
||||
|
||||
@@ -50,60 +50,55 @@ beforeEach(() => {
|
||||
// normalizeModelType (tested via getModelType which calls .then(normalizeModelType))
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('normalizeModelType (via getModelType)', () => {
|
||||
it('maps componentSkeleton to structure for COMPONENT', async () => {
|
||||
const skeleton = { customFields: [{ name: 'Weight' }] }
|
||||
it('returns structure as-is for COMPONENT', async () => {
|
||||
const structure = { customFields: [{ name: 'Weight' }] }
|
||||
mockFetch.mockResolvedValue(fakeModelType({
|
||||
category: 'COMPONENT',
|
||||
structure: null,
|
||||
componentSkeleton: skeleton as any,
|
||||
structure: structure as any,
|
||||
}))
|
||||
|
||||
const result = await getModelType('mt-1')
|
||||
expect(result.structure).toEqual(skeleton)
|
||||
expect(result.structure).toEqual(structure)
|
||||
})
|
||||
|
||||
it('maps pieceSkeleton to structure for PIECE', async () => {
|
||||
const skeleton = { customFields: [{ name: 'Size' }] }
|
||||
it('returns structure as-is for PIECE', async () => {
|
||||
const structure = { customFields: [{ name: 'Size' }] }
|
||||
mockFetch.mockResolvedValue(fakeModelType({
|
||||
category: 'PIECE',
|
||||
structure: null,
|
||||
pieceSkeleton: skeleton as any,
|
||||
structure: structure as any,
|
||||
}))
|
||||
|
||||
const result = await getModelType('mt-1')
|
||||
expect(result.structure).toEqual(skeleton)
|
||||
expect(result.structure).toEqual(structure)
|
||||
})
|
||||
|
||||
it('maps productSkeleton to structure for PRODUCT', async () => {
|
||||
const skeleton = { customFields: [{ name: 'Brand' }] }
|
||||
it('returns structure as-is for PRODUCT', async () => {
|
||||
const structure = { customFields: [{ name: 'Brand' }] }
|
||||
mockFetch.mockResolvedValue(fakeModelType({
|
||||
category: 'PRODUCT',
|
||||
structure: null,
|
||||
productSkeleton: skeleton as any,
|
||||
structure: structure as any,
|
||||
}))
|
||||
|
||||
const result = await getModelType('mt-1')
|
||||
expect(result.structure).toEqual(skeleton)
|
||||
expect(result.structure).toEqual(structure)
|
||||
})
|
||||
|
||||
it('does not override existing structure', async () => {
|
||||
const existing = { customFields: [{ name: 'Existing' }] }
|
||||
it('preserves null structure', async () => {
|
||||
mockFetch.mockResolvedValue(fakeModelType({
|
||||
category: 'COMPONENT',
|
||||
structure: existing as any,
|
||||
componentSkeleton: { customFields: [{ name: 'Skeleton' }] } as any,
|
||||
structure: null,
|
||||
}))
|
||||
|
||||
const result = await getModelType('mt-1')
|
||||
expect(result.structure).toEqual(existing)
|
||||
expect(result.structure).toBeNull()
|
||||
})
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// createModelType — maps structure to skeleton
|
||||
// createModelType — sends structure directly
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('createModelType', () => {
|
||||
it('sends POST with componentSkeleton for COMPONENT', async () => {
|
||||
it('sends POST with structure for COMPONENT', async () => {
|
||||
const structure = { customFields: [] }
|
||||
mockFetch.mockResolvedValue(fakeModelType())
|
||||
|
||||
@@ -120,11 +115,10 @@ describe('createModelType', () => {
|
||||
const [endpoint, options] = mockFetch.mock.calls[0]
|
||||
expect(endpoint).toBe('/model_types')
|
||||
expect(options.method).toBe('POST')
|
||||
expect(options.body.componentSkeleton).toEqual(structure)
|
||||
expect(options.body.structure).toBeUndefined()
|
||||
expect(options.body.structure).toEqual(structure)
|
||||
})
|
||||
|
||||
it('sends POST with pieceSkeleton for PIECE', async () => {
|
||||
it('sends POST with structure for PIECE', async () => {
|
||||
const structure = { customFields: [], products: [] }
|
||||
mockFetch.mockResolvedValue(fakeModelType({ category: 'PIECE' }))
|
||||
|
||||
@@ -136,11 +130,10 @@ describe('createModelType', () => {
|
||||
})
|
||||
|
||||
const [, options] = mockFetch.mock.calls[0]
|
||||
expect(options.body.pieceSkeleton).toEqual(structure)
|
||||
expect(options.body.structure).toBeUndefined()
|
||||
expect(options.body.structure).toEqual(structure)
|
||||
})
|
||||
|
||||
it('sends POST with productSkeleton for PRODUCT', async () => {
|
||||
it('sends POST with structure for PRODUCT', async () => {
|
||||
const structure = { customFields: [] }
|
||||
mockFetch.mockResolvedValue(fakeModelType({ category: 'PRODUCT' }))
|
||||
|
||||
@@ -152,15 +145,15 @@ describe('createModelType', () => {
|
||||
})
|
||||
|
||||
const [, options] = mockFetch.mock.calls[0]
|
||||
expect(options.body.productSkeleton).toEqual(structure)
|
||||
expect(options.body.structure).toEqual(structure)
|
||||
})
|
||||
})
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// updateModelType — maps structure to skeleton
|
||||
// updateModelType — sends structure directly
|
||||
// ---------------------------------------------------------------------------
|
||||
describe('updateModelType', () => {
|
||||
it('sends PATCH with correct endpoint and skeleton', async () => {
|
||||
it('sends PATCH with correct endpoint and structure', async () => {
|
||||
const structure = { customFields: [{ name: 'Updated' }] }
|
||||
mockFetch.mockResolvedValue(fakeModelType())
|
||||
|
||||
@@ -176,10 +169,10 @@ describe('updateModelType', () => {
|
||||
expect(endpoint).toBe('/model_types/mt-1')
|
||||
expect(options.method).toBe('PATCH')
|
||||
expect(options.headers['Content-Type']).toBe('application/merge-patch+json')
|
||||
expect(options.body.componentSkeleton).toEqual(structure)
|
||||
expect(options.body.structure).toEqual(structure)
|
||||
})
|
||||
|
||||
it('sends payload without skeleton when no structure', async () => {
|
||||
it('sends payload without structure when not provided', async () => {
|
||||
mockFetch.mockResolvedValue(fakeModelType())
|
||||
|
||||
await updateModelType('mt-1', {
|
||||
@@ -189,7 +182,7 @@ describe('updateModelType', () => {
|
||||
})
|
||||
|
||||
const [, options] = mockFetch.mock.calls[0]
|
||||
expect(options.body.componentSkeleton).toBeUndefined()
|
||||
expect(options.body.structure).toBeUndefined()
|
||||
expect(options.body.name).toBe('Just Name')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user