feat(portal): adiciona skeletons para melhor UX durante carregamento
All checks were successful
All checks were successful
- Adiciona skeleton no header quando dados do usuario estao carregando - Remove mensagem "Sem e-mail definido" durante loading - Substitui spinner por skeleton cards na lista de tickets - Cria componente PortalTicketCardSkeleton para estado de loading 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
aa9c09c30e
commit
811ad0641a
2 changed files with 81 additions and 28 deletions
|
|
@ -9,6 +9,7 @@ import { toast } from "sonner"
|
|||
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
||||
import { Skeleton } from "@/components/ui/skeleton"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { useAuth, signOut } from "@/lib/auth-client"
|
||||
|
||||
|
|
@ -115,6 +116,15 @@ export function PortalShell({ children }: PortalShellProps) {
|
|||
})}
|
||||
</nav>
|
||||
<div className="flex w-full flex-col items-start gap-3 sm:w-auto sm:flex-row sm:items-center">
|
||||
{machineContextLoading ? (
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<Skeleton className="size-9 rounded-full" />
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<Skeleton className="h-4 w-24" />
|
||||
<Skeleton className="h-3 w-32" />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex items-center gap-2 text-sm">
|
||||
<Avatar className="size-9 border border-slate-200">
|
||||
<AvatarImage src={session?.user.avatarUrl ?? undefined} alt={displayName ?? ""} />
|
||||
|
|
@ -128,6 +138,7 @@ export function PortalShell({ children }: PortalShellProps) {
|
|||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{!isMachineSession ? (
|
||||
<Button
|
||||
size="sm"
|
||||
|
|
@ -152,11 +163,6 @@ export function PortalShell({ children }: PortalShellProps) {
|
|||
</p>
|
||||
</div>
|
||||
) : null}
|
||||
{!machineContextError && machineContextLoading ? (
|
||||
<div className="rounded-xl border border-slate-200 bg-slate-50 px-4 py-3 text-sm text-neutral-600 shadow-sm">
|
||||
Recuperando dados do colaborador vinculado...
|
||||
</div>
|
||||
) : null}
|
||||
{children}
|
||||
</main>
|
||||
<footer className="border-t border-slate-200 bg-white/70">
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import { useAuth } from "@/lib/auth-client"
|
|||
import Link from "next/link"
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
||||
import { Empty, EmptyDescription, EmptyHeader, EmptyMedia, EmptyTitle } from "@/components/ui/empty"
|
||||
import { Skeleton } from "@/components/ui/skeleton"
|
||||
import { Spinner } from "@/components/ui/spinner"
|
||||
import { Button } from "@/components/ui/button"
|
||||
import { PortalTicketCard } from "@/components/portal/portal-ticket-card"
|
||||
|
|
@ -195,19 +196,26 @@ export function PortalTicketList() {
|
|||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Card className="rounded-2xl border border-slate-200 bg-white shadow-sm">
|
||||
<CardContent className="flex h-56 flex-col items-center justify-center gap-3 px-5 text-center">
|
||||
<div className="inline-flex size-12 items-center justify-center rounded-full border border-slate-200 bg-slate-50">
|
||||
<Spinner className="size-5 text-neutral-600" />
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<h2 className="text-lg font-semibold text-neutral-900">Meus chamados</h2>
|
||||
<p className="text-sm text-neutral-600">Acompanhe seus tickets e veja as últimas atualizações.</p>
|
||||
</div>
|
||||
<div className="space-y-1">
|
||||
<CardTitle className="text-lg font-semibold text-neutral-900">Carregando chamados...</CardTitle>
|
||||
<p className="text-sm text-neutral-600">
|
||||
Estamos buscando seus chamados mais recentes. Isso deve levar apenas alguns instantes.
|
||||
</p>
|
||||
</div>
|
||||
</CardContent>
|
||||
<Card className="rounded-2xl border border-slate-200 bg-white p-5 shadow-sm">
|
||||
<div className="grid grid-cols-3 gap-4">
|
||||
<Skeleton className="h-10 rounded-lg" />
|
||||
<Skeleton className="h-10 rounded-lg" />
|
||||
<Skeleton className="h-10 rounded-lg" />
|
||||
</div>
|
||||
</Card>
|
||||
<div className="grid gap-4">
|
||||
{Array.from({ length: 4 }).map((_, index) => (
|
||||
<PortalTicketCardSkeleton key={index} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -393,6 +401,45 @@ export function PortalTicketList() {
|
|||
)
|
||||
}
|
||||
|
||||
/** Skeleton do card de ticket para estado de carregamento */
|
||||
function PortalTicketCardSkeleton() {
|
||||
return (
|
||||
<Card className="overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm">
|
||||
<CardHeader className="flex flex-row items-start justify-between gap-3 px-5 pb-3 pt-5">
|
||||
<div className="flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<Skeleton className="h-4 w-16" />
|
||||
<Skeleton className="h-4 w-24" />
|
||||
</div>
|
||||
<Skeleton className="mt-2 h-6 w-3/4" />
|
||||
</div>
|
||||
<div className="flex flex-col items-end gap-2">
|
||||
<Skeleton className="h-9 w-24 rounded-full" />
|
||||
<Skeleton className="h-6 w-16 rounded-full" />
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="flex flex-wrap items-center justify-between gap-4 border-t border-slate-100 px-5 py-4">
|
||||
<div className="flex flex-col gap-1">
|
||||
<Skeleton className="h-3 w-8" />
|
||||
<Skeleton className="h-4 w-20" />
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Skeleton className="h-3 w-12" />
|
||||
<Skeleton className="h-4 w-24" />
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Skeleton className="h-3 w-20" />
|
||||
<Skeleton className="h-4 w-28" />
|
||||
</div>
|
||||
<div className="flex flex-col gap-1">
|
||||
<Skeleton className="h-3 w-16" />
|
||||
<Skeleton className="h-4 w-32" />
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gera array de números de página para exibição.
|
||||
* Mostra primeira, última e páginas próximas à atual com reticências.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue