From 9fef009610decc8a90931019c2671c7c1eb757c3 Mon Sep 17 00:00:00 2001 From: Matthieu Date: Thu, 12 Mar 2026 18:11:07 +0100 Subject: [PATCH] =?UTF-8?q?feat(skeleton)=20:=20remove=20skeleton=20JSON?= =?UTF-8?q?=20field=20references=20=E2=80=94=20use=20structure=20API=20fie?= =?UTF-8?q?ld=20directly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 --- app/pages/product/[id]/edit.vue | 4 +- app/services/modelTypes.ts | 42 +---------------- app/shared/utils/productDisplayUtils.ts | 15 ------ tests/services/modelTypes.test.ts | 61 +++++++++++-------------- 4 files changed, 31 insertions(+), 91 deletions(-) diff --git a/app/pages/product/[id]/edit.vue b/app/pages/product/[id]/edit.vue index 6e117ff..dbd83e3 100644 --- a/app/pages/product/[id]/edit.vue +++ b/app/pages/product/[id]/edit.vue @@ -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 diff --git a/app/services/modelTypes.ts b/app/services/modelTypes.ts index 772a81a..27353fb 100644 --- a/app/services/modelTypes.ts +++ b/app/services/modelTypes.ts @@ -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 = >(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; - 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 = {}; @@ -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(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, opts: { signal?: AbortSignal } = {}) { const requestFetch = useRequestFetch(); - const mappedPayload = mapStructureToSkeleton(payload); return requestFetch(`${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); } diff --git a/app/shared/utils/productDisplayUtils.ts b/app/shared/utils/productDisplayUtils.ts index a2b7aef..ff5b609 100644 --- a/app/shared/utils/productDisplayUtils.ts +++ b/app/shared/utils/productDisplayUtils.ts @@ -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 diff --git a/tests/services/modelTypes.test.ts b/tests/services/modelTypes.test.ts index 842c37b..aeed063 100644 --- a/tests/services/modelTypes.test.ts +++ b/tests/services/modelTypes.test.ts @@ -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') }) })