feat(frontend): add reusable search select and wire it into machine creation
fix(frontend): guard custom field persistence against non-string values
This commit is contained in:
@@ -53,14 +53,15 @@
|
||||
<label class="label">
|
||||
<span class="label-text">Type de machine</span>
|
||||
</label>
|
||||
<select v-model="newMachine.typeMachineId" class="select select-bordered" required>
|
||||
<option value="">
|
||||
Sélectionner un type
|
||||
</option>
|
||||
<option v-for="type in machineTypes" :key="type.id" :value="type.id">
|
||||
{{ type.name }} ({{ type.category }})
|
||||
</option>
|
||||
</select>
|
||||
<SearchSelect
|
||||
v-model="newMachine.typeMachineId"
|
||||
:options="machineTypes"
|
||||
:loading="machineTypesLoading"
|
||||
placeholder="Rechercher un type…"
|
||||
empty-text="Aucun type trouvé"
|
||||
:option-label="machineTypeLabel"
|
||||
:option-description="machineTypeDescription"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="form-control">
|
||||
@@ -164,22 +165,17 @@
|
||||
<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) || '')"
|
||||
>
|
||||
<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>
|
||||
<SearchSelect
|
||||
:model-value="entry.composantId || ''"
|
||||
:options="getComponentOptions(requirement, entry)"
|
||||
:loading="composantsLoading"
|
||||
size="sm"
|
||||
placeholder="Rechercher un composant…"
|
||||
empty-text="Aucun composant disponible"
|
||||
:option-label="componentOptionLabel"
|
||||
:option-description="componentOptionDescription"
|
||||
@update:modelValue="setComponentRequirementComponent(requirement, entryIndex, $event || '')"
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
v-if="getComponentOptions(requirement, entry).length === 0"
|
||||
@@ -271,22 +267,17 @@
|
||||
<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) || '')"
|
||||
>
|
||||
<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>
|
||||
<SearchSelect
|
||||
:model-value="entry.pieceId || ''"
|
||||
:options="getPieceOptions(requirement, entry)"
|
||||
:loading="piecesLoading"
|
||||
size="sm"
|
||||
placeholder="Rechercher une pièce…"
|
||||
empty-text="Aucune pièce disponible"
|
||||
:option-label="pieceOptionLabel"
|
||||
:option-description="pieceOptionDescription"
|
||||
@update:modelValue="setPieceRequirementPiece(requirement, entryIndex, $event || '')"
|
||||
/>
|
||||
</div>
|
||||
<p
|
||||
v-if="getPieceOptions(requirement, entry).length === 0"
|
||||
@@ -562,6 +553,7 @@ import { useComposants } from '~/composables/useComposants'
|
||||
import { usePieces } from '~/composables/usePieces'
|
||||
import { useToast } from '~/composables/useToast'
|
||||
import { sanitizeDefinitionOverrides } from '~/shared/modelUtils'
|
||||
import SearchSelect from '~/components/common/SearchSelect.vue'
|
||||
import IconLucidePlus from '~icons/lucide/plus'
|
||||
import IconLucideX from '~icons/lucide/x'
|
||||
import IconLucideEye from '~icons/lucide/eye'
|
||||
@@ -571,9 +563,9 @@ import IconLucideCircle from '~icons/lucide/circle'
|
||||
|
||||
const { createMachine, createMachineFromType } = useMachines()
|
||||
const { sites, loadSites } = useSites()
|
||||
const { machineTypes, loadMachineTypes } = useMachineTypesApi()
|
||||
const { composants, loadComposants } = useComposants()
|
||||
const { pieces, loadPieces } = usePieces()
|
||||
const { machineTypes, loadMachineTypes, loading: machineTypesLoading } = useMachineTypesApi()
|
||||
const { composants, loadComposants, loading: composantsLoading } = useComposants()
|
||||
const { pieces, loadPieces, loading: piecesLoading } = usePieces()
|
||||
const toast = useToast()
|
||||
|
||||
const submitting = ref(false)
|
||||
@@ -595,6 +587,27 @@ const selectedMachineType = computed(() => {
|
||||
return machineTypes.value.find(type => type.id === newMachine.typeMachineId) || null
|
||||
})
|
||||
|
||||
const machineTypeLabel = (type) => {
|
||||
if (!type) {
|
||||
return ''
|
||||
}
|
||||
return type.name || 'Type de machine'
|
||||
}
|
||||
|
||||
const machineTypeDescription = (type) => {
|
||||
if (!type) {
|
||||
return ''
|
||||
}
|
||||
const parts = []
|
||||
if (type.category) {
|
||||
parts.push(`Catégorie : ${type.category}`)
|
||||
}
|
||||
const componentCount = type.componentRequirements?.length ?? 0
|
||||
const pieceCount = type.pieceRequirements?.length ?? 0
|
||||
parts.push(`${componentCount} composant(s)`, `${pieceCount} pièce(s)`)
|
||||
return parts.join(' • ')
|
||||
}
|
||||
|
||||
const componentById = computed(() => {
|
||||
const map = new Map()
|
||||
;(composants.value || []).forEach((component) => {
|
||||
@@ -891,11 +904,13 @@ const getPieceOptions = (requirement, currentEntry) => {
|
||||
})
|
||||
}
|
||||
|
||||
const formatComponentOption = (component) => {
|
||||
const componentOptionLabel = (component) => component?.name || 'Composant'
|
||||
|
||||
const componentOptionDescription = (component) => {
|
||||
if (!component) {
|
||||
return ''
|
||||
}
|
||||
const parts = [component.name || 'Composant']
|
||||
const parts = []
|
||||
if (component.reference) {
|
||||
parts.push(`Réf. ${component.reference}`)
|
||||
}
|
||||
@@ -910,11 +925,13 @@ const formatComponentOption = (component) => {
|
||||
return parts.join(' • ')
|
||||
}
|
||||
|
||||
const formatPieceOption = (piece) => {
|
||||
const pieceOptionLabel = (piece) => piece?.name || 'Pièce'
|
||||
|
||||
const pieceOptionDescription = (piece) => {
|
||||
if (!piece) {
|
||||
return ''
|
||||
}
|
||||
const parts = [piece.name || 'Pièce']
|
||||
const parts = []
|
||||
if (piece.reference) {
|
||||
parts.push(`Réf. ${piece.reference}`)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user