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:
Matthieu
2026-03-12 18:11:07 +01:00
parent 4a3bceffa1
commit 9fef009610
4 changed files with 31 additions and 91 deletions

View File

@@ -390,7 +390,7 @@ const loadProductType = async () => {
// Try using the expanded typeProduct from entity response first // Try using the expanded typeProduct from entity response first
const embedded = product.value?.typeProduct const embedded = product.value?.typeProduct
if (embedded && typeof embedded === 'object' && embedded.id) { if (embedded && typeof embedded === 'object' && embedded.id) {
const embeddedStructure = embedded.structure ?? embedded.productSkeleton ?? null const embeddedStructure = embedded.structure ?? null
if (embeddedStructure) { if (embeddedStructure) {
productType.value = embedded productType.value = embedded
structure.value = normalizeProductStructureForSave(embeddedStructure) structure.value = normalizeProductStructureForSave(embeddedStructure)
@@ -406,7 +406,7 @@ const loadProductType = async () => {
try { try {
const type = await getModelType(product.value.typeProductId) const type = await getModelType(product.value.typeProductId)
productType.value = type productType.value = type
structure.value = normalizeProductStructureForSave(type?.structure ?? type?.productSkeleton ?? null) structure.value = normalizeProductStructureForSave(type?.structure ?? null)
} catch (error) { } catch (error) {
console.error('Erreur lors du chargement du type de produit:', error) console.error('Erreur lors du chargement du type de produit:', error)
productType.value = embedded ?? null productType.value = embedded ?? null

View File

@@ -46,9 +46,6 @@ export interface ModelType extends BaseModelTypePayload {
updatedAt: string; updatedAt: string;
category: ModelCategory; category: ModelCategory;
structure: ModelTypeStructure; structure: ModelTypeStructure;
componentSkeleton?: ComponentModelStructure | null;
pieceSkeleton?: PieceModelStructure | null;
productSkeleton?: ProductModelStructure | null;
} }
export interface ModelTypeListParams { export interface ModelTypeListParams {
@@ -86,42 +83,9 @@ const normalizeModelType = (item: any): ModelType => {
if (!item || typeof item !== 'object') { if (!item || typeof item !== 'object') {
return item as ModelType; 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; 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 } = {}) { export async function listModelTypes(params: ModelTypeListParams = {}, opts: { signal?: AbortSignal } = {}) {
const requestFetch = useRequestFetch(); const requestFetch = useRequestFetch();
const query: Record<string, string | number> = {}; 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 } = {}) { export function createModelType(payload: ModelTypePayload, opts: { signal?: AbortSignal } = {}) {
const requestFetch = useRequestFetch(); const requestFetch = useRequestFetch();
const mappedPayload = mapStructureToSkeleton(payload);
return requestFetch<ModelType>(ENDPOINT, createOptions({ return requestFetch<ModelType>(ENDPOINT, createOptions({
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/ld+json', 'Content-Type': 'application/ld+json',
Accept: 'application/ld+json', Accept: 'application/ld+json',
}, },
body: mappedPayload, body: payload,
signal: opts.signal, signal: opts.signal,
})).then(normalizeModelType); })).then(normalizeModelType);
} }
export function updateModelType(id: string, payload: Partial<ModelTypePayload>, opts: { signal?: AbortSignal } = {}) { export function updateModelType(id: string, payload: Partial<ModelTypePayload>, opts: { signal?: AbortSignal } = {}) {
const requestFetch = useRequestFetch(); const requestFetch = useRequestFetch();
const mappedPayload = mapStructureToSkeleton(payload);
return requestFetch<ModelType>(`${ENDPOINT}/${id}`, createOptions({ return requestFetch<ModelType>(`${ENDPOINT}/${id}`, createOptions({
method: 'PATCH', method: 'PATCH',
headers: { headers: {
'Content-Type': 'application/merge-patch+json', 'Content-Type': 'application/merge-patch+json',
Accept: 'application/ld+json', Accept: 'application/ld+json',
}, },
body: mappedPayload, body: payload,
signal: opts.signal, signal: opts.signal,
})).then(normalizeModelType); })).then(normalizeModelType);
} }

View File

@@ -196,34 +196,19 @@ export const getProductDisplay = (
const structuralCandidates = [ const structuralCandidates = [
source.products, source.products,
source.productSkeleton,
(source.definition as AnyRecord)?.products, (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)?.products,
((source.definition as AnyRecord)?.structure as AnyRecord)?.productSkeleton,
(source.structure as AnyRecord)?.products, (source.structure as AnyRecord)?.products,
(source.structure as AnyRecord)?.productSkeleton,
(source.requirement as AnyRecord)?.products, (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)?.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)?.products,
(source.typeComposant as AnyRecord)?.productSkeleton,
((source.typeComposant as AnyRecord)?.structure as AnyRecord)?.products, ((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)?.products,
(source.originalComposant as AnyRecord)?.productSkeleton,
((source.originalComposant as AnyRecord)?.definition as AnyRecord)?.products, ((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)?.products,
(((source.originalComposant as AnyRecord)?.definition as AnyRecord)?.structure as AnyRecord)?.productSkeleton,
(source.originalComponent as AnyRecord)?.products, (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)?.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)?.products,
(((source.originalComponent as AnyRecord)?.definition as AnyRecord)?.structure as AnyRecord)?.productSkeleton,
] ]
const structuralProducts = structuralCandidates const structuralProducts = structuralCandidates

View File

@@ -50,60 +50,55 @@ beforeEach(() => {
// normalizeModelType (tested via getModelType which calls .then(normalizeModelType)) // normalizeModelType (tested via getModelType which calls .then(normalizeModelType))
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
describe('normalizeModelType (via getModelType)', () => { describe('normalizeModelType (via getModelType)', () => {
it('maps componentSkeleton to structure for COMPONENT', async () => { it('returns structure as-is for COMPONENT', async () => {
const skeleton = { customFields: [{ name: 'Weight' }] } const structure = { customFields: [{ name: 'Weight' }] }
mockFetch.mockResolvedValue(fakeModelType({ mockFetch.mockResolvedValue(fakeModelType({
category: 'COMPONENT', category: 'COMPONENT',
structure: null, structure: structure as any,
componentSkeleton: skeleton as any,
})) }))
const result = await getModelType('mt-1') const result = await getModelType('mt-1')
expect(result.structure).toEqual(skeleton) expect(result.structure).toEqual(structure)
}) })
it('maps pieceSkeleton to structure for PIECE', async () => { it('returns structure as-is for PIECE', async () => {
const skeleton = { customFields: [{ name: 'Size' }] } const structure = { customFields: [{ name: 'Size' }] }
mockFetch.mockResolvedValue(fakeModelType({ mockFetch.mockResolvedValue(fakeModelType({
category: 'PIECE', category: 'PIECE',
structure: null, structure: structure as any,
pieceSkeleton: skeleton as any,
})) }))
const result = await getModelType('mt-1') const result = await getModelType('mt-1')
expect(result.structure).toEqual(skeleton) expect(result.structure).toEqual(structure)
}) })
it('maps productSkeleton to structure for PRODUCT', async () => { it('returns structure as-is for PRODUCT', async () => {
const skeleton = { customFields: [{ name: 'Brand' }] } const structure = { customFields: [{ name: 'Brand' }] }
mockFetch.mockResolvedValue(fakeModelType({ mockFetch.mockResolvedValue(fakeModelType({
category: 'PRODUCT', category: 'PRODUCT',
structure: null, structure: structure as any,
productSkeleton: skeleton as any,
})) }))
const result = await getModelType('mt-1') const result = await getModelType('mt-1')
expect(result.structure).toEqual(skeleton) expect(result.structure).toEqual(structure)
}) })
it('does not override existing structure', async () => { it('preserves null structure', async () => {
const existing = { customFields: [{ name: 'Existing' }] }
mockFetch.mockResolvedValue(fakeModelType({ mockFetch.mockResolvedValue(fakeModelType({
category: 'COMPONENT', category: 'COMPONENT',
structure: existing as any, structure: null,
componentSkeleton: { customFields: [{ name: 'Skeleton' }] } as any,
})) }))
const result = await getModelType('mt-1') 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', () => { describe('createModelType', () => {
it('sends POST with componentSkeleton for COMPONENT', async () => { it('sends POST with structure for COMPONENT', async () => {
const structure = { customFields: [] } const structure = { customFields: [] }
mockFetch.mockResolvedValue(fakeModelType()) mockFetch.mockResolvedValue(fakeModelType())
@@ -120,11 +115,10 @@ describe('createModelType', () => {
const [endpoint, options] = mockFetch.mock.calls[0] const [endpoint, options] = mockFetch.mock.calls[0]
expect(endpoint).toBe('/model_types') expect(endpoint).toBe('/model_types')
expect(options.method).toBe('POST') expect(options.method).toBe('POST')
expect(options.body.componentSkeleton).toEqual(structure) expect(options.body.structure).toEqual(structure)
expect(options.body.structure).toBeUndefined()
}) })
it('sends POST with pieceSkeleton for PIECE', async () => { it('sends POST with structure for PIECE', async () => {
const structure = { customFields: [], products: [] } const structure = { customFields: [], products: [] }
mockFetch.mockResolvedValue(fakeModelType({ category: 'PIECE' })) mockFetch.mockResolvedValue(fakeModelType({ category: 'PIECE' }))
@@ -136,11 +130,10 @@ describe('createModelType', () => {
}) })
const [, options] = mockFetch.mock.calls[0] const [, options] = mockFetch.mock.calls[0]
expect(options.body.pieceSkeleton).toEqual(structure) expect(options.body.structure).toEqual(structure)
expect(options.body.structure).toBeUndefined()
}) })
it('sends POST with productSkeleton for PRODUCT', async () => { it('sends POST with structure for PRODUCT', async () => {
const structure = { customFields: [] } const structure = { customFields: [] }
mockFetch.mockResolvedValue(fakeModelType({ category: 'PRODUCT' })) mockFetch.mockResolvedValue(fakeModelType({ category: 'PRODUCT' }))
@@ -152,15 +145,15 @@ describe('createModelType', () => {
}) })
const [, options] = mockFetch.mock.calls[0] 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', () => { 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' }] } const structure = { customFields: [{ name: 'Updated' }] }
mockFetch.mockResolvedValue(fakeModelType()) mockFetch.mockResolvedValue(fakeModelType())
@@ -176,10 +169,10 @@ describe('updateModelType', () => {
expect(endpoint).toBe('/model_types/mt-1') expect(endpoint).toBe('/model_types/mt-1')
expect(options.method).toBe('PATCH') expect(options.method).toBe('PATCH')
expect(options.headers['Content-Type']).toBe('application/merge-patch+json') 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()) mockFetch.mockResolvedValue(fakeModelType())
await updateModelType('mt-1', { await updateModelType('mt-1', {
@@ -189,7 +182,7 @@ describe('updateModelType', () => {
}) })
const [, options] = mockFetch.mock.calls[0] const [, options] = mockFetch.mock.calls[0]
expect(options.body.componentSkeleton).toBeUndefined() expect(options.body.structure).toBeUndefined()
expect(options.body.name).toBe('Just Name') expect(options.body.name).toBe('Just Name')
}) })
}) })