Files
Inventory/frontend/app/composables/useComposants.ts
r-dev 201485552a fix(ui) : remove legacy edit pages and history composables, unify create/edit forms
Consolidate create and edit pages into single create pages with edit mode support.
Remove obsolete catalog pages, history composables, and fix remaining code review issues.
Include migration to relink orphaned custom fields.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 11:19:50 +02:00

266 lines
8.3 KiB
TypeScript

import { ref } from 'vue'
import { useToast } from './useToast'
import { useApi } from './useApi'
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 Composant {
id: string
name: string
reference?: string | null
description?: string | null
typeComposantId?: string | null
typeComposant?: { id: string; name?: string } | null
productId?: string | null
product?: { id: string; name?: string } | null
constructeurs?: Constructeur[]
constructeurIds?: string[]
documents?: unknown[]
createdAt?: string | null
updatedAt?: string | null
[key: string]: unknown
}
interface ComposantListResult {
success: boolean
data?: { items: Composant[]; total: number; page: number; itemsPerPage: number }
error?: string
}
interface ComposantSingleResult {
success: boolean
data?: Composant
error?: string
}
interface LoadComposantsOptions {
search?: string
page?: number
itemsPerPage?: number
orderBy?: string
orderDir?: 'asc' | 'desc'
typeName?: string
typeComposantId?: string
force?: boolean
}
const composants = ref<Composant[]>([])
const total = ref(0)
const loading = ref(false)
const loaded = ref(false)
export function useComposants() {
const { showSuccess } = useToast()
const { get, post, patch, delete: del } = useApi()
const { ensureConstructeurs } = useConstructeurs()
const withResolvedConstructeurs = async (composant: Composant): Promise<Composant> => {
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,
)
const hasResolvedConstructeurs =
Array.isArray(composant.constructeurs) &&
composant.constructeurs.length > 0 &&
composant.constructeurs.every((item) => item && typeof item === 'object')
if (ids.length && !hasResolvedConstructeurs) {
const resolved = await ensureConstructeurs(ids)
if (resolved.length) {
composant.constructeurs = resolved
composant.constructeurIds = ids
}
}
return composant
}
const loadComposants = async (options: LoadComposantsOptions = {}): Promise<ComposantListResult> => {
const {
search = '',
page = 1,
itemsPerPage = 30,
orderBy = 'name',
orderDir = 'asc',
typeName,
typeComposantId,
force = false,
} = options
if (!force && loaded.value && !search && !typeName && !typeComposantId && page === 1) {
return {
success: true,
data: { items: composants.value, total: total.value, page, itemsPerPage },
}
}
if (!typeComposantId && loading.value) {
return {
success: true,
data: { items: composants.value, total: total.value, page, itemsPerPage },
}
}
loading.value = true
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('typeComposant.name', typeName.trim())
}
if (typeComposantId) {
params.set('typeComposant', typeComposantId)
}
params.set(`order[${orderBy}]`, orderDir)
const result = await get(`/composants?${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 (!typeComposantId) {
composants.value = enrichedItems
total.value = resultTotal
loaded.value = true
}
return {
success: true,
data: {
items: enrichedItems,
total: resultTotal,
page,
itemsPerPage,
},
}
}
return result as ComposantListResult
} catch (error) {
console.error('Erreur lors du chargement des composants:', error)
return { success: false, error: (error as Error).message }
} finally {
loading.value = false
}
}
const createComposant = async (composantData: Partial<Composant>): Promise<ComposantSingleResult> => {
loading.value = true
try {
const { constructeurIds, constructeurs, constructeurId, constructeur, ...cleanPayload } = composantData as any
const normalizedPayload = normalizeRelationIds(cleanPayload)
const result = await post('/composants', normalizedPayload)
if (result.success && result.data) {
const enriched = await withResolvedConstructeurs(result.data as Composant)
composants.value.unshift(enriched)
total.value += 1
const definition = (composantData as Record<string, unknown>)?.definition as Record<string, unknown> | undefined
const displayName =
(result.data as Composant)?.name ||
(definition?.name as string | undefined) ||
composantData?.name ||
'Composant'
showSuccess(`Composant "${displayName}" créé avec succès`)
return { success: true, data: enriched }
}
return { success: false, error: result.error }
} catch (error) {
console.error('Erreur lors de la création du composant:', error)
return { success: false, error: (error as Error).message }
} finally {
loading.value = false
}
}
const updateComposantData = async (id: string, composantData: Partial<Composant>): Promise<ComposantSingleResult> => {
loading.value = true
try {
const { constructeurIds, constructeurs, constructeurId, constructeur, ...cleanPayload } = composantData as any
const normalizedPayload = normalizeRelationIds(cleanPayload)
const result = await patch(`/composants/${id}`, normalizedPayload)
if (result.success && result.data) {
const updated = await withResolvedConstructeurs(result.data as Composant)
const index = composants.value.findIndex((comp) => comp.id === id)
if (index !== -1) {
composants.value[index] = updated
}
showSuccess(`Composant "${updated?.name || composantData.name || ''}" mis à jour avec succès`)
return { success: true, data: updated }
}
return { success: false, error: result.error }
} catch (error) {
console.error('Erreur lors de la mise à jour du composant:', error)
return { success: false, error: (error as Error).message }
} finally {
loading.value = false
}
}
const deleteComposant = async (id: string): Promise<ComposantSingleResult> => {
loading.value = true
try {
const result = await del(`/composants/${id}`)
if (result.success) {
const deletedComposant = composants.value.find((comp) => comp.id === id)
composants.value = composants.value.filter((comp) => comp.id !== id)
total.value = Math.max(0, total.value - 1)
showSuccess(`Composant "${deletedComposant?.name || 'inconnu'}" supprimé avec succès`)
return { success: true }
}
return { success: false, error: result.error }
} catch (error) {
console.error('Erreur lors de la suppression du composant:', error)
return { success: false, error: (error as Error).message }
} finally {
loading.value = false
}
}
const getComposants = () => composants.value
const isLoading = () => loading.value
const clearComposantsCache = () => {
composants.value = []
total.value = 0
loaded.value = false
}
return {
composants,
total,
loading,
loaded,
loadComposants,
createComposant,
updateComposant: updateComposantData,
deleteComposant,
getComposants,
isLoading,
clearComposantsCache,
}
}