Modulo de Reportes v1 filtro actualizado basico

This commit is contained in:
2025-11-20 00:21:12 -03:00
parent cfe49ee0c8
commit f029fab1b0
2 changed files with 172 additions and 59 deletions

View File

@@ -1292,10 +1292,18 @@ def get_dashboard_data(
# Aplicar filtros de fecha # Aplicar filtros de fecha
if start_date: 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) query = query.filter(models.Inspection.started_at >= start)
if end_date: 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) query = query.filter(models.Inspection.started_at <= end)
# Filtro por mecánico # Filtro por mecánico
@@ -1327,10 +1335,16 @@ def get_dashboard_data(
.filter(models.Inspection.is_active == True) .filter(models.Inspection.is_active == True)
if start_date: 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) flagged_items = flagged_items.filter(models.Inspection.started_at >= start)
if end_date: 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) flagged_items = flagged_items.filter(models.Inspection.started_at <= end)
if mechanic_id: if mechanic_id:
flagged_items = flagged_items.filter(models.Inspection.mechanic_id == 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) .filter(models.Inspection.is_active == True)
if start_date: 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) mechanic_stats = mechanic_stats.filter(models.Inspection.started_at >= start)
if end_date: 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.filter(models.Inspection.started_at <= end)
mechanic_stats = mechanic_stats.group_by(models.User.id, models.User.full_name)\ 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) .filter(models.Checklist.is_active == True)
if start_date: 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) checklist_stats_query = checklist_stats_query.filter(models.Inspection.started_at >= start)
if end_date: 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) checklist_stats_query = checklist_stats_query.filter(models.Inspection.started_at <= end)
if mechanic_id: if mechanic_id:
checklist_stats_query = checklist_stats_query.filter(models.Inspection.mechanic_id == 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'])) .filter(models.Answer.answer_value.in_(['pass', 'fail', 'good', 'bad', 'regular']))
if start_date: 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) pass_fail_data = pass_fail_data.filter(models.Inspection.started_at >= start)
if end_date: 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) pass_fail_data = pass_fail_data.filter(models.Inspection.started_at <= end)
if mechanic_id: if mechanic_id:
pass_fail_data = pass_fail_data.filter(models.Inspection.mechanic_id == 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( query = db.query(
models.Inspection.id, models.Inspection.id,
models.Inspection.vehicle_plate, models.Inspection.vehicle_plate,
models.Inspection.checklist_id,
models.Checklist.name.label('checklist_name'), models.Checklist.name.label('checklist_name'),
models.User.full_name.label('mechanic_name'), models.User.full_name.label('mechanic_name'),
models.Inspection.status, models.Inspection.status,
@@ -1515,10 +1548,16 @@ def get_inspections_report(
# Aplicar filtros # Aplicar filtros
if start_date: 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) query = query.filter(models.Inspection.started_at >= start)
if end_date: 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) query = query.filter(models.Inspection.started_at <= end)
if mechanic_id: if mechanic_id:
query = query.filter(models.Inspection.mechanic_id == mechanic_id) query = query.filter(models.Inspection.mechanic_id == mechanic_id)
@@ -1541,6 +1580,7 @@ def get_inspections_report(
{ {
"id": r.id, "id": r.id,
"vehicle_plate": r.vehicle_plate, "vehicle_plate": r.vehicle_plate,
"checklist_id": r.checklist_id,
"checklist_name": r.checklist_name, "checklist_name": r.checklist_name,
"mechanic_name": r.mechanic_name, "mechanic_name": r.mechanic_name,
"status": r.status, "status": r.status,

View File

@@ -3176,82 +3176,135 @@ function ReportsTab({ user }) {
const [dashboardData, setDashboardData] = useState(null) const [dashboardData, setDashboardData] = useState(null)
const [inspections, setInspections] = useState([]) const [inspections, setInspections] = useState([])
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
const [mechanics, setMechanics] = useState([])
const [checklists, setChecklists] = useState([])
const [filters, setFilters] = useState({ const [filters, setFilters] = useState({
startDate: '', date: '',
endDate: '', mechanicId: '',
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 { try {
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 || ''
let url = `${API_URL}/api/reports/dashboard?` let url = `${API_URL}/api/reports/dashboard?`
if (filters.startDate) url += `start_date=${filters.startDate}&` if (filtersToApply.date) url += `start_date=${filtersToApply.date}&`
if (filters.endDate) url += `end_date=${filters.endDate}&` if (filtersToApply.mechanicId) url += `mechanic_id=${filtersToApply.mechanicId}&`
if (filters.mechanicId) url += `mechanic_id=${filters.mechanicId}&`
console.log('Loading dashboard from:', url) console.log('Dashboard URL:', url)
console.log('Filters applied:', filtersToApply)
const response = await fetch(url, { const response = await fetch(url, {
headers: { 'Authorization': `Bearer ${token}` } headers: { 'Authorization': `Bearer ${token}` }
}) })
console.log('Dashboard response status:', response.status)
if (response.ok) { if (response.ok) {
const data = await response.json() const data = await response.json()
console.log('Dashboard data:', data) console.log('Dashboard data received:', data)
setDashboardData(data) setDashboardData(data)
} else { } else {
const errorText = await response.text() console.error('Dashboard request failed:', response.status)
console.error('Dashboard error response:', response.status, errorText)
} }
} catch (error) { } catch (error) {
console.error('Error loading dashboard:', error) console.error('Error loading dashboard:', error)
} }
} }
const loadInspections = async () => { const loadInspections = async (filtersToApply) => {
try { try {
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 || ''
let url = `${API_URL}/api/reports/inspections?` let url = `${API_URL}/api/reports/inspections?`
if (filters.startDate) url += `start_date=${filters.startDate}&` if (filtersToApply.date) url += `start_date=${filtersToApply.date}&`
if (filters.endDate) url += `end_date=${filters.endDate}&` if (filtersToApply.mechanicId) url += `mechanic_id=${filtersToApply.mechanicId}&`
if (filters.mechanicId) url += `mechanic_id=${filters.mechanicId}&`
console.log('Loading inspections from:', url) console.log('Inspections URL:', url)
const response = await fetch(url, { const response = await fetch(url, {
headers: { 'Authorization': `Bearer ${token}` } headers: { 'Authorization': `Bearer ${token}` }
}) })
console.log('Inspections response status:', response.status)
if (response.ok) { if (response.ok) {
const data = await response.json() let data = await response.json()
console.log('Inspections data:', data) 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) setInspections(data)
} else { } else {
const errorText = await response.text() console.error('Inspections request failed:', response.status)
console.error('Inspections error response:', response.status, errorText)
} }
} catch (error) { } catch (error) {
console.error('Error loading inspections:', error) console.error('Error loading inspections:', error)
} }
} }
const applyFilters = async () => {
setLoading(true)
setAppliedFilters(filters)
await Promise.all([loadDashboard(filters), loadInspections(filters)])
setLoading(false)
}
useEffect(() => { useEffect(() => {
const loadData = async () => { const loadInitialData = async () => {
setLoading(true) 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) setLoading(false)
} }
loadData() loadInitialData()
}, [filters]) }, [])
if (loading) { if (loading) {
return <div className="text-center py-12">Cargando reportes...</div> return <div className="text-center py-12">Cargando reportes...</div>
@@ -3265,26 +3318,16 @@ function ReportsTab({ user }) {
<div className="space-y-6"> <div className="space-y-6">
{/* Filtros */} {/* Filtros */}
<div className="bg-white rounded-lg shadow p-4"> <div className="bg-white rounded-lg shadow p-4">
<div className="grid grid-cols-1 md:grid-cols-3 gap-4"> <h3 className="text-lg font-semibold mb-4">🔍 Filtros</h3>
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
<div> <div>
<label className="block text-sm font-medium text-gray-700 mb-1"> <label className="block text-sm font-medium text-gray-700 mb-1">
Fecha Inicio Fecha
</label> </label>
<input <input
type="date" type="date"
value={filters.startDate} value={filters.date}
onChange={(e) => setFilters({...filters, startDate: e.target.value})} onChange={(e) => setFilters({...filters, date: e.target.value})}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Fecha Fin
</label>
<input
type="date"
value={filters.endDate}
onChange={(e) => setFilters({...filters, endDate: e.target.value})}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500" className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500"
/> />
</div> </div>
@@ -3293,15 +3336,45 @@ function ReportsTab({ user }) {
<label className="block text-sm font-medium text-gray-700 mb-1"> <label className="block text-sm font-medium text-gray-700 mb-1">
Mecánico Mecánico
</label> </label>
<input <select
type="number"
placeholder="ID del mecánico"
value={filters.mechanicId} value={filters.mechanicId}
onChange={(e) => setFilters({...filters, mechanicId: e.target.value})} onChange={(e) => setFilters({...filters, mechanicId: e.target.value})}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500" className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500"
/> >
<option value="">Todos los mecánicos</option>
{mechanics.map(mechanic => (
<option key={mechanic.id} value={mechanic.id}>
{mechanic.full_name}
</option>
))}
</select>
</div> </div>
)} )}
<div>
<label className="block text-sm font-medium text-gray-700 mb-1">
Checklist
</label>
<select
value={filters.checklistId}
onChange={(e) => setFilters({...filters, checklistId: e.target.value})}
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-indigo-500"
>
<option value="">Todos los checklists</option>
{checklists.map(checklist => (
<option key={checklist.id} value={checklist.id}>
{checklist.name}
</option>
))}
</select>
</div>
<div className="flex items-end">
<button
onClick={applyFilters}
className="w-full px-4 py-2 bg-gradient-to-r from-indigo-600 to-purple-600 text-white rounded-lg hover:from-indigo-700 hover:to-purple-700 transition"
>
Aplicar Filtros
</button>
</div>
</div> </div>
</div> </div>