diff --git a/app/pages/component-category/[id]/edit.vue b/app/pages/component-category/[id]/edit.vue index 184d233..8665930 100644 --- a/app/pages/component-category/[id]/edit.vue +++ b/app/pages/component-category/[id]/edit.vue @@ -41,6 +41,14 @@ show-resolved /> + + @@ -48,7 +56,7 @@ import { computed, onMounted, ref } from 'vue' import { useHead, useRoute, useRouter } from '#imports' import ModelTypeForm from '~/components/model-types/ModelTypeForm.vue' -import { getModelType, updateModelType, type ModelTypePayload } from '~/services/modelTypes' +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' @@ -62,6 +70,10 @@ const { loadComponentTypes } = useComponentTypes() const loading = ref(true) const saving = ref(false) const initialData = ref | null>(null) +const showSyncModal = ref(false) +const syncLoading = ref(false) +const syncPreviewData = ref(null) +const pendingPayload = ref | null>(null) const title = computed(() => initialData.value?.name @@ -125,10 +137,29 @@ const handleSubmit = async (payload: Parameters[1]) => { ...payload, description: payload?.notes ?? null, } - await updateModelType(id, enrichedPayload) - await loadComponentTypes({ force: true }) - showSuccess('Catégorie de composant mise à jour avec succès.') - await navigateBackToList() + + // 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.') + await navigateBackToList() + } } catch (error) { showError(normalizeError(error)) } finally { @@ -136,6 +167,39 @@ const handleSubmit = async (payload: Parameters[1]) => { } } +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.') + await navigateBackToList() + } 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() }) diff --git a/app/pages/piece-category/[id]/edit.vue b/app/pages/piece-category/[id]/edit.vue index e42ebed..9781dde 100644 --- a/app/pages/piece-category/[id]/edit.vue +++ b/app/pages/piece-category/[id]/edit.vue @@ -41,6 +41,14 @@ show-resolved /> + + @@ -48,7 +56,7 @@ import { computed, onMounted, ref } from 'vue' import { useHead, useRoute, useRouter } from '#imports' import ModelTypeForm from '~/components/model-types/ModelTypeForm.vue' -import { getModelType, updateModelType, type ModelTypePayload } from '~/services/modelTypes' +import { getModelType, updateModelType, syncPreview, syncExecute, type ModelTypePayload, type SyncPreviewResult } from '~/services/modelTypes' import type { PieceModelStructure } from '~/shared/types/inventory' import { usePieceTypes } from '~/composables/usePieceTypes' import { useToast } from '~/composables/useToast' @@ -62,6 +70,10 @@ const { loadPieceTypes } = usePieceTypes() const loading = ref(true) const saving = ref(false) const initialData = ref | null>(null) +const showSyncModal = ref(false) +const syncLoading = ref(false) +const syncPreviewData = ref(null) +const pendingPayload = ref | null>(null) const title = computed(() => initialData.value?.name ? `Modifier « ${initialData.value.name} »` : 'Modifier une catégorie de pièce', @@ -123,10 +135,29 @@ const handleSubmit = async (payload: Parameters[1]) => { ...payload, description: payload?.notes ?? null, } - await updateModelType(id, enrichedPayload) - await loadPieceTypes({ force: true }) - showSuccess('Catégorie de pièce mise à jour avec succès.') - await navigateBackToList() + + // 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 loadPieceTypes({ force: true }) + showSuccess('Catégorie de pièce mise à jour avec succès.') + await navigateBackToList() + } } catch (error) { showError(normalizeError(error)) } finally { @@ -134,6 +165,39 @@ const handleSubmit = async (payload: Parameters[1]) => { } } +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 loadPieceTypes({ force: true }) + showSuccess('Catégorie de pièce mise à jour avec succès.') + await navigateBackToList() + } 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() }) diff --git a/app/pages/product-category/[id]/edit.vue b/app/pages/product-category/[id]/edit.vue index 9875810..f687fb5 100644 --- a/app/pages/product-category/[id]/edit.vue +++ b/app/pages/product-category/[id]/edit.vue @@ -41,6 +41,14 @@ show-resolved /> + + @@ -48,7 +56,7 @@ import { computed, onMounted, ref } from 'vue' import { useHead, useRoute, useRouter } from '#imports' import ModelTypeForm from '~/components/model-types/ModelTypeForm.vue' -import { getModelType, updateModelType, type ModelTypePayload } from '~/services/modelTypes' +import { getModelType, updateModelType, syncPreview, syncExecute, type ModelTypePayload, type SyncPreviewResult } from '~/services/modelTypes' import type { ProductModelStructure } from '~/shared/types/inventory' import { useProductTypes } from '~/composables/useProductTypes' import { useToast } from '~/composables/useToast' @@ -62,6 +70,10 @@ const { loadProductTypes } = useProductTypes() const loading = ref(true) const saving = ref(false) const initialData = ref | null>(null) +const showSyncModal = ref(false) +const syncLoading = ref(false) +const syncPreviewData = ref(null) +const pendingPayload = ref | null>(null) const title = computed(() => initialData.value?.name ? `Modifier « ${initialData.value.name} »` : 'Modifier une catégorie de produit', @@ -123,10 +135,29 @@ const handleSubmit = async (payload: Parameters[1]) => { ...payload, description: payload?.notes ?? null, } - await updateModelType(id, enrichedPayload) - await loadProductTypes({ force: true }) - showSuccess('Catégorie de produit mise à jour avec succès.') - await navigateBackToList() + + // 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 loadProductTypes({ force: true }) + showSuccess('Catégorie de produit mise à jour avec succès.') + await navigateBackToList() + } } catch (error) { showError(normalizeError(error)) } finally { @@ -134,6 +165,39 @@ const handleSubmit = async (payload: Parameters[1]) => { } } +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 loadProductTypes({ force: true }) + showSuccess('Catégorie de produit mise à jour avec succès.') + await navigateBackToList() + } 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() })