From e99f0532332f1d33915885bc05a48f1809384297 Mon Sep 17 00:00:00 2001 From: matthieu Date: Sun, 11 Jan 2026 17:14:24 +0100 Subject: [PATCH] feat(front): aligner api platform et sessions [INV-20260111-02] --- app/app.vue | 2 +- app/composables/useApi.js | 3 +- app/composables/useComposants.js | 63 ++++++++++++++++++++------- app/composables/useConstructeurs.js | 18 +++++++- app/composables/useDocuments.js | 23 ++++++++-- app/composables/useMachineTypesApi.js | 58 +++++++++++++++++++----- app/composables/useMachines.js | 37 ++++++++++++---- app/composables/usePieces.js | 37 ++++++++++++++-- app/composables/useProducts.js | 35 ++++++++++++--- app/composables/useProfileSession.js | 5 ++- app/composables/useProfiles.js | 6 +-- app/composables/useSites.js | 15 ++++++- app/services/modelTypes.ts | 28 ++++++++++-- app/shared/apiRelations.ts | 57 ++++++++++++++++++++++++ app/shared/constructeurUtils.ts | 18 ++++++-- nuxt.config.ts | 5 ++- package-lock.json | 3 +- 17 files changed, 346 insertions(+), 67 deletions(-) create mode 100644 app/shared/apiRelations.ts diff --git a/app/app.vue b/app/app.vue index 329f5ac..e0dea99 100644 --- a/app/app.vue +++ b/app/app.vue @@ -30,7 +30,7 @@ : 'text-base-content hover:bg-primary/10 hover:text-primary' " > - Vue d'ensemble + Vue d'ensemblee
  • diff --git a/app/composables/useApi.js b/app/composables/useApi.js index b2945e7..b1a4233 100644 --- a/app/composables/useApi.js +++ b/app/composables/useApi.js @@ -10,6 +10,7 @@ export function useApi () { const apiCall = async (endpoint, options = {}) => { const url = `${API_BASE_URL}${endpoint}` const defaultOptions = { + credentials: 'include', headers: { 'Content-Type': 'application/json' } @@ -32,7 +33,7 @@ export function useApi () { let data = null if (response.status !== 204) { const contentType = response.headers.get('content-type') || '' - if (contentType.includes('application/json')) { + if (contentType.includes('application/json') || contentType.includes('application/ld+json') || contentType.includes('+json')) { const text = await response.text() data = text ? JSON.parse(text) : null } else { diff --git a/app/composables/useComposants.js b/app/composables/useComposants.js index 5599ede..5bdf629 100644 --- a/app/composables/useComposants.js +++ b/app/composables/useComposants.js @@ -3,10 +3,27 @@ import { useToast } from './useToast' import { useApi } from './useApi' import { buildConstructeurRequestPayload, uniqueConstructeurIds } from '~/shared/constructeurUtils' import { useConstructeurs } from './useConstructeurs' +import { extractRelationId, normalizeRelationIds } from '~/shared/apiRelations' const composants = ref([]) const loading = ref(false) +const extractCollection = (payload) => { + if (Array.isArray(payload)) { + return payload + } + if (Array.isArray(payload?.member)) { + return payload.member + } + if (Array.isArray(payload?.['hydra:member'])) { + return payload['hydra:member'] + } + if (Array.isArray(payload?.data)) { + return payload.data + } + return [] +} + export function useComposants () { const { showSuccess, showError, showInfo } = useToast() const { get, post, patch, delete: del } = useApi() @@ -16,6 +33,18 @@ export function useComposants () { if (!composant || typeof composant !== 'object') { return composant } + if (!composant.typeComposantId) { + const typeComposantId = extractRelationId(composant.typeComposant) + if (typeComposantId) { + composant.typeComposantId = typeComposantId + } + } + if (!composant.productId) { + const productId = extractRelationId(composant.product) + if (productId) { + composant.productId = productId + } + } const ids = uniqueConstructeurIds( composant.constructeurIds, composant.constructeurs, @@ -34,27 +63,28 @@ export function useComposants () { return composant } -const loadComposants = async () => { - loading.value = true - try { - const result = await get('/composants') - if (result.success) { - const items = Array.isArray(result.data) ? result.data : [] - const enrichedItems = await Promise.all(items.map((item) => withResolvedConstructeurs(item))) - composants.value = enrichedItems - showInfo(`Chargement de ${composants.value.length} composant(s) réussi`) + const loadComposants = async () => { + loading.value = true + try { + const result = await get('/composants') + if (result.success) { + const items = extractCollection(result.data) + const enrichedItems = await Promise.all(items.map((item) => withResolvedConstructeurs(item))) + composants.value = enrichedItems + showInfo(`Chargement de ${composants.value.length} composant(s) réussi`) + } + } catch (error) { + console.error('Erreur lors du chargement des composants:', error) + } finally { + loading.value = false } - } catch (error) { - console.error('Erreur lors du chargement des composants:', error) - } finally { - loading.value = false } -} const createComposant = async (composantData) => { loading.value = true try { - const result = await post('/composants', buildConstructeurRequestPayload(composantData)) + const normalizedPayload = normalizeRelationIds(buildConstructeurRequestPayload(composantData)) + const result = await post('/composants', normalizedPayload) if (result.success) { const enriched = await withResolvedConstructeurs(result.data) composants.value.push(enriched) @@ -76,7 +106,8 @@ const loadComposants = async () => { const updateComposantData = async (id, composantData) => { loading.value = true try { - const result = await patch(`/composants/${id}`, buildConstructeurRequestPayload(composantData)) + const normalizedPayload = normalizeRelationIds(buildConstructeurRequestPayload(composantData)) + const result = await patch(`/composants/${id}`, normalizedPayload) if (result.success) { const updated = await withResolvedConstructeurs(result.data) const index = composants.value.findIndex(comp => comp.id === id) diff --git a/app/composables/useConstructeurs.js b/app/composables/useConstructeurs.js index 025e5ed..67a2996 100644 --- a/app/composables/useConstructeurs.js +++ b/app/composables/useConstructeurs.js @@ -39,6 +39,22 @@ const upsertConstructeurs = (items = []) => { const getIndexedConstructeur = (id) => constructeurs.value.find((item) => item && item.id === id) || null +const extractCollection = (payload) => { + if (Array.isArray(payload)) { + return payload + } + if (Array.isArray(payload?.member)) { + return payload.member + } + if (Array.isArray(payload?.['hydra:member'])) { + return payload['hydra:member'] + } + if (Array.isArray(payload?.data)) { + return payload.data + } + return [] +} + const pendingFetches = new Map() export function useConstructeurs () { @@ -51,7 +67,7 @@ export function useConstructeurs () { const query = search ? `?search=${encodeURIComponent(search)}` : '' const result = await get(`/constructeurs${query}`) if (result.success) { - const items = Array.isArray(result.data) ? result.data : [] + const items = extractCollection(result.data) constructeurs.value = uniqueConstructeurs(items) } return result diff --git a/app/composables/useDocuments.js b/app/composables/useDocuments.js index 5b89cd6..8177fe2 100644 --- a/app/composables/useDocuments.js +++ b/app/composables/useDocuments.js @@ -1,10 +1,27 @@ import { ref } from 'vue' import { useApi } from './useApi' import { useToast } from './useToast' +import { normalizeRelationIds } from '~/shared/apiRelations' const documents = ref([]) const loading = ref(false) +const extractCollection = (payload) => { + if (Array.isArray(payload)) { + return payload + } + if (Array.isArray(payload?.member)) { + return payload.member + } + if (Array.isArray(payload?.['hydra:member'])) { + return payload['hydra:member'] + } + if (Array.isArray(payload?.data)) { + return payload.data + } + return [] +} + const fileToBase64 = file => new Promise((resolve, reject) => { const reader = new FileReader() @@ -22,7 +39,7 @@ export function useDocuments () { try { const result = await get(endpoint) if (result.success) { - const data = result.data || [] + const data = extractCollection(result.data) if (updateStore) { documents.value = data } @@ -80,14 +97,14 @@ export function useDocuments () { for (const file of files) { const dataUrl = await fileToBase64(file) - const payload = { + const payload = normalizeRelationIds({ name: file.name, filename: file.name, mimeType: file.type || 'application/octet-stream', size: file.size, path: dataUrl, ...context - } + }) const result = await post('/documents', payload) if (result.success) { diff --git a/app/composables/useMachineTypesApi.js b/app/composables/useMachineTypesApi.js index 2f38673..83f47fa 100644 --- a/app/composables/useMachineTypesApi.js +++ b/app/composables/useMachineTypesApi.js @@ -1,11 +1,30 @@ import { ref } from 'vue' import { useToast } from './useToast' import { useApi } from './useApi' +import { extractRelationId } from '~/shared/apiRelations' const machineTypes = ref([]) const loading = ref(false) -const normalizeRequirementList = (value) => (Array.isArray(value) ? value : []) +const normalizeRequirementList = (value, relationKey) => { + if (!Array.isArray(value)) { + return [] + } + return value.map((entry) => { + if (!entry || typeof entry !== 'object') { + return entry + } + const normalized = { ...entry } + if (relationKey && !normalized[relationKey]) { + const relationValue = normalized[relationKey.replace('Id', '')] + const relationId = extractRelationId(relationValue) + if (relationId) { + normalized[relationKey] = relationId + } + } + return normalized + }) +} const normalizeMachineType = (type) => { if (!type || typeof type !== 'object') { @@ -13,12 +32,28 @@ const normalizeMachineType = (type) => { } return { ...type, - componentRequirements: normalizeRequirementList(type.componentRequirements), - pieceRequirements: normalizeRequirementList(type.pieceRequirements), - productRequirements: normalizeRequirementList(type.productRequirements), + componentRequirements: normalizeRequirementList(type.componentRequirements, 'typeComposantId'), + pieceRequirements: normalizeRequirementList(type.pieceRequirements, 'typePieceId'), + productRequirements: normalizeRequirementList(type.productRequirements, 'typeProductId'), } } +const extractCollection = (payload) => { + if (Array.isArray(payload)) { + return payload + } + if (Array.isArray(payload?.member)) { + return payload.member + } + if (Array.isArray(payload?.['hydra:member'])) { + return payload['hydra:member'] + } + if (Array.isArray(payload?.data)) { + return payload.data + } + return [] +} + export function useMachineTypesApi () { const { showSuccess, showError, showInfo } = useToast() const { get, post, patch, delete: del } = useApi() @@ -26,11 +61,10 @@ export function useMachineTypesApi () { const loadMachineTypes = async () => { loading.value = true try { - const result = await get('/types/machines') + const result = await get('/type_machines') if (result.success) { - machineTypes.value = Array.isArray(result.data) - ? result.data.map(normalizeMachineType) - : [] + const items = extractCollection(result.data) + machineTypes.value = items.map(normalizeMachineType) showInfo(`Chargement de ${machineTypes.value.length} type(s) de machine réussi`) } } catch (error) { @@ -43,7 +77,7 @@ export function useMachineTypesApi () { const createMachineType = async (typeData) => { loading.value = true try { - const result = await post('/types/machines', typeData) + const result = await post('/type_machines', typeData) if (result.success) { machineTypes.value.push(normalizeMachineType(result.data)) showSuccess(`Type de machine "${typeData.name}" créé avec succès`) @@ -60,7 +94,7 @@ export function useMachineTypesApi () { const updateMachineType = async (id, typeData) => { loading.value = true try { - const result = await patch(`/types/machines/${id}`, typeData) + const result = await patch(`/type_machines/${id}`, typeData) if (result.success) { const normalized = normalizeMachineType(result.data) const index = machineTypes.value.findIndex(type => type.id === id) @@ -81,7 +115,7 @@ export function useMachineTypesApi () { const deleteMachineType = async (id) => { loading.value = true try { - const result = await del(`/types/machines/${id}`) + const result = await del(`/type_machines/${id}`) if (result.success) { const deletedType = machineTypes.value.find(type => type.id === id) machineTypes.value = machineTypes.value.filter(type => type.id !== id) @@ -105,7 +139,7 @@ export function useMachineTypesApi () { // Si pas trouvé localement, récupérer depuis l'API try { - const result = await get(`/types/machines/${id}`) + const result = await get(`/type_machines/${id}`) if (result.success) { // Ajouter au cache local machineTypes.value.push(normalizeMachineType(result.data)) diff --git a/app/composables/useMachines.js b/app/composables/useMachines.js index 9e4a0c6..35141df 100644 --- a/app/composables/useMachines.js +++ b/app/composables/useMachines.js @@ -2,6 +2,7 @@ import { ref } from 'vue' import { useToast } from './useToast' import { useApi } from './useApi' import { buildConstructeurRequestPayload } from '~/shared/constructeurUtils' +import { extractRelationId, normalizeRelationIds } from '~/shared/apiRelations' const machines = ref([]) const loading = ref(false) @@ -32,6 +33,20 @@ const normalizeMachineResponse = (payload) => { const normalized = { ...container } + if (!normalized.siteId) { + const siteId = extractRelationId(container.site) + if (siteId) { + normalized.siteId = siteId + } + } + + if (!normalized.typeMachineId) { + const typeMachineId = extractRelationId(container.typeMachine) + if (typeMachineId) { + normalized.typeMachineId = typeMachineId + } + } + const componentLinks = resolveLinkCollection(payload, ['componentLinks', 'machineComponentLinks']) ?? resolveLinkCollection(container, ['componentLinks', 'machineComponentLinks']) ?? [] @@ -56,11 +71,15 @@ export function useMachines () { if (result.success) { const machineList = Array.isArray(result.data) ? result.data - : Array.isArray(result.data?.machines) - ? result.data.machines - : Array.isArray(result.data?.data) - ? result.data.data - : [] + : Array.isArray(result.data?.member) + ? result.data.member + : Array.isArray(result.data?.['hydra:member']) + ? result.data['hydra:member'] + : Array.isArray(result.data?.machines) + ? result.data.machines + : Array.isArray(result.data?.data) + ? result.data.data + : [] const normalized = machineList .map((item) => normalizeMachineResponse(item)) .filter(Boolean) @@ -77,7 +96,8 @@ export function useMachines () { const createMachine = async (machineData) => { loading.value = true try { - const result = await post('/machines', buildConstructeurRequestPayload(machineData)) + const normalizedPayload = normalizeRelationIds(buildConstructeurRequestPayload(machineData)) + const result = await post('/machines', normalizedPayload) if (result.success) { const createdMachine = normalizeMachineResponse(result.data) || normalizeMachineResponse(result.data?.machine) || @@ -106,13 +126,14 @@ export function useMachines () { // Les composants et pièces seront créés automatiquement } - return await createMachine(buildConstructeurRequestPayload(machineWithStructure)) + return await createMachine(machineWithStructure) } const updateMachineData = async (id, machineData) => { loading.value = true try { - const result = await patch(`/machines/${id}`, buildConstructeurRequestPayload(machineData)) + const normalizedPayload = normalizeRelationIds(buildConstructeurRequestPayload(machineData)) + const result = await patch(`/machines/${id}`, normalizedPayload) if (result.success) { const updatedMachine = normalizeMachineResponse(result.data) || normalizeMachineResponse(result.data?.machine) || diff --git a/app/composables/usePieces.js b/app/composables/usePieces.js index aa0112e..b27964e 100644 --- a/app/composables/usePieces.js +++ b/app/composables/usePieces.js @@ -3,10 +3,27 @@ import { useToast } from './useToast' import { useApi } from './useApi' import { buildConstructeurRequestPayload, uniqueConstructeurIds } from '~/shared/constructeurUtils' import { useConstructeurs } from './useConstructeurs' +import { extractRelationId, normalizeRelationIds } from '~/shared/apiRelations' const pieces = ref([]) const loading = ref(false) +const extractCollection = (payload) => { + if (Array.isArray(payload)) { + return payload + } + if (Array.isArray(payload?.member)) { + return payload.member + } + if (Array.isArray(payload?.['hydra:member'])) { + return payload['hydra:member'] + } + if (Array.isArray(payload?.data)) { + return payload.data + } + return [] +} + export function usePieces () { const { showSuccess, showError, showInfo } = useToast() const { get, post, patch, delete: del } = useApi() @@ -16,6 +33,18 @@ export function usePieces () { if (!piece || typeof piece !== 'object') { return piece } + if (!piece.typePieceId) { + const typePieceId = extractRelationId(piece.typePiece) + if (typePieceId) { + piece.typePieceId = typePieceId + } + } + if (!piece.productId) { + const productId = extractRelationId(piece.product) + if (productId) { + piece.productId = productId + } + } const ids = uniqueConstructeurIds( piece.constructeurIds, piece.constructeurs, @@ -39,7 +68,7 @@ export function usePieces () { try { const result = await get('/pieces') if (result.success) { - const items = Array.isArray(result.data) ? result.data : [] + const items = extractCollection(result.data) const enrichedItems = await Promise.all(items.map((item) => withResolvedConstructeurs(item))) pieces.value = enrichedItems showInfo(`Chargement de ${pieces.value.length} pièce(s) réussi`) @@ -54,7 +83,8 @@ export function usePieces () { const createPiece = async (pieceData) => { loading.value = true try { - const result = await post('/pieces', buildConstructeurRequestPayload(pieceData)) + const normalizedPayload = normalizeRelationIds(buildConstructeurRequestPayload(pieceData)) + const result = await post('/pieces', normalizedPayload) if (result.success) { const enriched = await withResolvedConstructeurs(result.data) pieces.value.push(enriched) @@ -76,7 +106,8 @@ export function usePieces () { const updatePieceData = async (id, pieceData) => { loading.value = true try { - const result = await patch(`/pieces/${id}`, buildConstructeurRequestPayload(pieceData)) + const normalizedPayload = normalizeRelationIds(buildConstructeurRequestPayload(pieceData)) + const result = await patch(`/pieces/${id}`, normalizedPayload) if (result.success) { const updated = await withResolvedConstructeurs(result.data) const index = pieces.value.findIndex(piece => piece.id === id) diff --git a/app/composables/useProducts.js b/app/composables/useProducts.js index 9a90ed7..9b8b204 100644 --- a/app/composables/useProducts.js +++ b/app/composables/useProducts.js @@ -3,6 +3,7 @@ import { useToast } from './useToast' import { useApi } from './useApi' import { buildConstructeurRequestPayload, uniqueConstructeurIds } from '~/shared/constructeurUtils' import { useConstructeurs } from './useConstructeurs' +import { extractRelationId, normalizeRelationIds } from '~/shared/apiRelations' const products = ref([]) const total = ref(0) @@ -25,6 +26,22 @@ const replaceInCache = (item) => { return false } +const extractCollection = (payload) => { + if (Array.isArray(payload)) { + return payload + } + if (Array.isArray(payload?.member)) { + return payload.member + } + if (Array.isArray(payload?.['hydra:member'])) { + return payload['hydra:member'] + } + if (Array.isArray(payload?.data)) { + return payload.data + } + return [] +} + export function useProducts () { const { showError } = useToast() const { get, post, patch, delete: del } = useApi() @@ -34,6 +51,12 @@ export function useProducts () { if (!product || typeof product !== 'object') { return product } + if (!product.typeProductId) { + const typeProductId = extractRelationId(product.typeProduct) + if (typeProductId) { + product.typeProductId = typeProductId + } + } const ids = uniqueConstructeurIds( product.constructeurIds, product.constructeurs, @@ -69,12 +92,14 @@ export function useProducts () { loading.value = true error.value = null try { - const result = await get('/products?limit=100') + const result = await get('/products?itemsPerPage=100') if (result.success) { - const items = Array.isArray(result.data?.items) ? result.data.items : [] + const items = extractCollection(result.data) const enrichedItems = await Promise.all(items.map((item) => withResolvedConstructeurs(item))) products.value = enrichedItems - total.value = typeof result.data?.total === 'number' ? result.data.total : items.length + total.value = typeof result.data?.totalItems === 'number' + ? result.data.totalItems + : items.length loaded.value = true } else if (result.error) { error.value = result.error @@ -93,7 +118,7 @@ export function useProducts () { } const createProduct = async (payload) => { - const normalizedPayload = buildConstructeurRequestPayload(payload) + const normalizedPayload = normalizeRelationIds(buildConstructeurRequestPayload(payload)) loading.value = true error.value = null try { @@ -121,7 +146,7 @@ export function useProducts () { } const updateProduct = async (id, payload) => { - const normalizedPayload = buildConstructeurRequestPayload(payload) + const normalizedPayload = normalizeRelationIds(buildConstructeurRequestPayload(payload)) loading.value = true error.value = null try { diff --git a/app/composables/useProfileSession.js b/app/composables/useProfileSession.js index 0a09fd1..83be2f2 100644 --- a/app/composables/useProfileSession.js +++ b/app/composables/useProfileSession.js @@ -2,7 +2,10 @@ import { useState, useRequestHeaders, useRuntimeConfig } from '#imports' const buildUrl = (path) => { const config = useRuntimeConfig() - const base = config.public.apiBaseUrl?.replace(/\/$/, '') || '' + const baseUrl = process.server + ? (config.apiBaseUrl || config.public.apiBaseUrl || '') + : (config.public.apiBaseUrl || '') + const base = baseUrl.replace(/\/$/, '') return `${base}${path}` } diff --git a/app/composables/useProfiles.js b/app/composables/useProfiles.js index a6e0e40..ad1c80d 100644 --- a/app/composables/useProfiles.js +++ b/app/composables/useProfiles.js @@ -20,7 +20,7 @@ export function useProfiles () { const fetchProfiles = async () => { loadingProfiles.value = true try { - profiles.value = await $fetch(buildUrl('/profiles'), { + profiles.value = await $fetch(buildUrl('/session/profiles'), { method: 'GET', credentials: 'include', headers: getSessionHeaders() @@ -37,7 +37,7 @@ export function useProfiles () { } const createProfile = async ({ firstName, lastName }) => { - const profile = await $fetch(buildUrl('/profiles'), { + const profile = await $fetch(buildUrl('/session/profiles'), { method: 'POST', credentials: 'include', body: { firstName, lastName }, @@ -48,7 +48,7 @@ export function useProfiles () { } const deleteProfile = async (profileId) => { - await $fetch(buildUrl(`/profiles/${profileId}`), { + await $fetch(buildUrl(`/session/profiles/${profileId}`), { method: 'DELETE', credentials: 'include', headers: getSessionHeaders() diff --git a/app/composables/useSites.js b/app/composables/useSites.js index 9d1eecd..1c76e51 100644 --- a/app/composables/useSites.js +++ b/app/composables/useSites.js @@ -13,9 +13,20 @@ export function useSites () { loading.value = true try { const result = await get('/sites') + console.log('sites api result', result) + if (result.success) { - sites.value = result.data - showInfo(`Chargement de ${sites.value.length} site(s) réussi`) + const collection = Array.isArray(result.data) + ? result.data + : Array.isArray(result.data?.member) + ? result.data.member + : Array.isArray(result.data?.['hydra:member']) + ? result.data['hydra:member'] + : Array.isArray(result.data?.data) + ? result.data.data + : [] + sites.value = collection + showInfo(`Chargement de ${collection.length} site(s) réussi`) } } catch (error) { console.error('Erreur lors du chargement des sites:', error) diff --git a/app/services/modelTypes.ts b/app/services/modelTypes.ts index 2c54e6f..18f46c2 100644 --- a/app/services/modelTypes.ts +++ b/app/services/modelTypes.ts @@ -65,7 +65,7 @@ export interface ModelTypeListResponse { limit: number; } -const ENDPOINT = '/api/model-types'; +const ENDPOINT = '/model_types'; function resolveBaseUrl() { const runtimeConfig = useRuntimeConfig(); @@ -80,7 +80,7 @@ function createOptions(options: FetchOptions = {}) { }; } -export function listModelTypes(params: ModelTypeListParams = {}, opts: { signal?: AbortSignal } = {}) { +export async function listModelTypes(params: ModelTypeListParams = {}, opts: { signal?: AbortSignal } = {}) { const requestFetch = useRequestFetch(); const query: Record = {}; @@ -97,17 +97,37 @@ export function listModelTypes(params: ModelTypeListParams = {}, opts: { signal? query.dir = params.dir; } if (typeof params.limit === 'number') { - query.limit = params.limit; + query.itemsPerPage = params.limit; } if (typeof params.offset === 'number') { query.offset = params.offset; } - return requestFetch(ENDPOINT, createOptions({ + const payload = await requestFetch>(ENDPOINT, createOptions({ method: 'GET', query, signal: opts.signal, })); + + const items = Array.isArray(payload?.member) + ? payload.member + : Array.isArray(payload?.['hydra:member']) + ? payload['hydra:member'] + : Array.isArray(payload?.items) + ? payload.items + : []; + const total = typeof payload?.totalItems === 'number' + ? payload.totalItems + : Array.isArray(payload?.items) + ? payload.items.length + : items.length; + + return { + items, + total, + offset: params.offset ?? 0, + limit: typeof params.limit === 'number' ? params.limit : items.length, + } satisfies ModelTypeListResponse; } export function createModelType(payload: ModelTypePayload, opts: { signal?: AbortSignal } = {}) { diff --git a/app/shared/apiRelations.ts b/app/shared/apiRelations.ts new file mode 100644 index 0000000..3a1b1ac --- /dev/null +++ b/app/shared/apiRelations.ts @@ -0,0 +1,57 @@ +export const RELATION_ID_MAP: Record = { + siteId: { key: 'site', path: 'sites' }, + machineId: { key: 'machine', path: 'machines' }, + composantId: { key: 'composant', path: 'composants' }, + pieceId: { key: 'piece', path: 'pieces' }, + productId: { key: 'product', path: 'products' }, + typeMachineId: { key: 'typeMachine', path: 'type_machines' }, + typeComposantId: { key: 'typeComposant', path: 'model_types' }, + typePieceId: { key: 'typePiece', path: 'model_types' }, + typeProductId: { key: 'typeProduct', path: 'model_types' }, +}; + +export const toIri = (path: string, id: string): string => `/api/${path}/${id}`; + +export const extractRelationId = (value: unknown): string | null => { + if (!value) { + return null; + } + if (typeof value === 'string') { + const trimmed = value.trim(); + if (!trimmed) { + return null; + } + if (trimmed.includes('/')) { + const parts = trimmed.split('/').filter(Boolean); + return parts.length ? parts[parts.length - 1] : null; + } + return trimmed; + } + if (typeof value === 'object' && 'id' in (value as Record)) { + const id = (value as Record).id; + return typeof id === 'string' ? id : null; + } + return null; +}; + +export const normalizeRelationIds = >(payload: T): T => { + if (!payload || typeof payload !== 'object') { + return payload; + } + + const next: Record = { ...payload }; + Object.entries(RELATION_ID_MAP).forEach(([sourceKey, config]) => { + const raw = next[sourceKey]; + if (typeof raw !== 'string') { + return; + } + const trimmed = raw.trim(); + if (!trimmed) { + return; + } + next[config.key] = toIri(config.path, trimmed); + delete next[sourceKey]; + }); + + return next as T; +}; diff --git a/app/shared/constructeurUtils.ts b/app/shared/constructeurUtils.ts index 02649ed..31030d7 100644 --- a/app/shared/constructeurUtils.ts +++ b/app/shared/constructeurUtils.ts @@ -15,7 +15,14 @@ const toStringId = (value: unknown): string | null => { return null; } const trimmed = value.trim(); - return trimmed.length > 0 ? trimmed : null; + if (!trimmed) { + return null; + } + if (trimmed.includes('/')) { + const parts = trimmed.split('/').filter(Boolean); + return parts.length ? parts[parts.length - 1] : null; + } + return trimmed; }; export const uniqueConstructeurIds = (...sources: unknown[]): string[] => { @@ -107,7 +114,7 @@ export const formatConstructeurContact = ( export const buildConstructeurRequestPayload = >( payload: T, -): T & { constructeurIds: string[] } => { +): T & { constructeurs?: string[] } => { const ids = uniqueConstructeurIds( payload?.constructeurIds, payload?.constructeurId, @@ -116,10 +123,13 @@ export const buildConstructeurRequestPayload = >( ); const next = { ...payload } as Record; - next.constructeurIds = ids; + if (ids.length) { + next.constructeurs = ids.map((id) => `/api/constructeurs/${id}`); + } delete next.constructeurId; delete next.constructeur; delete next.constructeurs; + delete next.constructeurIds; - return next as T & { constructeurIds: string[] }; + return next as T & { constructeurs?: string[] }; }; diff --git a/nuxt.config.ts b/nuxt.config.ts index 6c85c85..964c43d 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -18,8 +18,11 @@ export default defineNuxtConfig({ ] ], runtimeConfig: { + apiBaseUrl: process.env.NUXT_API_BASE_URL + || process.env.NUXT_PUBLIC_API_BASE_URL + || 'http://localhost/api', public: { - apiBaseUrl: process.env.NUXT_PUBLIC_API_BASE_URL || 'http://localhost:3000', + apiBaseUrl: process.env.NUXT_PUBLIC_API_BASE_URL || 'http://localhost:8081/api', appUrl: process.env.NUXT_PUBLIC_APP_URL || 'http://localhost:3001', appName: process.env.NUXT_PUBLIC_APP_NAME || 'Inventory Management System', appVersion: process.env.NUXT_PUBLIC_APP_VERSION || '0.1.0', diff --git a/package-lock.json b/package-lock.json index 78d6e80..fffcc3e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3754,7 +3754,6 @@ "integrity": "sha512-UG8hdElzuBDzIbjG1QDwnYH0MQ73YLXDFHgZzB4Zh/YJfnw8XNsloVtytqzx0I2Qky9THSdpTmi8Vjn/pf/Lew==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.9.0", "@typescript-eslint/types": "^8.44.0", @@ -11673,7 +11672,6 @@ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.2.tgz", "integrity": "sha512-I25/2QgoROE1vYV+NQ1En9T9UFB9Cmfm2CJ83zZOlaDpvz29wGQSZXWKw7MiNXau7wYgB/T9fVIdIuEQ+KbiiA==", "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -12483,6 +12481,7 @@ "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.0.tgz", "integrity": "sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==", "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0",