chore: adicionar configuração de dependências no pnpm-workspace

Adiciona a configuração 'onlyBuiltDependencies' no arquivo
pnpm-workspace.yaml, especificando as dependências que devem ser
construídas: esbuild, sharp e unrs-resolver. Essa mudança visa
otimizar o gerenciamento de dependências no projeto.
This commit is contained in:
Felipe Coutinho
2025-11-16 12:28:15 -03:00
parent 7d88852ceb
commit b124d5193f
9 changed files with 1029 additions and 611 deletions

1
.gitignore vendored
View File

@@ -22,6 +22,7 @@ next-env.d.ts
/build
/dist
*.tsbuildinfo
.pnpm-store
# === Testing ===
/coverage

314
README.md
View File

@@ -1,6 +1,8 @@
# OpenSheets
> Uma aplicação moderna e completa construída com **Next.js 16**, **Better Auth**, **Drizzle ORM**, **PostgreSQL** e **shadcn/ui**.
> Projeto pessoal de gestão financeira. Self-hosted, manual e open source.
> **⚠️ Não há versão online hospedada.** Você precisa clonar o repositório e rodar localmente ou no seu próprio servidor.
[![Next.js](https://img.shields.io/badge/Next.js-16-black?style=flat-square&logo=next.js)](https://nextjs.org/)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue?style=flat-square&logo=typescript)](https://www.typescriptlang.org/)
@@ -23,23 +25,78 @@
- [Configuração de Variáveis de Ambiente](#-configuração-de-variáveis-de-ambiente)
- [Banco de Dados](#-banco-de-dados)
- [Arquitetura](#-arquitetura)
- [Troubleshooting](#-troubleshooting)
- [Contribuindo](#-contribuindo)
---
## 🎯 Sobre o Projeto
**OpenSheets** é uma aplicação full-stack moderna projetada para controle de finanças pessoais. Construída com as melhores práticas de desenvolvimento e ferramentas de ponta, oferece uma base sólida e escalável para gestão financeira completa.
**OpenSheets** é um projeto pessoal de gestão financeira que criei para organizar minhas próprias finanças. Cansei de usar planilhas desorganizadas e aplicativos que não fazem exatamente o que preciso, então decidi construir algo do jeito que funciona pra mim.
### Por que usar o OpenSheets?
A ideia é simples: ter um lugar onde consigo ver todas as minhas contas, cartões, gastos e receitas de forma clara. Se isso for útil pra você também, fique à vontade para usar e contribuir.
-**Pronto para Produção** - Docker, health checks, migrations automáticas
-**TypeScript First** - Type safety em toda a aplicação
-**Autenticação Completa** - Better Auth com OAuth, email magic links
-**ORM Moderno** - Drizzle com Drizzle Studio integrado
-**UI Components** - shadcn/ui com design system completo
-**Developer Experience** - Hot reload, Turbopack, ESLint configurado
### ⚠️ Avisos importantes
**1. Não há versão hospedada online**
Este projeto é self-hosted. Você precisa rodar no seu próprio computador ou servidor. Não existe uma versão pública online onde você pode simplesmente criar uma conta.
**2. Não há Open Finance**
Você precisa registrar manualmente suas transações. Se você procura algo que sincroniza automaticamente com seu banco, este projeto não é pra você.
**3. Requer disciplina**
O OpenSheets funciona melhor para quem:
- Tem disciplina de registrar os gastos regularmente
- Quer controle total sobre seus dados
- Gosta de entender exatamente onde o dinheiro está indo
- Sabe rodar projetos localmente ou tem vontade de aprender
Se você não se importa em dedicar alguns minutos por dia (ou semana) para manter tudo atualizado, vai funcionar bem. Caso contrário, provavelmente vai abandonar depois de uma semana.
### O que tem aqui
💰 **Controle de contas e transações**
- Registre suas contas bancárias, cartões e dinheiro em espécie
- Adicione receitas, despesas e transferências entre contas
- Organize tudo por categorias (moradia, alimentação, transporte, etc.)
- Veja o saldo atual de cada conta
📊 **Relatórios e gráficos**
- Dashboard com resumo mensal das suas finanças
- Gráficos de evolução do patrimônio
- Comparação de gastos por categoria
- Entenda pra onde seu dinheiro está indo
💳 **Faturas de cartão de crédito**
- Cadastre seus cartões e acompanhe as faturas
- Veja o que ainda não foi fechado na fatura atual
- Controle de limites e vencimentos
🎯 **Orçamentos**
- Defina quanto quer gastar por categoria no mês
- Acompanhe se está dentro do planejado
### Stack técnica
Construído com tecnologias modernas que facilitam o desenvolvimento:
- **Next.js 16** com App Router e Turbopack
- **TypeScript** em tudo
- **PostgreSQL 18** como banco de dados
- **Drizzle ORM** para trabalhar com o banco
- **Better Auth** para login (email + OAuth)
- **shadcn/ui** para os componentes da interface
- **Docker** para facilitar deploy e desenvolvimento
- **Tailwind CSS** para estilização
O projeto é open source, seus dados ficam no seu controle (pode rodar localmente ou no seu próprio servidor), e você pode customizar o que quiser.
---
@@ -727,242 +784,6 @@ opensheets/
---
## 🆘 Troubleshooting
### Erro: "DATABASE_URL env variable is not set"
**Causa:** Arquivo `.env` não existe ou `DATABASE_URL` não configurado
**Solução:**
```bash
cp .env.example .env
# Edite .env e configure DATABASE_URL
```
---
### Container do app não conecta ao banco
**Causa:** `DATABASE_URL` usa `localhost` em vez de `db`
**Solução:**
Para Docker, use o **nome do serviço**:
```env
# ❌ Errado (localhost não funciona dentro do container)
DATABASE_URL=postgresql://opensheets:senha@localhost:5432/opensheets_db
# ✅ Correto (usa nome do serviço Docker)
DATABASE_URL=postgresql://opensheets:senha@db:5432/opensheets_db
```
Para desenvolvimento local (sem Docker app):
```env
# ✅ Correto (app roda local, banco em Docker)
DATABASE_URL=postgresql://opensheets:senha@localhost:5432/opensheets_db
```
**Verifique o status do banco:**
```bash
docker compose ps
docker compose logs db
```
---
### Porta 3000 ou 5432 já está em uso
**Solução:**
Edite o `.env`:
```env
APP_PORT=3001
DB_PORT=5433
```
Ou pare o processo que está usando:
```bash
# Descobrir quem usa a porta
lsof -i :3000
lsof -i :5432
# Matar processo
kill -9 <PID>
```
---
### Migrations não rodam
**Com Docker:**
Migrations rodam automaticamente no startup. Veja logs:
```bash
pnpm docker:logs:app
```
Se falharem, rode manualmente:
```bash
docker compose exec app pnpm db:push
```
**Sem Docker:**
```bash
pnpm db:push
```
---
### Erro: "server.js not found"
**Causa:** Next.js não gerou standalone build
**Solução:**
1. Verifique `next.config.ts`:
```typescript
const nextConfig: NextConfig = {
output: "standalone", // ← Deve estar presente
};
```
2. Rebuild:
```bash
docker compose down
docker compose up --build
```
---
### Erro ao atualizar PostgreSQL 16 → 18
**Causa:** Volumes antigos são incompatíveis
**Solução:**
```bash
# ⚠️ ATENÇÃO: Isso apaga dados do banco local!
docker compose down -v
# Suba novamente com PostgreSQL 18
docker compose up --build
```
**Para preservar dados:**
```bash
# 1. Backup
docker compose exec db pg_dumpall -U opensheets > backup.sql
# 2. Limpa volumes
docker compose down -v
# 3. Sobe PG 18
docker compose up -d db
# 4. Aguarda (15s)
sleep 15
# 5. Restaura
docker compose exec -T db psql -U opensheets -d opensheets_db < backup.sql
```
---
### Drizzle Studio não abre
**Solução:**
1. Verifique se o banco está rodando:
```bash
docker compose ps
```
2. Teste conexão:
```bash
psql $DATABASE_URL
```
3. Abra Drizzle Studio:
```bash
pnpm db:studio
```
---
### Build do Docker muito lento
**Causa:** Cache não está sendo aproveitado
**Solução:**
1. Use BuildKit:
```bash
export DOCKER_BUILDKIT=1
docker compose build
```
2. Limpe cache antigo:
```bash
docker builder prune
```
3. Multi-stage build já otimiza camadas
---
### "Permission denied" ao rodar Docker
**Causa:** Usuário não está no grupo docker
**Solução (Linux):**
```bash
sudo usermod -aG docker $USER
newgrp docker
```
**Solução (Mac/Windows):**
- Docker Desktop deve estar rodando
- Verifique configurações de permissão
---
### Limpar tudo e começar do zero
```bash
# Para containers e remove volumes
docker compose down -v
# Remove images não usadas
docker system prune -a
# Remove TUDO do Docker (cuidado!)
docker system prune -a --volumes
# Rebuild do zero
pnpm docker:up
```
---
## 🤝 Contribuindo
Contribuições são muito bem-vindas!
@@ -991,7 +812,6 @@ Contribuições são muito bem-vindas!
### Padrões
- Use **TypeScript**
- Siga o **ESLint** configurado
- Documente **features novas**
- Use **commits semânticos** (feat, fix, docs, etc)

View File

@@ -9,12 +9,13 @@ import {
RiBankCardLine,
RiBarChartBoxLine,
RiCalendarLine,
RiCodeSSlashLine,
RiDatabase2Line,
RiDeviceLine,
RiEyeOffLine,
RiGithubFill,
RiLineChartLine,
RiLockLine,
RiMoneyDollarCircleLine,
RiNotificationLine,
RiPieChartLine,
RiShieldCheckLine,
RiTimeLine,
@@ -33,6 +34,29 @@ export default async function Page() {
<div className="flex items-center">
<Logo />
</div>
{/* Center Navigation Links */}
<nav className="hidden md:flex items-center gap-6 absolute left-1/2 transform -translate-x-1/2">
<a
href="#funcionalidades"
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
>
Funcionalidades
</a>
<a
href="#stack"
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
>
Stack
</a>
<a
href="#como-usar"
className="text-sm text-muted-foreground hover:text-foreground transition-colors"
>
Como usar
</a>
</nav>
<nav className="flex items-center gap-2 md:gap-4">
<AnimatedThemeToggler />
{session?.user ? (
@@ -50,7 +74,7 @@ export default async function Page() {
</Link>
<Link href="/signup">
<Button size="sm" className="gap-2">
Começar Grátis
Começar
<RiArrowRightSLine size={16} />
</Button>
</Link>
@@ -64,77 +88,90 @@ export default async function Page() {
<section className="relative py-16 md:py-24 lg:py-32">
<div className="container">
<div className="mx-auto flex max-w-5xl flex-col items-center text-center gap-6">
<Badge variant="secondary" className="mb-2">
<RiLineChartLine size={14} className="mr-1" />
Controle Financeiro Inteligente
<Badge variant="primary" className="mb-2">
<RiGithubFill size={14} className="mr-1" />
Projeto Open Source
</Badge>
<h1 className="text-4xl md:text-5xl lg:text-6xl font-bold tracking-tight">
Gerencie suas finanças
<span className="text-primary"> com simplicidade</span>
Suas finanças,
<span className="text-primary"> do seu jeito</span>
</h1>
<p className="text-lg md:text-xl text-muted-foreground max-w-2xl">
Organize seus gastos, acompanhe receitas, gerencie cartões de
crédito e tome decisões financeiras mais inteligentes. Tudo em um
só lugar.
Um projeto pessoal de gestão financeira. Self-hosted, sem Open
Finance, sem sincronização automática. Rode no seu computador ou
servidor e tenha controle total sobre suas finanças.
</p>
<div className="rounded-lg border bg-muted/30 p-4 max-w-2xl">
<p className="text-sm text-muted-foreground">
<span className="font-semibold text-foreground">
Aviso importante:
</span>{" "}
Este sistema requer disciplina. Você precisa registrar
manualmente cada transação. Se prefere algo automático, este
projeto não é pra você.
</p>
</div>
<div className="flex flex-col sm:flex-row gap-4 mt-4">
<Link href="/signup">
<Link
href="https://github.com/felipegcoutinho/opensheets-app"
target="_blank"
>
<Button size="lg" className="gap-2 w-full sm:w-auto">
Começar Gratuitamente
<RiArrowRightSLine size={18} />
<RiGithubFill size={18} />
Baixar no GitHub
</Button>
</Link>
<Link href="/login">
<Link
href="https://github.com/felipegcoutinho/opensheets-app#readme"
target="_blank"
>
<Button
size="lg"
variant="outline"
className="w-full sm:w-auto"
className="w-full sm:w-auto gap-2"
>
Fazer Login
Ver Documentação
</Button>
</Link>
</div>
<div className="mt-8 flex flex-wrap items-center justify-center gap-6 text-sm text-muted-foreground">
<div className="flex items-center gap-2">
<RiShieldCheckLine size={18} className="text-primary" />
Dados Seguros
<RiLockLine size={18} className="text-primary" />
Seus dados, seu servidor
</div>
<div className="flex items-center gap-2">
<RiEyeOffLine size={18} className="text-primary" />
Modo Privacidade
</div>
<div className="flex items-center gap-2">
<RiDeviceLine size={18} className="text-primary" />
100% Responsivo
<RiGithubFill size={18} className="text-primary" />
100% Open Source
</div>
</div>
</div>
</div>
</section>
{/* Features Section */}
<section className="py-16 md:py-24 bg-muted/30">
{/* What's Here Section */}
<section id="funcionalidades" className="py-16 md:py-24 bg-muted/30">
<div className="container">
<div className="mx-auto max-w-5xl">
<div className="text-center mb-12">
<Badge variant="secondary" className="mb-4">
Funcionalidades
<Badge variant="primary" className="mb-4">
O que tem aqui
</Badge>
<h2 className="text-3xl md:text-4xl font-bold tracking-tight mb-4">
Tudo que você precisa para gerenciar suas finanças
Funcionalidades que importam
</h2>
<p className="text-lg text-muted-foreground max-w-2xl mx-auto">
Ferramentas poderosas e intuitivas para controle financeiro
completo
Ferramentas simples para organizar suas contas, cartões, gastos
e receitas
</p>
</div>
<div className="grid gap-6 md:grid-cols-2 lg:grid-cols-3">
<Card className="border-2 hover:border-primary/50 transition-colors">
<Card className="border-[1.5px] hover:border-primary/50 transition-colors">
<CardContent className="pt-6">
<div className="flex flex-col gap-4">
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10">
@@ -142,18 +179,38 @@ export default async function Page() {
</div>
<div>
<h3 className="font-semibold text-lg mb-2">
Lançamentos
Contas e transações
</h3>
<p className="text-sm text-muted-foreground">
Registre receitas e despesas com categorização
automática e controle detalhado de pagadores e contas.
Registre suas contas bancárias, cartões e dinheiro.
Adicione receitas, despesas e transferências. Organize
por categorias.
</p>
</div>
</div>
</CardContent>
</Card>
<Card className="border-2 hover:border-primary/50 transition-colors">
<Card className="border-[1.5px] hover:border-primary/50 transition-colors">
<CardContent className="pt-6">
<div className="flex flex-col gap-4">
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10">
<RiBarChartBoxLine size={24} className="text-primary" />
</div>
<div>
<h3 className="font-semibold text-lg mb-2">
Relatórios e gráficos
</h3>
<p className="text-sm text-muted-foreground">
Dashboard com resumo mensal. Gráficos de evolução do
patrimônio. Entenda pra onde seu dinheiro está indo.
</p>
</div>
</div>
</CardContent>
</Card>
<Card className="border-[1.5px] hover:border-primary/50 transition-colors">
<CardContent className="pt-6">
<div className="flex flex-col gap-4">
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10">
@@ -161,35 +218,38 @@ export default async function Page() {
</div>
<div>
<h3 className="font-semibold text-lg mb-2">
Cartões de Crédito
Faturas de cartão
</h3>
<p className="text-sm text-muted-foreground">
Gerencie múltiplos cartões, acompanhe faturas, limites e
nunca perca o controle dos gastos.
Cadastre seus cartões e acompanhe as faturas. Veja o que
ainda não foi fechado. Controle limites e vencimentos.
</p>
</div>
</div>
</CardContent>
</Card>
<Card className="border-2 hover:border-primary/50 transition-colors">
<Card className="border-[1.5px] hover:border-primary/50 transition-colors">
<CardContent className="pt-6">
<div className="flex flex-col gap-4">
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10">
<RiPieChartLine size={24} className="text-primary" />
</div>
<div>
<h3 className="font-semibold text-lg mb-2">Categorias</h3>
<h3 className="font-semibold text-lg mb-2">
Categorias personalizadas
</h3>
<p className="text-sm text-muted-foreground">
Organize suas transações em categorias personalizadas e
visualize onde seu dinheiro está indo.
Crie e organize suas próprias categorias. Moradia,
alimentação, transporte, ou o que fizer sentido pra
você.
</p>
</div>
</div>
</CardContent>
</Card>
<Card className="border-2 hover:border-primary/50 transition-colors">
<Card className="border-[1.5px] hover:border-primary/50 transition-colors">
<CardContent className="pt-6">
<div className="flex flex-col gap-4">
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10">
@@ -201,41 +261,26 @@ export default async function Page() {
<div>
<h3 className="font-semibold text-lg mb-2">Orçamentos</h3>
<p className="text-sm text-muted-foreground">
Defina limites de gastos por categoria e receba alertas
para manter suas finanças no caminho certo.
Defina quanto quer gastar por categoria no mês.
Acompanhe se está dentro do planejado.
</p>
</div>
</div>
</CardContent>
</Card>
<Card className="border-2 hover:border-primary/50 transition-colors">
<CardContent className="pt-6">
<div className="flex flex-col gap-4">
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10">
<RiBarChartBoxLine size={24} className="text-primary" />
</div>
<div>
<h3 className="font-semibold text-lg mb-2">Insights</h3>
<p className="text-sm text-muted-foreground">
Análise detalhada de padrões de gastos com gráficos e
relatórios para decisões mais inteligentes.
</p>
</div>
</div>
</CardContent>
</Card>
<Card className="border-2 hover:border-primary/50 transition-colors">
<Card className="border-[1.5px] hover:border-primary/50 transition-colors">
<CardContent className="pt-6">
<div className="flex flex-col gap-4">
<div className="flex h-12 w-12 items-center justify-center rounded-lg bg-primary/10">
<RiCalendarLine size={24} className="text-primary" />
</div>
<div>
<h3 className="font-semibold text-lg mb-2">Calendário</h3>
<h3 className="font-semibold text-lg mb-2">
Calendário financeiro
</h3>
<p className="text-sm text-muted-foreground">
Visualize suas transações em calendário mensal e nunca
Visualize suas transações em calendário mensal. Nunca
perca prazos importantes.
</p>
</div>
@@ -247,167 +292,343 @@ export default async function Page() {
</div>
</section>
{/* Benefits Section */}
<section className="py-16 md:py-24">
{/* Tech Stack Section */}
<section id="stack" className="py-16 md:py-24">
<div className="container">
<div className="mx-auto max-w-5xl">
<div className="grid gap-12 lg:grid-cols-2 items-center">
<div>
<Badge variant="secondary" className="mb-4">
Vantagens
<div className="text-center mb-12">
<Badge variant="primary" className="mb-4">
Stack técnica
</Badge>
<h2 className="text-3xl md:text-4xl font-bold tracking-tight mb-6">
Controle financeiro descomplicado
<h2 className="text-3xl md:text-4xl font-bold tracking-tight mb-4">
Construído com tecnologias modernas
</h2>
<div className="space-y-6">
<div className="flex gap-4">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary/10">
<RiShieldCheckLine size={20} className="text-primary" />
<p className="text-lg text-muted-foreground max-w-2xl mx-auto">
Open source, self-hosted e fácil de customizar
</p>
</div>
<div className="grid gap-6 md:grid-cols-2">
<Card className="border-[1.5px]">
<CardContent>
<div className="flex items-start gap-4">
<RiCodeSSlashLine
size={32}
className="text-primary shrink-0"
/>
<div>
<h3 className="font-semibold mb-1">
Segurança em Primeiro Lugar
</h3>
<p className="text-sm text-muted-foreground">
Seus dados financeiros são criptografados e armazenados
com os mais altos padrões de segurança.
<h3 className="font-semibold text-lg mb-2">Frontend</h3>
<p className="text-sm text-muted-foreground mb-3">
Next.js 16, TypeScript, Tailwind CSS, shadcn/ui
</p>
<p className="text-xs text-muted-foreground">
Interface moderna e responsiva com React 19 e App Router
</p>
</div>
</div>
</CardContent>
</Card>
<Card className="border-[1.5px]">
<CardContent>
<div className="flex items-start gap-4">
<RiDatabase2Line
size={32}
className="text-primary shrink-0"
/>
<div>
<h3 className="font-semibold text-lg mb-2">Backend</h3>
<p className="text-sm text-muted-foreground mb-3">
PostgreSQL 18, Drizzle ORM, Better Auth
</p>
<p className="text-xs text-muted-foreground">
Banco relacional robusto com type-safe ORM
</p>
</div>
</div>
</CardContent>
</Card>
<Card className="border-[1.5px]">
<CardContent>
<div className="flex items-start gap-4">
<RiShieldCheckLine
size={32}
className="text-primary shrink-0"
/>
<div>
<h3 className="font-semibold text-lg mb-2">Segurança</h3>
<p className="text-sm text-muted-foreground mb-3">
Better Auth com OAuth (Google) e autenticação por email
</p>
<p className="text-xs text-muted-foreground">
Sessões seguras e proteção de rotas por middleware
</p>
</div>
</div>
</CardContent>
</Card>
<Card className="border-[1.5px]">
<CardContent>
<div className="flex items-start gap-4">
<RiDeviceLine size={32} className="text-primary shrink-0" />
<div>
<h3 className="font-semibold text-lg mb-2">Deploy</h3>
<p className="text-sm text-muted-foreground mb-3">
Docker com multi-stage build, health checks e volumes
persistentes
</p>
<p className="text-xs text-muted-foreground">
Fácil de rodar localmente ou em qualquer servidor
</p>
</div>
</div>
</CardContent>
</Card>
</div>
<div className="mt-8 text-center">
<p className="text-sm text-muted-foreground">
Seus dados ficam no seu controle. Pode rodar localmente ou no
seu próprio servidor.
</p>
</div>
</div>
</div>
</section>
{/* How to run Section */}
<section id="como-usar" className="py-16 md:py-24">
<div className="container">
<div className="mx-auto max-w-3xl">
<div className="text-center mb-12">
<Badge variant="primary" className="mb-4">
Como usar
</Badge>
<h2 className="text-3xl md:text-4xl font-bold tracking-tight mb-4">
Rode no seu computador
</h2>
<p className="text-lg text-muted-foreground">
Não versão hospedada online. Você precisa rodar localmente.
</p>
</div>
<div className="space-y-6">
<Card className="border-[1.5px]">
<CardContent>
<div className="flex gap-4">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground font-bold">
1
</div>
<div>
<h3 className="font-semibold mb-2">
Clone o repositório
</h3>
<code className="text-sm bg-muted px-2 py-1 rounded">
git clone
https://github.com/felipegcoutinho/opensheets-app.git
</code>
</div>
</div>
</CardContent>
</Card>
<Card className="border-[1.5px]">
<CardContent>
<div className="flex gap-4">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground font-bold">
2
</div>
<div>
<h3 className="font-semibold mb-2">
Configure as variáveis de ambiente
</h3>
<p className="text-sm text-muted-foreground">
Copie o{" "}
<code className="bg-muted px-1 rounded">
.env.example
</code>{" "}
para <code className="bg-muted px-1 rounded">.env</code>{" "}
e configure o banco de dados
</p>
</div>
</div>
</CardContent>
</Card>
<Card className="border-[1.5px]">
<CardContent>
<div className="flex gap-4">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground font-bold">
3
</div>
<div>
<h3 className="font-semibold mb-2">
Suba o banco via Docker
</h3>
<code className="text-sm bg-muted px-2 py-1 rounded">
docker compose up db -d
</code>
</div>
</div>
</CardContent>
</Card>
<Card className="border-[1.5px]">
<CardContent>
<div className="flex gap-4">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-primary text-primary-foreground font-bold">
4
</div>
<div>
<h3 className="font-semibold mb-2">
Rode a aplicação localmente
</h3>
<div className="space-y-2">
<code className="block text-sm bg-muted px-2 py-1 rounded">
pnpm install
</code>
<code className="block text-sm bg-muted px-2 py-1 rounded">
pnpm db:push
</code>
<code className="block text-sm bg-muted px-2 py-1 rounded">
pnpm dev
</code>
</div>
</div>
</div>
</CardContent>
</Card>
</div>
<div className="mt-8 text-center">
<Link
href="https://github.com/felipegcoutinho/opensheets-app#-início-rápido"
target="_blank"
className="text-sm text-primary hover:underline"
>
Ver documentação completa
</Link>
</div>
</div>
</div>
</section>
{/* Who is this for Section */}
<section className="py-16 md:py-24 bg-muted/30">
<div className="container">
<div className="mx-auto max-w-3xl">
<div className="text-center mb-12">
<h2 className="text-3xl md:text-4xl font-bold tracking-tight mb-4">
Para quem funciona?
</h2>
<p className="text-lg text-muted-foreground">
O OpenSheets funciona melhor se você:
</p>
</div>
<div className="space-y-4">
<Card className="border-[1.5px]">
<CardContent>
<div className="flex gap-4">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary/10">
<RiTimeLine size={20} className="text-primary" />
</div>
<div>
<h3 className="font-semibold mb-1">Economize Tempo</h3>
<h3 className="font-semibold mb-1">
Tem disciplina de registrar gastos
</h3>
<p className="text-sm text-muted-foreground">
Interface intuitiva que permite registrar transações em
segundos e acompanhar tudo de forma visual.
Não se importa em dedicar alguns minutos por dia ou
semana para manter tudo atualizado
</p>
</div>
</div>
</CardContent>
</Card>
<Card className="border-[1.5px]">
<CardContent>
<div className="flex gap-4">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary/10">
<RiNotificationLine size={20} className="text-primary" />
<RiLockLine size={20} className="text-primary" />
</div>
<div>
<h3 className="font-semibold mb-1">
Alertas Inteligentes
Quer controle total sobre seus dados
</h3>
<p className="text-sm text-muted-foreground">
Receba notificações sobre vencimentos, limites de
orçamento e padrões incomuns de gastos.
Prefere hospedar seus próprios dados ao invés de
depender de serviços terceiros
</p>
</div>
</div>
</CardContent>
</Card>
<Card className="border-[1.5px]">
<CardContent>
<div className="flex gap-4">
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-primary/10">
<RiEyeOffLine size={20} className="text-primary" />
<RiLineChartLine size={20} className="text-primary" />
</div>
<div>
<h3 className="font-semibold mb-1">Modo Privacidade</h3>
<p className="text-sm text-muted-foreground">
Oculte valores sensíveis com um clique para visualizar
suas finanças em qualquer lugar com discrição.
</p>
</div>
</div>
</div>
</div>
<div className="space-y-4">
<Card className="border-2">
<CardContent className="pt-6">
<div className="flex items-start gap-4">
<RiLineChartLine
size={32}
className="text-primary shrink-0"
/>
<div>
<h3 className="font-semibold text-lg mb-2">
Visualização Clara
<h3 className="font-semibold mb-1">
Gosta de entender exatamente onde o dinheiro vai
</h3>
<p className="text-sm text-muted-foreground">
Gráficos interativos e dashboards personalizáveis
mostram sua situação financeira de forma clara e
objetiva.
</p>
</div>
</div>
</CardContent>
</Card>
<Card className="border-2">
<CardContent className="pt-6">
<div className="flex items-start gap-4">
<RiDeviceLine
size={32}
className="text-primary shrink-0"
/>
<div>
<h3 className="font-semibold text-lg mb-2">
Acesso em Qualquer Lugar
</h3>
<p className="text-sm text-muted-foreground">
Design responsivo que funciona perfeitamente em
desktop, tablet e smartphone. Suas finanças sempre à
mão.
</p>
</div>
</div>
</CardContent>
</Card>
<Card className="border-2">
<CardContent className="pt-6">
<div className="flex items-start gap-4">
<RiLockLine size={32} className="text-primary shrink-0" />
<div>
<h3 className="font-semibold text-lg mb-2">
Privacidade Garantida
</h3>
<p className="text-sm text-muted-foreground">
Seus dados são seus. Sem compartilhamento com
terceiros, sem anúncios, sem surpresas.
Quer visualizar padrões de gastos e tomar decisões
informadas
</p>
</div>
</div>
</CardContent>
</Card>
</div>
<div className="mt-8 rounded-lg border bg-background p-6 text-center">
<p className="text-sm text-muted-foreground">
Se você não se encaixa nisso, provavelmente vai abandonar depois
de uma semana. E tudo bem! Existem outras ferramentas com
sincronização automática que podem funcionar melhor pra você.
</p>
</div>
</div>
</div>
</section>
{/* CTA Section */}
<section className="py-16 md:py-24 bg-muted/30">
<section className="py-16 md:py-24">
<div className="container">
<div className="mx-auto max-w-3xl text-center">
<h2 className="text-3xl md:text-4xl font-bold tracking-tight mb-4">
Pronto para transformar suas finanças?
Pronto para testar?
</h2>
<p className="text-lg text-muted-foreground mb-8">
Comece agora mesmo a organizar seu dinheiro de forma inteligente.
É grátis e leva menos de um minuto.
Clone o repositório, rode localmente e veja se faz sentido pra
você. É open source e gratuito.
</p>
<div className="flex flex-col sm:flex-row gap-4 justify-center">
<Link href="/signup">
<Link
href="https://github.com/felipegcoutinho/opensheets-app"
target="_blank"
>
<Button size="lg" className="gap-2 w-full sm:w-auto">
Criar Conta Gratuita
<RiArrowRightSLine size={18} />
<RiGithubFill size={18} />
Baixar Projeto
</Button>
</Link>
<Link href="/login">
<Link
href="https://github.com/felipegcoutinho/opensheets-app#-início-rápido"
target="_blank"
>
<Button
size="lg"
variant="outline"
className="w-full sm:w-auto"
className="w-full sm:w-auto gap-2"
>
tenho conta
Como Instalar
</Button>
</Link>
</div>
@@ -418,113 +639,70 @@ export default async function Page() {
{/* Footer */}
<footer className="border-t py-12 mt-auto">
<div className="container">
<div className="grid gap-8 md:grid-cols-2 lg:grid-cols-4">
<div className="mx-auto max-w-5xl">
<div className="grid gap-8 md:grid-cols-3">
<div>
<Logo />
<p className="text-sm text-muted-foreground mt-4">
Gerencie suas finanças pessoais com simplicidade e segurança.
Projeto pessoal de gestão financeira. Open source e
self-hosted.
</p>
</div>
<div>
<h3 className="font-semibold mb-4">Produto</h3>
<h3 className="font-semibold mb-4">Projeto</h3>
<ul className="space-y-3 text-sm text-muted-foreground">
<li>
<Link
href="/login"
className="hover:text-foreground transition-colors"
href="https://github.com/felipegcoutinho/opensheets"
target="_blank"
className="hover:text-foreground transition-colors flex items-center gap-2"
>
Funcionalidades
<RiGithubFill size={16} />
GitHub
</Link>
</li>
<li>
<Link
href="/login"
href="https://github.com/felipegcoutinho/opensheets#readme"
target="_blank"
className="hover:text-foreground transition-colors"
>
Preços
Documentação
</Link>
</li>
<li>
<Link
href="/login"
href="https://github.com/felipegcoutinho/opensheets/issues"
target="_blank"
className="hover:text-foreground transition-colors"
>
Segurança
Reportar Bug
</Link>
</li>
</ul>
</div>
<div>
<h3 className="font-semibold mb-4">Recursos</h3>
<h3 className="font-semibold mb-4">Stack</h3>
<ul className="space-y-3 text-sm text-muted-foreground">
<li>
<Link
href="/login"
className="hover:text-foreground transition-colors"
>
Blog
</Link>
</li>
<li>
<Link
href="/login"
className="hover:text-foreground transition-colors"
>
Ajuda
</Link>
</li>
<li>
<Link
href="/login"
className="hover:text-foreground transition-colors"
>
Tutoriais
</Link>
</li>
</ul>
</div>
<div>
<h3 className="font-semibold mb-4">Legal</h3>
<ul className="space-y-3 text-sm text-muted-foreground">
<li>
<Link
href="/login"
className="hover:text-foreground transition-colors"
>
Privacidade
</Link>
</li>
<li>
<Link
href="/login"
className="hover:text-foreground transition-colors"
>
Termos de Uso
</Link>
</li>
<li>
<Link
href="/login"
className="hover:text-foreground transition-colors"
>
Cookies
</Link>
</li>
<li>Next.js 16 + TypeScript</li>
<li>PostgreSQL 18 + Drizzle ORM</li>
<li>Better Auth + shadcn/ui</li>
<li>Docker + Docker Compose</li>
</ul>
</div>
</div>
<div className="border-t mt-12 pt-8 flex flex-col md:flex-row justify-between items-center gap-4 text-sm text-muted-foreground">
<p>
© {new Date().getFullYear()} OpenSheets. Todos os direitos
reservados.
© {new Date().getFullYear()} OpenSheets. Projeto open source sob
licença MIT.
</p>
<div className="flex items-center gap-2">
<RiShieldCheckLine size={16} className="text-primary" />
<span>Seus dados são protegidos e criptografados</span>
<span>Seus dados, seu servidor</span>
</div>
</div>
</div>
</div>

View File

@@ -178,6 +178,11 @@
@apply border-border outline-ring/50;
}
html {
scroll-behavior: smooth;
scroll-padding-top: 80px; /* Offset para o header sticky */
}
body {
@apply bg-background text-foreground;
}

View File

@@ -4,6 +4,7 @@ import { Toaster } from "@/components/ui/sonner";
import { main_font } from "@/public/fonts/font_index";
import type { Metadata } from "next";
import "./globals.css";
import { Analytics } from "@vercel/analytics/next";
export const metadata: Metadata = {
title: "OpenSheets",
@@ -17,13 +18,6 @@ export default function RootLayout({
}>) {
return (
<html lang="en" suppressHydrationWarning>
<head>
<script
defer
src="https://umami.felipecoutinho.com/script.js"
data-website-id="42f8519e-de88-467e-8969-d13a76211e43"
></script>
</head>
<body
className={`${main_font.className} antialiased`}
suppressHydrationWarning
@@ -34,6 +28,7 @@ export default function RootLayout({
<Toaster position="top-right" />
</PrivacyProvider>
</ThemeProvider>
<Analytics />
</body>
</html>
);

View File

@@ -7,7 +7,7 @@ function Card({ className, ...props }: React.ComponentProps<"div">) {
<div
data-slot="card"
className={cn(
"bg-card text-card-foreground flex flex-col gap-6 border drop-shadow-xs py-6 rounded-md hover:border-primary/50 transition-colors",
"bg-card text-card-foreground flex flex-col gap-6 border-[1.5px] drop-shadow-xs py-6 rounded-md hover:border-primary/50 transition-colors",
className
)}
{...props}

View File

@@ -14,7 +14,8 @@
"db:push": "drizzle-kit push",
"db:studio": "drizzle-kit studio",
"docker:up": "docker compose up --build",
"docker:up:detached": "docker compose up --build -d",
"docker:up:db": "docker compose up -d db",
"docker:up:d": "docker compose up --build -d",
"docker:down": "docker compose down",
"docker:down:volumes": "docker compose down -v",
"docker:logs": "docker compose logs -f",
@@ -27,10 +28,6 @@
"@ai-sdk/anthropic": "^2.0.44",
"@ai-sdk/google": "^2.0.31",
"@ai-sdk/openai": "^2.0.66",
"@dnd-kit/core": "6.3.1",
"@dnd-kit/modifiers": "9.0.0",
"@dnd-kit/sortable": "10.0.0",
"@dnd-kit/utilities": "3.2.2",
"@openrouter/ai-sdk-provider": "^1.2.2",
"@radix-ui/react-alert-dialog": "1.1.15",
"@radix-ui/react-avatar": "1.1.11",
@@ -53,6 +50,7 @@
"@radix-ui/react-tooltip": "1.2.8",
"@remixicon/react": "4.7.0",
"@tanstack/react-table": "8.21.3",
"@vercel/analytics": "^1.5.0",
"ai": "^5.0.93",
"better-auth": "1.3.34",
"class-variance-authority": "0.7.1",
@@ -80,6 +78,7 @@
"@types/node": "24.10.1",
"@types/react": "19.2.4",
"@types/react-dom": "19.2.3",
"depcheck": "^1.4.7",
"drizzle-kit": "0.31.7",
"eslint": "9.39.1",
"eslint-config-next": "16.0.3",

564
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

4
pnpm-workspace.yaml Normal file
View File

@@ -0,0 +1,4 @@
onlyBuiltDependencies:
- esbuild
- sharp
- unrs-resolver