feat: adição de novos ícones SVG e configuração do ambiente

- Adicionados ícones SVG para ChatGPT, Claude, Gemini e OpenRouter
- Implementados ícones para modos claro e escuro do ChatGPT
- Criado script de inicialização para PostgreSQL com extensão pgcrypto
- Adicionado script de configuração de ambiente que faz backup do .env
- Configurado tsconfig.json para TypeScript com opções de compilação
This commit is contained in:
Felipe Coutinho
2025-11-15 15:49:36 -03:00
commit ea0b8618e0
441 changed files with 53569 additions and 0 deletions

View File

@@ -0,0 +1,43 @@
import { Skeleton } from "@/components/ui/skeleton";
/**
* Skeleton para o card de resumo da conta (AccountStatementCard)
* Reflete fielmente o layout: logo + nome + tipo + badge + métricas
*/
export function AccountStatementCardSkeleton() {
return (
<div className="rounded-2xl border p-6 space-y-6">
{/* Header com logo, nome, tipo e badge */}
<div className="flex items-start justify-between">
<div className="flex items-start gap-4">
{/* Logo */}
<Skeleton className="size-12 rounded-2xl bg-foreground/10" />
<div className="space-y-2">
{/* Nome da conta */}
<Skeleton className="h-6 w-48 rounded-2xl bg-foreground/10" />
{/* Tipo de conta */}
<Skeleton className="h-4 w-32 rounded-2xl bg-foreground/10" />
</div>
</div>
<div className="flex items-center gap-2">
{/* Badge de status */}
<Skeleton className="h-6 w-16 rounded-2xl bg-foreground/10" />
{/* Botão de editar */}
<Skeleton className="size-8 rounded-2xl bg-foreground/10" />
</div>
</div>
{/* Métricas em grid */}
<div className="grid grid-cols-2 gap-4 pt-4 border-t md:grid-cols-4">
{Array.from({ length: 4 }).map((_, i) => (
<div key={i} className="space-y-2">
<Skeleton className="h-4 w-24 rounded-2xl bg-foreground/10" />
<Skeleton className="h-6 w-32 rounded-2xl bg-foreground/10" />
</div>
))}
</div>
</div>
);
}

View File

@@ -0,0 +1,22 @@
import { SectionCardsSkeleton } from "./section-cards-skeleton";
import { WidgetSkeleton } from "./widget-skeleton";
/**
* Skeleton completo para o dashboard grid
* Mantém a mesma estrutura de layout do dashboard real
*/
export function DashboardGridSkeleton() {
return (
<div className="@container/main space-y-4">
{/* Section Cards no topo */}
<SectionCardsSkeleton />
{/* Grid de widgets */}
<div className="grid grid-cols-1 gap-4 @3xl/main:grid-cols-2 @7xl/main:grid-cols-3">
{Array.from({ length: 12 }).map((_, i) => (
<WidgetSkeleton key={i} />
))}
</div>
</div>
);
}

View File

@@ -0,0 +1,20 @@
import { Skeleton } from "@/components/ui/skeleton";
/**
* Skeleton para os filtros de lançamentos
* Mantém o layout horizontal com múltiplos selects
*/
export function FilterSkeleton() {
return (
<div className="flex flex-wrap items-center gap-2">
{Array.from({ length: 6 }).map((_, i) => (
<Skeleton
key={i}
className="h-10 w-[130px] rounded-2xl bg-foreground/10"
/>
))}
<Skeleton className="h-10 w-[150px] rounded-2xl bg-foreground/10" />
<Skeleton className="h-8 w-16 rounded-2xl bg-foreground/10" />
</div>
);
}

View File

@@ -0,0 +1,11 @@
/**
* Barrel export para todos os skeletons
* Facilita a importação em outros componentes
*/
export { AccountStatementCardSkeleton } from "./account-statement-card-skeleton";
export { DashboardGridSkeleton } from "./dashboard-grid-skeleton";
export { FilterSkeleton } from "./filter-skeleton";
export { InvoiceSummaryCardSkeleton } from "./invoice-summary-card-skeleton";
export { SectionCardsSkeleton } from "./section-cards-skeleton";
export { TransactionsTableSkeleton } from "./transactions-table-skeleton";
export { WidgetSkeleton } from "./widget-skeleton";

