import { ref } from 'vue' 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) const loading = ref(false) const loaded = ref(false) const error = ref(null) const replaceInCache = (item) => { if (!item?.id) { return false } const index = products.value.findIndex((product) => product.id === item.id) if (index === -1) { products.value.unshift(item) return true } const clone = products.value.slice() clone[index] = item products.value = clone 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 [] } const extractTotal = (payload, fallbackLength) => { if (typeof payload?.totalItems === 'number') { return payload.totalItems } if (typeof payload?.['hydra:totalItems'] === 'number') { return payload['hydra:totalItems'] } return fallbackLength } export function useProducts () { const { showError } = useToast() const { get, post, patch, delete: del } = useApi() const { ensureConstructeurs } = useConstructeurs() const withResolvedConstructeurs = async (product) => { 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, product.constructeur, ) const hasResolvedConstructeurs = Array.isArray(product.constructeurs) && product.constructeurs.length > 0 && product.constructeurs.every((item) => item && typeof item === 'object') if (ids.length && !hasResolvedConstructeurs) { const resolved = await ensureConstructeurs(ids) if (resolved.length) { product.constructeurs = resolved product.constructeurIds = ids } } return product } /** * Load products with pagination and search support * @param {Object} options - Query options * @param {string} [options.search] - Search term for name/reference * @param {number} [options.page=1] - Current page (1-based) * @param {number} [options.itemsPerPage=30] - Items per page * @param {string} [options.orderBy='name'] - Field to order by * @param {string} [options.orderDir='asc'] - Order direction (asc/desc) * @param {boolean} [options.force=false] - Force reload even if already loaded */ const loadProducts = async (options = {}) => { const { search = '', page = 1, itemsPerPage = 30, orderBy = 'name', orderDir = 'asc', force = false } = options if (loading.value) { return { success: true, data: { items: products.value, total: total.value, page, itemsPerPage }, } } loading.value = true error.value = null try { const params = new URLSearchParams() params.set('itemsPerPage', String(itemsPerPage)) params.set('page', String(page)) if (search && search.trim()) { params.set('name', search.trim()) } params.set(`order[${orderBy}]`, orderDir) const result = await get(`/products?${params.toString()}`) if (result.success) { const items = extractCollection(result.data) const enrichedItems = await Promise.all(items.map((item) => withResolvedConstructeurs(item))) products.value = enrichedItems total.value = extractTotal(result.data, items.length) loaded.value = true return { success: true, data: { items: enrichedItems, total: total.value, page, itemsPerPage } } } else if (result.error) { error.value = result.error showError(`Impossible de charger les produits: ${result.error}`) } return result } catch (err) { console.error('Erreur lors du chargement des produits:', err) const message = err?.message ?? 'Erreur inconnue' error.value = message showError(`Impossible de charger les produits: ${message}`) return { success: false, error: message } } finally { loading.value = false } } const createProduct = async (payload) => { const normalizedPayload = normalizeRelationIds(buildConstructeurRequestPayload(payload)) loading.value = true error.value = null try { const result = await post('/products', normalizedPayload) if (result.success && result.data) { const enriched = await withResolvedConstructeurs(result.data) const added = replaceInCache(enriched) if (added) { total.value += 1 } } else if (result.error) { error.value = result.error showError(result.error) } return result } catch (err) { console.error('Erreur lors de la création du produit:', err) const message = err?.message ?? 'Erreur inconnue' error.value = message showError(message) return { success: false, error: message } } finally { loading.value = false } } const updateProduct = async (id, payload) => { const normalizedPayload = normalizeRelationIds(buildConstructeurRequestPayload(payload)) loading.value = true error.value = null try { const result = await patch(`/products/${id}`, normalizedPayload) if (result.success && result.data) { const enriched = await withResolvedConstructeurs(result.data) replaceInCache(enriched) } else if (result.error) { error.value = result.error showError(result.error) } return result } catch (err) { console.error('Erreur lors de la mise à jour du produit:', err) const message = err?.message ?? 'Erreur inconnue' error.value = message showError(message) return { success: false, error: message } } finally { loading.value = false } } const deleteProduct = async (id) => { loading.value = true error.value = null try { const result = await del(`/products/${id}`) if (result.success) { const removed = products.value.find((product) => product.id === id) products.value = products.value.filter((product) => product.id !== id) total.value = Math.max(0, total.value - 1) } else if (result.error) { error.value = result.error showError(result.error) } return result } catch (err) { console.error('Erreur lors de la suppression du produit:', err) const message = err?.message ?? 'Erreur inconnue' error.value = message showError(message) return { success: false, error: message } } finally { loading.value = false } } const getProduct = async (id, options = {}) => { const shouldForce = !!options.force if (!shouldForce) { const cached = products.value.find((product) => product.id === id) if (cached && Array.isArray(cached.constructeurs) && cached.constructeurs.length > 0) { return { success: true, data: cached } } } try { const result = await get(`/products/${id}`) if (result.success && result.data) { const enriched = await withResolvedConstructeurs(result.data) replaceInCache(enriched) return { success: true, data: enriched } } return result } catch (err) { console.error('Erreur lors du chargement du produit:', err) const message = err?.message ?? 'Erreur inconnue' return { success: false, error: message } } } const clearProductsCache = () => { products.value = [] total.value = 0 loaded.value = false error.value = null } return { products, total, loading, loaded, error, loadProducts, createProduct, updateProduct, deleteProduct, getProduct, clearProductsCache, } }