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

@@ -908,7 +908,9 @@ function QuestionsManagerModal({ checklist, onClose }) {
points: 1,
allow_photos: true,
max_photos: 3,
requires_comment_on_fail: false
requires_comment_on_fail: false,
parent_question_id: null,
show_if_answer: ''
})
useEffect(() => {
@@ -963,7 +965,9 @@ function QuestionsManagerModal({ checklist, onClose }) {
points: 1,
allow_photos: true,
max_photos: 3,
requires_comment_on_fail: false
requires_comment_on_fail: false,
parent_question_id: null,
show_if_answer: ''
})
loadQuestions()
} else {
@@ -1090,6 +1094,70 @@ function QuestionsManagerModal({ checklist, onClose }) {
/>
</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>
<label className="block text-sm font-medium text-gray-700 mb-1">
@@ -1161,33 +1229,55 @@ function QuestionsManagerModal({ checklist, onClose }) {
</p>
</div>
<div className="divide-y divide-gray-200">
{sectionQuestions.map((question) => (
<div key={question.id} className="p-4 hover:bg-gray-50 flex justify-between items-start">
<div className="flex-1">
<div className="flex items-start gap-3">
<span className="text-xs text-gray-400 mt-1">#{question.id}</span>
<div>
<p className="text-gray-900">{question.text}</p>
<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">
{question.type}
</span>
<span>{question.points} pts</span>
{question.allow_photos && (
<span>📷 Máx {question.max_photos} fotos</span>
{sectionQuestions.map((question) => {
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 items-start gap-3">
<span className="text-xs text-gray-400 mt-1">#{question.id}</span>
<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>
</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">
<span className="px-2 py-1 bg-blue-100 text-blue-800 rounded text-xs">
{question.type}
</span>
<span>{question.points} pts</span>
{question.allow_photos && (
<span>📷 Máx {question.max_photos} fotos</span>
)}
</div>
</div>
</div>
</div>
<button
onClick={() => handleDeleteQuestion(question.id)}
className="ml-4 text-red-600 hover:text-red-700 text-sm"
>
Eliminar
</button>
</div>
<button
onClick={() => handleDeleteQuestion(question.id)}
className="ml-4 text-red-600 hover:text-red-700 text-sm"
>
Eliminar
</button>
</div>
))}
)
})}
</div>
</div>
))}