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

@@ -124,7 +124,7 @@ class Inspection(Base):
flagged_items_count = Column(Integer, default=0) flagged_items_count = Column(Integer, default=0)
# Estado # Estado
status = Column(String(20), default="draft") # draft, completed, inactive status = Column(String(20), default="incomplete") # incomplete, completed, inactive
is_active = Column(Boolean, default=True) is_active = Column(Boolean, default=True)
# Firma # Firma

View File

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

View File

@@ -348,7 +348,7 @@ function DashboardPage({ user, setUser }) {
onStartInspection={setActiveInspection} onStartInspection={setActiveInspection}
/> />
) : activeTab === 'inspections' ? ( ) : activeTab === 'inspections' ? (
<InspectionsTab inspections={inspections} user={user} onUpdate={loadData} /> <InspectionsTab inspections={inspections} user={user} onUpdate={loadData} onContinue={setActiveInspection} />
) : activeTab === 'settings' ? ( ) : activeTab === 'settings' ? (
<SettingsTab user={user} /> <SettingsTab user={user} />
) : activeTab === 'api-tokens' ? ( ) : activeTab === 'api-tokens' ? (
@@ -365,7 +365,8 @@ function DashboardPage({ user, setUser }) {
{/* Modal de Inspección Activa */} {/* Modal de Inspección Activa */}
{activeInspection && ( {activeInspection && (
<InspectionModal <InspectionModal
checklist={activeInspection} checklist={activeInspection.checklist_id ? activeInspection.checklist : activeInspection}
existingInspection={activeInspection.checklist_id ? activeInspection : null}
user={user} user={user}
onClose={() => setActiveInspection(null)} onClose={() => setActiveInspection(null)}
onComplete={() => { 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 [loading, setLoading] = useState(true)
const [inspectionDetail, setInspectionDetail] = useState(null) const [inspectionDetail, setInspectionDetail] = useState(null)
const [isInactivating, setIsInactivating] = useState(false) const [isInactivating, setIsInactivating] = useState(false)
@@ -3454,6 +3455,20 @@ function InspectionDetailModal({ inspection, user, onClose, onUpdate }) {
{/* Footer */} {/* Footer */}
<div className="border-t p-4 bg-gray-50"> <div className="border-t p-4 bg-gray-50">
<div className="flex gap-3"> <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' && ( {user?.role === 'admin' && (
<button <button
onClick={loadAuditLog} 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 [selectedInspection, setSelectedInspection] = useState(null)
const [searchTerm, setSearchTerm] = useState('') 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 [currentPage, setCurrentPage] = useState(1)
const itemsPerPage = 10 const itemsPerPage = 10
@@ -3699,7 +3714,7 @@ function InspectionsTab({ inspections, user, onUpdate }) {
const matchesStatus = const matchesStatus =
statusFilter === 'all' || statusFilter === 'all' ||
(statusFilter === 'completed' && inspection.status === 'completed') || (statusFilter === 'completed' && inspection.status === 'completed') ||
(statusFilter === 'draft' && inspection.status !== 'completed') (statusFilter === 'incomplete' && inspection.status !== 'completed')
return matchesSearch && matchesStatus return matchesSearch && matchesStatus
}) })
@@ -3748,7 +3763,7 @@ function InspectionsTab({ inspections, user, onUpdate }) {
> >
<option value="all">Todos los estados</option> <option value="all">Todos los estados</option>
<option value="completed">Completadas</option> <option value="completed">Completadas</option>
<option value="draft">Borradores</option> <option value="incomplete">Incompletas</option>
</select> </select>
</div> </div>
@@ -3787,7 +3802,7 @@ function InspectionsTab({ inspections, user, onUpdate }) {
? 'bg-green-100 text-green-800' ? 'bg-green-100 text-green-800'
: 'bg-yellow-100 text-yellow-800' : 'bg-yellow-100 text-yellow-800'
}`}> }`}>
{inspection.status === 'completed' ? 'Completada' : 'Borrador'} {inspection.status === 'completed' ? 'Completada' : 'Incompleta'}
</span> </span>
</div> </div>
<p className="text-sm text-gray-600 mt-1"> <p className="text-sm text-gray-600 mt-1">
@@ -3876,26 +3891,27 @@ function InspectionsTab({ inspections, user, onUpdate }) {
user={user} user={user}
onClose={() => setSelectedInspection(null)} onClose={() => setSelectedInspection(null)}
onUpdate={onUpdate} 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 [step, setStep] = useState(1) // 1: Vehicle Info, 2: Questions, 3: Signatures
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const [questions, setQuestions] = useState([]) const [questions, setQuestions] = useState([])
const [inspectionId, setInspectionId] = useState(null) const [inspectionId, setInspectionId] = useState(existingInspection?.id || null)
// Form data for vehicle and client // Form data for vehicle and client
const [vehicleData, setVehicleData] = useState({ const [vehicleData, setVehicleData] = useState({
vehicle_plate: '', vehicle_plate: existingInspection?.vehicle_plate || '',
vehicle_brand: '', vehicle_brand: existingInspection?.vehicle_brand || '',
vehicle_model: '', vehicle_model: existingInspection?.vehicle_model || '',
vehicle_km: '', vehicle_km: existingInspection?.vehicle_km || '',
order_number: '', order_number: existingInspection?.order_number || '',
or_number: '' or_number: existingInspection?.or_number || ''
}) })
// Answers data // Answers data
@@ -3927,15 +3943,54 @@ function InspectionModal({ checklist, user, onClose, onComplete }) {
console.log('Questions loaded:', questionsData.length, 'questions') console.log('Questions loaded:', questionsData.length, 'questions')
setQuestions(questionsData) setQuestions(questionsData)
// Initialize answers object - empty values to force user interaction // Initialize answers object
const initialAnswers = {} const initialAnswers = {}
questionsData.forEach(q => {
initialAnswers[q.id] = { // Si hay inspección existente, cargar sus respuestas
value: '', // No default value - user must choose if (existingInspection?.id) {
observations: '', console.log('Loading existing inspection:', existingInspection.id)
photos: [] 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) console.log('Initial answers:', initialAnswers)
setAnswers(initialAnswers) setAnswers(initialAnswers)
} else { } else {
@@ -3948,7 +4003,7 @@ function InspectionModal({ checklist, user, onClose, onComplete }) {
} }
loadQuestions() loadQuestions()
}, [checklist.id]) }, [checklist.id, existingInspection])
// Step 1: Create inspection with vehicle data // Step 1: Create inspection with vehicle data
const handleVehicleSubmit = async (e) => { const handleVehicleSubmit = async (e) => {
@@ -3959,6 +4014,18 @@ function InspectionModal({ checklist, user, onClose, onComplete }) {
const token = localStorage.getItem('token') const token = localStorage.getItem('token')
const API_URL = import.meta.env.VITE_API_URL || '' 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) console.log('Creating inspection with data:', vehicleData)
// Prepare data for API // Prepare data for API