Campo de Observaciones Opcional

 Agregado checkbox "Agregar campo observaciones" en QuestionTypeEditor.jsx (sección "Opciones Generales")
 Por defecto está marcado (compatibilidad con preguntas existentes)
 El campo de observaciones solo se muestra si show_observations !== false
 El admin ahora tiene control total sobre si mostrar o no las observaciones
2. Botón "Consultar Asistente IA" Siempre Visible
 El botón ahora aparece siempre para preguntas tipo ai_assistant
 No depende de que la pregunta tenga fotos habilitadas
 Movido a una sección independiente (fuera del bloque de fotos)
 Removido el botón duplicado que estaba dentro de la sección de fotos
3. Versiones Actualizadas
Frontend: 1.0.89 → 1.0.90
Service Worker: ayutec-v1.0.89 → ayutec-v1.0.90
Backend: Sin cambios (no fue necesario)
📋 Detalles Técnicos
App.jsx:

Campo de respuesta oculto para photo_only y ai_assistant
Botón de Asistente IA en sección dedicada (siempre visible para ai_assistant)
Observaciones solo si show_observations !== false y no es photo_only ni ai_assistant
QuestionTypeEditor.jsx:

Nueva sección "⚙️ Opciones Generales" con checkbox azul
Texto de ayuda: "Si está marcado, el mecánico podrá agregar notas adicionales"
This commit is contained in:
2025-11-30 23:51:04 -03:00
parent c226fbd34b
commit 54006d5756
4 changed files with 70 additions and 31 deletions

View File

