Add dashboard and RFV skeleton loaders
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 48s
All checks were successful
Build and Deploy / build-and-deploy (push) Successful in 48s
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
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, LabelList } from 'recharts';
|
||||
import { DollarSign, Loader2, ShoppingCart, TrendingUp } from 'lucide-react';
|
||||
import { DollarSign, ShoppingCart, TrendingUp } from 'lucide-react';
|
||||
import DateRangePicker from '../components/DateRangePicker';
|
||||
import type { DashboardAnalytics, OrderData, DateRange } from '../types';
|
||||
import { applyDashboardColors, buildDashboardMetrics } from '../analytics/dashboard';
|
||||
@@ -52,6 +52,69 @@ const kpiIconStyle = (color: string) => ({
|
||||
color
|
||||
});
|
||||
|
||||
const DashboardSkeleton = () => (
|
||||
<div className="space-y-6" aria-label="Carregando dashboard">
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{[0, 1, 2].map(item => (
|
||||
<div key={`dashboard-kpi-skeleton-${item}`} className="bg-dark-card p-6 rounded-2xl border border-dark-border shadow-sm">
|
||||
<div className="flex justify-between gap-6">
|
||||
<div className="w-full">
|
||||
<div className="skeleton h-4 w-32" />
|
||||
<div className="skeleton mt-3 h-8 w-44" />
|
||||
</div>
|
||||
<div className="skeleton h-12 w-12 shrink-0 rounded-xl" />
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
{[0, 1].map(item => (
|
||||
<div key={`dashboard-seller-chart-skeleton-${item}`} className="bg-dark-card p-6 rounded-2xl border border-dark-border shadow-sm">
|
||||
<div className="skeleton h-5 w-48" />
|
||||
<div className="mt-8 space-y-4">
|
||||
{[0, 1, 2, 3, 4, 5].map(row => (
|
||||
<div key={`dashboard-bar-skeleton-${item}-${row}`} className="flex items-center gap-4">
|
||||
<div className="skeleton h-3 w-28 shrink-0" />
|
||||
<div className="skeleton h-7" style={{ width: `${88 - row * 10}%` }} />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="bg-dark-card p-6 rounded-2xl border border-dark-border shadow-sm">
|
||||
<div className="skeleton h-5 w-44" />
|
||||
<div className="mt-5 space-y-4">
|
||||
{[0, 1, 2, 3, 4].map(row => (
|
||||
<div key={`dashboard-table-skeleton-${row}`} className="grid grid-cols-[1fr_120px_80px_120px] gap-4">
|
||||
<div className="skeleton h-4" />
|
||||
<div className="skeleton h-4" />
|
||||
<div className="skeleton h-4" />
|
||||
<div className="skeleton h-4" />
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
{[0, 1].map(item => (
|
||||
<div key={`dashboard-product-chart-skeleton-${item}`} className="bg-dark-card p-6 rounded-2xl border border-dark-border shadow-sm">
|
||||
<div className="skeleton h-5 w-44" />
|
||||
<div className="mt-8 h-80">
|
||||
<div className="flex h-full items-end justify-center gap-3">
|
||||
{[0, 1, 2, 3, 4, 5, 6, 7].map(bar => (
|
||||
<div key={`dashboard-column-skeleton-${item}-${bar}`} className="skeleton w-10" style={{ height: `${35 + ((bar * 17) % 55)}%` }} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
type ChartTooltipPayload = {
|
||||
value: number;
|
||||
name?: string;
|
||||
@@ -194,6 +257,7 @@ const Dashboard = () => {
|
||||
const handleManualRefresh = () => {
|
||||
void loadDashboardMetrics(dateRange, { force: true });
|
||||
};
|
||||
const shouldShowSkeleton = isMetricsLoading && !serverMetrics;
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
@@ -211,12 +275,10 @@ const Dashboard = () => {
|
||||
/>
|
||||
</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>
|
||||
)}
|
||||
{shouldShowSkeleton ? (
|
||||
<DashboardSkeleton />
|
||||
) : (
|
||||
<>
|
||||
|
||||
<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">
|
||||
@@ -444,6 +506,8 @@ const Dashboard = () => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user