Frontend v1.0.73:

- Implementado drag & drop nativo HTML5 para reordenar preguntas
- Agregados estados draggedQuestion y dragOverQuestion
- Handlers: handleDragStart, handleDragEnd, handleDragOver, handleDrop
- Indicador visual: línea azul en drop zone
- Icono de agarre (⋮⋮) con tooltip "Arrastra para reordenar"
- Opacidad 50% en elemento arrastrado
- Cursor 'move' indica elemento arrastrable
- Mantiene función moveQuestion para compatibilidad
- Reordenamiento automático al soltar
This commit is contained in:
2025-11-27 16:43:14 -03:00
parent 97c5aab93d
commit bd2b11d543

View File

@@ -928,6 +928,8 @@ function QuestionsManagerModal({ checklist, onClose }) {
const [viewingAudit, setViewingAudit] = useState(null)
const [auditHistory, setAuditHistory] = useState([])
const [loadingAudit, setLoadingAudit] = useState(false)
const [draggedQuestion, setDraggedQuestion] = useState(null)
const [dragOverQuestion, setDragOverQuestion] = useState(null)
const [formData, setFormData] = useState({
section: '',
text: '',
@@ -1212,6 +1214,80 @@ function QuestionsManagerModal({ checklist, onClose }) {
}
}
// Drag & Drop handlers
const handleDragStart = (e, question) => {
setDraggedQuestion(question)
e.dataTransfer.effectAllowed = 'move'
e.currentTarget.style.opacity = '0.5'
}
const handleDragEnd = (e) => {
e.currentTarget.style.opacity = '1'
setDraggedQuestion(null)
setDragOverQuestion(null)
}
const handleDragOver = (e, question) => {
e.preventDefault()
e.dataTransfer.dropEffect = 'move'
setDragOverQuestion(question)
}
const handleDragLeave = (e) => {
setDragOverQuestion(null)
}
const handleDrop = async (e, targetQuestion) => {
e.preventDefault()
if (!draggedQuestion || draggedQuestion.id === targetQuestion.id) {
setDraggedQuestion(null)
setDragOverQuestion(null)
return
}
const questionsList = Object.values(questionsBySection).flat()
const draggedIndex = questionsList.findIndex(q => q.id === draggedQuestion.id)
const targetIndex = questionsList.findIndex(q => q.id === targetQuestion.id)
// Crear nueva lista con el orden actualizado
const newList = [...questionsList]
const [movedQuestion] = newList.splice(draggedIndex, 1)
newList.splice(targetIndex, 0, movedQuestion)
// Preparar datos para el backend
const reorderData = newList.map((q, index) => ({
question_id: q.id,
new_order: index
}))
try {
const token = localStorage.getItem('token')
const API_URL = import.meta.env.VITE_API_URL || ''
const response = await fetch(`${API_URL}/api/checklists/${checklist.id}/questions/reorder`, {
method: 'PATCH',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify(reorderData)
})
if (response.ok) {
loadQuestions()
} else {
alert('Error al reordenar pregunta')
}
} catch (error) {
console.error('Error:', error)
alert('Error al reordenar pregunta')
}
setDraggedQuestion(null)
setDragOverQuestion(null)
}
const questionsBySection = questions.reduce((acc, q) => {
const section = q.section || 'Sin sección'
if (!acc[section]) acc[section] = []
@@ -1588,9 +1664,17 @@ function QuestionsManagerModal({ checklist, onClose }) {
return (
<div
key={question.id}
className={`p-4 hover:bg-gray-50 flex justify-between items-start ${
key={question.id}
draggable={true}
onDragStart={(e) => handleDragStart(e, question)}
onDragEnd={handleDragEnd}
onDragOver={(e) => handleDragOver(e, question)}
onDragLeave={handleDragLeave}
onDrop={(e) => handleDrop(e, question)}
className={`p-4 hover:bg-gray-50 flex justify-between items-start cursor-move transition-all ${
isSubQuestion ? 'bg-blue-50 ml-8 border-l-4 border-blue-300' : ''
} ${
dragOverQuestion?.id === question.id ? 'border-t-4 border-indigo-500' : ''
}`}
>
<div className="flex-1">
@@ -1628,22 +1712,11 @@ function QuestionsManagerModal({ checklist, onClose }) {
</div>
</div>
<div className="ml-4 flex gap-2 items-center">
{/* Botones de reordenamiento */}
<div className="flex flex-col gap-1">
<button
onClick={() => moveQuestion(question.id, 'up')}
className="text-gray-500 hover:text-indigo-600 text-xs px-2 py-1 hover:bg-indigo-50 rounded transition"
title="Mover arriba"
>
</button>
<button
onClick={() => moveQuestion(question.id, 'down')}
className="text-gray-500 hover:text-indigo-600 text-xs px-2 py-1 hover:bg-indigo-50 rounded transition"
title="Mover abajo"
>
</button>
{/* Indicador de drag */}
<div className="text-gray-400 hover:text-gray-600 cursor-move px-2" title="Arrastra para reordenar">
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 20 20">
<path d="M7 2a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 2zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 8zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 7 14zm6-8a2 2 0 1 0-.001-4.001A2 2 0 0 0 13 6zm0 2a2 2 0 1 0 .001 4.001A2 2 0 0 0 13 8zm0 6a2 2 0 1 0 .001 4.001A2 2 0 0 0 13 14z"></path>
</svg>
</div>
<div className="h-8 w-px bg-gray-300"></div>
<button