✅ 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:
@@ -124,7 +124,7 @@ class Inspection(Base):
|
||||
flagged_items_count = Column(Integer, default=0)
|
||||
|
||||
# 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)
|
||||
|
||||
# Firma
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "checklist-frontend",
|
||||
"private": true,
|
||||
"version": "1.0.78",
|
||||
"version": "1.0.79",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user