Optimize products list analytics loading
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 54s

This commit is contained in:
Cauê Faleiros
2026-06-22 15:30:54 -03:00
parent bd98348989
commit 3cd4bfc426
7 changed files with 204 additions and 28 deletions

View File

@@ -1,30 +1,63 @@
import { useMemo, useState } from 'react';
import { useEffect, useMemo, useState } from 'react';
import { Link, useOutletContext } from 'react-router-dom';
import { Search, Package, TrendingUp, ChevronLeft, ChevronRight, Download } from 'lucide-react';
import { Search, Package, TrendingUp, ChevronLeft, ChevronRight, Download, Loader2 } from 'lucide-react';
import DateRangePicker from '../components/DateRangePicker';
import type { OrderData, DateRange, StockData } from '../types';
import { exportToCSV } from '../dataService';
import { buildProductsSummary } from '../analytics/products';
import type { DateRange, ProductAnalyticsItem } from '../types';
import { exportToCSV, fetchProductAnalytics } from '../dataService';
import type { ProductSummary } from '../analytics/products';
const Products = () => {
const { dateRange, setDateRange, ordersData, stockData } = useOutletContext<{
const { dateRange, setDateRange } = useOutletContext<{
dateRange: DateRange,
setDateRange: (range: DateRange) => void,
ordersData: OrderData[],
stockData: StockData[],
refreshInterval: number,
setRefreshInterval: (interval: number) => void,
loadData: (showLoading?: boolean) => void
setDateRange: (range: DateRange) => void
}>();
const [searchTerm, setSearchTerm] = useState('');
const [productAnalytics, setProductAnalytics] = useState<ProductAnalyticsItem[]>([]);
const [isLoading, setIsLoading] = useState(true);
// Pagination state
const [currentPage, setCurrentPage] = useState(1);
const [itemsPerPage, setItemsPerPage] = useState(10);
useEffect(() => {
let isMounted = true;
const loadProducts = async () => {
setIsLoading(true);
const products = await fetchProductAnalytics(dateRange);
if (isMounted) {
setProductAnalytics(products);
setIsLoading(false);
}
};
void loadProducts();
return () => {
isMounted = false;
};
}, [dateRange]);
const productsData = useMemo(() => {
return buildProductsSummary(ordersData, stockData, dateRange, searchTerm);
}, [dateRange, searchTerm, ordersData, stockData]);
const normalizedSearch = searchTerm.trim().toLowerCase();
const products: ProductSummary[] = productAnalytics.map(product => ({
id: product.id,
name: product.name,
totalSold: product.quantitySold,
revenue: product.revenue,
lastPrice: product.lastPrice,
stock: product.stock
}));
const filteredProducts = normalizedSearch
? products.filter(product =>
product.name.toLowerCase().includes(normalizedSearch) ||
product.id.toLowerCase().includes(normalizedSearch)
)
: products;
return filteredProducts.sort((a, b) => b.totalSold - a.totalSold);
}, [productAnalytics, searchTerm]);
// Pagination logic
const totalPages = Math.ceil(productsData.length / itemsPerPage);
@@ -86,6 +119,13 @@ const Products = () => {
</div>
</div>
{isLoading && (
<div className="inline-flex items-center gap-2 rounded-xl border border-dark-border bg-dark-card px-3 py-2 text-sm font-semibold text-dark-muted">
<Loader2 className="h-4 w-4 animate-spin text-brand-primary" />
Atualizando produtos
</div>
)}
<div className="bg-white dark:bg-dark-card border border-zinc-200 dark:border-dark-border rounded-2xl overflow-hidden shadow-sm">
<div className="overflow-x-auto">
<table className="w-full text-left text-sm">