View File

@@ -0,0 +1,64 @@
import { Skeleton } from "@/components/ui/skeleton";
/**
* Skeleton para o card de resumo da fatura (InvoiceSummaryCard)
* Reflete fielmente o layout: logo + nome + bandeira + badges + total + limite + ações
*/
export function InvoiceSummaryCardSkeleton() {
return (
<div className="rounded-2xl border p-6 space-y-6">
{/* Header com logo, nome, bandeira e badges */}
<div className="flex items-start justify-between">
<div className="flex items-start gap-4">
{/* Logo do cartão */}
<Skeleton className="size-12 rounded-2xl bg-foreground/10" />
<div className="space-y-2">
{/* Nome do cartão */}
<Skeleton className="h-6 w-48 rounded-2xl bg-foreground/10" />
<div className="flex items-center gap-2">
{/* Bandeira */}
<Skeleton className="h-4 w-20 rounded-2xl bg-foreground/10" />
{/* Badge de status */}
<Skeleton className="h-6 w-16 rounded-2xl bg-foreground/10" />
</div>
</div>
</div>
{/* Botão de editar */}
<Skeleton className="size-8 rounded-2xl bg-foreground/10" />
</div>
{/* Informações da fatura */}
<div className="space-y-4 pt-4 border-t">
{/* Período e status */}
<div className="flex items-center justify-between">
<Skeleton className="h-5 w-32 rounded-2xl bg-foreground/10" />
<Skeleton className="h-6 w-24 rounded-2xl bg-foreground/10" />
</div>
{/* Total da fatura */}
<div className="space-y-2">
<Skeleton className="h-4 w-28 rounded-2xl bg-foreground/10" />
<Skeleton className="h-8 w-40 rounded-2xl bg-foreground/10" />
</div>
{/* Limite e utilização */}
<div className="grid grid-cols-2 gap-4 pt-4">
<div className="space-y-2">
<Skeleton className="h-4 w-20 rounded-2xl bg-foreground/10" />
<Skeleton className="h-6 w-28 rounded-2xl bg-foreground/10" />
</div>
<div className="space-y-2">
<Skeleton className="h-4 w-24 rounded-2xl bg-foreground/10" />
<Skeleton className="h-6 w-28 rounded-2xl bg-foreground/10" />
</div>
</div>
{/* Botão de ação */}
<Skeleton className="h-10 w-full rounded-2xl bg-foreground/10" />
</div>
</div>
);
}

View File

@@ -0,0 +1,37 @@
import { Card, CardFooter, CardHeader } from "@/components/ui/card";
import { Skeleton } from "@/components/ui/skeleton";
/**
* Skeleton fiel aos cards de métricas do dashboard (SectionCards)
* Mantém o mesmo layout de 4 colunas responsivo
*/
export function SectionCardsSkeleton() {
return (
<div className="*:data-[slot=card]:from-primary/5 *:data-[slot=card]:to-card dark:*:data-[slot=card]:bg-card grid grid-cols-1 gap-3 @xl/main:grid-cols-2 @5xl/main:grid-cols-4">
{Array.from({ length: 4 }).map((_, index) => (
<Card key={index} className="@container/card gap-2">
<CardHeader>
<div className="space-y-3">
{/* Título com ícone */}
<div className="flex items-center gap-1">
<Skeleton className="size-4 rounded-2xl bg-foreground/10" />
<Skeleton className="h-5 w-20 rounded-2xl bg-foreground/10" />
</div>
{/* Valor principal */}
<Skeleton className="h-8 w-32 rounded-2xl bg-foreground/10" />
{/* Badge de tendência */}
<Skeleton className="h-6 w-16 rounded-2xl bg-foreground/10" />
</div>
</CardHeader>
<CardFooter className="flex-col items-start gap-1.5 text-sm">
<Skeleton className="h-4 w-24 rounded-2xl bg-foreground/10" />
<Skeleton className="h-4 w-20 rounded-2xl bg-foreground/10" />
</CardFooter>
</Card>
))}
</div>
);
}

View File

