perf: load dashboard metrics from analytics API
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 50s

This commit is contained in:
Cauê Faleiros
2026-05-28 11:59:56 -03:00
parent f4cf4366ee
commit b886b357d7
7 changed files with 126 additions and 22 deletions

View File

@@ -7,7 +7,7 @@ import { buildClientDetailsMetrics } from '../analytics/clients';
const ClientDetails = () => {
const { name } = useParams<{ name: string }>();
const decodedName = name ? decodeURIComponent(name) : '';
const { ordersData } = useOutletContext<{ ordersData: OrderData[] }>();
const { ordersData, isDataLoading } = useOutletContext<{ ordersData: OrderData[], isDataLoading: boolean }>();
const { groupedOrders, totalSpent, totalItems, clientPhone } = useMemo(() => {
return buildClientDetailsMetrics(ordersData, decodedName);
@@ -17,7 +17,7 @@ const ClientDetails = () => {
return new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(value);
};
if (!groupedOrders.length && ordersData.length === 0) {
if (!groupedOrders.length && isDataLoading) {
return (
<div className="text-center py-12">
<p className="text-zinc-500 dark:text-dark-muted font-medium">Carregando cliente...</p>

View File

@@ -1,10 +1,11 @@
import { useMemo } from 'react';
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useOutletContext, useNavigate } from 'react-router-dom';
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, PieChart, Pie, Cell } from 'recharts';
import { DollarSign, ShoppingCart, TrendingUp } from 'lucide-react';
import { DollarSign, Loader2, ShoppingCart, TrendingUp } from 'lucide-react';
import DateRangePicker from '../components/DateRangePicker';
import type { OrderData, DateRange } from '../types';
import { buildDashboardMetrics } from '../analytics/dashboard';
import type { DashboardAnalytics, OrderData, DateRange } from '../types';
import { applyDashboardColors, buildDashboardMetrics } from '../analytics/dashboard';
import { fetchDashboardAnalytics } from '../dataService';
const formatCurrency = (value: number) => {
return new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(value);
@@ -44,18 +45,47 @@ const CustomTooltip = ({ active, payload, label, isCurrency }: CustomTooltipProp
const Dashboard = () => {
const navigate = useNavigate();
const { dateRange, setDateRange, ordersData, refreshInterval, setRefreshInterval, loadData } = useOutletContext<{
const { dateRange, setDateRange, ordersData, refreshInterval, setRefreshInterval } = useOutletContext<{
dateRange: DateRange,
setDateRange: (range: DateRange) => void,
ordersData: OrderData[],
refreshInterval: number,
setRefreshInterval: (interval: number) => void,
loadData: (showLoading?: boolean) => void
}>();
const [serverMetrics, setServerMetrics] = useState<DashboardAnalytics | null>(null);
const [isMetricsLoading, setIsMetricsLoading] = useState(true);
const loadDashboardMetrics = useCallback(async (range: DateRange) => {
setIsMetricsLoading(true);
const metrics = await fetchDashboardAnalytics(range);
setServerMetrics(metrics);
setIsMetricsLoading(false);
}, []);
useEffect(() => {
// Dashboard metrics are synchronized with the selected server-side date range.
// eslint-disable-next-line react-hooks/set-state-in-effect
void loadDashboardMetrics(dateRange);
}, [dateRange, loadDashboardMetrics]);
useEffect(() => {
if (refreshInterval === 0) return;
const intervalId = setInterval(() => {
void loadDashboardMetrics(dateRange);
}, refreshInterval);
return () => clearInterval(intervalId);
}, [dateRange, loadDashboardMetrics, refreshInterval]);
const { totalRevenue, totalOrders, averageOrderValue, salesByProduct, revenueByProduct } = useMemo(() => {
if (serverMetrics) return applyDashboardColors(serverMetrics);
return buildDashboardMetrics(ordersData, dateRange);
}, [dateRange, ordersData]);
}, [dateRange, ordersData, serverMetrics]);
const handleManualRefresh = () => {
void loadDashboardMetrics(dateRange);
};
return (
<div className="space-y-6">
@@ -69,10 +99,17 @@ const Dashboard = () => {
onChange={setDateRange}
refreshInterval={refreshInterval}
setRefreshInterval={setRefreshInterval}
onManualRefresh={() => loadData(true)}
onManualRefresh={handleManualRefresh}
/>
</div>
{isMetricsLoading && (
<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 indicadores
</div>
)}
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
<div className="bg-dark-card p-6 rounded-2xl border border-dark-border shadow-sm">
<div className="flex justify-between items-start">

View File

@@ -26,10 +26,11 @@ const CustomTooltip = ({ active, payload, label }: CustomTooltipProps) => {
const ProductDetails = () => {
const { id } = useParams<{ id: string }>();
const { dateRange, setDateRange, ordersData } = useOutletContext<{
const { dateRange, setDateRange, ordersData, isDataLoading } = useOutletContext<{
dateRange: DateRange,
setDateRange: (range: DateRange) => void,
ordersData: OrderData[],
isDataLoading: boolean,
refreshInterval: number,
setRefreshInterval: (interval: number) => void,
loadData: (showLoading?: boolean) => void
@@ -43,7 +44,7 @@ const ProductDetails = () => {
return new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(value);
};
if (!productInfo && ordersData.length === 0) {
if (!productInfo && isDataLoading) {
return (
<div className="text-center py-12">
<p className="text-zinc-500 dark:text-dark-muted font-medium">Carregando produto...</p>