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="productOptions"
|
|
: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 { useProducts } from '~/composables/useProducts'
|
|
|
|
const props = withDefaults(
|
|
defineProps<{
|
|
modelValue?: string | null
|
|
placeholder?: string
|
|
emptyText?: string
|
|
helperText?: string
|
|
disabled?: boolean
|
|
typeProductId?: string | null
|
|
}>(),
|
|
{
|
|
modelValue: '',
|
|
placeholder: 'Sélectionner un produit…',
|
|
emptyText: 'Aucun produit disponible',
|
|
helperText: '',
|
|
disabled: false,
|
|
typeProductId: null,
|
|
},
|
|
)
|
|
|
|
const emit = defineEmits<{
|
|
(e: 'update:modelValue', value: string | null): void
|
|
}>()
|
|
|
|
const { loading: globalLoading, loadProducts } = useProducts()
|
|
|
|
const localProducts = ref<any[]>([])
|
|
const localLoading = ref(false)
|
|
const loading = computed(() => localLoading.value || globalLoading.value)
|
|
|
|
const productOptions = computed(() => localProducts.value)
|
|
|
|
const loadFilteredProducts = async () => {
|
|
if (!props.typeProductId) return
|
|
localLoading.value = true
|
|
try {
|
|
const result = await loadProducts({ typeProductId: props.typeProductId, itemsPerPage: 500, force: true })
|
|
if (result.success && result.data?.items) {
|
|
localProducts.value = result.data.items
|
|
}
|
|
}
|
|
catch (error: unknown) {
|
|
console.error('Erreur lors du chargement des produits:', error)
|
|
}
|
|
finally {
|
|
localLoading.value = false
|
|
}
|
|
}
|
|
|
|
onMounted(() => {
|
|
loadFilteredProducts()
|
|
})
|
|
|
|
watch(
|
|
() => props.typeProductId,
|
|
() => {
|
|
loadFilteredProducts()
|
|
},
|
|
)
|
|
|
|
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?.supplierPrice !== undefined && option.supplierPrice !== null) {
|
|
const price = Number(option.supplierPrice)
|
|
if (!Number.isNaN(price)) {
|
|
parts.push(`${price.toFixed(2)} €`)
|
|
}
|
|
}
|
|
return parts.length ? parts.join(' • ') : 'Sans référence'
|
|
}
|
|
</script>
|