Overview
The Reports & Analytics module provides real-time business intelligence for your vehicle service operations. Track key performance indicators, analyze top-selling products, and monitor recent sales activity.
Reporting Features
Real-time KPI dashboard
Top products by revenue analysis
Recent sales tracking
Customer analytics
Automatic calculation and aggregation
Filterable and exportable data
The reporting system calculates essential business metrics automatically:
const kpis = useMemo (() => {
const ventasActivas = db . sales . filter ( s => ! s . isDeleted );
const ingresosTotales = ventasActivas . reduce (( sum , sale ) => sum + sale . total , 0 );
const ticketPromedio = ventasActivas . length > 0 ? ingresosTotales / ventasActivas . length : 0 ;
const clientesUnicos = new Set ( ventasActivas . map ( s => s . customerId )). size ;
return {
totalVentas: ventasActivas . length ,
ingresosTotales ,
ticketPromedio ,
clientesUnicos
};
}, []);
Available KPIs
Count of all active (non-deleted) sales transactions in the system. totalVentas : ventasActivas . length
Sum of all sale totals, representing total income from all transactions. ingresosTotales = ventasActivas . reduce (( sum , sale ) => sum + sale . total , 0 )
Average transaction value, calculated as total revenue divided by number of sales. ticketPromedio = ingresosTotales / ventasActivas . length
Count of distinct customers who have made purchases. clientesUnicos = new Set ( ventasActivas . map ( s => s . customerId )). size
Top Products Analysis
The system analyzes product performance across all sales:
const topProductos = useMemo (() => {
const ventasActivasIds = db . sales . filter ( s => ! s . isDeleted ). map ( s => s . id );
const detallesValidos = db . saleDetails . filter ( sd => ventasActivasIds . includes ( sd . saleId ));
const conteoProductos : Record < string , { cantidad : number , ingresos : number }> = {};
// Aggregate quantities and revenue per product
detallesValidos . forEach ( detalle => {
if ( ! conteoProductos [ detalle . productId ]) {
conteoProductos [ detalle . productId ] = { cantidad: 0 , ingresos: 0 };
}
conteoProductos [ detalle . productId ]. cantidad += detalle . cantidad ;
conteoProductos [ detalle . productId ]. ingresos += detalle . subtotal ;
});
// Sort by revenue and get top 5
return Object . entries ( conteoProductos )
. map (([ productId , data ]) => {
const prod = db . products . find ( p => p . id === productId );
return {
id: productId ,
nombre: prod ?. nombre || 'Producto Eliminado' ,
... data
};
})
. sort (( a , b ) => b . ingresos - a . ingresos )
. slice ( 0 , 5 );
}, []);
Product Metrics
For each product in the top products report:
Product Analytics
Product Name - From product catalog
Total Quantity Sold - Sum of quantities across all sales
Total Revenue - Sum of subtotals (before IGV) generated
Example Top Products Data
[
{
id: "prod-001" ,
nombre: "Aceite Castrol Magnatec 10W-40 (Galón)" ,
cantidad: 15 ,
ingresos: 1800.00
},
{
id: "prod-002" ,
nombre: "Filtro de Aceite Bosh TOY-01" ,
cantidad: 18 ,
ingresos: 450.00
},
{
id: "prod-005" ,
nombre: "Pastillas de Freno Delanteras Toyota" ,
cantidad: 3 ,
ingresos: 540.00
}
]
Recent Sales Tracking
The reports module displays the most recent sales transactions:
const ventasRecientes = useMemo (() => {
return db . sales
. filter ( s => ! s . isDeleted )
. sort (( a , b ) => new Date ( b . fechaEmision ). getTime () - new Date ( a . fechaEmision ). getTime ())
. slice ( 0 , 5 )
. map ( sale => {
const cliente = db . customers . find ( c => c . id === sale . customerId );
return {
... sale ,
clienteNombre: cliente ?. nombreRazonSocial || 'Cliente Desconocido'
};
});
}, []);
Recent Sales Data Structure
Each recent sale includes:
{
id : "sale-001" ,
branchId : "branch-001" ,
userId : "user-001" ,
customerId : "cust-001" ,
clienteNombre : "TRANSPORTES RAPIDOS S.A.C." ,
vehicleId : "veh-001" ,
choferId : "cust-002" ,
tipoComprobante : "FACTURA" ,
serie : "F001" ,
correlativo : "000150" ,
fechaEmision : "2023-10-26T14:30:00Z" ,
moneda : "PEN" ,
subtotal : 122.88 ,
igv : 22.12 ,
total : 145.00 ,
kilometrajeIngreso : 45000 ,
proximoCambioKm : 50000 ,
estadoSunat : "ACEPTADO"
}
Report Categories
MotorDesk supports multiple report types organized by category:
// Report definitions (referenced in src/pages/Reports.tsx:4)
const REPORTES_DISPONIBLES = [
{
id: "ventas" ,
titulo: "Ventas" ,
descripcion: "Reporte de ventas por período" ,
icono: "receipt"
},
{
id: "productos" ,
titulo: "Productos" ,
descripcion: "Análisis de inventario y productos" ,
icono: "package"
},
{
id: "vehiculos" ,
titulo: "Vehículos" ,
descripcion: "Estadísticas de flota" ,
icono: "truck"
},
{
id: "clientes" ,
titulo: "Clientes" ,
descripcion: "Análisis de clientes" ,
icono: "users"
}
];
Icon Mapping
Reports use Lucide icons for visual identification:
const iconMap : Record < string , React . ElementType > = {
receipt: Receipt ,
package: Package ,
truck: Truck ,
users: Users
};
Report Grid Interface
Reports are displayed in a responsive grid layout:
// Report cards grid (src/pages/Reports.tsx:42)
< div className = "gap-5 grid sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4" >
{ items . map (( item , idx ) => {
const handleClick = () => navigate ( `/reports/ ${ item . id } ` );
const IconComponent = iconMap [ item . icono ] || FileText ;
return (
< button
key = { idx }
onClick = { handleClick }
className = "group flex flex-col justify-center items-center bg-white shadow-sm hover:shadow-lg border rounded-xl w-full h-40"
>
< div className = "flex justify-center items-center bg-orange-50 mb-3 rounded-full ring-1 ring-orange-100 w-14 h-14" >
< IconComponent size = { 28 } className = "text-orange-500" strokeWidth = { 1.5 } />
</ div >
< div className = "px-4 text-center" >
< div className = "font-semibold text-gray-800 text-base" >
{ item . titulo }
</ div >
< div className = "mt-0.5 text-gray-500 text-sm" >
{ item . descripcion }
</ div >
</ div >
</ button >
);
}) }
</ div >
Report Filtering
Reports can be filtered using a search query:
const [ query , setQuery ] = useState ( "" );
const items = useMemo (() => {
const q = query . trim (). toLowerCase ();
if ( ! q ) return REPORTES_DISPONIBLES ;
return REPORTES_DISPONIBLES . filter (
( it ) =>
it . titulo . toLowerCase (). includes ( q ) ||
it . descripcion . toLowerCase (). includes ( q )
);
}, [ query ]);
Data Aggregation Patterns
Sales Aggregation
Common aggregation patterns used across reports:
Filter Active Records
const ventasActivas = db . sales . filter ( s => ! s . isDeleted );
Calculate Totals
const total = ventasActivas . reduce (( sum , sale ) => sum + sale . total , 0 );
Count Unique Values
const unicos = new Set ( ventasActivas . map ( s => s . customerId )). size ;
Sort by Date
const sorted = sales . sort (( a , b ) =>
new Date ( b . fechaEmision ). getTime () - new Date ( a . fechaEmision ). getTime ()
);
Get Valid Sale IDs
const ventasActivasIds = db . sales . filter ( s => ! s . isDeleted ). map ( s => s . id );
Filter Sale Details
const detallesValidos = db . saleDetails . filter ( sd =>
ventasActivasIds . includes ( sd . saleId )
);
Aggregate by Product
const productStats : Record < string , { cantidad : number , ingresos : number }> = {};
detallesValidos . forEach ( detalle => {
if ( ! productStats [ detalle . productId ]) {
productStats [ detalle . productId ] = { cantidad: 0 , ingresos: 0 };
}
productStats [ detalle . productId ]. cantidad += detalle . cantidad ;
productStats [ detalle . productId ]. ingresos += detalle . subtotal ;
});
Sort and Limit
const topProducts = Object . entries ( productStats )
. sort (([, a ], [, b ]) => b . ingresos - a . ingresos )
. slice ( 0 , 5 );
Report Types
Sales Reports
Total sales by period
Sales by document type (FACTURA/BOLETA)
Sales by branch
Sales by user/seller
SUNAT status tracking (ACEPTADO/PENDIENTE/RECHAZADO)
Product Reports
Top products by revenue
Top products by quantity sold
Product category analysis
Inventory movement
Product profitability
Vehicle Reports
Fleet service history
Maintenance frequency analysis
Service revenue by vehicle
Upcoming maintenance alerts
Customer vehicle relationships
Customer Reports
Customer purchase history
Top customers by revenue
Customer retention analysis
New vs. returning customers
Customer contact information
Empty State Handling
When no reports match the search criteria:
// Empty state (src/pages/Reports.tsx:70)
{ items . length === 0 && (
< div className = "flex flex-col justify-center items-center mt-12 text-gray-500" >
< Inbox size = { 48 } className = "mb-3 text-gray-300" />
< p > No se encontraron reportes para " { query } ". </ p >
</ div >
)}
Report Export Capabilities
MotorDesk reports are designed to be exportable to common formats like PDF, Excel, and CSV for further analysis or record-keeping.
Best Practices
Effective Reporting
Review KPIs daily to track business performance
Monitor top products to optimize inventory
Track recent sales for customer follow-up
Export reports regularly for accounting purposes
Use filters to analyze specific time periods or categories
Compare metrics month-over-month for trend analysis
Share reports with stakeholders for business insights
The reporting module uses React’s useMemo hook to optimize calculations:
// Memoized calculations prevent unnecessary recalculation
const kpis = useMemo (() => {
// Expensive aggregation calculations
return { /* ... */ };
}, []); // Only recalculates when dependencies change
All report calculations are performed client-side using memoized computations, ensuring fast performance even with large datasets.