207 lines
6.4 KiB
Vue
207 lines
6.4 KiB
Vue
<template>
|
|
<main class="mx-auto flex w-full max-w-4xl flex-col gap-8 px-4 py-8 sm:px-6 lg:px-8">
|
|
<header class="space-y-2">
|
|
<div class="flex items-center justify-between gap-4">
|
|
<div>
|
|
<h1 class="text-3xl font-bold text-base-content">{{ title }}</h1>
|
|
<p class="text-base text-base-content/70">
|
|
Ajustez le squelette et les métadonnées de cette catégorie de composant. Les modifications seront appliquées lors des prochaines créations de composants.
|
|
</p>
|
|
</div>
|
|
<button type="button" class="btn btn-ghost" @click="$router.back()">
|
|
Retour au catalogue
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="rounded-xl border border-base-300 bg-base-100 p-6 shadow-sm">
|
|
<div v-if="loading" class="flex items-center justify-center py-16">
|
|
<span class="loading loading-spinner loading-lg" aria-hidden="true"></span>
|
|
<span class="ml-3 text-sm text-base-content/70">Chargement de la catégorie…</span>
|
|
</div>
|
|
<ModelTypeForm
|
|
v-else
|
|
mode="edit"
|
|
initial-category="COMPONENT"
|
|
:initial-data="initialData"
|
|
:lock-category="true"
|
|
:saving="saving"
|
|
:readonly="!canEdit"
|
|
@submit="handleSubmit"
|
|
@cancel="handleCancel"
|
|
/>
|
|
</section>
|
|
|
|
<!-- Comments -->
|
|
<div class="mt-4">
|
|
<CommentSection
|
|
entity-type="component_category"
|
|
:entity-id="String(route.params.id)"
|
|
:entity-name="initialData?.name"
|
|
show-resolved
|
|
/>
|
|
</div>
|
|
|
|
<SyncConfirmationModal
|
|
:preview="syncPreviewData"
|
|
:open="showSyncModal"
|
|
:loading="syncLoading"
|
|
@confirm="handleSyncConfirm"
|
|
@cancel="handleSyncCancel"
|
|
/>
|
|
</main>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, onMounted, ref } from 'vue'
|
|
import { useHead, useRoute, useRouter } from '#imports'
|
|
import ModelTypeForm from '~/components/model-types/ModelTypeForm.vue'
|
|
import { getModelType, updateModelType, syncPreview, syncExecute, type ModelTypePayload, type SyncPreviewResult } from '~/services/modelTypes'
|
|
import type { ComponentModelStructure } from '~/shared/types/inventory'
|
|
import { useComponentTypes } from '~/composables/useComponentTypes'
|
|
import { useToast } from '~/composables/useToast'
|
|
|
|
const { canEdit } = usePermissions()
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
const { showError, showSuccess } = useToast()
|
|
const { loadComponentTypes } = useComponentTypes()
|
|
|
|
const loading = ref(true)
|
|
const saving = ref(false)
|
|
const initialData = ref<Partial<ModelTypePayload> | null>(null)
|
|
const showSyncModal = ref(false)
|
|
const syncLoading = ref(false)
|
|
const syncPreviewData = ref<SyncPreviewResult | null>(null)
|
|
const pendingPayload = ref<Partial<ModelTypePayload> | null>(null)
|
|
|
|
const title = computed(() =>
|
|
initialData.value?.name
|
|
? `Modifier « ${initialData.value.name} »`
|
|
: 'Modifier une catégorie de composant',
|
|
)
|
|
|
|
useHead(() => ({
|
|
title: title.value,
|
|
}))
|
|
|
|
const navigateBackToList = async () => {
|
|
await router.push('/component-category').catch(() => {
|
|
showError("Navigation impossible vers la liste des catégories.")
|
|
})
|
|
}
|
|
|
|
const normalizeError = (error: any) => {
|
|
const message = error?.data?.message || error?.message || 'Une erreur est survenue.'
|
|
return Array.isArray(message) ? message[0] : message
|
|
}
|
|
|
|
const loadCategory = async () => {
|
|
loading.value = true
|
|
try {
|
|
const id = String(route.params.id)
|
|
const response = await getModelType(id)
|
|
|
|
if (response.category !== 'COMPONENT') {
|
|
showError("Cette catégorie n'est pas un composant." )
|
|
await navigateBackToList()
|
|
return
|
|
}
|
|
|
|
initialData.value = {
|
|
name: response.name,
|
|
code: response.code,
|
|
category: response.category,
|
|
notes: response.notes ?? response.description ?? '',
|
|
structure: (response.structure as ComponentModelStructure | null) ?? undefined,
|
|
referenceFormula: response.referenceFormula ?? null,
|
|
requiredFieldsForReference: response.requiredFieldsForReference ?? null,
|
|
}
|
|
|
|
} catch (error) {
|
|
showError(normalizeError(error))
|
|
await navigateBackToList()
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
const handleCancel = () => {
|
|
navigateBackToList()
|
|
}
|
|
|
|
const handleSubmit = async (payload: Parameters<typeof updateModelType>[1]) => {
|
|
if (!canEdit.value) return
|
|
const id = String(route.params.id)
|
|
saving.value = true
|
|
try {
|
|
const enrichedPayload = {
|
|
...payload,
|
|
description: payload?.notes ?? null,
|
|
}
|
|
|
|
// Get sync preview BEFORE saving
|
|
const preview = await syncPreview(id, enrichedPayload.structure || {})
|
|
|
|
const hasImpact = preview && (
|
|
Object.values(preview.additions || {}).some(v => v > 0)
|
|
|| Object.values(preview.deletions || {}).some(v => v > 0)
|
|
|| Object.values(preview.modifications || {}).some(v => v > 0)
|
|
)
|
|
|
|
if (hasImpact) {
|
|
// Show modal for confirmation
|
|
pendingPayload.value = enrichedPayload
|
|
syncPreviewData.value = preview
|
|
showSyncModal.value = true
|
|
} else {
|
|
// No impact — save directly + sync
|
|
await updateModelType(id, enrichedPayload)
|
|
await syncExecute(id, { confirmDeletions: false, confirmTypeChanges: false })
|
|
await loadComponentTypes({ force: true })
|
|
showSuccess('Catégorie de composant mise à jour avec succès.')
|
|
}
|
|
} catch (error) {
|
|
showError(normalizeError(error))
|
|
} finally {
|
|
saving.value = false
|
|
}
|
|
}
|
|
|
|
const handleSyncConfirm = async () => {
|
|
if (!pendingPayload.value) return
|
|
const id = String(route.params.id)
|
|
|
|
syncLoading.value = true
|
|
try {
|
|
const hasDeletions = syncPreviewData.value && Object.values(syncPreviewData.value.deletions || {}).some(v => v > 0)
|
|
const hasModifications = syncPreviewData.value && Object.values(syncPreviewData.value.modifications || {}).some(v => v > 0)
|
|
|
|
await updateModelType(id, pendingPayload.value)
|
|
await syncExecute(id, {
|
|
confirmDeletions: !!hasDeletions,
|
|
confirmTypeChanges: !!hasModifications,
|
|
})
|
|
await loadComponentTypes({ force: true })
|
|
showSuccess('Catégorie de composant mise à jour avec succès.')
|
|
} catch (error) {
|
|
showError(normalizeError(error))
|
|
} finally {
|
|
syncLoading.value = false
|
|
showSyncModal.value = false
|
|
pendingPayload.value = null
|
|
syncPreviewData.value = null
|
|
}
|
|
}
|
|
|
|
const handleSyncCancel = () => {
|
|
showSyncModal.value = false
|
|
pendingPayload.value = null
|
|
syncPreviewData.value = null
|
|
}
|
|
|
|
onMounted(() => {
|
|
loadCategory()
|
|
})
|
|
</script>
|