feat(machines) : allow category-only links on machine structure
Enable adding a component, piece, or product to a machine by selecting only the category (ModelType) without a specific entity. The link displays a red "À remplir" badge; clicking it reopens the modal pre-filled with the category so the user can associate an item later. Backend: entity FKs made nullable on the 3 link tables, modelType FK added, controller/audit/version/MCP normalization adapted for null entities. Frontend: modal accepts category-only confirm, page handles fill mode, hierarchy builder creates pending nodes, display components show clickable badge with event propagation through the full hierarchy. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -12,6 +12,7 @@
|
||||
@edit-piece="$emit('edit-piece', $event)"
|
||||
@custom-field-update="$emit('custom-field-update', $event)"
|
||||
@delete="$emit('delete')"
|
||||
@fill-entity="(linkId, typeId) => $emit('fill-entity', linkId, typeId)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -43,5 +44,5 @@ defineProps({
|
||||
}
|
||||
})
|
||||
|
||||
defineEmits(['update', 'edit-piece', 'custom-field-update', 'delete'])
|
||||
defineEmits(['update', 'edit-piece', 'custom-field-update', 'delete', 'fill-entity'])
|
||||
</script>
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
/>
|
||||
|
||||
<!-- Component Header -->
|
||||
<div class="flex items-center gap-3 p-3 bg-base-200 rounded-lg cursor-pointer" @click="toggleCollapse">
|
||||
<div class="flex items-center gap-3 p-3 rounded-lg cursor-pointer" :class="component.pendingEntity ? 'bg-error/10 border border-error' : 'bg-base-200'" @click="toggleCollapse">
|
||||
<IconLucideChevronRight
|
||||
class="w-4 h-4 shrink-0 transition-transform text-base-content/50"
|
||||
:class="{ 'rotate-90': !isCollapsed }"
|
||||
@@ -22,9 +22,18 @@
|
||||
/>
|
||||
<div class="flex-1 min-w-0">
|
||||
<div class="flex items-center gap-2 flex-wrap">
|
||||
<h3 class="text-sm font-semibold text-base-content truncate">
|
||||
<h3 class="text-sm font-semibold truncate" :class="component.pendingEntity ? 'text-error' : 'text-base-content'">
|
||||
{{ component.name }}
|
||||
</h3>
|
||||
<button
|
||||
v-if="component.pendingEntity"
|
||||
type="button"
|
||||
class="badge badge-error badge-sm cursor-pointer hover:badge-outline transition-colors"
|
||||
title="Cliquer pour associer un item"
|
||||
@click.stop="$emit('fill-entity', component.linkId, component.modelTypeId)"
|
||||
>
|
||||
À remplir
|
||||
</button>
|
||||
<span v-if="component.reference" class="badge badge-outline badge-xs">{{ component.reference }}</span>
|
||||
<span v-if="component.prix" class="badge badge-primary badge-xs">{{ component.prix }}€</span>
|
||||
</div>
|
||||
@@ -54,7 +63,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Expanded content -->
|
||||
<div v-show="!isCollapsed" class="mt-3 space-y-4 pl-7">
|
||||
<div v-show="!isCollapsed && !component.pendingEntity" class="mt-3 space-y-4 pl-7">
|
||||
<!-- Info fields -->
|
||||
<div v-if="isEditMode" class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
<div class="form-control">
|
||||
@@ -241,6 +250,7 @@
|
||||
@update="updatePiece"
|
||||
@edit="editPiece"
|
||||
@custom-field-update="updatePieceCustomField"
|
||||
@fill-entity="(linkId, typeId) => $emit('fill-entity', linkId, typeId)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -276,6 +286,7 @@
|
||||
@update="$emit('update', $event)"
|
||||
@edit-piece="$emit('edit-piece', $event)"
|
||||
@custom-field-update="$emit('custom-field-update', $event)"
|
||||
@fill-entity="(linkId, typeId) => $emit('fill-entity', linkId, typeId)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -317,7 +328,7 @@ const props = defineProps({
|
||||
toggleToken: { type: Number, default: 0 },
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update', 'edit-piece', 'custom-field-update', 'delete'])
|
||||
const emit = defineEmits(['update', 'edit-piece', 'custom-field-update', 'delete', 'fill-entity'])
|
||||
|
||||
// --- Shared composables ---
|
||||
const {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
/>
|
||||
|
||||
<!-- Piece Header (collapsible, same pattern as ComponentItem) -->
|
||||
<div class="flex items-start justify-between p-4 rounded-lg" :class="piece._emptySlot ? 'bg-error/10 border border-error' : 'bg-base-200'">
|
||||
<div class="flex items-start justify-between p-4 rounded-lg" :class="piece._emptySlot || piece.pendingEntity ? 'bg-error/10 border border-error' : 'bg-base-200'">
|
||||
<div class="flex items-start gap-3 flex-1 min-w-0">
|
||||
<button
|
||||
type="button"
|
||||
@@ -28,9 +28,18 @@
|
||||
<span class="sr-only">{{ isCollapsed ? 'Déplier' : 'Replier' }} la pièce</span>
|
||||
</button>
|
||||
<div class="flex-1 min-w-0">
|
||||
<h3 class="text-lg font-semibold" :class="{ 'text-error': piece._emptySlot }">
|
||||
<h3 class="text-lg font-semibold" :class="{ 'text-error': piece._emptySlot || piece.pendingEntity }">
|
||||
{{ pieceData.name }}
|
||||
<span v-if="piece._emptySlot" class="text-sm font-semibold text-error ml-1">— manquant</span>
|
||||
<button
|
||||
v-if="piece.pendingEntity"
|
||||
type="button"
|
||||
class="badge badge-error badge-sm cursor-pointer hover:badge-outline transition-colors ml-1"
|
||||
title="Cliquer pour associer un item"
|
||||
@click.stop="$emit('fill-entity', piece.linkId, piece.modelTypeId)"
|
||||
>
|
||||
À remplir
|
||||
</button>
|
||||
<span
|
||||
v-if="displayQuantity > 1"
|
||||
class="text-sm font-normal text-base-content/60 ml-1"
|
||||
@@ -77,7 +86,7 @@
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div v-show="!isCollapsed" class="space-y-4">
|
||||
<div v-show="!isCollapsed && !piece.pendingEntity" class="space-y-4">
|
||||
<div class="p-4 bg-base-100 border border-base-200 rounded-lg">
|
||||
<div class="space-y-2 text-sm">
|
||||
<div v-if="isEditMode" class="form-control">
|
||||
@@ -308,7 +317,7 @@ const props = defineProps({
|
||||
toggleToken: { type: Number, default: 0 },
|
||||
})
|
||||
|
||||
const emit = defineEmits(['update', 'edit', 'custom-field-update', 'delete'])
|
||||
const emit = defineEmits(['update', 'edit', 'custom-field-update', 'delete', 'fill-entity'])
|
||||
|
||||
// --- Local reactive data for editing ---
|
||||
const pieceData = reactive({
|
||||
|
||||
@@ -49,6 +49,12 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="selectedTypeName && !selectedEntityId && !loadingEntities" class="bg-warning/10 border border-warning rounded-lg p-3 mb-4">
|
||||
<p class="text-sm text-warning font-medium">
|
||||
Aucun item sélectionné — la catégorie sera ajoutée avec le statut "À remplir".
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- Summary of selection -->
|
||||
<div v-if="selectedEntitySummary" class="bg-base-200 rounded-lg p-3 mb-4">
|
||||
<p class="text-sm font-medium">{{ selectedEntitySummary.name }}</p>
|
||||
@@ -64,10 +70,10 @@
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary"
|
||||
:disabled="!selectedEntityId"
|
||||
:disabled="!selectedTypeId"
|
||||
@click="handleConfirm"
|
||||
>
|
||||
Ajouter
|
||||
{{ selectedEntityId ? 'Ajouter' : 'Ajouter (catégorie seule)' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -90,11 +96,12 @@ type EntityKind = 'component' | 'piece' | 'product'
|
||||
const props = defineProps<{
|
||||
open: boolean
|
||||
entityKind: EntityKind
|
||||
prefillTypeId?: string
|
||||
}>()
|
||||
|
||||
const emit = defineEmits<{
|
||||
close: []
|
||||
confirm: [entityId: string]
|
||||
confirm: [payload: { entityId?: string; modelTypeId: string; modelTypeName: string }]
|
||||
}>()
|
||||
|
||||
const selectedTypeId = ref('')
|
||||
@@ -166,6 +173,10 @@ watch(() => props.open, async (isOpen) => {
|
||||
if (props.entityKind === 'component') await loadComponentTypes()
|
||||
else if (props.entityKind === 'piece') await loadPieceTypes()
|
||||
else await loadProductTypes()
|
||||
|
||||
if (props.prefillTypeId) {
|
||||
selectedTypeId.value = props.prefillTypeId
|
||||
}
|
||||
})
|
||||
|
||||
// Load entities when type changes
|
||||
@@ -222,8 +233,12 @@ const handleClose = () => {
|
||||
}
|
||||
|
||||
const handleConfirm = () => {
|
||||
if (!selectedEntityId.value) return
|
||||
emit('confirm', selectedEntityId.value)
|
||||
if (!selectedTypeId.value) return
|
||||
emit('confirm', {
|
||||
entityId: selectedEntityId.value || undefined,
|
||||
modelTypeId: selectedTypeId.value,
|
||||
modelTypeName: selectedTypeName.value,
|
||||
})
|
||||
resetState()
|
||||
emit('close')
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
:toggle-token="collapseToggleToken"
|
||||
@edit-piece="$emit('edit-piece', $event)"
|
||||
@delete="$emit('remove-component', component.linkId || component.id)"
|
||||
@fill-entity="(linkId, typeId) => $emit('fill-entity', linkId, typeId)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -68,5 +69,6 @@ defineEmits<{
|
||||
'custom-field-update': [fieldUpdate: any]
|
||||
'add-component': []
|
||||
'remove-component': [linkId: string]
|
||||
'fill-entity': [linkId: string, modelTypeId: string]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
@update="$emit('update-piece', $event)"
|
||||
@edit="$emit('edit-piece', $event)"
|
||||
@delete="$emit('remove-piece', piece.linkId || piece.id)"
|
||||
@fill-entity="(linkId, typeId) => $emit('fill-entity', linkId, typeId)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -67,5 +68,6 @@ defineEmits<{
|
||||
'edit-piece': [piece: any]
|
||||
'add-piece': []
|
||||
'remove-piece': [linkId: string]
|
||||
'fill-entity': [linkId: string, modelTypeId: string]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
@@ -23,14 +23,24 @@
|
||||
<div v-if="products.length" class="space-y-3">
|
||||
<div
|
||||
v-for="product in products"
|
||||
:key="product.id || product.name"
|
||||
class="rounded border border-base-200 bg-base-200/60 p-3 text-sm space-y-2"
|
||||
:key="product.id || product.linkId || product.name"
|
||||
class="rounded border p-3 text-sm space-y-2"
|
||||
:class="product.pendingEntity ? 'border-error bg-error/10' : 'border-base-200 bg-base-200/60'"
|
||||
>
|
||||
<div class="flex items-center justify-between flex-wrap gap-2">
|
||||
<p class="font-semibold text-base-content">
|
||||
<p class="font-semibold" :class="product.pendingEntity ? 'text-error' : 'text-base-content'">
|
||||
{{ product.name }}
|
||||
</p>
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
v-if="product.pendingEntity"
|
||||
type="button"
|
||||
class="badge badge-error badge-sm cursor-pointer hover:badge-outline transition-colors"
|
||||
title="Cliquer pour associer un item"
|
||||
@click="$emit('fill-entity', (product.linkId || product.id) as string, product.modelTypeId as string)"
|
||||
>
|
||||
À remplir
|
||||
</button>
|
||||
<span v-if="product.groupLabel" class="badge badge-ghost badge-sm">
|
||||
{{ product.groupLabel }}
|
||||
</span>
|
||||
@@ -141,6 +151,9 @@ defineProps<{
|
||||
supplierLabel?: string | null
|
||||
priceLabel?: string | null
|
||||
groupLabel?: string
|
||||
pendingEntity?: boolean
|
||||
modelTypeId?: string | null
|
||||
modelType?: string | null
|
||||
documents?: Array<{
|
||||
id?: string
|
||||
name?: string
|
||||
@@ -156,6 +169,7 @@ defineProps<{
|
||||
defineEmits<{
|
||||
'add-product': []
|
||||
'remove-product': [linkId: string]
|
||||
'fill-entity': [linkId: string, modelTypeId: string]
|
||||
}>()
|
||||
|
||||
const previewDocument = ref<any>(null)
|
||||
|
||||
@@ -193,6 +193,10 @@ export function useMachineDetailData(machineId: string) {
|
||||
removePieceLink,
|
||||
addProductLink,
|
||||
removeProductLink,
|
||||
addComponentLinkCategoryOnly,
|
||||
addPieceLinkCategoryOnly,
|
||||
addProductLinkCategoryOnly,
|
||||
fillEntityLink,
|
||||
} = hierarchy
|
||||
|
||||
// Keep the product links proxy in sync with the hierarchy's machineProductLinks
|
||||
@@ -511,6 +515,8 @@ export function useMachineDetailData(machineId: string) {
|
||||
loadMachineData, loadInitialData,
|
||||
addComponentLink, removeComponentLink, addPieceLink, removePieceLink,
|
||||
addProductLink, removeProductLink, reloadMachineStructure,
|
||||
addComponentLinkCategoryOnly, addPieceLinkCategoryOnly,
|
||||
addProductLinkCategoryOnly, fillEntityLink,
|
||||
|
||||
// External
|
||||
constructeurs, loadProducts, updateMachineStructure, toast,
|
||||
|
||||
@@ -39,7 +39,7 @@ export function useMachineDetailHierarchy(deps: MachineDetailHierarchyDeps) {
|
||||
syncMachineCustomFields,
|
||||
} = deps
|
||||
|
||||
const { get, post: apiPost, delete: apiDel } = useApi()
|
||||
const { get, post: apiPost, delete: apiDel, patch: apiPatch } = useApi()
|
||||
const toast = useToast()
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -263,6 +263,69 @@ export function useMachineDetailHierarchy(deps: MachineDetailHierarchyDeps) {
|
||||
return result
|
||||
}
|
||||
|
||||
const addComponentLinkCategoryOnly = async (modelTypeId: string) => {
|
||||
const result: any = await apiPost('/machine_component_links', {
|
||||
machine: `/api/machines/${machineId}`,
|
||||
modelType: `/api/model_types/${modelTypeId}`,
|
||||
})
|
||||
if (result.success) {
|
||||
toast.showSuccess('Catégorie ajoutée — item à remplir')
|
||||
await reloadMachineStructure()
|
||||
} else {
|
||||
toast.showError('Erreur lors de l\'ajout')
|
||||
}
|
||||
}
|
||||
|
||||
const addPieceLinkCategoryOnly = async (modelTypeId: string) => {
|
||||
const result: any = await apiPost('/machine_piece_links', {
|
||||
machine: `/api/machines/${machineId}`,
|
||||
modelType: `/api/model_types/${modelTypeId}`,
|
||||
})
|
||||
if (result.success) {
|
||||
toast.showSuccess('Catégorie ajoutée — item à remplir')
|
||||
await reloadMachineStructure()
|
||||
} else {
|
||||
toast.showError('Erreur lors de l\'ajout')
|
||||
}
|
||||
}
|
||||
|
||||
const addProductLinkCategoryOnly = async (modelTypeId: string) => {
|
||||
const result: any = await apiPost('/machine_product_links', {
|
||||
machine: `/api/machines/${machineId}`,
|
||||
modelType: `/api/model_types/${modelTypeId}`,
|
||||
})
|
||||
if (result.success) {
|
||||
toast.showSuccess('Catégorie ajoutée — item à remplir')
|
||||
await reloadMachineStructure()
|
||||
} else {
|
||||
toast.showError('Erreur lors de l\'ajout')
|
||||
}
|
||||
}
|
||||
|
||||
const fillEntityLink = async (linkId: string, entityId: string, entityKind: string) => {
|
||||
let endpoint = ''
|
||||
let payload: Record<string, string> = {}
|
||||
|
||||
if (entityKind === 'component') {
|
||||
endpoint = `/machine_component_links/${linkId}`
|
||||
payload = { composant: `/api/composants/${entityId}` }
|
||||
} else if (entityKind === 'piece') {
|
||||
endpoint = `/machine_piece_links/${linkId}`
|
||||
payload = { piece: `/api/pieces/${entityId}` }
|
||||
} else {
|
||||
endpoint = `/machine_product_links/${linkId}`
|
||||
payload = { product: `/api/products/${entityId}` }
|
||||
}
|
||||
|
||||
const result: any = await apiPatch(endpoint, payload)
|
||||
if (result.success) {
|
||||
toast.showSuccess('Item associé avec succès')
|
||||
await reloadMachineStructure()
|
||||
} else {
|
||||
toast.showError('Erreur lors de l\'association')
|
||||
}
|
||||
}
|
||||
|
||||
const removeProductLink = async (linkId: string) => {
|
||||
const result: any = await apiDel(`/machine_product_links/${linkId}`)
|
||||
if (result.success) {
|
||||
@@ -301,6 +364,10 @@ export function useMachineDetailHierarchy(deps: MachineDetailHierarchyDeps) {
|
||||
addPieceLink,
|
||||
removePieceLink,
|
||||
addProductLink,
|
||||
addComponentLinkCategoryOnly,
|
||||
addPieceLinkCategoryOnly,
|
||||
addProductLinkCategoryOnly,
|
||||
fillEntityLink,
|
||||
removeProductLink,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ export function useMachineDetailProducts(deps: MachineDetailProductsDeps) {
|
||||
return {
|
||||
id: (resolved?.id as string) || productId || null,
|
||||
linkId: (link.id as string) || (typeof link['@id'] === 'string' ? link['@id'].split('/').pop() : null) || null,
|
||||
name: (resolved?.name as string) || 'Produit inconnu',
|
||||
name: (resolved?.name as string) || (link.modelType as AnyRecord)?.name as string || 'Produit inconnu',
|
||||
reference: (resolved?.reference as string) || null,
|
||||
supplierLabel: resolvedConstructeurs.length
|
||||
? resolvedConstructeurs.map((c) => c.name).filter(Boolean).join(', ') || null
|
||||
@@ -111,6 +111,9 @@ export function useMachineDetailProducts(deps: MachineDetailProductsDeps) {
|
||||
priceLabel: resolved ? getProductPriceLabel(resolved) : null,
|
||||
groupLabel: ((resolved?.typeProduct as AnyRecord)?.name as string) || '',
|
||||
documents: productId ? (productDocumentsMap.value.get(productId) || []) : [],
|
||||
pendingEntity: (link.pendingEntity as boolean) || false,
|
||||
modelTypeId: (link.modelTypeId as string) || null,
|
||||
modelType: (link.modelType as string) || null,
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
@@ -150,6 +150,30 @@ export const buildMachineHierarchyFromLinks = (
|
||||
const createPieceNode = (link: AnyRecord, parentComponentName: string | null = null): AnyRecord | null => {
|
||||
if (!link || typeof link !== 'object') return null
|
||||
|
||||
// Category-only link (no entity yet)
|
||||
if (link.pendingEntity || (!link.piece && !link.pieceId)) {
|
||||
const machinePieceLinkId = normalizePieceLinkId(link)
|
||||
const mt = (link.modelType || null) as AnyRecord | null
|
||||
return {
|
||||
id: machinePieceLinkId || `pending-${link.id}`,
|
||||
linkId: machinePieceLinkId,
|
||||
name: mt?.name || 'Catégorie sans item',
|
||||
reference: null,
|
||||
prix: null,
|
||||
pendingEntity: true,
|
||||
modelTypeId: link.modelTypeId || mt?.id || null,
|
||||
modelType: mt,
|
||||
pieceId: null,
|
||||
constructeurs: [],
|
||||
documents: [],
|
||||
customFields: [],
|
||||
parentComponentLinkId: link.parentComponentLinkId || link.parentLinkId || null,
|
||||
parentComponentName,
|
||||
machinePieceLinkId,
|
||||
quantity: 1,
|
||||
}
|
||||
}
|
||||
|
||||
const appliedPiece = (link.piece && typeof link.piece === 'object' ? link.piece : {}) as AnyRecord
|
||||
const originalPiece = (link.originalPiece && typeof link.originalPiece === 'object' ? link.originalPiece : null) as AnyRecord | null
|
||||
|
||||
@@ -205,6 +229,35 @@ export const buildMachineHierarchyFromLinks = (
|
||||
const createComponentNode = (link: AnyRecord): AnyRecord | null => {
|
||||
if (!link || typeof link !== 'object') return null
|
||||
|
||||
// Category-only link (no entity yet)
|
||||
if (link.pendingEntity || (!link.composant && !link.composantId)) {
|
||||
const machineComponentLinkId = normalizeComponentLinkId(link)
|
||||
const mt = (link.modelType || null) as AnyRecord | null
|
||||
return {
|
||||
id: machineComponentLinkId || `pending-${link.id}`,
|
||||
linkId: machineComponentLinkId,
|
||||
name: mt?.name || 'Catégorie sans item',
|
||||
reference: null,
|
||||
prix: null,
|
||||
pendingEntity: true,
|
||||
modelTypeId: link.modelTypeId || mt?.id || null,
|
||||
modelType: mt,
|
||||
composantId: null,
|
||||
composant: null,
|
||||
constructeurs: [],
|
||||
documents: [],
|
||||
customFields: [],
|
||||
customFieldValues: [],
|
||||
subComponents: [],
|
||||
pieces: [],
|
||||
overrides: null,
|
||||
parentComponentLinkId: link.parentComponentLinkId || link.parentLinkId || null,
|
||||
machineComponentLinkId,
|
||||
childLinks: [],
|
||||
pieceLinks: [],
|
||||
}
|
||||
}
|
||||
|
||||
const appliedComponent = (link.composant && typeof link.composant === 'object' ? link.composant : {}) as AnyRecord
|
||||
const originalComponent = (link.originalComposant && typeof link.originalComposant === 'object' ? link.originalComposant : null) as AnyRecord | null
|
||||
|
||||
|
||||
@@ -100,6 +100,7 @@
|
||||
:is-edit-mode="d.isEditMode.value"
|
||||
@add-product="openAddModal('product')"
|
||||
@remove-product="async (id) => { await d.removeProductLink(id); refreshVersions() }"
|
||||
@fill-entity="(linkId, typeId) => handleFillEntity(linkId, 'product', typeId)"
|
||||
/>
|
||||
|
||||
<!-- Components Section -->
|
||||
@@ -115,6 +116,7 @@
|
||||
@custom-field-update="d.updatePieceCustomField"
|
||||
@add-component="openAddModal('component')"
|
||||
@remove-component="async (id) => { await d.removeComponentLink(id); refreshVersions() }"
|
||||
@fill-entity="(linkId, typeId) => handleFillEntity(linkId, 'component', typeId)"
|
||||
/>
|
||||
|
||||
<!-- Machine Pieces Section -->
|
||||
@@ -129,6 +131,7 @@
|
||||
@custom-field-update="d.updatePieceCustomField"
|
||||
@add-piece="openAddModal('piece')"
|
||||
@remove-piece="async (id) => { await d.removePieceLink(id); refreshVersions() }"
|
||||
@fill-entity="(linkId, typeId) => handleFillEntity(linkId, 'piece', typeId)"
|
||||
@toggle-collapse="d.toggleAllPieces"
|
||||
/>
|
||||
|
||||
@@ -136,7 +139,8 @@
|
||||
<AddEntityToMachineModal
|
||||
:open="addModalOpen"
|
||||
:entity-kind="addModalKind"
|
||||
@close="addModalOpen = false"
|
||||
:prefill-type-id="fillTypeId"
|
||||
@close="addModalOpen = false; fillLinkId = ''; fillTypeId = ''"
|
||||
@confirm="handleAddEntity"
|
||||
/>
|
||||
|
||||
@@ -277,6 +281,8 @@ const historyFieldLabels = {
|
||||
|
||||
const addModalOpen = ref(false)
|
||||
const addModalKind = ref('component')
|
||||
const fillLinkId = ref('')
|
||||
const fillTypeId = ref('')
|
||||
|
||||
const openAddModal = (kind) => {
|
||||
addModalKind.value = kind
|
||||
@@ -288,17 +294,40 @@ const handleRemoveConstructeurLink = (constructeurId) => {
|
||||
d.handleMachineConstructeurChange(ids)
|
||||
}
|
||||
|
||||
const handleAddEntity = async (entityId) => {
|
||||
if (addModalKind.value === 'component') {
|
||||
await d.addComponentLink(entityId)
|
||||
} else if (addModalKind.value === 'piece') {
|
||||
await d.addPieceLink(entityId)
|
||||
const handleAddEntity = async (payload) => {
|
||||
const { entityId, modelTypeId } = payload
|
||||
|
||||
if (fillLinkId.value) {
|
||||
await d.fillEntityLink(fillLinkId.value, entityId, addModalKind.value)
|
||||
fillLinkId.value = ''
|
||||
fillTypeId.value = ''
|
||||
} else if (entityId) {
|
||||
if (addModalKind.value === 'component') {
|
||||
await d.addComponentLink(entityId)
|
||||
} else if (addModalKind.value === 'piece') {
|
||||
await d.addPieceLink(entityId)
|
||||
} else {
|
||||
await d.addProductLink(entityId)
|
||||
}
|
||||
} else {
|
||||
await d.addProductLink(entityId)
|
||||
if (addModalKind.value === 'component') {
|
||||
await d.addComponentLinkCategoryOnly(modelTypeId)
|
||||
} else if (addModalKind.value === 'piece') {
|
||||
await d.addPieceLinkCategoryOnly(modelTypeId)
|
||||
} else {
|
||||
await d.addProductLinkCategoryOnly(modelTypeId)
|
||||
}
|
||||
}
|
||||
refreshVersions()
|
||||
}
|
||||
|
||||
const handleFillEntity = (linkId, entityKind, modelTypeId) => {
|
||||
fillLinkId.value = linkId
|
||||
fillTypeId.value = modelTypeId
|
||||
addModalKind.value = entityKind
|
||||
addModalOpen.value = true
|
||||
}
|
||||
|
||||
const machineViewTitle = computed(() => {
|
||||
return d.isEditMode.value ? 'Modification de la machine' : 'Détails de la machine'
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user