feat: Editor de preguntas condicionales - frontend v1.0.19

This commit is contained in:
2025-11-21 02:15:45 -03:00
parent 78742dc906
commit b730c4f7c2
2 changed files with 115 additions and 25 deletions

View File

@@ -38,7 +38,7 @@ services:
command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4 command: uvicorn app.main:app --host 0.0.0.0 --port 8000 --workers 4
frontend: frontend:
image: dymai/syntria-frontend:1.0.18 image: dymai/syntria-frontend:1.0.19
container_name: syntria-frontend-prod container_name: syntria-frontend-prod
restart: always restart: always
depends_on: depends_on:

View File

@@ -908,7 +908,9 @@ function QuestionsManagerModal({ checklist, onClose }) {
points: 1, points: 1,
allow_photos: true, allow_photos: true,
max_photos: 3, max_photos: 3,
requires_comment_on_fail: false requires_comment_on_fail: false,
parent_question_id: null,
show_if_answer: ''
}) })
useEffect(() => { useEffect(() => {
@@ -963,7 +965,9 @@ function QuestionsManagerModal({ checklist, onClose }) {
points: 1, points: 1,
allow_photos: true, allow_photos: true,
max_photos: 3, max_photos: 3,
requires_comment_on_fail: false requires_comment_on_fail: false,
parent_question_id: null,
show_if_answer: ''
}) })
loadQuestions() loadQuestions()
} else { } else {
@@ -1090,6 +1094,70 @@ function QuestionsManagerModal({ checklist, onClose }) {
/> />
</div> </div>
{/* Pregunta Condicional */}
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
<h4 className="text-sm font-semibold text-blue-900 mb-3"> Pregunta Condicional (opcional)</h4>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Pregunta padre
</label>
<select
value={formData.parent_question_id || ''}
onChange={(e) => setFormData({
...formData,
parent_question_id: e.target.value ? parseInt(e.target.value) : null
})}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 bg-white"
>
<option value="">Ninguna (pregunta principal)</option>
{questions
.filter(q => (q.type === 'pass_fail' || q.type === 'good_bad') && !q.parent_question_id)
.map(q => (
<option key={q.id} value={q.id}>
#{q.id} - {q.text.substring(0, 50)}{q.text.length > 50 ? '...' : ''}
</option>
))
}
</select>
<p className="text-xs text-gray-500 mt-1">
Esta pregunta aparecerá solo si se responde la pregunta padre
</p>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Mostrar si la respuesta es:
</label>
<select
value={formData.show_if_answer}
onChange={(e) => setFormData({ ...formData, show_if_answer: e.target.value })}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-500 bg-white"
disabled={!formData.parent_question_id}
>
<option value="">Seleccione...</option>
{formData.parent_question_id && (() => {
const parentQ = questions.find(q => q.id === formData.parent_question_id)
if (parentQ?.type === 'pass_fail') {
return [
<option key="pass" value="pass"> Pasa</option>,
<option key="fail" value="fail"> Falla</option>
]
} else if (parentQ?.type === 'good_bad') {
return [
<option key="good" value="good"> Bueno</option>,
<option key="bad" value="bad"> Malo</option>
]
}
return null
})()}
</select>
<p className="text-xs text-gray-500 mt-1">
La pregunta solo se mostrará con esta respuesta
</p>
</div>
</div>
</div>
<div className="grid grid-cols-3 gap-4"> <div className="grid grid-cols-3 gap-4">
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1"> <label className="block text-sm font-medium text-gray-700 mb-1">
@@ -1161,13 +1229,34 @@ function QuestionsManagerModal({ checklist, onClose }) {
</p> </p>
</div> </div>
<div className="divide-y divide-gray-200"> <div className="divide-y divide-gray-200">
{sectionQuestions.map((question) => ( {sectionQuestions.map((question) => {
<div key={question.id} className="p-4 hover:bg-gray-50 flex justify-between items-start"> const isSubQuestion = question.parent_question_id
const parentQuestion = isSubQuestion ? questions.find(q => q.id === question.parent_question_id) : null
return (
<div
key={question.id}
className={`p-4 hover:bg-gray-50 flex justify-between items-start ${
isSubQuestion ? 'bg-blue-50 ml-8 border-l-4 border-blue-300' : ''
}`}
>
<div className="flex-1"> <div className="flex-1">
<div className="flex items-start gap-3"> <div className="flex items-start gap-3">
<span className="text-xs text-gray-400 mt-1">#{question.id}</span> <span className="text-xs text-gray-400 mt-1">#{question.id}</span>
<div> <div>
<div className="flex items-center gap-2">
{isSubQuestion && (
<span className="text-xs bg-blue-200 text-blue-800 px-2 py-1 rounded">
Condicional
</span>
)}
<p className="text-gray-900">{question.text}</p> <p className="text-gray-900">{question.text}</p>
</div>
{isSubQuestion && parentQuestion && (
<p className="text-xs text-blue-600 mt-1">
Aparece si <strong>#{question.parent_question_id}</strong> es <strong>{question.show_if_answer}</strong>
</p>
)}
<div className="flex gap-4 mt-2 text-sm text-gray-600"> <div className="flex gap-4 mt-2 text-sm text-gray-600">
<span className="px-2 py-1 bg-blue-100 text-blue-800 rounded text-xs"> <span className="px-2 py-1 bg-blue-100 text-blue-800 rounded text-xs">
{question.type} {question.type}
@@ -1187,7 +1276,8 @@ function QuestionsManagerModal({ checklist, onClose }) {
Eliminar Eliminar
</button> </button>
</div> </div>
))} )
})}
</div> </div>
</div> </div>
))} ))}