Files
asaas-checkout/app/admin/page.tsx
Felipe Carvalho 038ce3f556 feat: initial commit — asaas-checkout template white-label
Template genérico de checkout com ASAAS, parametrizado via env vars.
Inclui fluxo completo: checkout → pedido → polling → webhook → admin.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-16 06:40:41 +02:00

145 lines
5.7 KiB
TypeScript

import { getProdutos, getPedidos, getClientes, getCupons, getAgendamentos } from "@/lib/actions"
import { ProductTable } from "@/components/product-table"
import { TransactionTable } from "@/components/transaction-table"
import { CustomerTable } from "@/components/customer-table"
import { DashboardHeader } from "@/components/dashboard-header"
import { DashboardShell } from "@/components/dashboard-shell"
import { DashboardStats } from "@/components/dashboard-stats"
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
import { ProductFormDialog } from "@/components/product-form-dialog"
import { CustomerFormDialog } from "@/components/customer-form-dialog"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Overview } from "@/components/overview"
import { RecentSales } from "@/components/recent-sales"
import { AuthGuard } from "@/components/auth-guard"
import { CupomTable } from "@/components/cupom-table"
import { AgendamentosTable } from "@/components/agendamentos-table"
export const dynamic = "force-dynamic"
export const metadata = {
title: "Dashboard — Admin",
}
export default async function AdminDashboard() {
const [produtos, pedidos, clientes, cupons, agendamentos] = await Promise.all([
getProdutos(),
getPedidos({ limit: 200 }),
getClientes({ limit: 200 }),
getCupons(),
getAgendamentos({ limit: 100 }),
])
const pedidosConfirmados = pedidos.filter(
(p) => p.status === "RECEIVED" || p.status === "CONFIRMED"
)
const pedidosPendentes = pedidos.filter((p) => p.status === "PENDING").length
const receitaTotal = pedidosConfirmados.reduce((acc, p) => acc + p.valor_centavos / 100, 0)
const ticketMedio = pedidosConfirmados.length > 0 ? receitaTotal / pedidosConfirmados.length : 0
// Receita mensal (12 meses) para o gráfico
const receitaMensal = Array(12).fill(0)
pedidosConfirmados.forEach((p) => {
const mes = new Date(p.created_at).getMonth()
receitaMensal[mes] += p.valor_centavos / 100
})
// Últimas 5 vendas para o card de resumo
const ultimasVendas = pedidosConfirmados.slice(0, 5) as Parameters<typeof RecentSales>[0]["vendas"]
// Contagem de agendamentos pendentes para badge
const agendamentosPendentes = agendamentos.filter(
(a) => a.status === "pendente" || a.status === "confirmado"
).length
return (
<AuthGuard>
<DashboardShell>
<DashboardHeader
heading="Dashboard de Administração"
text="Gerencie seus produtos, clientes e visualize estatísticas de vendas."
>
<ProductFormDialog />
</DashboardHeader>
{/* Cards de métricas */}
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<DashboardStats
receitaTotal={receitaTotal}
totalPedidosConfirmados={pedidosConfirmados.length}
ticketMedio={ticketMedio}
pedidosPendentes={pedidosPendentes}
/>
</div>
{/* Gráfico + últimas vendas */}
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-7">
<Card className="col-span-4">
<CardHeader>
<CardTitle className="font-heading">Receita Mensal</CardTitle>
</CardHeader>
<CardContent className="pl-2">
<Overview data={receitaMensal} />
</CardContent>
</Card>
<Card className="col-span-3">
<CardHeader>
<CardTitle className="font-heading">Últimas Vendas</CardTitle>
<CardDescription className="font-sans">
{pedidosConfirmados.length} venda{pedidosConfirmados.length !== 1 ? "s" : ""} confirmada{pedidosConfirmados.length !== 1 ? "s" : ""} no total.
</CardDescription>
</CardHeader>
<CardContent>
<RecentSales vendas={ultimasVendas} />
</CardContent>
</Card>
</div>
{/* Abas */}
<Tabs defaultValue="pedidos" className="space-y-4">
<TabsList>
<TabsTrigger value="pedidos">Pedidos</TabsTrigger>
<TabsTrigger value="agendamentos">
Agendamentos
{agendamentosPendentes > 0 && (
<span className="ml-1.5 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground text-[10px] font-bold w-4 h-4">
{agendamentosPendentes}
</span>
)}
</TabsTrigger>
<TabsTrigger value="products">Produtos</TabsTrigger>
<TabsTrigger value="customers">Clientes</TabsTrigger>
<TabsTrigger value="cupons">Cupons</TabsTrigger>
</TabsList>
<TabsContent value="pedidos" className="space-y-4">
<TransactionTable transactions={pedidos} />
</TabsContent>
<TabsContent value="agendamentos" className="space-y-4">
<AgendamentosTable agendamentos={agendamentos as Parameters<typeof AgendamentosTable>[0]["agendamentos"]} />
</TabsContent>
<TabsContent value="products" className="space-y-4">
<div className="flex justify-end mb-4">
<ProductFormDialog />
</div>
<ProductTable products={produtos} />
</TabsContent>
<TabsContent value="customers" className="space-y-4">
<div className="flex justify-end mb-4">
<CustomerFormDialog />
</div>
<CustomerTable customers={clientes} />
</TabsContent>
<TabsContent value="cupons" className="space-y-4">
<CupomTable cupons={cupons} />
</TabsContent>
</Tabs>
</DashboardShell>
</AuthGuard>
)
}