Backend v1.0.73:
- Implementado sistema de reordenamiento de preguntas
- Nuevo endpoint PATCH /api/checklists/{id}/questions/reorder
- Schema QuestionReorder para validar datos de reorden
- Actualización en lote de campo 'order' en preguntas
- Auditoría automática de cambios de orden
- Validación de permisos y existencia de checklist
Frontend v1.0.71:
- Agregada funcionalidad de reordenamiento de preguntas
- Botones ▲ ▼ para mover preguntas arriba/abajo
- Función moveQuestion() para gestionar reordenamiento
- Interfaz visual mejorada con separadores
- Tooltips descriptivos en botones de orden
- Recarga automática tras reordenar
This commit is contained in:
@@ -204,7 +204,7 @@ def send_completed_inspection_to_n8n(inspection, db):
|
||||
# No lanzamos excepción para no interrumpir el flujo normal
|
||||
|
||||
|
||||
BACKEND_VERSION = "1.0.72"
|
||||
BACKEND_VERSION = "1.0.73"
|
||||
app = FastAPI(title="Checklist Inteligente API", version=BACKEND_VERSION)
|
||||
|
||||
# S3/MinIO configuration
|
||||
@@ -1041,6 +1041,52 @@ def update_question(
|
||||
return db_question
|
||||
|
||||
|
||||
@app.patch("/api/checklists/{checklist_id}/questions/reorder")
|
||||
def reorder_questions(
|
||||
checklist_id: int,
|
||||
reorder_data: List[schemas.QuestionReorder],
|
||||
db: Session = Depends(get_db),
|
||||
current_user: models.User = Depends(get_current_user)
|
||||
):
|
||||
"""Reordenar preguntas de un checklist"""
|
||||
if current_user.role != "admin":
|
||||
raise HTTPException(status_code=403, detail="No autorizado")
|
||||
|
||||
# Verificar que el checklist existe
|
||||
checklist = db.query(models.Checklist).filter(models.Checklist.id == checklist_id).first()
|
||||
if not checklist:
|
||||
raise HTTPException(status_code=404, detail="Checklist no encontrado")
|
||||
|
||||
# Actualizar el orden de cada pregunta
|
||||
for item in reorder_data:
|
||||
question = db.query(models.Question).filter(
|
||||
models.Question.id == item.question_id,
|
||||
models.Question.checklist_id == checklist_id
|
||||
).first()
|
||||
|
||||
if question:
|
||||
old_order = question.order
|
||||
question.order = item.new_order
|
||||
question.updated_at = datetime.utcnow()
|
||||
|
||||
# Registrar auditoría
|
||||
audit_log = models.QuestionAuditLog(
|
||||
question_id=question.id,
|
||||
checklist_id=checklist_id,
|
||||
user_id=current_user.id,
|
||||
action="updated",
|
||||
field_name="order",
|
||||
old_value=str(old_order),
|
||||
new_value=str(item.new_order),
|
||||
comment="Orden de pregunta actualizado"
|
||||
)
|
||||
db.add(audit_log)
|
||||
|
||||
db.commit()
|
||||
|
||||
return {"message": "Orden de preguntas actualizado exitosamente", "updated_count": len(reorder_data)}
|
||||
|
||||
|
||||
@app.delete("/api/questions/{question_id}")
|
||||
def delete_question(
|
||||
question_id: int,
|
||||
|
||||
@@ -134,6 +134,10 @@ class QuestionCreate(QuestionBase):
|
||||
class QuestionUpdate(QuestionBase):
|
||||
pass
|
||||
|
||||
class QuestionReorder(BaseModel):
|
||||
question_id: int
|
||||
new_order: int
|
||||
|
||||
class Question(QuestionBase):
|
||||
id: int
|
||||
checklist_id: int
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "checklist-frontend",
|
||||
"private": true,
|
||||
"version": "1.0.70",
|
||||
"version": "1.0.71",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -1167,6 +1167,51 @@ function QuestionsManagerModal({ checklist, onClose }) {
|
||||
}
|
||||
}
|
||||
|
||||
const moveQuestion = async (questionId, direction) => {
|
||||
const questionsList = Object.values(questionsBySection).flat()
|
||||
const currentIndex = questionsList.findIndex(q => q.id === questionId)
|
||||
|
||||
if (currentIndex === -1) return
|
||||
|
||||
const newIndex = direction === 'up' ? currentIndex - 1 : currentIndex + 1
|
||||
|
||||
if (newIndex < 0 || newIndex >= questionsList.length) return
|
||||
|
||||
// Crear nueva lista con el orden actualizado
|
||||
const newList = [...questionsList]
|
||||
const [movedQuestion] = newList.splice(currentIndex, 1)
|
||||
newList.splice(newIndex, 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')
|
||||
}
|
||||
}
|
||||
|
||||
const questionsBySection = questions.reduce((acc, q) => {
|
||||
const section = q.section || 'Sin sección'
|
||||
if (!acc[section]) acc[section] = []
|
||||
@@ -1582,7 +1627,25 @@ function QuestionsManagerModal({ checklist, onClose }) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="ml-4 flex gap-2">
|
||||
<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>
|
||||
</div>
|
||||
<div className="h-8 w-px bg-gray-300"></div>
|
||||
<button
|
||||
onClick={() => loadAuditHistory(question.id)}
|
||||
className="text-gray-600 hover:text-gray-700 text-sm"
|
||||
|
||||
Reference in New Issue
Block a user