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="composantOptions"
|
|
: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 { useComposants } from '~/composables/useComposants'
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
modelValue?: string | null
|
|
placeholder?: string
|
|
emptyText?: string
|
|
helperText?: string
|
|
disabled?: boolean
|
|
typeComposantId?: string | null
|
|
}>(),
|
|
{
|
|
modelValue: '',
|
|
placeholder: 'Sélectionner un composant…',
|
|
emptyText: 'Aucun composant disponible',
|
|
helperText: '',
|
|
disabled: false,
|
|
typeComposantId: null,
|
|
},
|
|
)
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'update:modelValue', value: string | null): void
|
|
}>()
|
|
|
|
const { loading: globalLoading, loadComposants } = useComposants()
|
|
|
|
const localComposants = ref<any[]>([])
|
|
const localLoading = ref(false)
|
|
const loading = computed(() => localLoading.value || globalLoading.value)
|
|
|
|
const composantOptions = computed(() => localComposants.value)
|
|
|
|
const loadFilteredComposants = async () => {
|
|
if (!props.typeComposantId) return
|
|
localLoading.value = true
|
|
try {
|
|
const result = await loadComposants({ typeComposantId: props.typeComposantId, itemsPerPage: 500, force: true })
|
|
if (result.success && result.data?.items) {
|
|
localComposants.value = result.data.items
|
|
}
|
|
}
|
|
catch (error: unknown) {
|
|
console.error('Erreur lors du chargement des composants:', error)
|
|
}
|
|
finally {
|
|
localLoading.value = false
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
loadFilteredComposants()
|
|
})
|
|
|
|
watch(
|
|
() => props.typeComposantId,
|
|
() => {
|
|
loadFilteredComposants()
|
|
},
|
|
)
|
|
|
|
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>
|