feat(constructeur) : update piece edit flow with supplier references
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,11 +7,13 @@ import { useApi } from '~/composables/useApi'
|
|||||||
import { useToast } from '~/composables/useToast'
|
import { useToast } from '~/composables/useToast'
|
||||||
import { useDocuments } from '~/composables/useDocuments'
|
import { useDocuments } from '~/composables/useDocuments'
|
||||||
import { useConstructeurs } from '~/composables/useConstructeurs'
|
import { useConstructeurs } from '~/composables/useConstructeurs'
|
||||||
|
import { useConstructeurLinks } from '~/composables/useConstructeurLinks'
|
||||||
import { usePieceHistory } from '~/composables/usePieceHistory'
|
import { usePieceHistory } from '~/composables/usePieceHistory'
|
||||||
import { extractRelationId } from '~/shared/apiRelations'
|
import { extractRelationId } from '~/shared/apiRelations'
|
||||||
import { canPreviewDocument } from '~/utils/documentPreview'
|
import { canPreviewDocument } from '~/utils/documentPreview'
|
||||||
import { formatPieceStructurePreview } from '~/shared/modelUtils'
|
import { formatPieceStructurePreview } from '~/shared/modelUtils'
|
||||||
import { uniqueConstructeurIds } from '~/shared/constructeurUtils'
|
import { uniqueConstructeurIds, constructeurIdsFromLinks } from '~/shared/constructeurUtils'
|
||||||
|
import type { ConstructeurLinkEntry } from '~/shared/constructeurUtils'
|
||||||
import type { PieceModelStructure } from '~/shared/types/inventory'
|
import type { PieceModelStructure } from '~/shared/types/inventory'
|
||||||
import type { ModelType } from '~/services/modelTypes'
|
import type { ModelType } from '~/services/modelTypes'
|
||||||
import {
|
import {
|
||||||
@@ -46,6 +48,7 @@ export function usePieceEdit(pieceId: string) {
|
|||||||
const toast = useToast()
|
const toast = useToast()
|
||||||
const { loadDocumentsByPiece, uploadDocuments, deleteDocument } = useDocuments()
|
const { loadDocumentsByPiece, uploadDocuments, deleteDocument } = useDocuments()
|
||||||
const { ensureConstructeurs } = useConstructeurs()
|
const { ensureConstructeurs } = useConstructeurs()
|
||||||
|
const { fetchLinks, syncLinks } = useConstructeurLinks()
|
||||||
const {
|
const {
|
||||||
history,
|
history,
|
||||||
loading: historyLoading,
|
loading: historyLoading,
|
||||||
@@ -82,6 +85,9 @@ export function usePieceEdit(pieceId: string) {
|
|||||||
constructeurIds: [] as string[],
|
constructeurIds: [] as string[],
|
||||||
prix: '' as string,
|
prix: '' as string,
|
||||||
})
|
})
|
||||||
|
const constructeurLinks = ref<ConstructeurLinkEntry[]>([])
|
||||||
|
const originalConstructeurLinks = ref<ConstructeurLinkEntry[]>([])
|
||||||
|
const constructeurIdsFromForm = computed(() => constructeurIdsFromLinks(constructeurLinks.value))
|
||||||
const productSelections = ref<(string | null)[]>([])
|
const productSelections = ref<(string | null)[]>([])
|
||||||
|
|
||||||
const customFieldInputs = ref<CustomFieldInput[]>([])
|
const customFieldInputs = ref<CustomFieldInput[]>([])
|
||||||
@@ -303,15 +309,16 @@ export function usePieceEdit(pieceId: string) {
|
|||||||
editionForm.name = currentPiece.name || ''
|
editionForm.name = currentPiece.name || ''
|
||||||
editionForm.description = currentPiece.description || ''
|
editionForm.description = currentPiece.description || ''
|
||||||
editionForm.reference = currentPiece.reference || ''
|
editionForm.reference = currentPiece.reference || ''
|
||||||
editionForm.constructeurIds = uniqueConstructeurIds(
|
// Load constructeur links
|
||||||
currentPiece,
|
fetchLinks('piece', pieceId).then((links) => {
|
||||||
Array.isArray(currentPiece.constructeurs) ? currentPiece.constructeurs : [],
|
constructeurLinks.value = links
|
||||||
currentPiece.constructeur ? [currentPiece.constructeur] : [],
|
originalConstructeurLinks.value = links.map(l => ({ ...l }))
|
||||||
)
|
editionForm.constructeurIds = constructeurIdsFromLinks(links)
|
||||||
|
if (editionForm.constructeurIds.length) {
|
||||||
|
void ensureConstructeurs(editionForm.constructeurIds)
|
||||||
|
}
|
||||||
|
})
|
||||||
editionForm.prix = currentPiece.prix !== null && currentPiece.prix !== undefined ? String(currentPiece.prix) : ''
|
editionForm.prix = currentPiece.prix !== null && currentPiece.prix !== undefined ? String(currentPiece.prix) : ''
|
||||||
if (editionForm.constructeurIds.length) {
|
|
||||||
void ensureConstructeurs(editionForm.constructeurIds)
|
|
||||||
}
|
|
||||||
|
|
||||||
const existingProductIds = Array.isArray(currentPiece.productIds) && currentPiece.productIds.length
|
const existingProductIds = Array.isArray(currentPiece.productIds) && currentPiece.productIds.length
|
||||||
? currentPiece.productIds.map((id: unknown) => String(id))
|
? currentPiece.productIds.map((id: unknown) => String(id))
|
||||||
@@ -370,12 +377,9 @@ export function usePieceEdit(pieceId: string) {
|
|||||||
? ''
|
? ''
|
||||||
: String(editionForm.prix).trim()
|
: String(editionForm.prix).trim()
|
||||||
|
|
||||||
const constructeurIds = uniqueConstructeurIds(editionForm.constructeurIds)
|
|
||||||
|
|
||||||
const payload: Record<string, any> = {
|
const payload: Record<string, any> = {
|
||||||
name: editionForm.name.trim(),
|
name: editionForm.name.trim(),
|
||||||
description: editionForm.description.trim() || null,
|
description: editionForm.description.trim() || null,
|
||||||
constructeurIds,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const reference = editionForm.reference.trim()
|
const reference = editionForm.reference.trim()
|
||||||
@@ -412,6 +416,8 @@ export function usePieceEdit(pieceId: string) {
|
|||||||
],
|
],
|
||||||
{ customFieldInputs, upsertCustomFieldValue, updateCustomFieldValue, toast },
|
{ customFieldInputs, upsertCustomFieldValue, updateCustomFieldValue, toast },
|
||||||
)
|
)
|
||||||
|
await syncLinks('piece', piece.value.id, originalConstructeurLinks.value, constructeurLinks.value)
|
||||||
|
originalConstructeurLinks.value = constructeurLinks.value.map(l => ({ ...l }))
|
||||||
toast.showSuccess('Pièce mise à jour avec succès.')
|
toast.showSuccess('Pièce mise à jour avec succès.')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -441,6 +447,9 @@ export function usePieceEdit(pieceId: string) {
|
|||||||
previewVisible,
|
previewVisible,
|
||||||
selectedTypeId,
|
selectedTypeId,
|
||||||
editionForm,
|
editionForm,
|
||||||
|
constructeurLinks,
|
||||||
|
originalConstructeurLinks,
|
||||||
|
constructeurIdsFromForm,
|
||||||
productSelections,
|
productSelections,
|
||||||
customFieldInputs,
|
customFieldInputs,
|
||||||
canEdit,
|
canEdit,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useToast } from './useToast'
|
import { useToast } from './useToast'
|
||||||
import { useApi } from './useApi'
|
import { useApi } from './useApi'
|
||||||
import { buildConstructeurRequestPayload, uniqueConstructeurIds } from '~/shared/constructeurUtils'
|
import { uniqueConstructeurIds } from '~/shared/constructeurUtils'
|
||||||
import { useConstructeurs, type Constructeur } from './useConstructeurs'
|
import { useConstructeurs, type Constructeur } from './useConstructeurs'
|
||||||
import { extractRelationId, normalizeRelationIds } from '~/shared/apiRelations'
|
import { extractRelationId, normalizeRelationIds } from '~/shared/apiRelations'
|
||||||
import { extractCollection } from '~/shared/utils/apiHelpers'
|
import { extractCollection } from '~/shared/utils/apiHelpers'
|
||||||
@@ -196,7 +196,8 @@ export function usePieces() {
|
|||||||
const createPiece = async (pieceData: Partial<Piece>): Promise<PieceSingleResult> => {
|
const createPiece = async (pieceData: Partial<Piece>): Promise<PieceSingleResult> => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const normalizedPayload = normalizeRelationIds(buildConstructeurRequestPayload(pieceData))
|
const { constructeurIds, constructeurs, constructeurId, constructeur, ...cleanPayload } = pieceData as any
|
||||||
|
const normalizedPayload = normalizeRelationIds(cleanPayload)
|
||||||
const result = await post('/pieces', normalizedPayload)
|
const result = await post('/pieces', normalizedPayload)
|
||||||
if (result.success && result.data) {
|
if (result.success && result.data) {
|
||||||
const enriched = await withResolvedConstructeurs(result.data as Piece)
|
const enriched = await withResolvedConstructeurs(result.data as Piece)
|
||||||
@@ -223,7 +224,8 @@ export function usePieces() {
|
|||||||
const updatePieceData = async (id: string, pieceData: Partial<Piece>): Promise<PieceSingleResult> => {
|
const updatePieceData = async (id: string, pieceData: Partial<Piece>): Promise<PieceSingleResult> => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
try {
|
try {
|
||||||
const normalizedPayload = normalizeRelationIds(buildConstructeurRequestPayload(pieceData))
|
const { constructeurIds, constructeurs, constructeurId, constructeur, ...cleanPayload } = pieceData as any
|
||||||
|
const normalizedPayload = normalizeRelationIds(cleanPayload)
|
||||||
const result = await patch(`/pieces/${id}`, normalizedPayload)
|
const result = await patch(`/pieces/${id}`, normalizedPayload)
|
||||||
if (result.success && result.data) {
|
if (result.success && result.data) {
|
||||||
const updated = await withResolvedConstructeurs(result.data as Piece)
|
const updated = await withResolvedConstructeurs(result.data as Piece)
|
||||||
|
|||||||
@@ -146,27 +146,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="isEditMode || editionForm.constructeurIds.length" class="form-control">
|
<div v-if="isEditMode || constructeurLinks.length" class="form-control">
|
||||||
<label class="label">
|
<label class="label">
|
||||||
<span class="label-text">Fournisseur</span>
|
<span class="label-text">Fournisseur</span>
|
||||||
</label>
|
</label>
|
||||||
<ConstructeurSelect
|
<template v-if="isEditMode">
|
||||||
v-if="isEditMode"
|
<ConstructeurSelect
|
||||||
v-model="editionForm.constructeurIds"
|
v-model="editionForm.constructeurIds"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
:disabled="!canEdit || saving"
|
:disabled="!canEdit || saving"
|
||||||
placeholder="Rechercher un ou plusieurs fournisseurs..."
|
placeholder="Rechercher un ou plusieurs fournisseurs..."
|
||||||
:initial-options="piece?.constructeurs || []"
|
:initial-options="piece?.constructeurs || []"
|
||||||
|
/>
|
||||||
|
<ConstructeurLinksTable
|
||||||
|
v-model="constructeurLinks"
|
||||||
|
class="mt-2"
|
||||||
|
@remove="handleConstructeurRemoved"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<ConstructeurLinksTable
|
||||||
|
v-else
|
||||||
|
:model-value="constructeurLinks"
|
||||||
|
readonly
|
||||||
/>
|
/>
|
||||||
<div v-else class="flex flex-wrap gap-2">
|
|
||||||
<span
|
|
||||||
v-for="id in editionForm.constructeurIds"
|
|
||||||
:key="id"
|
|
||||||
class="badge badge-outline"
|
|
||||||
>
|
|
||||||
{{ getConstructeurById(id)?.name || id }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -389,16 +391,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, onMounted, ref } from 'vue'
|
import { computed, onMounted, ref, watch } from 'vue'
|
||||||
import { useRoute } from '#imports'
|
import { useRoute } from '#imports'
|
||||||
import { usePieceEdit } from '~/composables/usePieceEdit'
|
import { usePieceEdit } from '~/composables/usePieceEdit'
|
||||||
import { useDocuments } from '~/composables/useDocuments'
|
import { useDocuments } from '~/composables/useDocuments'
|
||||||
import { usePermissions } from '~/composables/usePermissions'
|
import { usePermissions } from '~/composables/usePermissions'
|
||||||
import { useConstructeurs } from '~/composables/useConstructeurs'
|
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const { canEdit } = usePermissions()
|
const { canEdit } = usePermissions()
|
||||||
const { getConstructeurById } = useConstructeurs()
|
|
||||||
const { updateDocument } = useDocuments()
|
const { updateDocument } = useDocuments()
|
||||||
|
|
||||||
const isEditMode = ref(false)
|
const isEditMode = ref(false)
|
||||||
@@ -416,6 +416,7 @@ const {
|
|||||||
previewVisible,
|
previewVisible,
|
||||||
selectedTypeId,
|
selectedTypeId,
|
||||||
editionForm,
|
editionForm,
|
||||||
|
constructeurLinks,
|
||||||
productSelections,
|
productSelections,
|
||||||
customFieldInputs,
|
customFieldInputs,
|
||||||
pieceTypeList,
|
pieceTypeList,
|
||||||
@@ -448,6 +449,18 @@ const submitEdition = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sync ConstructeurSelect changes → constructeurLinks
|
||||||
|
watch(() => editionForm.constructeurIds, (newIds) => {
|
||||||
|
const existing = new Map(constructeurLinks.value.map(l => [l.constructeurId, l]))
|
||||||
|
constructeurLinks.value = newIds.map(id =>
|
||||||
|
existing.get(id) || { constructeurId: id, supplierReference: null },
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const handleConstructeurRemoved = (constructeurId: string) => {
|
||||||
|
editionForm.constructeurIds = editionForm.constructeurIds.filter(id => id !== constructeurId)
|
||||||
|
}
|
||||||
|
|
||||||
const editingDocument = ref<any | null>(null)
|
const editingDocument = ref<any | null>(null)
|
||||||
const editModalVisible = ref(false)
|
const editModalVisible = ref(false)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user