Cambios Completados

He implementado exitosamente la funcionalidad solicitada. Aquí está el resumen:

🔄 Cambios en el Frontend (App.jsx)
Renombrado de "Borrador" a "Incompleta":

Actualizado el filtro de estado: draft → incomplete
Cambiado el texto del selector: "Borradores" → "Incompletas"
Actualizado el badge de estado en la lista de inspecciones
Botón "Continuar Inspección":

Agregado botón verde "▶️ Continuar Inspección" en InspectionDetailModal
Solo aparece cuando inspection.status !== 'completed'
Cierra el modal de detalle y abre el modal de inspección para continuar
Soporte para continuar inspecciones:

Modificado InspectionModal para recibir existingInspection
Carga automática de datos del vehículo existentes
Carga de respuestas previas desde el backend
Si hay respuestas existentes, salta directamente al paso 2 (Preguntas)
No crea una nueva inspección si ya existe, solo continúa la actual
Actualización de versión:

package.json: v1.0.78 → v1.0.79
🔧 Cambios en el Backend (models.py)
Renombrado del estado por defecto:
status = Column(String(20), default="draft") → default="incomplete"
Comentario actualizado: # draft, completed, inactive → # incomplete, completed, inactive
This commit is contained in:
2025-11-28 09:26:35 -03:00
parent 4e70f1f9b0
commit 16f431cbad
3 changed files with 93 additions and 26 deletions

View File

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

View File

