fix: stabilize constructeur selector ui

This commit is contained in:
Matthieu
2025-09-17 16:21:42 +02:00
parent 0a95b90553
commit 8a32ef4bbc
4 changed files with 92 additions and 55 deletions

View File

@@ -81,7 +81,9 @@
<label class="label"><span class="label-text font-medium">Constructeur</span></label>
<ConstructeurSelect
v-if="isEditMode"
v-model="component.constructeurId"
class="w-full"
:model-value="component.constructeurId || component.constructeur?.id || null"
@update:modelValue="handleConstructeurChange"
/>
<div v-else class="input input-bordered input-sm bg-base-200">
<div class="flex flex-col">
@@ -288,6 +290,11 @@ const documentsLoaded = ref(!!(props.component.documents && props.component.docu
const componentDocuments = computed(() => props.component.documents || [])
const documentIcon = (doc) => getFileIcon({ name: doc.filename || doc.name, mime: doc.mimeType })
const handleConstructeurChange = async (value) => {
props.component.constructeurId = value
await updateComponent()
}
const { uploadDocuments, deleteDocument, loadDocumentsByComponent } = useDocuments()
watch(
@@ -301,16 +308,6 @@ watch(
{ immediate: true }
)
watch(
() => props.component.constructeurId,
(newVal, oldVal) => {
if (!props.isEditMode) return
if (oldVal === undefined) return
if (newVal !== oldVal) {
updateComponent()
}
}
)
watch(
() => props.component.documents,

View File

@@ -20,17 +20,21 @@
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16l4-4 4 4m0-8l-4 4-4-4" />
</svg>
</button>
<ul
<div
v-if="openDropdown"
class="menu bg-base-100 border border-base-200 rounded-box shadow-lg mt-1 w-full max-h-48 overflow-y-auto absolute z-20"
class="absolute z-20 mt-1 w-full max-h-48 overflow-y-auto bg-base-100 border border-base-200 rounded-box shadow-lg flex flex-col"
>
<li v-if="options.length === 0" class="px-3 py-2 text-xs text-gray-500">
<div
v-if="options.length === 0"
class="px-3 py-2 text-xs text-gray-500"
>
Aucun constructeur trouvé
</li>
<li
</div>
<button
v-for="option in options"
:key="option.id"
class="px-3 py-2"
type="button"
class="w-full text-left px-3 py-2 hover:bg-base-200 focus:bg-base-200 focus:outline-none"
@click="selectOption(option)"
>
<div class="flex flex-col">
@@ -39,8 +43,8 @@
{{ [option.email, option.phone].filter(Boolean).join(' • ') || '—' }}
</span>
</div>
</li>
</ul>
</button>
</div>
</div>
<button type="button" class="btn btn-outline btn-sm" @click="openCreateModal = true">
Nouveau
@@ -111,6 +115,23 @@ const openCreateModal = ref(false)
const creating = ref(false)
const options = ref([])
let searchTimeout = null
let lastSearchTerm = ''
const applyOptions = (items = []) => {
const selectedId = props.modelValue
const cloned = [...items]
const limited = cloned.slice(0, 10)
if (selectedId && !limited.some(item => item.id === selectedId)) {
const selected = cloned.find(item => item.id === selectedId)
if (selected) {
if (limited.length >= 10) limited.pop()
limited.unshift(selected)
}
}
options.value = limited
}
const createForm = ref({
name: '',
@@ -140,10 +161,16 @@ watch(
)
const ensureOptionsLoaded = async (force = false) => {
if (!force && !searchTerm.value && constructeurs.value.length) {
applyOptions(constructeurs.value)
return
}
if (!force && searchTerm.value === lastSearchTerm && options.value.length) return
if (options.value.length && !force) return
const result = await searchConstructeurs(searchTerm.value)
if (result.success) {
options.value = result.data
applyOptions(result.data || [])
lastSearchTerm = searchTerm.value
}
}
@@ -151,9 +178,16 @@ const onSearch = () => {
openDropdown.value = true
clearTimeout(searchTimeout)
searchTimeout = setTimeout(async () => {
if (!searchTerm.value && constructeurs.value.length) {
applyOptions(constructeurs.value)
lastSearchTerm = ''
return
}
if (searchTerm.value === lastSearchTerm) return
const result = await searchConstructeurs(searchTerm.value)
if (result.success) {
options.value = result.data
applyOptions(result.data || [])
lastSearchTerm = searchTerm.value
}
}, 250)
}
@@ -184,6 +218,17 @@ const handleCreate = async () => {
}
}
watch(
constructeurs,
(list) => {
applyOptions(list || [])
if (!searchTerm.value) {
lastSearchTerm = ''
}
},
{ immediate: true }
)
const clickHandler = (event) => {
const element = event.target
if (element && element.closest) {
@@ -209,10 +254,3 @@ onBeforeUnmount(() => {
clearTimeout(searchTimeout)
})
</script>
watch(
constructeurs,
(list) => {
options.value = [...list]
},
{ immediate: true }
)

View File

@@ -42,7 +42,9 @@
</span>
<ConstructeurSelect
v-else
v-model="piece.constructeurId"
class="w-full"
:model-value="piece.constructeurId || piece.constructeur?.id || null"
@update:modelValue="handleConstructeurChange"
/>
</div>
<div>
@@ -260,6 +262,11 @@ const documentsLoaded = ref(!!(props.piece.documents && props.piece.documents.le
const pieceDocuments = computed(() => props.piece.documents || [])
const documentIcon = (doc) => getFileIcon({ name: doc.filename || doc.name, mime: doc.mimeType })
const handleConstructeurChange = (value) => {
props.piece.constructeurId = value
updatePiece()
}
const { uploadDocuments, deleteDocument, loadDocumentsByPiece } = useDocuments()
const refreshDocuments = async () => {
@@ -337,16 +344,6 @@ watch(
}
)
watch(
() => props.piece.constructeurId,
(newVal, oldVal) => {
if (!props.isEditMode) return
if (oldVal === undefined) return
if (newVal !== oldVal) {
updatePiece()
}
}
)
// Méthodes pour gérer les champs personnalisés
const setCustomFieldValue = (fieldValueId, value) => {