feat: reuse inventory items when configuring machines

This commit is contained in:
Matthieu
2025-10-08 16:29:00 +02:00
parent bc8823a776
commit f89364d04e
9 changed files with 3158 additions and 1198 deletions

View File

@@ -158,73 +158,61 @@
</button>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
<div class="form-control md:col-span-2">
<label class="label">
<span class="label-text text-xs">Catégorie de composant</span>
</label>
<select
class="select select-bordered select-xs md:select-sm"
:value="entry.typeComposantId || requirement.typeComposantId || ''"
@change="setComponentRequirementType(requirement.id, entryIndex, ($event.target && $event.target.value) || '')"
>
<option value="">
{{ requirement.typeComposant?.name ? `Type du requirement (${requirement.typeComposant.name})` : 'Suivre le requirement' }}
</option>
<option
v-for="type in componentTypeOptions"
:key="type.id"
:value="type.id"
<div class="grid grid-cols-1 gap-3">
<div class="space-y-2">
<div class="form-control">
<label class="label">
<span class="label-text text-xs">Composant existant</span>
</label>
<select
class="select select-bordered select-sm"
:value="entry.composantId || ''"
@change="setComponentRequirementComponent(requirement, entryIndex, ($event.target && $event.target.value) || '')"
>
{{ type.name }}
</option>
</select>
</div>
<div class="form-control">
<label class="label">
<span class="label-text text-xs">Nom du composant</span>
</label>
<input
v-model="entry.definition.name"
type="text"
class="input input-bordered input-sm"
placeholder="Nom du composant"
<option value="">
Sélectionner un composant disponible
</option>
<option
v-for="component in getComponentOptions(requirement, entry)"
:key="component.id"
:value="component.id"
>
{{ formatComponentOption(component) }}
</option>
</select>
</div>
<p
v-if="getComponentOptions(requirement, entry).length === 0"
class="text-xs text-error"
>
Aucun composant disponible pour cette famille.
</p>
</div>
<div class="form-control">
<label class="label">
<span class="label-text text-xs">Référence (optionnel)</span>
</label>
<input
v-model="entry.definition.reference"
type="text"
class="input input-bordered input-sm"
placeholder="Référence du composant"
>
</div>
<div class="form-control">
<label class="label">
<span class="label-text text-xs">Constructeur (optionnel)</span>
</label>
<ConstructeurSelect
class="w-full"
:model-value="entry.definition.constructeurId || null"
placeholder="Sélectionner un constructeur"
@update:modelValue="(value) => setComponentRequirementConstructeur(requirement.id, entryIndex, value)"
/>
</div>
<div class="form-control">
<label class="label">
<span class="label-text text-xs">Prix (optionnel)</span>
</label>
<input
v-model.number="entry.definition.prix"
type="number"
min="0"
step="0.01"
class="input input-bordered input-sm"
placeholder="0.00"
<div
v-if="entry.composantId"
class="bg-base-300/60 rounded-md p-3 text-xs text-gray-600 space-y-1"
>
<div class="font-medium">
{{ (findComponentById(entry.composantId)?.name) || "Composant" }}
</div>
<div>
Référence : {{ findComponentById(entry.composantId)?.reference || "—" }}
</div>
<div>
Constructeur :
{{ findComponentById(entry.composantId)?.constructeur?.name || findComponentById(entry.composantId)?.constructeurName || "—" }}
</div>
<div>
Machine actuelle :
{{ findComponentById(entry.composantId)?.machine?.name || findComponentById(entry.composantId)?.machineId || "Non affecté" }}
</div>
<div
v-if="findComponentById(entry.composantId)?.machine?.name"
class="text-warning mt-1"
>
La réaffectation détachera ce composant de sa machine actuelle lors de la création.
</div>
</div>
</div>
</div>
@@ -286,73 +274,65 @@
</button>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
<div class="form-control md:col-span-2">
<label class="label">
<span class="label-text text-xs">Catégorie de pièce</span>
</label>
<select
class="select select-bordered select-xs md:select-sm"
:value="entry.typePieceId || requirement.typePieceId || ''"
@change="setPieceRequirementType(requirement.id, entryIndex, ($event.target && $event.target.value) || '')"
>
<option value="">
{{ requirement.typePiece?.name ? `Type du requirement (${requirement.typePiece.name})` : 'Suivre le requirement' }}
</option>
<option
v-for="type in pieceTypeOptions"
:key="type.id"
:value="type.id"
<div class="grid grid-cols-1 gap-3">
<div class="space-y-2">
<div class="form-control">
<label class="label">
<span class="label-text text-xs">Pièce existante</span>
</label>
<select
class="select select-bordered select-sm"
:value="entry.pieceId || ''"
@change="setPieceRequirementPiece(requirement, entryIndex, ($event.target && $event.target.value) || '')"
>
{{ type.name }}
</option>
</select>
</div>
<div class="form-control">
<label class="label">
<span class="label-text text-xs">Nom de la pièce</span>
</label>
<input
v-model="entry.definition.name"
type="text"
class="input input-bordered input-sm"
placeholder="Nom de la pièce"
<option value="">
Sélectionner une pièce disponible
</option>
<option
v-for="pieceOption in getPieceOptions(requirement, entry)"
:key="pieceOption.id"
:value="pieceOption.id"
>
{{ formatPieceOption(pieceOption) }}
</option>
</select>
</div>
<p
v-if="getPieceOptions(requirement, entry).length === 0"
class="text-xs text-error"
>
Aucune pièce disponible pour cette famille.
</p>
</div>
<div class="form-control">
<label class="label">
<span class="label-text text-xs">Référence (optionnel)</span>
</label>
<input
v-model="entry.definition.reference"
type="text"
class="input input-bordered input-sm"
placeholder="Référence de la pièce"
>
</div>
<div class="form-control">
<label class="label">
<span class="label-text text-xs">Constructeur (optionnel)</span>
</label>
<ConstructeurSelect
class="w-full"
:model-value="entry.definition.constructeurId || null"
placeholder="Sélectionner un constructeur"
@update:modelValue="(value) => setPieceRequirementConstructeur(requirement.id, entryIndex, value)"
/>
</div>
<div class="form-control">
<label class="label">
<span class="label-text text-xs">Prix (optionnel)</span>
</label>
<input
v-model.number="entry.definition.prix"
type="number"
min="0"
step="0.01"
class="input input-bordered input-sm"
placeholder="0.00"
<div
v-if="entry.pieceId"
class="bg-base-300/60 rounded-md p-3 text-xs text-gray-600 space-y-1"
>
<div class="font-medium">
{{ (findPieceById(entry.pieceId)?.name) || "Pièce" }}
</div>
<div>
Référence : {{ findPieceById(entry.pieceId)?.reference || "—" }}
</div>
<div>
Constructeur :
{{ findPieceById(entry.pieceId)?.constructeur?.name || findPieceById(entry.pieceId)?.constructeurName || "—" }}
</div>
<div>
Machine actuelle :
{{ findPieceById(entry.pieceId)?.machine?.name || findPieceById(entry.pieceId)?.machineId || "Non affecté" }}
</div>
<div>
Composant actuel :
{{ findPieceById(entry.pieceId)?.composant?.name || findPieceById(entry.pieceId)?.composantId || "Non affecté" }}
</div>
<div
v-if="findPieceById(entry.pieceId)?.machine?.name || findPieceById(entry.pieceId)?.composant?.name"
class="text-warning mt-1"
>
Cette pièce sera détachée de son affectation actuelle pendant la création.
</div>
</div>
</div>
</div>
@@ -597,14 +577,12 @@
<script setup>
import { ref, reactive, computed, watch, onMounted } from 'vue'
import ConstructeurSelect from '~/components/ConstructeurSelect.vue'
import { useMachines } from '~/composables/useMachines'
import { useSites } from '~/composables/useSites'
import { useMachineTypesApi } from '~/composables/useMachineTypesApi'
import { useComponentTypes } from '~/composables/useComponentTypes'
import { usePieceTypes } from '~/composables/usePieceTypes'
import { useComposants } from '~/composables/useComposants'
import { usePieces } from '~/composables/usePieces'
import { useToast } from '~/composables/useToast'
import { sanitizeDefinitionOverrides } from '~/shared/modelUtils'
import IconLucidePlus from '~icons/lucide/plus'
import IconLucideX from '~icons/lucide/x'
import IconLucideEye from '~icons/lucide/eye'
@@ -615,8 +593,8 @@ import IconLucideCircle from '~icons/lucide/circle'
const { createMachine, createMachineFromType } = useMachines()
const { sites, loadSites } = useSites()
const { machineTypes, loadMachineTypes } = useMachineTypesApi()
const { componentTypes, loadComponentTypes } = useComponentTypes()
const { pieceTypes, loadPieceTypes } = usePieceTypes()
const { composants, loadComposants } = useComposants()
const { pieces, loadPieces } = usePieces()
const toast = useToast()
const submitting = ref(false)
@@ -638,9 +616,177 @@ const selectedMachineType = computed(() => {
return machineTypes.value.find(type => type.id === newMachine.typeMachineId) || null
})
const componentTypeOptions = computed(() => componentTypes.value || [])
const pieceTypeOptions = computed(() => pieceTypes.value || [])
const componentById = computed(() => {
const map = new Map()
;(composants.value || []).forEach((component) => {
if (component?.id) {
map.set(component.id, component)
}
})
return map
})
const pieceById = computed(() => {
const map = new Map()
;(pieces.value || []).forEach((piece) => {
if (piece?.id) {
map.set(piece.id, piece)
}
})
return map
})
const componentInventory = computed(() => composants.value || [])
const pieceInventory = computed(() => pieces.value || [])
const selectedComponentIds = computed(() => {
const ids = []
Object.values(componentRequirementSelections).forEach((entries) => {
;(entries || []).forEach((entry) => {
if (entry?.composantId) {
ids.push(entry.composantId)
}
})
})
return ids
})
const selectedPieceIds = computed(() => {
const ids = []
Object.values(pieceRequirementSelections).forEach((entries) => {
;(entries || []).forEach((entry) => {
if (entry?.pieceId) {
ids.push(entry.pieceId)
}
})
})
return ids
})
const getComponentOptions = (requirement, currentEntry) => {
const requirementTypeId = requirement?.typeComposantId || requirement?.typeComposant?.id || null
const usedIds = new Set(
selectedComponentIds.value.filter((id) => id && (!currentEntry || id !== currentEntry.composantId)),
)
return componentInventory.value.filter((component) => {
if (requirementTypeId && component.typeComposantId !== requirementTypeId) {
return false
}
if (!component.id) {
return false
}
if (currentEntry?.composantId === component.id) {
return true
}
return !usedIds.has(component.id)
})
}
const getPieceOptions = (requirement, currentEntry) => {
const requirementTypeId = requirement?.typePieceId || requirement?.typePiece?.id || null
const usedIds = new Set(
selectedPieceIds.value.filter((id) => id && (!currentEntry || id !== currentEntry.pieceId)),
)
return pieceInventory.value.filter((piece) => {
if (requirementTypeId && piece.typePieceId !== requirementTypeId) {
return false
}
if (!piece.id) {
return false
}
if (currentEntry?.pieceId === piece.id) {
return true
}
return !usedIds.has(piece.id)
})
}
const formatComponentOption = (component) => {
if (!component) {
return ''
}
const parts = [component.name || 'Composant']
if (component.reference) {
parts.push(`Réf. ${component.reference}`)
}
const constructeurName = component.constructeur?.name || component.constructeurName
if (constructeurName) {
parts.push(constructeurName)
}
if (component.machine?.name) {
parts.push(`Machine: ${component.machine.name}`)
} else if (component.machineId) {
parts.push(`Machine: ${component.machineId}`)
}
return parts.join(' • ')
}
const formatPieceOption = (piece) => {
if (!piece) {
return ''
}
const parts = [piece.name || 'Pièce']
if (piece.reference) {
parts.push(`Réf. ${piece.reference}`)
}
const constructeurName = piece.constructeur?.name || piece.constructeurName
if (constructeurName) {
parts.push(constructeurName)
}
if (piece.machine?.name) {
parts.push(`Machine: ${piece.machine.name}`)
} else if (piece.machineId) {
parts.push(`Machine: ${piece.machineId}`)
}
if (piece.composant?.name) {
parts.push(`Composant: ${piece.composant.name}`)
} else if (piece.composantId) {
parts.push(`Composant: ${piece.composantId}`)
}
return parts.join(' • ')
}
const setComponentRequirementComponent = (requirement, index, componentId) => {
const entries = getComponentRequirementEntries(requirement.id)
const entry = entries[index]
if (!entry) return
entry.composantId = componentId || null
if (componentId) {
const component = findComponentById(componentId)
entry.typeComposantId = component?.typeComposantId || requirement?.typeComposantId || null
} else {
entry.typeComposantId = requirement?.typeComposantId || null
}
}
const setPieceRequirementPiece = (requirement, index, pieceId) => {
const entries = getPieceRequirementEntries(requirement.id)
const entry = entries[index]
if (!entry) return
entry.pieceId = pieceId || null
if (pieceId) {
const piece = findPieceById(pieceId)
entry.typePieceId = piece?.typePieceId || requirement?.typePieceId || null
} else {
entry.typePieceId = requirement?.typePieceId || null
}
}
const findComponentById = (id) => {
if (!id) {
return null
}
return componentById.value.get(id) || null
}
const findPieceById = (id) => {
if (!id) {
return null
}
return pieceById.value.get(id) || null
}
const getStatusBadgeClass = (status) => {
if (status === 'ready') {
return 'badge-success'
@@ -651,89 +797,39 @@ const getStatusBadgeClass = (status) => {
return 'badge-error'
}
const componentTypeLabelMap = computed(() => {
const map = new Map()
componentTypeOptions.value.forEach((type) => {
if (type?.id) {
map.set(type.id, type.name || '')
}
})
const requirementTypes = selectedMachineType.value?.componentRequirements || []
requirementTypes.forEach((requirement) => {
const type = requirement?.typeComposant
if (type?.id && type?.name) {
map.set(type.id, type.name)
}
})
return map
})
const pieceTypeLabelMap = computed(() => {
const map = new Map()
pieceTypeOptions.value.forEach((type) => {
if (type?.id) {
map.set(type.id, type.name || '')
}
})
const requirementTypes = selectedMachineType.value?.pieceRequirements || []
requirementTypes.forEach((requirement) => {
const type = requirement?.typePiece
if (type?.id && type?.name) {
map.set(type.id, type.name)
}
})
return map
})
const resolveComponentRequirementTypeLabel = (requirement, entry) => {
const typeId = entry?.typeComposantId || requirement?.typeComposantId || null
if (!typeId) {
return requirement?.typeComposant?.name || 'Type non défini'
if (entry?.composantId) {
const component = findComponentById(entry.composantId)
if (component?.typeComposant?.name) {
return component.typeComposant.name
}
}
return componentTypeLabelMap.value.get(typeId) || requirement?.typeComposant?.name || 'Type non défini'
return requirement?.typeComposant?.name || 'Type non défini'
}
const resolvePieceRequirementTypeLabel = (requirement, entry) => {
const typeId = entry?.typePieceId || requirement?.typePieceId || null
if (!typeId) {
return requirement?.typePiece?.name || 'Type non défini'
if (entry?.pieceId) {
const piece = findPieceById(entry.pieceId)
if (piece?.typePiece?.name) {
return piece.typePiece.name
}
}
return pieceTypeLabelMap.value.get(typeId) || requirement?.typePiece?.name || 'Type non défini'
return requirement?.typePiece?.name || 'Type non défini'
}
const getComponentRequirementEntries = requirementId => componentRequirementSelections[requirementId] || []
const getPieceRequirementEntries = requirementId => pieceRequirementSelections[requirementId] || []
const createComponentSelectionEntry = (requirement, source = null) => ({
typeComposantId: source?.typeComposantId
|| source?.typeComposant?.id
|| requirement?.typeComposantId
|| null,
definition: {
name:
(typeof source?.name === 'string' ? source.name : null)
|| requirement?.typeComposant?.name
|| '',
reference: source?.reference || '',
constructeurId: source?.constructeurId || source?.constructeur?.id || null,
prix: source?.prix ?? source?.price ?? null,
},
typeComposantId: requirement?.typeComposantId || requirement?.typeComposant?.id || null,
composantId: source?.composantId || null,
definition: {},
})
const createPieceSelectionEntry = (requirement, source = null) => ({
typePieceId: source?.typePieceId
|| source?.typePiece?.id
|| requirement?.typePieceId
|| null,
definition: {
name:
(typeof source?.name === 'string' ? source.name : null)
|| requirement?.typePiece?.name
|| '',
reference: source?.reference || '',
constructeurId: source?.constructeurId || source?.constructeur?.id || null,
prix: source?.prix ?? source?.price ?? null,
},
typePieceId: requirement?.typePieceId || requirement?.typePiece?.id || null,
pieceId: source?.pieceId || null,
definition: {},
})
const clearRequirementSelections = () => {
@@ -763,20 +859,6 @@ const removeComponentSelectionEntry = (requirementId, index) => {
componentRequirementSelections[requirementId] = entries.filter((_, i) => i !== index)
}
const setComponentRequirementType = (requirementId, index, value) => {
const entries = getComponentRequirementEntries(requirementId)
const entry = entries[index]
if (!entry) return
entry.typeComposantId = value || null
}
const setComponentRequirementConstructeur = (requirementId, index, value) => {
const entries = getComponentRequirementEntries(requirementId)
const entry = entries[index]
if (!entry) return
entry.definition.constructeurId = value || null
}
const addPieceSelectionEntry = (requirement) => {
const entries = getPieceRequirementEntries(requirement.id)
const max = requirement.maxCount ?? null
@@ -795,20 +877,6 @@ const removePieceSelectionEntry = (requirementId, index) => {
pieceRequirementSelections[requirementId] = entries.filter((_, i) => i !== index)
}
const setPieceRequirementType = (requirementId, index, value) => {
const entries = getPieceRequirementEntries(requirementId)
const entry = entries[index]
if (!entry) return
entry.typePieceId = value || null
}
const setPieceRequirementConstructeur = (requirementId, index, value) => {
const entries = getPieceRequirementEntries(requirementId)
const entry = entries[index]
if (!entry) return
entry.definition.constructeurId = value || null
}
const validateRequirementSelections = (type) => {
const errors = []
const componentSelectionsPayload = []
@@ -828,23 +896,41 @@ const validateRequirementSelections = (type) => {
}
entries.forEach((entry) => {
const resolvedTypeId = entry.typeComposantId || requirement.typeComposantId || null
if (!resolvedTypeId) {
errors.push(`Le groupe "${requirement.label || requirement.typeComposant?.name || 'Composants'}" nécessite un type de composant.`)
if (!entry.composantId) {
errors.push(`Sélectionner un composant existant pour "${requirement.label || requirement.typeComposant?.name || 'Composants'}".`)
return
}
const payload = { requirementId: requirement.id }
if (entry.typeComposantId && entry.typeComposantId !== requirement.typeComposantId) {
payload.typeComposantId = entry.typeComposantId
const component = findComponentById(entry.composantId)
if (!component) {
errors.push(`Le composant sélectionné est introuvable (ID: ${entry.composantId}).`)
return
}
const overrides = sanitizeDefinitionOverrides(entry.definition)
if (overrides) {
payload.definition = overrides
if (component.machineId) {
errors.push(`Le composant "${component.name || component.id}" est déjà affecté à une machine.`)
return
}
componentSelectionsPayload.push(payload)
if (component.parentComposantId) {
errors.push(`Le composant "${component.name || component.id}" est déjà rattaché à un autre composant.`)
return
}
const requiredTypeId = requirement.typeComposantId || requirement.typeComposant?.id || null
if (
requiredTypeId &&
component.typeComposantId &&
component.typeComposantId !== requiredTypeId
) {
errors.push(`Le composant "${component.name || component.id}" n'appartient pas à la famille attendue.`)
return
}
componentSelectionsPayload.push({
requirementId: requirement.id,
composantId: entry.composantId,
})
})
}
@@ -862,23 +948,36 @@ const validateRequirementSelections = (type) => {
}
entries.forEach((entry) => {
const resolvedTypeId = entry.typePieceId || requirement.typePieceId || null
if (!resolvedTypeId) {
errors.push(`Le groupe "${requirement.label || requirement.typePiece?.name || 'Pièces'}" nécessite un type de pièce.`)
if (!entry.pieceId) {
errors.push(`Sélectionner une pièce existante pour "${requirement.label || requirement.typePiece?.name || 'Pièces'}".`)
return
}
const payload = { requirementId: requirement.id }
if (entry.typePieceId && entry.typePieceId !== requirement.typePieceId) {
payload.typePieceId = entry.typePieceId
const piece = findPieceById(entry.pieceId)
if (!piece) {
errors.push(`La pièce sélectionnée est introuvable (ID: ${entry.pieceId}).`)
return
}
const overrides = sanitizeDefinitionOverrides(entry.definition)
if (overrides) {
payload.definition = overrides
if (piece.machineId || piece.composantId) {
errors.push(`La pièce "${piece.name || piece.id}" est déjà affectée.`)
return
}
pieceSelectionsPayload.push(payload)
const requiredTypeId = requirement.typePieceId || requirement.typePiece?.id || null
if (
requiredTypeId &&
piece.typePieceId &&
piece.typePieceId !== requiredTypeId
) {
errors.push(`La pièce "${piece.name || piece.id}" n'appartient pas à la famille attendue.`)
return
}
pieceSelectionsPayload.push({
requirementId: requirement.id,
pieceId: entry.pieceId,
})
})
}
@@ -945,16 +1044,22 @@ const machinePreview = computed(() => {
const componentGroups = (type.componentRequirements || []).map((requirement) => {
const entries = getComponentRequirementEntries(requirement.id)
const normalizedEntries = entries.map((entry, index) => {
const overrides = sanitizeDefinitionOverrides(entry.definition)
const fallbackName = requirement.typeComposant?.name || 'Composant'
const rawName = typeof entry.definition?.name === 'string' ? entry.definition.name.trim() : ''
const displayName = overrides?.name || rawName || fallbackName
const selectedComponent = entry.composantId
? findComponentById(entry.composantId)
: null
const displayName = selectedComponent?.name
|| requirement.typeComposant?.name
|| 'Composant'
const subtitleParts = []
if (overrides?.reference) {
subtitleParts.push(`Réf. ${overrides.reference}`)
if (selectedComponent?.reference) {
subtitleParts.push(`Réf. ${selectedComponent.reference}`)
}
const status = entry.typeComposantId || requirement.typeComposantId ? 'complete' : 'pending'
const constructeurName = selectedComponent?.constructeur?.name || selectedComponent?.constructeurName
if (constructeurName) {
subtitleParts.push(constructeurName)
}
const status = entry.composantId ? 'complete' : 'pending'
return {
key: `${requirement.id}-${index}`,
@@ -978,7 +1083,7 @@ const machinePreview = computed(() => {
}
if (normalizedEntries.some(entry => entry.status !== 'complete')) {
issues.push({ message: 'Sélectionner un type pour chaque composant.', kind: 'error', anchor: `component-group-${requirement.id}` })
issues.push({ message: 'Sélectionner un composant pour chaque entrée.', kind: 'error', anchor: `component-group-${requirement.id}` })
}
const status = issues.some(issue => issue.kind === 'error')
@@ -1004,16 +1109,18 @@ const machinePreview = computed(() => {
const pieceGroups = (type.pieceRequirements || []).map((requirement) => {
const entries = getPieceRequirementEntries(requirement.id)
const normalizedEntries = entries.map((entry, index) => {
const overrides = sanitizeDefinitionOverrides(entry.definition)
const fallbackName = requirement.typePiece?.name || 'Pièce'
const rawName = typeof entry.definition?.name === 'string' ? entry.definition.name.trim() : ''
const displayName = overrides?.name || rawName || fallbackName
const selectedPiece = entry.pieceId ? findPieceById(entry.pieceId) : null
const displayName = selectedPiece?.name || requirement.typePiece?.name || 'Pièce'
const subtitleParts = []
if (overrides?.reference) {
subtitleParts.push(`Réf. ${overrides.reference}`)
if (selectedPiece?.reference) {
subtitleParts.push(`Réf. ${selectedPiece.reference}`)
}
const status = entry.typePieceId || requirement.typePieceId ? 'complete' : 'pending'
const constructeurName = selectedPiece?.constructeur?.name || selectedPiece?.constructeurName
if (constructeurName) {
subtitleParts.push(constructeurName)
}
const status = entry.pieceId ? 'complete' : 'pending'
return {
key: `${requirement.id}-${index}`,
@@ -1037,7 +1144,7 @@ const machinePreview = computed(() => {
}
if (normalizedEntries.some(entry => entry.status !== 'complete')) {
issues.push({ message: 'Sélectionner un type pour chaque pièce.', kind: 'error', anchor: `piece-group-${requirement.id}` })
issues.push({ message: 'Sélectionner une pièce pour chaque entrée.', kind: 'error', anchor: `piece-group-${requirement.id}` })
}
const status = issues.some(issue => issue.kind === 'error')
@@ -1251,8 +1358,8 @@ onMounted(async () => {
await Promise.all([
loadSites(),
loadMachineTypes(),
loadComponentTypes(),
loadPieceTypes()
loadComposants(),
loadPieces()
])
})
</script>