@@ -1,7 +1,7 @@
{ {
"name": "checklist-frontend", "name": "checklist-frontend",
"private": true, "private": true,
"version": "1.0.89", "version": "1.0.90",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@@ -1,6 +1,6 @@
// Service Worker para PWA con detección de actualizaciones // Service Worker para PWA con detección de actualizaciones
// IMPORTANTE: Actualizar esta versión cada vez que se despliegue una nueva versión // IMPORTANTE: Actualizar esta versión cada vez que se despliegue una nueva versión
const CACHE_NAME = 'ayutec-v1.0.89'; const CACHE_NAME = 'ayutec-v1.0.90';
const urlsToCache = [ const urlsToCache = [
'/', '/',
'/index.html' '/index.html'

View File

@@ -4921,8 +4921,8 @@ function InspectionModal({ checklist, existingInspection, user, onClose, onCompl
</div> </div>
<div className="space-y-3 sm:space-y-4"> <div className="space-y-3 sm:space-y-4">
{/* Answer input based on type - NO mostrar para photo_only */} {/* Answer input based on type - NO mostrar para photo_only ni ai_assistant */}
{currentQuestion.options?.type !== 'photo_only' && ( {currentQuestion.options?.type !== 'photo_only' && currentQuestion.options?.type !== 'ai_assistant' && (
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-2"> <label className="block text-sm font-medium text-gray-700 mb-2">
Respuesta * Respuesta *
@@ -4942,8 +4942,47 @@ function InspectionModal({ checklist, existingInspection, user, onClose, onCompl
</div> </div>
)} )}
{/* Observations - NO mostrar para photo_only */} {/* Botón de Chat IA - Mostrar SIEMPRE si es tipo ai_assistant */}
{currentQuestion.options?.type !== 'photo_only' && ( {currentQuestion.options?.type === 'ai_assistant' && (
<div>
<QuestionAnswerInput
question={currentQuestion}
value={answers[currentQuestion.id]?.value}
onChange={(newValue) => {
setAnswers(prev => ({
...prev,
[currentQuestion.id]: { ...prev[currentQuestion.id], value: newValue }
}))
}}
onSave={() => setTimeout(() => saveAnswer(currentQuestion.id), 500)}
/>
<button
type="button"
onClick={() => {
setShowAIChat(true)
// Cargar historial si existe
if (answers[currentQuestion.id]?.chatHistory) {
setAiChatMessages(answers[currentQuestion.id].chatHistory)
}
}}
className="w-full mt-3 px-4 py-3 bg-gradient-to-r from-purple-600 to-blue-600 text-white rounded-lg hover:from-purple-700 hover:to-blue-700 transition flex items-center justify-center gap-2 font-semibold shadow-lg"
>
<span>💬</span>
<span>Consultar Asistente IA</span>
{answers[currentQuestion.id]?.chatHistory?.length > 0 && (
<span className="ml-1 px-2 py-0.5 bg-white/20 rounded-full text-xs">
{answers[currentQuestion.id].chatHistory.length} mensajes
</span>
)}
</button>
</div>
)}
{/* Observations - Mostrar solo si show_observations !== false */}
{currentQuestion.options?.type !== 'photo_only' &&
currentQuestion.options?.type !== 'ai_assistant' &&
currentQuestion.options?.show_observations !== false && (
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-2"> <label className="block text-sm font-medium text-gray-700 mb-2">
Observaciones (opcional) Observaciones (opcional)
@@ -5024,8 +5063,9 @@ function InspectionModal({ checklist, existingInspection, user, onClose, onCompl
))} ))}
</div> </div>
{/* Analyze Button */} {/* Analyze Button - Solo para ai_mode assisted/full y NO para ai_assistant */}
{(checklist.ai_mode === 'assisted' || checklist.ai_mode === 'full') && ( {(checklist.ai_mode === 'assisted' || checklist.ai_mode === 'full') &&
currentQuestion.options?.type !== 'ai_assistant' && (
<button <button
type="button" type="button"
onClick={() => handleAnalyzePhotos(currentQuestion.id)} onClick={() => handleAnalyzePhotos(currentQuestion.id)}
@@ -5045,29 +5085,6 @@ function InspectionModal({ checklist, existingInspection, user, onClose, onCompl
)} )}
</button> </button>
)} )}
{/* Botón para abrir chat IA (si es tipo ai_assistant) */}
{currentQuestion.options?.type === 'ai_assistant' && (
<button
type="button"
onClick={() => {
setShowAIChat(true)
// Cargar historial si existe
if (answers[currentQuestion.id]?.chatHistory) {
setAiChatMessages(answers[currentQuestion.id].chatHistory)
}
}}
className="w-full mt-3 px-4 py-3 bg-gradient-to-r from-purple-600 to-blue-600 text-white rounded-lg hover:from-purple-700 hover:to-blue-700 transition flex items-center justify-center gap-2 font-semibold shadow-lg"
>
<span>💬</span>
<span>Consultar Asistente IA</span>
{answers[currentQuestion.id]?.chatHistory?.length > 0 && (
<span className="ml-1 px-2 py-0.5 bg-white/20 rounded-full text-xs">
{answers[currentQuestion.id].chatHistory.length} mensajes
</span>
)}
</button>
)}
</div> </div>
)} )}

View File

@@ -197,6 +197,28 @@ export function QuestionTypeEditor({ value, onChange, maxPoints = 1 }) {
</div> </div>
</div> </div>
{/* Opciones Globales */}
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4">
<h4 className="font-semibold text-blue-900 mb-3 text-sm"> Opciones Generales</h4>
<div className="space-y-2">
{/* Checkbox para campo de observaciones */}
<label className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={config.show_observations !== false}
onChange={(e) => updateConfig({ show_observations: e.target.checked })}
className="rounded border-gray-300 text-blue-600 focus:ring-blue-500"
/>
<span className="text-sm text-gray-700">
📝 Mostrar campo de observaciones al mecánico
</span>
</label>
<p className="text-xs text-gray-500 ml-6">
Si está marcado, el mecánico podrá agregar notas adicionales en esta pregunta
</p>
</div>
</div>
{/* Configuración específica según tipo */} {/* Configuración específica según tipo */}
<div className="bg-gray-50 border border-gray-200 rounded-lg p-4"> <div className="bg-gray-50 border border-gray-200 rounded-lg p-4">
{/* BOOLEAN */} {/* BOOLEAN */}