diff --git a/backend/app/main.py b/backend/app/main.py index 83c1789..b2453ac 100644 --- a/backend/app/main.py +++ b/backend/app/main.py @@ -1292,10 +1292,18 @@ def get_dashboard_data( # Aplicar filtros de fecha if start_date: - start = datetime.fromisoformat(start_date) + # Parsear fecha y establecer al inicio del día en UTC + from datetime import timezone + start = datetime.fromisoformat(start_date).replace(hour=0, minute=0, second=0, microsecond=0) + if start.tzinfo is None: + start = start.replace(tzinfo=timezone.utc) query = query.filter(models.Inspection.started_at >= start) if end_date: - end = datetime.fromisoformat(end_date) + # Parsear fecha y establecer al final del día en UTC + from datetime import timezone + end = datetime.fromisoformat(end_date).replace(hour=23, minute=59, second=59, microsecond=999999) + if end.tzinfo is None: + end = end.replace(tzinfo=timezone.utc) query = query.filter(models.Inspection.started_at <= end) # Filtro por mecánico @@ -1327,10 +1335,16 @@ def get_dashboard_data( .filter(models.Inspection.is_active == True) if start_date: - start = datetime.fromisoformat(start_date) + from datetime import timezone + start = datetime.fromisoformat(start_date).replace(hour=0, minute=0, second=0, microsecond=0) + if start.tzinfo is None: + start = start.replace(tzinfo=timezone.utc) flagged_items = flagged_items.filter(models.Inspection.started_at >= start) if end_date: - end = datetime.fromisoformat(end_date) + from datetime import timezone + end = datetime.fromisoformat(end_date).replace(hour=23, minute=59, second=59, microsecond=999999) + if end.tzinfo is None: + end = end.replace(tzinfo=timezone.utc) flagged_items = flagged_items.filter(models.Inspection.started_at <= end) if mechanic_id: flagged_items = flagged_items.filter(models.Inspection.mechanic_id == mechanic_id) @@ -1364,10 +1378,16 @@ def get_dashboard_data( .filter(models.Inspection.is_active == True) if start_date: - start = datetime.fromisoformat(start_date) + from datetime import timezone + start = datetime.fromisoformat(start_date).replace(hour=0, minute=0, second=0, microsecond=0) + if start.tzinfo is None: + start = start.replace(tzinfo=timezone.utc) mechanic_stats = mechanic_stats.filter(models.Inspection.started_at >= start) if end_date: - end = datetime.fromisoformat(end_date) + from datetime import timezone + end = datetime.fromisoformat(end_date).replace(hour=23, minute=59, second=59, microsecond=999999) + if end.tzinfo is None: + end = end.replace(tzinfo=timezone.utc) mechanic_stats = mechanic_stats.filter(models.Inspection.started_at <= end) mechanic_stats = mechanic_stats.group_by(models.User.id, models.User.full_name)\ @@ -1401,10 +1421,16 @@ def get_dashboard_data( .filter(models.Checklist.is_active == True) if start_date: - start = datetime.fromisoformat(start_date) + from datetime import timezone + start = datetime.fromisoformat(start_date).replace(hour=0, minute=0, second=0, microsecond=0) + if start.tzinfo is None: + start = start.replace(tzinfo=timezone.utc) checklist_stats_query = checklist_stats_query.filter(models.Inspection.started_at >= start) if end_date: - end = datetime.fromisoformat(end_date) + from datetime import timezone + end = datetime.fromisoformat(end_date).replace(hour=23, minute=59, second=59, microsecond=999999) + if end.tzinfo is None: + end = end.replace(tzinfo=timezone.utc) checklist_stats_query = checklist_stats_query.filter(models.Inspection.started_at <= end) if mechanic_id: checklist_stats_query = checklist_stats_query.filter(models.Inspection.mechanic_id == mechanic_id) @@ -1456,10 +1482,16 @@ def get_dashboard_data( .filter(models.Answer.answer_value.in_(['pass', 'fail', 'good', 'bad', 'regular'])) if start_date: - start = datetime.fromisoformat(start_date) + from datetime import timezone + start = datetime.fromisoformat(start_date).replace(hour=0, minute=0, second=0, microsecond=0) + if start.tzinfo is None: + start = start.replace(tzinfo=timezone.utc) pass_fail_data = pass_fail_data.filter(models.Inspection.started_at >= start) if end_date: - end = datetime.fromisoformat(end_date) + from datetime import timezone + end = datetime.fromisoformat(end_date).replace(hour=23, minute=59, second=59, microsecond=999999) + if end.tzinfo is None: + end = end.replace(tzinfo=timezone.utc) pass_fail_data = pass_fail_data.filter(models.Inspection.started_at <= end) if mechanic_id: pass_fail_data = pass_fail_data.filter(models.Inspection.mechanic_id == mechanic_id) @@ -1496,6 +1528,7 @@ def get_inspections_report( query = db.query( models.Inspection.id, models.Inspection.vehicle_plate, + models.Inspection.checklist_id, models.Checklist.name.label('checklist_name'), models.User.full_name.label('mechanic_name'), models.Inspection.status, @@ -1515,10 +1548,16 @@ def get_inspections_report( # Aplicar filtros if start_date: - start = datetime.fromisoformat(start_date) + from datetime import timezone + start = datetime.fromisoformat(start_date).replace(hour=0, minute=0, second=0, microsecond=0) + if start.tzinfo is None: + start = start.replace(tzinfo=timezone.utc) query = query.filter(models.Inspection.started_at >= start) if end_date: - end = datetime.fromisoformat(end_date) + from datetime import timezone + end = datetime.fromisoformat(end_date).replace(hour=23, minute=59, second=59, microsecond=999999) + if end.tzinfo is None: + end = end.replace(tzinfo=timezone.utc) query = query.filter(models.Inspection.started_at <= end) if mechanic_id: query = query.filter(models.Inspection.mechanic_id == mechanic_id) @@ -1541,6 +1580,7 @@ def get_inspections_report( { "id": r.id, "vehicle_plate": r.vehicle_plate, + "checklist_id": r.checklist_id, "checklist_name": r.checklist_name, "mechanic_name": r.mechanic_name, "status": r.status, diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 102b560..147f08c 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -3176,82 +3176,135 @@ function ReportsTab({ user }) { const [dashboardData, setDashboardData] = useState(null) const [inspections, setInspections] = useState([]) const [loading, setLoading] = useState(true) + const [mechanics, setMechanics] = useState([]) + const [checklists, setChecklists] = useState([]) const [filters, setFilters] = useState({ - startDate: '', - endDate: '', - mechanicId: '' + date: '', + mechanicId: '', + checklistId: '' + }) + const [appliedFilters, setAppliedFilters] = useState({ + date: '', + mechanicId: '', + checklistId: '' }) - const loadDashboard = async () => { + const loadMechanics = async () => { + try { + const token = localStorage.getItem('token') + const API_URL = import.meta.env.VITE_API_URL || '' + const response = await fetch(`${API_URL}/api/users`, { + headers: { 'Authorization': `Bearer ${token}` } + }) + if (response.ok) { + const data = await response.json() + setMechanics(data.filter(u => u.role === 'mechanic' || u.role === 'mecanico')) + } + } catch (error) { + console.error('Error loading mechanics:', error) + } + } + + const loadChecklists = async () => { + try { + const token = localStorage.getItem('token') + const API_URL = import.meta.env.VITE_API_URL || '' + const response = await fetch(`${API_URL}/api/checklists`, { + headers: { 'Authorization': `Bearer ${token}` } + }) + if (response.ok) { + const data = await response.json() + setChecklists(data) + } + } catch (error) { + console.error('Error loading checklists:', error) + } + } + + const loadDashboard = async (filtersToApply) => { try { const token = localStorage.getItem('token') const API_URL = import.meta.env.VITE_API_URL || '' let url = `${API_URL}/api/reports/dashboard?` - if (filters.startDate) url += `start_date=${filters.startDate}&` - if (filters.endDate) url += `end_date=${filters.endDate}&` - if (filters.mechanicId) url += `mechanic_id=${filters.mechanicId}&` + if (filtersToApply.date) url += `start_date=${filtersToApply.date}&` + if (filtersToApply.mechanicId) url += `mechanic_id=${filtersToApply.mechanicId}&` - console.log('Loading dashboard from:', url) + console.log('Dashboard URL:', url) + console.log('Filters applied:', filtersToApply) const response = await fetch(url, { headers: { 'Authorization': `Bearer ${token}` } }) - console.log('Dashboard response status:', response.status) - if (response.ok) { const data = await response.json() - console.log('Dashboard data:', data) + console.log('Dashboard data received:', data) setDashboardData(data) } else { - const errorText = await response.text() - console.error('Dashboard error response:', response.status, errorText) + console.error('Dashboard request failed:', response.status) } } catch (error) { console.error('Error loading dashboard:', error) } } - const loadInspections = async () => { + const loadInspections = async (filtersToApply) => { try { const token = localStorage.getItem('token') const API_URL = import.meta.env.VITE_API_URL || '' let url = `${API_URL}/api/reports/inspections?` - if (filters.startDate) url += `start_date=${filters.startDate}&` - if (filters.endDate) url += `end_date=${filters.endDate}&` - if (filters.mechanicId) url += `mechanic_id=${filters.mechanicId}&` + if (filtersToApply.date) url += `start_date=${filtersToApply.date}&` + if (filtersToApply.mechanicId) url += `mechanic_id=${filtersToApply.mechanicId}&` - console.log('Loading inspections from:', url) + console.log('Inspections URL:', url) const response = await fetch(url, { headers: { 'Authorization': `Bearer ${token}` } }) - console.log('Inspections response status:', response.status) - if (response.ok) { - const data = await response.json() - console.log('Inspections data:', data) + let data = await response.json() + console.log('Inspections data received:', data.length, 'items') + console.log('First inspection data:', data[0]) + // Filtrar por checklist en el frontend + if (filtersToApply.checklistId) { + const filtered = data.filter(i => { + console.log('Comparing:', i.checklist_id, '===', parseInt(filtersToApply.checklistId)) + return i.checklist_id === parseInt(filtersToApply.checklistId) + }) + data = filtered + console.log('After checklist filter:', data.length, 'items') + } setInspections(data) } else { - const errorText = await response.text() - console.error('Inspections error response:', response.status, errorText) + console.error('Inspections request failed:', response.status) } } catch (error) { console.error('Error loading inspections:', error) } } + const applyFilters = async () => { + setLoading(true) + setAppliedFilters(filters) + await Promise.all([loadDashboard(filters), loadInspections(filters)]) + setLoading(false) + } + useEffect(() => { - const loadData = async () => { + const loadInitialData = async () => { setLoading(true) - await Promise.all([loadDashboard(), loadInspections()]) + // Cargar mecánicos y checklists primero + await Promise.all([loadMechanics(), loadChecklists()]) + // Luego cargar datos sin filtros (filtros vacíos = todos los datos) + const emptyFilters = { date: '', mechanicId: '', checklistId: '' } + await Promise.all([loadDashboard(emptyFilters), loadInspections(emptyFilters)]) setLoading(false) } - loadData() - }, [filters]) + loadInitialData() + }, []) if (loading) { return