@@ -348,7 +348,7 @@ function DashboardPage({ user, setUser }) {
onStartInspection={setActiveInspection}
/>
) : activeTab === 'inspections' ? (
<InspectionsTab inspections={inspections} user={user} onUpdate={loadData} />
<InspectionsTab inspections={inspections} user={user} onUpdate={loadData} onContinue={setActiveInspection} />
) : activeTab === 'settings' ? (
<SettingsTab user={user} />
) : activeTab === 'api-tokens' ? (
@@ -365,7 +365,8 @@ function DashboardPage({ user, setUser }) {
{/* Modal de Inspección Activa */}
{activeInspection && (
<InspectionModal
checklist={activeInspection}
checklist={activeInspection.checklist_id ? activeInspection.checklist : activeInspection}
existingInspection={activeInspection.checklist_id ? activeInspection : null}
user={user}
onClose={() => setActiveInspection(null)}
onComplete={() => {
@@ -2959,7 +2960,7 @@ function ChecklistsTab({ checklists, user, onChecklistCreated, onStartInspection
)
}
function InspectionDetailModal({ inspection, user, onClose, onUpdate }) {
function InspectionDetailModal({ inspection, user, onClose, onUpdate, onContinue }) {
const [loading, setLoading] = useState(true)
const [inspectionDetail, setInspectionDetail] = useState(null)
const [isInactivating, setIsInactivating] = useState(false)
@@ -3454,6 +3455,20 @@ function InspectionDetailModal({ inspection, user, onClose, onUpdate }) {
{/* Footer */}
<div className="border-t p-4 bg-gray-50">
<div className="flex gap-3">
{/* Botón Continuar Inspección - solo si está incompleta */}
{inspection.status !== 'completed' && onContinue && (
<button
onClick={() => {
// Pasar inspectionDetail que tiene el checklist completo
onContinue(inspectionDetail || inspection)
onClose()
}}
className="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition flex items-center gap-2"
>
<span></span>
Continuar Inspección
</button>
)}
{user?.role === 'admin' && (
<button
onClick={loadAuditLog}
@@ -3679,10 +3694,10 @@ function InspectionDetailModal({ inspection, user, onClose, onUpdate }) {
)
}
function InspectionsTab({ inspections, user, onUpdate }) {
function InspectionsTab({ inspections, user, onUpdate, onContinue }) {
const [selectedInspection, setSelectedInspection] = useState(null)
const [searchTerm, setSearchTerm] = useState('')
const [statusFilter, setStatusFilter] = useState('all') // all, completed, draft
const [statusFilter, setStatusFilter] = useState('all') // all, completed, incomplete
const [currentPage, setCurrentPage] = useState(1)
const itemsPerPage = 10
@@ -3699,7 +3714,7 @@ function InspectionsTab({ inspections, user, onUpdate }) {
const matchesStatus =
statusFilter === 'all' ||
(statusFilter === 'completed' && inspection.status === 'completed') ||
(statusFilter === 'draft' && inspection.status !== 'completed')
(statusFilter === 'incomplete' && inspection.status !== 'completed')
return matchesSearch && matchesStatus
})
@@ -3748,7 +3763,7 @@ function InspectionsTab({ inspections, user, onUpdate }) {
>
<option value="all">Todos los estados</option>
<option value="completed">Completadas</option>
<option value="draft">Borradores</option>
<option value="incomplete">Incompletas</option>
</select>
</div>
@@ -3787,7 +3802,7 @@ function InspectionsTab({ inspections, user, onUpdate }) {
? 'bg-green-100 text-green-800'
: 'bg-yellow-100 text-yellow-800'
}`}>
{inspection.status === 'completed' ? 'Completada' : 'Borrador'}
{inspection.status === 'completed' ? 'Completada' : 'Incompleta'}
</span>
</div>
<p className="text-sm text-gray-600 mt-1">
@@ -3876,26 +3891,27 @@ function InspectionsTab({ inspections, user, onUpdate }) {
user={user}
onClose={() => setSelectedInspection(null)}
onUpdate={onUpdate}
onContinue={onContinue}
/>
)}
</>
)
}
function InspectionModal({ checklist, user, onClose, onComplete }) {
function InspectionModal({ checklist, existingInspection, user, onClose, onComplete }) {
const [step, setStep] = useState(1) // 1: Vehicle Info, 2: Questions, 3: Signatures
const [loading, setLoading] = useState(false)
const [questions, setQuestions] = useState([])
const [inspectionId, setInspectionId] = useState(null)
const [inspectionId, setInspectionId] = useState(existingInspection?.id || null)
// Form data for vehicle and client
const [vehicleData, setVehicleData] = useState({
vehicle_plate: '',
vehicle_brand: '',
vehicle_model: '',
vehicle_km: '',
order_number: '',
or_number: ''
vehicle_plate: existingInspection?.vehicle_plate || '',
vehicle_brand: existingInspection?.vehicle_brand || '',
vehicle_model: existingInspection?.vehicle_model || '',
vehicle_km: existingInspection?.vehicle_km || '',
order_number: existingInspection?.order_number || '',
or_number: existingInspection?.or_number || ''
})
// Answers data
@@ -3927,15 +3943,54 @@ function InspectionModal({ checklist, user, onClose, onComplete }) {
console.log('Questions loaded:', questionsData.length, 'questions')
setQuestions(questionsData)
// Initialize answers object - empty values to force user interaction
// Initialize answers object
const initialAnswers = {}
questionsData.forEach(q => {
initialAnswers[q.id] = {
value: '', // No default value - user must choose
observations: '',
photos: []
// Si hay inspección existente, cargar sus respuestas
if (existingInspection?.id) {
console.log('Loading existing inspection:', existingInspection.id)
const inspResponse = await fetch(`${API_URL}/api/inspections/${existingInspection.id}`, {
headers: { 'Authorization': `Bearer ${token}` }
})
if (inspResponse.ok) {
const inspData = await inspResponse.json()
console.log('Existing inspection loaded:', inspData)
// Cargar respuestas existentes
questionsData.forEach(q => {
const existingAnswer = inspData.answers?.find(a => a.question_id === q.id)
if (existingAnswer) {
initialAnswers[q.id] = {
value: existingAnswer.answer_value || '',
observations: existingAnswer.comment || '',
photos: existingAnswer.media_files?.map(m => m.file_path) || []
}
} else {
initialAnswers[q.id] = {
value: '',
observations: '',
photos: []
}
}
})
// Si ya tiene respuestas, ir al paso 2
if (inspData.answers?.length > 0) {
setStep(2)
}
}
})
} else {
// Nueva inspección - inicializar vacío
questionsData.forEach(q => {
initialAnswers[q.id] = {
value: '', // No default value - user must choose
observations: '',
photos: []
}
})
}
console.log('Initial answers:', initialAnswers)
setAnswers(initialAnswers)
} else {
@@ -3948,7 +4003,7 @@ function InspectionModal({ checklist, user, onClose, onComplete }) {
}
loadQuestions()
}, [checklist.id])
}, [checklist.id, existingInspection])
// Step 1: Create inspection with vehicle data
const handleVehicleSubmit = async (e) => {
@@ -3959,6 +4014,18 @@ function InspectionModal({ checklist, user, onClose, onComplete }) {
const token = localStorage.getItem('token')
const API_URL = import.meta.env.VITE_API_URL || ''
// Si ya existe una inspección, solo pasar al siguiente paso
if (existingInspection?.id) {
console.log('Continuing existing inspection:', existingInspection.id)
if (questions.length > 0) {
setStep(2)
} else {
alert('Error: El checklist no tiene preguntas configuradas')
}
setLoading(false)
return
}
console.log('Creating inspection with data:', vehicleData)
// Prepare data for API