Replace spinner with skeleton loading in emprestimos page

- Remove initial spinner and show skeleton layout immediately
- Add skeleton to stats cards during loading
- Show skeleton rows in table while data loads
- Provides better visual feedback with layout structure

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
rever-tecnologia 2025-12-05 13:55:55 -03:00
parent b5b74a674d
commit ff90f16cef

View file

@ -47,7 +47,6 @@ import {
TableHeader, TableHeader,
TableRow, TableRow,
} from "@/components/ui/table" } from "@/components/ui/table"
import { Spinner } from "@/components/ui/spinner"
import { Skeleton } from "@/components/ui/skeleton" import { Skeleton } from "@/components/ui/skeleton"
import { SearchableCombobox, type SearchableComboboxOption } from "@/components/ui/searchable-combobox" import { SearchableCombobox, type SearchableComboboxOption } from "@/components/ui/searchable-combobox"
import { DateRangeButton, type DateRangeValue } from "@/components/date-range-button" import { DateRangeButton, type DateRangeValue } from "@/components/date-range-button"
@ -388,13 +387,7 @@ export function EmprestimosPageClient() {
setDateRange({ from: null, to: null }) setDateRange({ from: null, to: null })
}, []) }, [])
if (!tenantId || !convexUserId) { const isLoading = !tenantId || !convexUserId
return (
<div className="flex items-center justify-center py-20">
<Spinner className="size-8" />
</div>
)
}
const fieldWrap = "min-w-[180px] flex-1" const fieldWrap = "min-w-[180px] flex-1"
const fieldTrigger = const fieldTrigger =
@ -410,21 +403,37 @@ export function EmprestimosPageClient() {
<div className="grid gap-4 md:grid-cols-4"> <div className="grid gap-4 md:grid-cols-4">
<div className="rounded-2xl border border-slate-200 bg-white/90 p-4 shadow-sm transition-colors hover:border-cyan-200/60 hover:bg-cyan-50/30"> <div className="rounded-2xl border border-slate-200 bg-white/90 p-4 shadow-sm transition-colors hover:border-cyan-200/60 hover:bg-cyan-50/30">
<p className="text-xs font-medium uppercase tracking-wide text-neutral-500">Total</p> <p className="text-xs font-medium uppercase tracking-wide text-neutral-500">Total</p>
<p className="mt-1 text-2xl font-bold text-neutral-900">{stats?.total ?? 0}</p> {isLoading || stats === undefined ? (
<Skeleton className="mt-2 h-7 w-12" />
) : (
<p className="mt-1 text-2xl font-bold text-neutral-900">{stats.total}</p>
)}
</div> </div>
<div className="rounded-2xl border border-cyan-200/60 bg-cyan-50/40 p-4 shadow-sm"> <div className="rounded-2xl border border-cyan-200/60 bg-cyan-50/40 p-4 shadow-sm">
<p className="text-xs font-medium uppercase tracking-wide text-cyan-600">Ativos</p> <p className="text-xs font-medium uppercase tracking-wide text-cyan-600">Ativos</p>
<p className="mt-1 text-2xl font-bold text-cyan-700">{stats?.ativos ?? 0}</p> {isLoading || stats === undefined ? (
<Skeleton className="mt-2 h-7 w-10" />
) : (
<p className="mt-1 text-2xl font-bold text-cyan-700">{stats.ativos}</p>
)}
</div> </div>
<div className="rounded-2xl border border-red-200/60 bg-red-50/40 p-4 shadow-sm"> <div className="rounded-2xl border border-red-200/60 bg-red-50/40 p-4 shadow-sm">
<p className="text-xs font-medium uppercase tracking-wide text-red-600">Atrasados</p> <p className="text-xs font-medium uppercase tracking-wide text-red-600">Atrasados</p>
<p className="mt-1 text-2xl font-bold text-red-700">{stats?.atrasados ?? 0}</p> {isLoading || stats === undefined ? (
<Skeleton className="mt-2 h-7 w-8" />
) : (
<p className="mt-1 text-2xl font-bold text-red-700">{stats.atrasados}</p>
)}
</div> </div>
<div className="rounded-2xl border border-emerald-200/60 bg-emerald-50/40 p-4 shadow-sm"> <div className="rounded-2xl border border-emerald-200/60 bg-emerald-50/40 p-4 shadow-sm">
<p className="text-xs font-medium uppercase tracking-wide text-emerald-600">Valor ativo</p> <p className="text-xs font-medium uppercase tracking-wide text-emerald-600">Valor ativo</p>
<p className="mt-1 text-2xl font-bold text-emerald-700"> {isLoading || stats === undefined ? (
R$ {(stats?.valorTotalAtivo ?? 0).toLocaleString("pt-BR", { minimumFractionDigits: 2 })} <Skeleton className="mt-2 h-7 w-24" />
</p> ) : (
<p className="mt-1 text-2xl font-bold text-emerald-700">
R$ {stats.valorTotalAtivo.toLocaleString("pt-BR", { minimumFractionDigits: 2 })}
</p>
)}
</div> </div>
</div> </div>
@ -535,7 +544,7 @@ export function EmprestimosPageClient() {
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{!emprestimos ? ( {isLoading || !emprestimos ? (
// Skeleton rows durante o loading // Skeleton rows durante o loading
Array.from({ length: 5 }).map((_, idx) => ( Array.from({ length: 5 }).map((_, idx) => (
<TableRow key={`skeleton-${idx}`} className="animate-pulse"> <TableRow key={`skeleton-${idx}`} className="animate-pulse">