@@ -0,0 +1,84 @@
import { Skeleton } from "@/components/ui/skeleton";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from "@/components/ui/table";
/**
* Skeleton fiel à tabela de lançamentos
* Mantém a mesma estrutura de colunas
*/
export function TransactionsTableSkeleton() {
return (
<div className="rounded-2xl border">
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-[180px]">Nome</TableHead>
<TableHead className="w-[100px]">Data</TableHead>
<TableHead className="w-[120px]">Tipo</TableHead>
<TableHead className="w-[120px]">Valor</TableHead>
<TableHead className="w-[120px]">Condição</TableHead>
<TableHead className="w-[120px]">Pagamento</TableHead>
<TableHead className="w-[140px]">Pagador</TableHead>
<TableHead className="w-[140px]">Categoria</TableHead>
<TableHead className="w-[140px]">Conta/Cartão</TableHead>
<TableHead className="w-[80px]">Ações</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{Array.from({ length: 8 }).map((_, i) => (
<TableRow key={i}>
<TableCell>
<Skeleton className="h-4 w-full rounded-2xl bg-foreground/10" />
</TableCell>
<TableCell>
<Skeleton className="h-4 w-16 rounded-2xl bg-foreground/10" />
</TableCell>
<TableCell>
<Skeleton className="h-6 w-20 rounded-2xl bg-foreground/10" />
</TableCell>
<TableCell>
<Skeleton className="h-4 w-20 rounded-2xl bg-foreground/10" />
</TableCell>
<TableCell>
<Skeleton className="h-6 w-16 rounded-2xl bg-foreground/10" />
</TableCell>
<TableCell>
<Skeleton className="h-6 w-20 rounded-2xl bg-foreground/10" />
</TableCell>
<TableCell>
<div className="flex items-center gap-2">
<Skeleton className="size-6 rounded-full bg-foreground/10" />
<Skeleton className="h-4 w-16 rounded-2xl bg-foreground/10" />
</div>
</TableCell>
<TableCell>
<div className="flex items-center gap-2">
<Skeleton className="size-4 rounded-2xl bg-foreground/10" />
<Skeleton className="h-4 w-16 rounded-2xl bg-foreground/10" />
</div>
</TableCell>
<TableCell>
<div className="flex items-center gap-2">
<Skeleton className="size-6 rounded-2xl bg-foreground/10" />
<Skeleton className="h-4 w-20 rounded-2xl bg-foreground/10" />
</div>
</TableCell>
<TableCell>
<div className="flex gap-1">
<Skeleton className="size-8 rounded-2xl bg-foreground/10" />
<Skeleton className="size-8 rounded-2xl bg-foreground/10" />
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
);
}

View File

@@ -0,0 +1,44 @@
import { Card, CardContent, CardHeader } from "@/components/ui/card";
import { Skeleton } from "@/components/ui/skeleton";
/**
* Skeleton fiel ao WidgetCard
* Usado enquanto widgets do dashboard estão carregando
*/
export function WidgetSkeleton() {
return (
<Card className="md:h-custom-height-1 relative h-auto md:overflow-hidden">
<CardHeader className="border-b [.border-b]:pb-2">
<div className="flex w-full items-start justify-between">
<div className="space-y-2">
{/* Title com ícone */}
<div className="flex items-center gap-1">
<Skeleton className="size-4 rounded-2xl bg-foreground/10" />
<Skeleton className="h-5 w-32 rounded-2xl bg-foreground/10" />
</div>
{/* Subtitle */}
<Skeleton className="h-4 w-48 rounded-2xl bg-foreground/10" />
</div>
</div>
</CardHeader>
<CardContent className="max-h-[calc(var(--spacing-custom-height-1)-5rem)] overflow-hidden md:max-h-[calc(100%-5rem)]">
<div className="flex flex-col gap-3 py-4">
{/* Simula 5 linhas de conteúdo */}
{Array.from({ length: 5 }).map((_, i) => (
<div key={i} className="flex items-center justify-between gap-3">
<div className="flex flex-1 items-center gap-3">
<Skeleton className="size-10 rounded-2xl bg-foreground/10" />
<div className="flex-1 space-y-2">
<Skeleton className="h-4 w-full rounded-2xl bg-foreground/10" />
<Skeleton className="h-3 w-24 rounded-2xl bg-foreground/10" />
</div>
</div>
<Skeleton className="h-6 w-20 rounded-2xl bg-foreground/10" />
</div>
))}
</div>
</CardContent>
</Card>
);
}