From d00e5c058bdec7f1c7333e5e8965eaa3cc85606b Mon Sep 17 00:00:00 2001 From: matthieu Date: Sun, 8 Mar 2026 17:13:27 +0100 Subject: [PATCH] refactor(frontend) : extract RelatedItemsModal from ManagementView Co-Authored-By: Claude Opus 4.6 --- app/components/model-types/ManagementView.vue | 153 +-------------- .../model-types/RelatedItemsModal.vue | 182 ++++++++++++++++++ 2 files changed, 189 insertions(+), 146 deletions(-) create mode 100644 app/components/model-types/RelatedItemsModal.vue diff --git a/app/components/model-types/ManagementView.vue b/app/components/model-types/ManagementView.vue index 3d933eb..d3fb763 100644 --- a/app/components/model-types/ManagementView.vue +++ b/app/components/model-types/ManagementView.vue @@ -106,59 +106,12 @@ @converted="onConverted" /> - - - + @@ -167,9 +120,7 @@ import { computed, onBeforeUnmount, onMounted, ref, watch, type Ref } from 'vue' import { useHead, useRouter } from '#imports' import DataTable from '~/components/common/DataTable.vue' import ModelTypesConversionModal from '~/components/model-types/ConversionModal.vue' -import { useApi } from '~/composables/useApi' import { useUrlState } from '~/composables/useUrlState' -import { extractCollection } from '~/shared/utils/apiHelpers' import type { DataTableSort } from '~/shared/types/dataTable' import { deleteModelType, @@ -233,7 +184,6 @@ let activeController: AbortController | null = null const router = useRouter() const { showError, showSuccess } = useToast() -const { get } = useApi() const { canEdit } = usePermissions() const headingText = computed(() => props.heading) @@ -422,106 +372,21 @@ const confirmDelete = async (item: ModelType) => { } } -type RelatedEntry = { - id: string - name: string - reference?: string | null -} - const relatedModalOpen = ref(false) -const relatedLoading = ref(false) -const relatedError = ref(null) -const relatedItems = ref([]) const relatedType = ref(null) -const relatedCategoryLabels: Record = { - COMPONENT: { plural: 'composants', singular: 'composant' }, - PIECE: { plural: 'pièces', singular: 'pièce' }, - PRODUCT: { plural: 'produits', singular: 'produit' }, -} - -const relatedModalTitle = computed(() => { - const current = relatedType.value - if (!current) return 'Éléments liés' - return `Éléments liés à « ${current.name} »` -}) - -const relatedModalSubtitle = computed(() => { - const current = relatedType.value - if (!current) return '' - const labels = relatedCategoryLabels[current.category] ?? relatedCategoryLabels.COMPONENT - const count = relatedItems.value.length - if (relatedLoading.value) return `Chargement des ${labels.plural}…` - if (count === 0) return `Aucun ${labels.singular} lié.` - if (count === 1) return `1 ${labels.singular} lié.` - return `${count} ${labels.plural} liés.` -}) - -const buildModelTypeIri = (id: string) => `/api/model_types/${id}` - -const resolveRelatedConfig = (category: ModelCategory) => { - if (category === 'COMPONENT') return { endpoint: '/composants', filterKey: 'typeComposant' } - if (category === 'PIECE') return { endpoint: '/pieces', filterKey: 'typePiece' } - return { endpoint: '/products', filterKey: 'typeProduct' } -} - const resolveRelatedEditBasePath = (category: ModelCategory) => { if (category === 'COMPONENT') return '/component' if (category === 'PIECE') return '/pieces' return '/product' } -const mapRelatedEntry = (item: unknown): RelatedEntry | null => { - if (!item || typeof item !== 'object') return null - const record = item as Record - if (typeof record.id !== 'string') return null - const name = typeof record.name === 'string' && record.name.trim() ? record.name : 'Sans nom' - const reference - = typeof record.reference === 'string' && record.reference.trim() - ? record.reference - : typeof record.code === 'string' && record.code.trim() - ? record.code - : null - return { id: record.id, name, reference } -} - -const loadRelatedItems = async (item: ModelType) => { - const { endpoint, filterKey } = resolveRelatedConfig(item.category) - const params = new URLSearchParams() - params.set('itemsPerPage', '200') - params.set(filterKey, buildModelTypeIri(item.id)) - params.set('order[name]', 'asc') - - relatedLoading.value = true - relatedError.value = null - relatedItems.value = [] - - try { - const result = await get(`${endpoint}?${params.toString()}`) - if (!result.success) { - relatedError.value = result.error ?? 'Impossible de charger les éléments liés.' - return - } - const collection = extractCollection(result.data) - relatedItems.value = collection - .map(mapRelatedEntry) - .filter((entry): entry is RelatedEntry => Boolean(entry)) - } - catch (error) { - relatedError.value = extractErrorMessage(error) - } - finally { - relatedLoading.value = false - } -} - const openRelatedModal = (item: ModelType) => { relatedType.value = item relatedModalOpen.value = true - void loadRelatedItems(item) } -const openRelatedEdit = (entry: RelatedEntry) => { +const openRelatedEdit = (entry: { id: string }) => { const current = relatedType.value if (!current) return const basePath = resolveRelatedEditBasePath(current.category) @@ -531,10 +396,6 @@ const openRelatedEdit = (entry: RelatedEntry) => { }) } -const closeRelatedModal = () => { - relatedModalOpen.value = false -} - const conversionModalOpen = ref(false) const conversionTarget = ref(null) diff --git a/app/components/model-types/RelatedItemsModal.vue b/app/components/model-types/RelatedItemsModal.vue new file mode 100644 index 0000000..646d76c --- /dev/null +++ b/app/components/model-types/RelatedItemsModal.vue @@ -0,0 +1,182 @@ + + +