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()
})