PieceSelect, ProductSelect and ComposantSelect were loading up to 200 items then filtering client-side by typeId. If the matching items were not in the first 200, the dropdown appeared empty. Now each select component uses API Platform filters (typePiece, typeProduct, typeComposant) to fetch only relevant items server-side, with local state to avoid overwriting the global catalog cache. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
113 lines
2.8 KiB
Vue
113 lines
2.8 KiB
Vue
<template>
|
|
<div class="space-y-1">
|
|
<SearchSelect
|
|
:model-value="modelValue ?? undefined"
|
|
:options="pieceOptions"
|
|
:loading="loading"
|
|
:placeholder="placeholder"
|
|
:empty-text="emptyText"
|
|
size="sm"
|
|
option-value="id"
|
|
option-label="name"
|
|
:disabled="disabled"
|
|
@update:modelValue="updateValue"
|
|
>
|
|
<template #option-description="{ option }">
|
|
<span class="text-xs text-base-content/60">
|
|
{{ formatDescription(option) }}
|
|
</span>
|
|
</template>
|
|
</SearchSelect>
|
|
<p v-if="helperText" class="text-xs text-base-content/60">
|
|
{{ helperText }}
|
|
</p>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { computed, onMounted, ref, watch } from 'vue'
|
|
import SearchSelect from '~/components/common/SearchSelect.vue'
|
|
import { usePieces } from '~/composables/usePieces'
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
modelValue?: string | null
|
|
placeholder?: string
|
|
emptyText?: string
|
|
helperText?: string
|
|
disabled?: boolean
|
|
typePieceId?: string | null
|
|
}>(),
|
|
{
|
|
modelValue: '',
|
|
placeholder: 'Sélectionner une pièce…',
|
|
emptyText: 'Aucune pièce disponible',
|
|
helperText: '',
|
|
disabled: false,
|
|
typePieceId: null,
|
|
},
|
|
)
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'update:modelValue', value: string | null): void
|
|
}>()
|
|
|
|
const { loading: globalLoading, loadPieces } = usePieces()
|
|
|
|
const localPieces = ref<any[]>([])
|
|
const localLoading = ref(false)
|
|
const loading = computed(() => localLoading.value || globalLoading.value)
|
|
|
|
const pieceOptions = computed(() => localPieces.value)
|
|
|
|
const loadFilteredPieces = async () => {
|
|
if (!props.typePieceId) return
|
|
localLoading.value = true
|
|
try {
|
|
const result = await loadPieces({ typePieceId: props.typePieceId, itemsPerPage: 500, force: true })
|
|
if (result.success && result.data?.items) {
|
|
localPieces.value = result.data.items
|
|
}
|
|
}
|
|
catch (error: unknown) {
|
|
console.error('Erreur lors du chargement des pièces:', error)
|
|
}
|
|
finally {
|
|
localLoading.value = false
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
loadFilteredPieces()
|
|
})
|
|
|
|
watch(
|
|
() => props.typePieceId,
|
|
() => {
|
|
loadFilteredPieces()
|
|
},
|
|
)
|
|
|
|
const updateValue = (value: string | number | null | undefined) => {
|
|
if (value === undefined || value === null || value === '') {
|
|
emit('update:modelValue', null)
|
|
return
|
|
}
|
|
emit('update:modelValue', String(value))
|
|
}
|
|
|
|
const formatDescription = (option: any) => {
|
|
const parts: string[] = []
|
|
if (option?.reference) {
|
|
parts.push(option.reference)
|
|
}
|
|
if (option?.prix !== undefined && option.prix !== null) {
|
|
const price = Number(option.prix)
|
|
if (!Number.isNaN(price)) {
|
|
parts.push(`${price.toFixed(2)} €`)
|
|
}
|
|
}
|
|
return parts.length ? parts.join(' • ') : 'Sans référence'
|
|
}
|
|
</script>
|