Modulo de Reportes v1 filtro actualizado basico
This commit is contained in:
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
const applyFilters = async () => {
|
||||||
const loadData = async () => {
|
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
await Promise.all([loadDashboard(), loadInspections()])
|
setAppliedFilters(filters)
|
||||||
|
await Promise.all([loadDashboard(filters), loadInspections(filters)])
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
}
|
}
|
||||||
loadData()
|
|
||||||
}, [filters])
|
useEffect(() => {
|
||||||
|
const loadInitialData = async () => {
|
||||||
|
setLoading(true)
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
loadInitialData()
|
||||||
|
}, [])
|
||||||
|
|
||||||
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>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user