fixed product section of the reports page

This commit is contained in:
2ManyProjects 2025-04-28 21:09:36 -05:00
parent 3d4d876283
commit b88fb93637

View file

@ -235,6 +235,11 @@ const ReportsPage = () => {
.slice(0, 8); // Top 8 locations
}, [filteredOrders]);
// Debug - log filtered orders
useEffect(() => {
console.log("Filtered orders:", filteredOrders);
}, [filteredOrders]);
// Prepare time series data for revenue chart
const revenueTimeSeriesData = useMemo(() => {
if (!orders) return [];
@ -293,9 +298,41 @@ const ReportsPage = () => {
return Array.from(dataMap.values());
}, [filteredOrders, timelineType, fromDate, toDate]);
// Fetch order details for product analysis
const { data: orderDetails, isLoading: orderDetailsLoading } = useQuery({
queryKey: ['admin-order-details', filteredOrders.map(order => order.id)],
queryFn: async () => {
// Only fetch if we have filtered orders
if (!filteredOrders.length) return [];
// Fetch details for each order in parallel
const detailPromises = filteredOrders.map(order =>
apiClient.get(`/admin/orders/${order.id}`)
.then(response => response.data)
.catch(error => {
console.error(`Error fetching details for order ${order.id}:`, error);
return null;
})
);
const results = await Promise.all(detailPromises);
return results.filter(Boolean); // Remove any failed requests
},
enabled: filteredOrders.length > 0
});
// Debug - log order details
useEffect(() => {
if (orderDetails) {
console.log("Order details:", orderDetails);
}
}, [orderDetails]);
// Prepare product sales data
const productSalesData = useMemo(() => {
if (!orders || !products) return [];
if (!products || !orderDetails) return [];
const productSales = {};
@ -310,13 +347,23 @@ const ReportsPage = () => {
};
});
// Aggregate sales data from orders
filteredOrders.forEach(order => {
// Aggregate sales data from order details
orderDetails.forEach(order => {
if (order.items && Array.isArray(order.items)) {
order.items.forEach(item => {
if (productSales[item.product_id]) {
productSales[item.product_id].quantity += item.quantity;
productSales[item.product_id].revenue += parseFloat(item.price_at_purchase) * item.quantity;
productSales[item.product_id].quantity += parseInt(item.quantity) || 0;
productSales[item.product_id].revenue += parseFloat(item.price_at_purchase || 0) * (parseInt(item.quantity) || 0);
} else {
// Handle case where product might exist in orders but not in current products list
console.log(`Product ${item.product_id} found in orders but not in products list`);
productSales[item.product_id] = {
id: item.product_id,
name: item.product_name || `Product ${item.product_id.substring(0, 8)}...`,
category: item.product_category || 'Unknown',
quantity: parseInt(item.quantity) || 0,
revenue: parseFloat(item.price_at_purchase || 0) * (parseInt(item.quantity) || 0)
};
}
});
}
@ -326,7 +373,7 @@ const ReportsPage = () => {
return Object.values(productSales)
.filter(product => product.quantity > 0)
.sort((a, b) => b.quantity - a.quantity);
}, [filteredOrders, products]);
}, [products, orderDetails]);
// Top selling products for the chart
const topSellingProducts = useMemo(() => {
@ -358,7 +405,7 @@ const ReportsPage = () => {
}, [productSalesData]);
// Loading state
const isLoading = ordersLoading || productsLoading;
const isLoading = ordersLoading || productsLoading || orderDetailsLoading;
// Handle export reports
const handleExportCSV = () => {
@ -629,24 +676,32 @@ const ReportsPage = () => {
<Typography variant="h6" gutterBottom>
Top Selling Products
</Typography>
<Box sx={{ height: 400 }}>
<ResponsiveContainer width="100%" height="100%">
<BarChart
data={topSellingProducts}
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" />
<YAxis />
<Tooltip formatter={(value, name) => {
return name === 'revenue' ? `$${value.toFixed(2)}` : value;
}} />
<Legend />
<Bar dataKey="quantity" name="Units Sold" fill="#8884d8" />
<Bar dataKey="revenue" name="Revenue ($)" fill="#82ca9d" />
</BarChart>
</ResponsiveContainer>
</Box>
{topSellingProducts.length > 0 ? (
<Box sx={{ height: 400 }}>
<ResponsiveContainer width="100%" height="100%">
<BarChart
data={topSellingProducts}
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}
>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="name" />
<YAxis />
<Tooltip formatter={(value, name) => {
return name === 'revenue' ? `${value.toFixed(2)}` : value;
}} />
<Legend />
<Bar dataKey="quantity" name="Units Sold" fill="#8884d8" />
<Bar dataKey="revenue" name="Revenue ($)" fill="#82ca9d" />
</BarChart>
</ResponsiveContainer>
</Box>
) : (
<Box sx={{ p: 4, textAlign: 'center', bgcolor: 'background.paper', borderRadius: 1 }}>
<Typography variant="body1" color="text.secondary">
No product sales data available for the selected time period.
</Typography>
</Box>
)}
</Grid>
<Grid item xs={12}>
@ -654,28 +709,45 @@ const ReportsPage = () => {
Product Sales Details
</Typography>
<Box sx={{ overflowX: 'auto' }}>
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
<thead>
<tr>
<th style={{ textAlign: 'left', padding: '8px', borderBottom: '1px solid #ddd' }}>Product</th>
<th style={{ textAlign: 'left', padding: '8px', borderBottom: '1px solid #ddd' }}>Category</th>
<th style={{ textAlign: 'right', padding: '8px', borderBottom: '1px solid #ddd' }}>Units Sold</th>
<th style={{ textAlign: 'right', padding: '8px', borderBottom: '1px solid #ddd' }}>Revenue</th>
</tr>
</thead>
<tbody>
{productSalesData.map((product) => (
<tr key={product.id}>
<td style={{ padding: '8px', borderBottom: '1px solid #f0f0f0' }}>{product.name}</td>
<td style={{ padding: '8px', borderBottom: '1px solid #f0f0f0' }}>{product.category}</td>
<td style={{ textAlign: 'right', padding: '8px', borderBottom: '1px solid #f0f0f0' }}>{product.quantity}</td>
<td style={{ textAlign: 'right', padding: '8px', borderBottom: '1px solid #f0f0f0' }}>${product.revenue.toFixed(2)}</td>
{productSalesData.length > 0 ? (
<table style={{ width: '100%', borderCollapse: 'collapse' }}>
<thead>
<tr>
<th style={{ textAlign: 'left', padding: '8px', borderBottom: '1px solid #ddd' }}>Product</th>
<th style={{ textAlign: 'left', padding: '8px', borderBottom: '1px solid #ddd' }}>Category</th>
<th style={{ textAlign: 'right', padding: '8px', borderBottom: '1px solid #ddd' }}>Units Sold</th>
<th style={{ textAlign: 'right', padding: '8px', borderBottom: '1px solid #ddd' }}>Revenue</th>
</tr>
))}
</tbody>
</table>
</thead>
<tbody>
{productSalesData.map((product) => (
<tr key={product.id}>
<td style={{ padding: '8px', borderBottom: '1px solid #f0f0f0' }}>{product.name}</td>
<td style={{ padding: '8px', borderBottom: '1px solid #f0f0f0' }}>{product.category}</td>
<td style={{ textAlign: 'right', padding: '8px', borderBottom: '1px solid #f0f0f0' }}>{product.quantity}</td>
<td style={{ textAlign: 'right', padding: '8px', borderBottom: '1px solid #f0f0f0' }}>${product.revenue.toFixed(2)}</td>
</tr>
))}
</tbody>
</table>
) : (
<Box sx={{ p: 4, textAlign: 'center', bgcolor: 'background.paper', borderRadius: 1 }}>
<Typography variant="body1" color="text.secondary">
No product sales data available for the selected time period.
</Typography>
</Box>
)}
</Box>
</Grid>
{orderDetailsLoading && (
<Grid item xs={12} sx={{ display: 'flex', justifyContent: 'center', py: 2 }}>
<CircularProgress size={28} />
<Typography variant="body2" color="text.secondary" sx={{ ml: 2 }}>
Loading product data...
</Typography>
</Grid>
)}
</Grid>
)}