import { ref } from 'vue' import { useToast } from './useToast' import { useApi } from './useApi' import { humanizeError } from '~/shared/utils/errorMessages' import { uniqueConstructeurIds } from '~/shared/constructeurUtils' import { useConstructeurs, type Constructeur } from './useConstructeurs' import { extractRelationId, normalizeRelationIds } from '~/shared/apiRelations' import { extractCollection, extractTotal } from '~/shared/utils/apiHelpers' export interface Product { id: string name: string reference?: string | null typeProductId?: string | null typeProduct?: { id: string; name?: string } | null constructeurs?: Constructeur[] constructeurIds?: string[] supplierPrice?: number | null createdAt?: string | null updatedAt?: string | null documents?: unknown[] [key: string]: unknown } interface ProductListResult { success: boolean data?: { items: Product[]; total: number; page: number; itemsPerPage: number } error?: string } interface ProductSingleResult { success: boolean data?: Product error?: string } interface LoadProductsOptions { search?: string page?: number itemsPerPage?: number orderBy?: string orderDir?: 'asc' | 'desc' typeName?: string typeProductId?: string force?: boolean } const products = ref([]) const total = ref(0) const loading = ref(false) const loaded = ref(false) const error = ref(null) const replaceInCache = (item: Product): boolean => { 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 } export function useProducts() { const { showError } = useToast() const { get, post, patch, delete: del } = useApi() const { ensureConstructeurs } = useConstructeurs() const withResolvedConstructeurs = async (product: Product): Promise => { 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, ) 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 } const loadProducts = async (options: LoadProductsOptions = {}): Promise => { const { search = '', page = 1, itemsPerPage = 30, orderBy = 'name', orderDir = 'asc', typeName, typeProductId, force = false, } = options if (!force && loaded.value && !search && !typeName && !typeProductId && page === 1) { return { success: true, data: { items: products.value, total: total.value, page, itemsPerPage }, } } if (!typeProductId && 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('search', search.trim()) } if (typeName && typeName.trim()) { params.set('typeProduct.name', typeName.trim()) } if (typeProductId) { params.set('typeProduct', typeProductId) } 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))) const resultTotal = extractTotal(result.data, items.length) if (!typeProductId) { products.value = enrichedItems total.value = resultTotal loaded.value = true } return { success: true, data: { items: enrichedItems, total: resultTotal, page, itemsPerPage, }, } } else if (result.error) { error.value = result.error showError(`Impossible de charger les produits: ${result.error}`) } return result as ProductListResult } catch (err) { console.error('Erreur lors du chargement des produits:', err) const message = humanizeError((err as Error)?.message) error.value = message showError(`Impossible de charger les produits.`) return { success: false, error: message } } finally { loading.value = false } } const createProduct = async (payload: Partial): Promise => { const { constructeurIds, constructeurs, constructeurId, constructeur, ...cleanPayload } = payload as any const normalizedPayload = normalizeRelationIds(cleanPayload) loading.value = true error.value = null try { const result = await post('/products', normalizedPayload) if (result.success && result.data) { const enriched = await withResolvedConstructeurs(result.data as Product) const added = replaceInCache(enriched) if (added) { total.value += 1 } return { success: true, data: enriched } } else if (result.error) { error.value = result.error showError(result.error) } return { success: false, error: result.error } } catch (err) { console.error('Erreur lors de la création du produit:', err) const message = humanizeError((err as Error)?.message) error.value = message showError('Impossible de créer le produit.') return { success: false, error: message } } finally { loading.value = false } } const updateProduct = async (id: string, payload: Partial): Promise => { const { constructeurIds, constructeurs, constructeurId, constructeur, ...cleanPayload } = payload as any const normalizedPayload = normalizeRelationIds(cleanPayload) 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 as Product) replaceInCache(enriched) return { success: true, data: enriched } } else if (result.error) { error.value = result.error showError(result.error) } return { success: false, error: result.error } } catch (err) { console.error('Erreur lors de la mise à jour du produit:', err) const message = humanizeError((err as Error)?.message) error.value = message showError('Impossible de mettre à jour le produit.') return { success: false, error: message } } finally { loading.value = false } } const deleteProduct = async (id: string): Promise => { loading.value = true error.value = null try { const result = await del(`/products/${id}`) if (result.success) { products.value = products.value.filter((product) => product.id !== id) total.value = Math.max(0, total.value - 1) return { success: true } } else if (result.error) { error.value = result.error showError(result.error) } return { success: false, error: result.error } } catch (err) { console.error('Erreur lors de la suppression du produit:', err) const message = humanizeError((err as Error)?.message) error.value = message showError('Impossible de supprimer le produit.') return { success: false, error: message } } finally { loading.value = false } } const getProduct = async (id: string, options: { force?: boolean } = {}): Promise => { 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 as Product) replaceInCache(enriched) return { success: true, data: enriched } } return { success: false, error: result.error } } catch (err) { console.error('Erreur lors du chargement du produit:', err) const message = (err as Error)?.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, } }