From 399ec1f7b406e102bbf18d039d5b5a8f71df2f9e Mon Sep 17 00:00:00 2001 From: Matthieu Date: Mon, 9 Feb 2026 11:13:31 +0100 Subject: [PATCH] refactor(composables): merge 3 history composables into generic (F2.2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create useEntityHistory.ts with parameterized entity type. Rewrite useComponentHistory, usePieceHistory, useProductHistory as thin backward-compatible wrappers (67→13 LOC each). Co-Authored-By: Claude Opus 4.6 --- app/composables/useComponentHistory.ts | 75 ++++---------------------- app/composables/useEntityHistory.ts | 69 ++++++++++++++++++++++++ app/composables/usePieceHistory.ts | 75 ++++---------------------- app/composables/useProductHistory.ts | 75 ++++---------------------- 4 files changed, 99 insertions(+), 195 deletions(-) create mode 100644 app/composables/useEntityHistory.ts diff --git a/app/composables/useComponentHistory.ts b/app/composables/useComponentHistory.ts index 314c1ba..620eb36 100644 --- a/app/composables/useComponentHistory.ts +++ b/app/composables/useComponentHistory.ts @@ -1,67 +1,12 @@ -import { ref } from 'vue' -import { useApi } from '~/composables/useApi' +/** + * Backward-compatible wrapper around useEntityHistory. + * Real logic lives in useEntityHistory.ts. + */ +import { useEntityHistory, type EntityHistoryActor, type EntityHistoryEntry } from './useEntityHistory' -export type ComponentHistoryActor = { - id: string - label: string +export type ComponentHistoryActor = EntityHistoryActor +export type ComponentHistoryEntry = EntityHistoryEntry + +export function useComponentHistory() { + return useEntityHistory('composant') } - -export type ComponentHistoryEntry = { - id: string - action: 'create' | 'update' | 'delete' | string - createdAt: string - actor: ComponentHistoryActor | null - diff: Record | null - snapshot: Record | null -} - -const extractItems = (payload: any): ComponentHistoryEntry[] => { - if (Array.isArray(payload?.items)) { - return payload.items - } - if (Array.isArray(payload?.member)) { - return payload.member - } - if (Array.isArray(payload?.['hydra:member'])) { - return payload['hydra:member'] - } - return [] -} - -export function useComponentHistory () { - const { get } = useApi() - - const history = ref([]) - const loading = ref(false) - const error = ref(null) - - const loadHistory = async (componentId: string) => { - loading.value = true - error.value = null - try { - const result = await get(`/composants/${componentId}/history`) - if (!result.success) { - error.value = result.error ?? 'Impossible de charger l’historique.' - history.value = [] - return result - } - history.value = extractItems(result.data) as ComponentHistoryEntry[] - return { success: true, data: history.value } - } catch (err: any) { - const message = err?.message ?? 'Erreur inconnue' - error.value = message - history.value = [] - return { success: false, error: message } - } finally { - loading.value = false - } - } - - return { - history, - loading, - error, - loadHistory, - } -} - diff --git a/app/composables/useEntityHistory.ts b/app/composables/useEntityHistory.ts new file mode 100644 index 0000000..12b0a5e --- /dev/null +++ b/app/composables/useEntityHistory.ts @@ -0,0 +1,69 @@ +/** + * Generic entity history composable. + * + * Replaces useComponentHistory, usePieceHistory, useProductHistory which were + * 99% identical (only the API endpoint differed). + */ + +import { ref } from 'vue' +import { useApi } from '~/composables/useApi' + +export type EntityHistoryActor = { + id: string + label: string +} + +export type EntityHistoryEntry = { + id: string + action: 'create' | 'update' | 'delete' | string + createdAt: string + actor: EntityHistoryActor | null + diff: Record | null + snapshot: Record | null +} + +const ENTITY_ENDPOINTS: Record = { + composant: '/composants', + piece: '/pieces', + product: '/products', +} + +const extractItems = (payload: any): EntityHistoryEntry[] => { + if (Array.isArray(payload?.items)) return payload.items + if (Array.isArray(payload?.member)) return payload.member + if (Array.isArray(payload?.['hydra:member'])) return payload['hydra:member'] + return [] +} + +export function useEntityHistory(entityType: 'composant' | 'piece' | 'product') { + const { get } = useApi() + const basePath = ENTITY_ENDPOINTS[entityType] + + const history = ref([]) + const loading = ref(false) + const error = ref(null) + + const loadHistory = async (entityId: string) => { + loading.value = true + error.value = null + try { + const result = await get(`${basePath}/${entityId}/history`) + if (!result.success) { + error.value = result.error ?? 'Impossible de charger l\'historique.' + history.value = [] + return result + } + history.value = extractItems(result.data) + return { success: true, data: history.value } + } catch (err: any) { + const message = err?.message ?? 'Erreur inconnue' + error.value = message + history.value = [] + return { success: false, error: message } + } finally { + loading.value = false + } + } + + return { history, loading, error, loadHistory } +} diff --git a/app/composables/usePieceHistory.ts b/app/composables/usePieceHistory.ts index 33fd9ae..7f4e607 100644 --- a/app/composables/usePieceHistory.ts +++ b/app/composables/usePieceHistory.ts @@ -1,67 +1,12 @@ -import { ref } from 'vue' -import { useApi } from '~/composables/useApi' +/** + * Backward-compatible wrapper around useEntityHistory. + * Real logic lives in useEntityHistory.ts. + */ +import { useEntityHistory, type EntityHistoryActor, type EntityHistoryEntry } from './useEntityHistory' -export type PieceHistoryActor = { - id: string - label: string +export type PieceHistoryActor = EntityHistoryActor +export type PieceHistoryEntry = EntityHistoryEntry + +export function usePieceHistory() { + return useEntityHistory('piece') } - -export type PieceHistoryEntry = { - id: string - action: 'create' | 'update' | 'delete' | string - createdAt: string - actor: PieceHistoryActor | null - diff: Record | null - snapshot: Record | null -} - -const extractItems = (payload: any): PieceHistoryEntry[] => { - if (Array.isArray(payload?.items)) { - return payload.items - } - if (Array.isArray(payload?.member)) { - return payload.member - } - if (Array.isArray(payload?.['hydra:member'])) { - return payload['hydra:member'] - } - return [] -} - -export function usePieceHistory () { - const { get } = useApi() - - const history = ref([]) - const loading = ref(false) - const error = ref(null) - - const loadHistory = async (pieceId: string) => { - loading.value = true - error.value = null - try { - const result = await get(`/pieces/${pieceId}/history`) - if (!result.success) { - error.value = result.error ?? 'Impossible de charger l’historique.' - history.value = [] - return result - } - history.value = extractItems(result.data) as PieceHistoryEntry[] - return { success: true, data: history.value } - } catch (err: any) { - const message = err?.message ?? 'Erreur inconnue' - error.value = message - history.value = [] - return { success: false, error: message } - } finally { - loading.value = false - } - } - - return { - history, - loading, - error, - loadHistory, - } -} - diff --git a/app/composables/useProductHistory.ts b/app/composables/useProductHistory.ts index 8923cf0..aa45d20 100644 --- a/app/composables/useProductHistory.ts +++ b/app/composables/useProductHistory.ts @@ -1,67 +1,12 @@ -import { ref } from 'vue' -import { useApi } from '~/composables/useApi' +/** + * Backward-compatible wrapper around useEntityHistory. + * Real logic lives in useEntityHistory.ts. + */ +import { useEntityHistory, type EntityHistoryActor, type EntityHistoryEntry } from './useEntityHistory' -export type ProductHistoryActor = { - id: string - label: string +export type ProductHistoryActor = EntityHistoryActor +export type ProductHistoryEntry = EntityHistoryEntry + +export function useProductHistory() { + return useEntityHistory('product') } - -export type ProductHistoryEntry = { - id: string - action: 'create' | 'update' | 'delete' | string - createdAt: string - actor: ProductHistoryActor | null - diff: Record | null - snapshot: Record | null -} - -const extractItems = (payload: any): ProductHistoryEntry[] => { - if (Array.isArray(payload?.items)) { - return payload.items - } - if (Array.isArray(payload?.member)) { - return payload.member - } - if (Array.isArray(payload?.['hydra:member'])) { - return payload['hydra:member'] - } - return [] -} - -export function useProductHistory () { - const { get } = useApi() - - const history = ref([]) - const loading = ref(false) - const error = ref(null) - - const loadHistory = async (productId: string) => { - loading.value = true - error.value = null - try { - const result = await get(`/products/${productId}/history`) - if (!result.success) { - error.value = result.error ?? 'Impossible de charger l’historique.' - history.value = [] - return result - } - history.value = extractItems(result.data) as ProductHistoryEntry[] - return { success: true, data: history.value } - } catch (err: any) { - const message = err?.message ?? 'Erreur inconnue' - error.value = message - history.value = [] - return { success: false, error: message } - } finally { - loading.value = false - } - } - - return { - history, - loading, - error, - loadHistory, - } -} -