fix: stabilize constructeur selector ui
This commit is contained in:
@@ -81,7 +81,9 @@
|
|||||||
<label class="label"><span class="label-text font-medium">Constructeur</span></label>
|
<label class="label"><span class="label-text font-medium">Constructeur</span></label>
|
||||||
<ConstructeurSelect
|
<ConstructeurSelect
|
||||||
v-if="isEditMode"
|
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 v-else class="input input-bordered input-sm bg-base-200">
|
||||||
<div class="flex flex-col">
|
<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 componentDocuments = computed(() => props.component.documents || [])
|
||||||
const documentIcon = (doc) => getFileIcon({ name: doc.filename || doc.name, mime: doc.mimeType })
|
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()
|
const { uploadDocuments, deleteDocument, loadDocumentsByComponent } = useDocuments()
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
@@ -301,16 +308,6 @@ watch(
|
|||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
watch(
|
|
||||||
() => props.component.constructeurId,
|
|
||||||
(newVal, oldVal) => {
|
|
||||||
if (!props.isEditMode) return
|
|
||||||
if (oldVal === undefined) return
|
|
||||||
if (newVal !== oldVal) {
|
|
||||||
updateComponent()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.component.documents,
|
() => props.component.documents,
|
||||||
|
|||||||
@@ -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" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16l4-4 4 4m0-8l-4 4-4-4" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<ul
|
<div
|
||||||
v-if="openDropdown"
|
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é
|
Aucun constructeur trouvé
|
||||||
</li>
|
</div>
|
||||||
<li
|
<button
|
||||||
v-for="option in options"
|
v-for="option in options"
|
||||||
:key="option.id"
|
: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)"
|
@click="selectOption(option)"
|
||||||
>
|
>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
@@ -39,8 +43,8 @@
|
|||||||
{{ [option.email, option.phone].filter(Boolean).join(' • ') || '—' }}
|
{{ [option.email, option.phone].filter(Boolean).join(' • ') || '—' }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</button>
|
||||||
</ul>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<button type="button" class="btn btn-outline btn-sm" @click="openCreateModal = true">
|
<button type="button" class="btn btn-outline btn-sm" @click="openCreateModal = true">
|
||||||
Nouveau
|
Nouveau
|
||||||
@@ -111,6 +115,23 @@ const openCreateModal = ref(false)
|
|||||||
const creating = ref(false)
|
const creating = ref(false)
|
||||||
const options = ref([])
|
const options = ref([])
|
||||||
let searchTimeout = null
|
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({
|
const createForm = ref({
|
||||||
name: '',
|
name: '',
|
||||||
@@ -140,10 +161,16 @@ watch(
|
|||||||
)
|
)
|
||||||
|
|
||||||
const ensureOptionsLoaded = async (force = false) => {
|
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
|
if (options.value.length && !force) return
|
||||||
const result = await searchConstructeurs(searchTerm.value)
|
const result = await searchConstructeurs(searchTerm.value)
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
options.value = result.data
|
applyOptions(result.data || [])
|
||||||
|
lastSearchTerm = searchTerm.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,9 +178,16 @@ const onSearch = () => {
|
|||||||
openDropdown.value = true
|
openDropdown.value = true
|
||||||
clearTimeout(searchTimeout)
|
clearTimeout(searchTimeout)
|
||||||
searchTimeout = setTimeout(async () => {
|
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)
|
const result = await searchConstructeurs(searchTerm.value)
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
options.value = result.data
|
applyOptions(result.data || [])
|
||||||
|
lastSearchTerm = searchTerm.value
|
||||||
}
|
}
|
||||||
}, 250)
|
}, 250)
|
||||||
}
|
}
|
||||||
@@ -184,6 +218,17 @@ const handleCreate = async () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
constructeurs,
|
||||||
|
(list) => {
|
||||||
|
applyOptions(list || [])
|
||||||
|
if (!searchTerm.value) {
|
||||||
|
lastSearchTerm = ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
const clickHandler = (event) => {
|
const clickHandler = (event) => {
|
||||||
const element = event.target
|
const element = event.target
|
||||||
if (element && element.closest) {
|
if (element && element.closest) {
|
||||||
@@ -209,10 +254,3 @@ onBeforeUnmount(() => {
|
|||||||
clearTimeout(searchTimeout)
|
clearTimeout(searchTimeout)
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
watch(
|
|
||||||
constructeurs,
|
|
||||||
(list) => {
|
|
||||||
options.value = [...list]
|
|
||||||
},
|
|
||||||
{ immediate: true }
|
|
||||||
)
|
|
||||||
|
|||||||
@@ -42,7 +42,9 @@
|
|||||||
</span>
|
</span>
|
||||||
<ConstructeurSelect
|
<ConstructeurSelect
|
||||||
v-else
|
v-else
|
||||||
v-model="piece.constructeurId"
|
class="w-full"
|
||||||
|
:model-value="piece.constructeurId || piece.constructeur?.id || null"
|
||||||
|
@update:modelValue="handleConstructeurChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
@@ -260,6 +262,11 @@ const documentsLoaded = ref(!!(props.piece.documents && props.piece.documents.le
|
|||||||
const pieceDocuments = computed(() => props.piece.documents || [])
|
const pieceDocuments = computed(() => props.piece.documents || [])
|
||||||
const documentIcon = (doc) => getFileIcon({ name: doc.filename || doc.name, mime: doc.mimeType })
|
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 { uploadDocuments, deleteDocument, loadDocumentsByPiece } = useDocuments()
|
||||||
|
|
||||||
const refreshDocuments = async () => {
|
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
|
// Méthodes pour gérer les champs personnalisés
|
||||||
const setCustomFieldValue = (fieldValueId, value) => {
|
const setCustomFieldValue = (fieldValueId, value) => {
|
||||||
|
|||||||
@@ -107,15 +107,17 @@
|
|||||||
</label>
|
</label>
|
||||||
<ConstructeurSelect
|
<ConstructeurSelect
|
||||||
v-if="isEditMode"
|
v-if="isEditMode"
|
||||||
|
class="w-full"
|
||||||
:key="machine.value?.id"
|
:key="machine.value?.id"
|
||||||
v-model="machineConstructeurId"
|
:model-value="machineConstructeurId"
|
||||||
placeholder="Rechercher un constructeur..."
|
placeholder="Rechercher un constructeur..."
|
||||||
|
@update:modelValue="handleMachineConstructeurChange"
|
||||||
/>
|
/>
|
||||||
<div v-else class="input input-bordered bg-base-200">
|
<div v-else class="input input-bordered bg-base-200">
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<span class="font-medium">{{ machine.value?.constructeur?.name || 'Non défini' }}</span>
|
<span class="font-medium">{{ machineConstructeurDisplay?.name || 'Non défini' }}</span>
|
||||||
<span class="text-xs text-gray-500">
|
<span class="text-xs text-gray-500">
|
||||||
{{ [machine.value?.constructeur?.email, machine.value?.constructeur?.phone].filter(Boolean).join(' • ') }}
|
{{ [machineConstructeurDisplay?.email, machineConstructeurDisplay?.phone].filter(Boolean).join(' • ') || '' }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -366,6 +368,7 @@ import { getFileIcon } from '~/utils/fileIcons'
|
|||||||
import ComponentHierarchy from '~/components/ComponentHierarchy.vue'
|
import ComponentHierarchy from '~/components/ComponentHierarchy.vue'
|
||||||
import DocumentUpload from '~/components/DocumentUpload.vue'
|
import DocumentUpload from '~/components/DocumentUpload.vue'
|
||||||
import ConstructeurSelect from '~/components/ConstructeurSelect.vue'
|
import ConstructeurSelect from '~/components/ConstructeurSelect.vue'
|
||||||
|
import { useConstructeurs } from '~/composables/useConstructeurs'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const machineId = route.params.id
|
const machineId = route.params.id
|
||||||
@@ -401,11 +404,18 @@ const machine = ref(null)
|
|||||||
const components = ref([])
|
const components = ref([])
|
||||||
const pieces = ref([])
|
const pieces = ref([])
|
||||||
|
|
||||||
|
const { constructeurs, loadConstructeurs } = useConstructeurs()
|
||||||
|
|
||||||
// Champs de la machine
|
// Champs de la machine
|
||||||
const machineName = ref('')
|
const machineName = ref('')
|
||||||
const machineReference = ref('')
|
const machineReference = ref('')
|
||||||
const machineEmplacement = ref('')
|
const machineEmplacement = ref('')
|
||||||
const machineConstructeurId = ref(null)
|
const machineConstructeurId = ref(null)
|
||||||
|
const machineConstructeurDisplay = computed(() => {
|
||||||
|
const id = machineConstructeurId.value || machine.value?.constructeur?.id || machine.value?.constructeurId
|
||||||
|
if (!id) return machine.value?.constructeur || null
|
||||||
|
return constructeurs.value.find(item => item.id === id) || machine.value?.constructeur || null
|
||||||
|
})
|
||||||
|
|
||||||
// Valeurs des champs personnalisés de la machine
|
// Valeurs des champs personnalisés de la machine
|
||||||
const machineCustomFieldValues = reactive({})
|
const machineCustomFieldValues = reactive({})
|
||||||
@@ -413,18 +423,11 @@ const machineCustomFieldValues = reactive({})
|
|||||||
const machineDocumentFiles = ref([])
|
const machineDocumentFiles = ref([])
|
||||||
const machineDocumentsUploading = ref(false)
|
const machineDocumentsUploading = ref(false)
|
||||||
const machineDocumentsLoaded = ref(false)
|
const machineDocumentsLoaded = ref(false)
|
||||||
const machineConstructeurInitialized = ref(false)
|
|
||||||
|
|
||||||
watch(machineConstructeurId, (newValue, oldValue) => {
|
const handleMachineConstructeurChange = async (value) => {
|
||||||
if (!machine.value) return
|
machineConstructeurId.value = value
|
||||||
if (!machineConstructeurInitialized.value) {
|
await updateMachineInfo()
|
||||||
machineConstructeurInitialized.value = true
|
}
|
||||||
return
|
|
||||||
}
|
|
||||||
if (newValue !== oldValue) {
|
|
||||||
updateMachineInfo()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// Mode d'édition
|
// Mode d'édition
|
||||||
const isEditMode = ref(false)
|
const isEditMode = ref(false)
|
||||||
@@ -451,7 +454,6 @@ const initMachineFields = () => {
|
|||||||
machineReference.value = machine.value.reference || ''
|
machineReference.value = machine.value.reference || ''
|
||||||
machineEmplacement.value = machine.value.emplacement || ''
|
machineEmplacement.value = machine.value.emplacement || ''
|
||||||
machineConstructeurId.value = machine.value.constructeurId || machine.value.constructeur?.id || null
|
machineConstructeurId.value = machine.value.constructeurId || machine.value.constructeur?.id || null
|
||||||
machineConstructeurInitialized.value = false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -839,6 +841,9 @@ const toggleEditMode = () => {
|
|||||||
// Lifecycle
|
// Lifecycle
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
loadMachineData()
|
loadMachineData()
|
||||||
|
if (!constructeurs.value.length) {
|
||||||
|
loadConstructeurs()
|
||||||
|
}
|
||||||
|
|
||||||
// Vérifier si on doit activer le mode édition depuis l'URL
|
// Vérifier si on doit activer le mode édition depuis l'URL
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
|
|||||||
Reference in New Issue
Block a user