feat: adicionar versão na sidebar e atualizar documentação
- Exibir versão (v1.2.5) ao lado do logo na sidebar - Adicionar link do repositório Companion na aba de configurações - Atualizar README com documentação completa do OpenSheets Companion - Atualizar versões das dependências e estrutura do banco de dados - Adicionar logo do Creditas - Remover fontes não utilizadas - Atualizar logo_text.png Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
246
README.md
246
README.md
@@ -12,6 +12,7 @@
|
|||||||
[](https://www.typescriptlang.org/)
|
[](https://www.typescriptlang.org/)
|
||||||
[](https://www.postgresql.org/)
|
[](https://www.postgresql.org/)
|
||||||
[](https://www.docker.com/)
|
[](https://www.docker.com/)
|
||||||
|
[](https://github.com/felipegcoutinho/opensheets-companion)
|
||||||
[](LICENSE)
|
[](LICENSE)
|
||||||
[](https://github.com/sponsors/felipegcoutinho)
|
[](https://github.com/sponsors/felipegcoutinho)
|
||||||
|
|
||||||
@@ -37,6 +38,7 @@
|
|||||||
- [Configuração de Variáveis de Ambiente](#-configuração-de-variáveis-de-ambiente)
|
- [Configuração de Variáveis de Ambiente](#-configuração-de-variáveis-de-ambiente)
|
||||||
- [Banco de Dados](#-banco-de-dados)
|
- [Banco de Dados](#-banco-de-dados)
|
||||||
- [Arquitetura](#-arquitetura)
|
- [Arquitetura](#-arquitetura)
|
||||||
|
- [Destaques e Funcionalidades Recentes](#-destaques-e-funcionalidades-recentes)
|
||||||
- [Contribuindo](#-contribuindo)
|
- [Contribuindo](#-contribuindo)
|
||||||
- [Apoie o Projeto](#-apoie-o-projeto)
|
- [Apoie o Projeto](#-apoie-o-projeto)
|
||||||
|
|
||||||
@@ -51,13 +53,14 @@ A ideia é simples: ter um lugar onde consigo ver todas as minhas contas, cartõ
|
|||||||
### 📊 Estatísticas do Projeto
|
### 📊 Estatísticas do Projeto
|
||||||
|
|
||||||
- **~200 componentes React** organizados por feature
|
- **~200 componentes React** organizados por feature
|
||||||
- **15+ tabelas de banco de dados** com relações complexas
|
- **17+ tabelas de banco de dados** com relações complexas
|
||||||
- **20+ widgets** no dashboard principal
|
- **20+ widgets** no dashboard principal
|
||||||
- **18+ queries paralelas** otimizadas para performance
|
- **18+ queries paralelas** otimizadas para performance
|
||||||
- **736 linhas** de schema Drizzle ORM
|
- **~820 linhas** de schema Drizzle ORM
|
||||||
- **Docker multi-stage** com imagem final de ~200MB
|
- **Docker multi-stage** com imagem final de ~200MB
|
||||||
- **100% TypeScript** com strict mode
|
- **100% TypeScript** com strict mode
|
||||||
- **Self-hosted** - seus dados, seu controle
|
- **Self-hosted** - seus dados, seu controle
|
||||||
|
- **App Companion Android** - captura automática de notificações bancárias
|
||||||
|
|
||||||
> 💡 **Licença Não-Comercial:** Este projeto é gratuito para uso pessoal, mas não pode ser usado comercialmente. Veja mais detalhes na seção [Licença](#-licença).
|
> 💡 **Licença Não-Comercial:** Este projeto é gratuito para uso pessoal, mas não pode ser usado comercialmente. Veja mais detalhes na seção [Licença](#-licença).
|
||||||
|
|
||||||
@@ -71,7 +74,14 @@ Este projeto é self-hosted. Você precisa rodar no seu próprio computador ou s
|
|||||||
|
|
||||||
Você precisa registrar manualmente suas transações. Se você procura algo que sincroniza automaticamente com seu banco, este projeto não é pra você.
|
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**
|
**3. 🤖 OpenSheets Companion (Android)**
|
||||||
|
|
||||||
|
Existe um app Android complementar que captura notificações de transações dos seus apps de banco (Nubank, Itaú, Bradesco, Inter, C6 e outros) e envia automaticamente para sua caixa de entrada no OpenSheets. As notificações ficam como "pré-lançamentos" para você revisar e aprovar.
|
||||||
|
|
||||||
|
- **Repositório:** [github.com/felipegcoutinho/opensheets-companion](https://github.com/felipegcoutinho/opensheets-companion)
|
||||||
|
- **Configuração:** Ajustes → OpenSheets Companion → Gere um token de API
|
||||||
|
|
||||||
|
**4. Requer disciplina**
|
||||||
|
|
||||||
O Opensheets funciona melhor para quem:
|
O Opensheets funciona melhor para quem:
|
||||||
|
|
||||||
@@ -147,6 +157,21 @@ Se você não se importa em dedicar alguns minutos por dia (ou semana) para mant
|
|||||||
- Navegação intuitiva por data
|
- Navegação intuitiva por data
|
||||||
- Filtros e organização temporal
|
- Filtros e organização temporal
|
||||||
|
|
||||||
|
📲 **OpenSheets Companion (Android)**
|
||||||
|
|
||||||
|
- App Android que captura notificações bancárias
|
||||||
|
- Suporte a Nubank, Itaú, Bradesco, Inter, C6 e outros
|
||||||
|
- Pré-lançamentos para revisão antes de aprovar
|
||||||
|
- Autenticação via tokens de API seguros
|
||||||
|
- Sincronização automática em segundo plano
|
||||||
|
|
||||||
|
📊 **Relatórios avançados**
|
||||||
|
|
||||||
|
- Tendências de categorias ao longo do tempo
|
||||||
|
- Análise de uso de cartões de crédito
|
||||||
|
- Top estabelecimentos mais frequentes
|
||||||
|
- Filtros por período personalizáveis
|
||||||
|
|
||||||
⚙️ **Preferências e personalização**
|
⚙️ **Preferências e personalização**
|
||||||
|
|
||||||
- Tema claro/escuro
|
- Tema claro/escuro
|
||||||
@@ -175,12 +200,13 @@ O projeto é open source, seus dados ficam no seu controle (pode rodar localment
|
|||||||
|
|
||||||
### 🔐 Autenticação
|
### 🔐 Autenticação
|
||||||
|
|
||||||
- Better Auth 1.4.10 integrado
|
- Better Auth 1.4.18 integrado
|
||||||
- OAuth (Google)
|
- OAuth (Google)
|
||||||
- Autenticação por email/senha
|
- Autenticação por email/senha
|
||||||
- Session management com tokens
|
- Session management com tokens
|
||||||
- Protected routes via middleware
|
- Protected routes via middleware
|
||||||
- Verificação de email
|
- Verificação de email
|
||||||
|
- Tokens de API para integrações externas
|
||||||
|
|
||||||
### 🗄️ Banco de Dados
|
### 🗄️ Banco de Dados
|
||||||
|
|
||||||
@@ -238,11 +264,21 @@ O projeto é open source, seus dados ficam no seu controle (pode rodar localment
|
|||||||
- shadcn/ui components (Radix UI)
|
- shadcn/ui components (Radix UI)
|
||||||
- Tailwind CSS v4
|
- Tailwind CSS v4
|
||||||
- Dark mode com next-themes
|
- Dark mode com next-themes
|
||||||
- Animações fluidas com Motion
|
- Drag-and-drop com dnd-kit
|
||||||
- Responsive design
|
- Responsive design
|
||||||
- Modo privacidade (oculta valores)
|
- Modo privacidade (oculta valores)
|
||||||
- Componentes acessíveis (ARIA)
|
- Componentes acessíveis (ARIA)
|
||||||
|
|
||||||
|
### 📲 OpenSheets Companion
|
||||||
|
|
||||||
|
- App Android para captura de notificações bancárias
|
||||||
|
- Suporte a múltiplos bancos (Nubank, Itaú, Bradesco, Inter, C6, etc.)
|
||||||
|
- Caixa de entrada (pré-lançamentos) para revisão
|
||||||
|
- Tokens de API com hash SHA-256 (nunca armazenados em texto)
|
||||||
|
- Rate limiting (100 requests/min por usuário)
|
||||||
|
- API batch para envio de múltiplas notificações
|
||||||
|
- Endpoint de health check para validação
|
||||||
|
|
||||||
### 📝 Produtividade
|
### 📝 Produtividade
|
||||||
|
|
||||||
- Sistema de anotações e tarefas
|
- Sistema de anotações e tarefas
|
||||||
@@ -263,11 +299,11 @@ O projeto é open source, seus dados ficam no seu controle (pode rodar localment
|
|||||||
|
|
||||||
### 🧪 Desenvolvimento
|
### 🧪 Desenvolvimento
|
||||||
|
|
||||||
- Next.js 16.1 com App Router
|
- Next.js 16.1.6 com App Router
|
||||||
- Turbopack (fast refresh)
|
- Turbopack (fast refresh)
|
||||||
- TypeScript 5.9 (strict mode)
|
- TypeScript 5.9.3 (strict mode)
|
||||||
- Biome (linting + formatting)
|
- Biome 2.x (linting + formatting + import organization)
|
||||||
- React 19.2 (com Compiler)
|
- React 19.2.4 (com Compiler)
|
||||||
- Server Actions
|
- Server Actions
|
||||||
- Parallel data fetching
|
- Parallel data fetching
|
||||||
- Streaming SSR
|
- Streaming SSR
|
||||||
@@ -278,16 +314,16 @@ O projeto é open source, seus dados ficam no seu controle (pode rodar localment
|
|||||||
|
|
||||||
### Frontend
|
### Frontend
|
||||||
|
|
||||||
- **Framework:** Next.js 16.1.1 (App Router)
|
- **Framework:** Next.js 16.1.6 (App Router)
|
||||||
- **Linguagem:** TypeScript 5.9.3
|
- **Linguagem:** TypeScript 5.9.3
|
||||||
- **UI Library:** React 19.2.3
|
- **UI Library:** React 19.2.4
|
||||||
- **Styling:** Tailwind CSS 4.1.18
|
- **Styling:** Tailwind CSS 4.1.18
|
||||||
- **Components:** shadcn/ui (Radix UI)
|
- **Components:** shadcn/ui (Radix UI)
|
||||||
- **Icons:** Remixicon 4.8.0
|
- **Icons:** Remixicon 4.9.0
|
||||||
- **Animations:** Motion 12.23.26
|
- **Drag & Drop:** dnd-kit
|
||||||
- **Tables:** TanStack React Table 8.21.3
|
- **Tables:** TanStack React Table 8.21.3
|
||||||
- **Charts:** Recharts 3.6.0
|
- **Charts:** Recharts 3.7.0
|
||||||
- **Forms:** React Hook Form + Zod 4.3.4
|
- **Validation:** Zod 4.3.6
|
||||||
- **Theme:** next-themes 0.4.6
|
- **Theme:** next-themes 0.4.6
|
||||||
|
|
||||||
### Backend
|
### Backend
|
||||||
@@ -295,18 +331,18 @@ O projeto é open source, seus dados ficam no seu controle (pode rodar localment
|
|||||||
- **Runtime:** Node.js 22
|
- **Runtime:** Node.js 22
|
||||||
- **Database:** PostgreSQL 18
|
- **Database:** PostgreSQL 18
|
||||||
- **ORM:** Drizzle ORM 0.45.1
|
- **ORM:** Drizzle ORM 0.45.1
|
||||||
- **Database Driver:** pg 8.16.3
|
- **Database Driver:** pg 8.18.0
|
||||||
- **Auth:** Better Auth 1.4.10
|
- **Auth:** Better Auth 1.4.18
|
||||||
- **Email:** Resend 6.6.0
|
- **Email:** Resend 6.9.1
|
||||||
- **Validation:** Zod 4.3.4
|
- **Validation:** Zod 4.3.6
|
||||||
|
|
||||||
### AI Integration (Opcional)
|
### AI Integration (Opcional)
|
||||||
|
|
||||||
- **AI SDK:** Vercel AI SDK 6.0.6
|
- **AI SDK:** Vercel AI SDK 6.0.67
|
||||||
- **Anthropic:** Claude (via @ai-sdk/anthropic 3.0.2)
|
- **Anthropic:** Claude (via @ai-sdk/anthropic 3.0.35)
|
||||||
- **OpenAI:** GPT (via @ai-sdk/openai 3.0.2)
|
- **OpenAI:** GPT (via @ai-sdk/openai 3.0.25)
|
||||||
- **Google:** Gemini (via @ai-sdk/google 3.0.2)
|
- **Google:** Gemini (via @ai-sdk/google 3.0.20)
|
||||||
- **OpenRouter:** via @openrouter/ai-sdk-provider 1.5.4
|
- **OpenRouter:** via @openrouter/ai-sdk-provider 2.1.1
|
||||||
|
|
||||||
### Utilities
|
### Utilities
|
||||||
|
|
||||||
@@ -322,7 +358,8 @@ O projeto é open source, seus dados ficam no seu controle (pode rodar localment
|
|||||||
- **Containerization:** Docker + Docker Compose
|
- **Containerization:** Docker + Docker Compose
|
||||||
- **Package Manager:** pnpm
|
- **Package Manager:** pnpm
|
||||||
- **Build Tool:** Turbopack
|
- **Build Tool:** Turbopack
|
||||||
- **Linting & Formatting:** Biome 2.x
|
- **Linting & Formatting:** Biome 2.3.13
|
||||||
|
- **Database Tools:** Drizzle Kit 0.31.8
|
||||||
- **Analytics:** Vercel Analytics + Speed Insights
|
- **Analytics:** Vercel Analytics + Speed Insights
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -869,7 +906,10 @@ opensheets/
|
|||||||
├── app/ # Next.js App Router
|
├── app/ # Next.js App Router
|
||||||
│ ├── api/ # API Routes
|
│ ├── api/ # API Routes
|
||||||
│ │ ├── auth/[...all]/ # Better Auth endpoints
|
│ │ ├── auth/[...all]/ # Better Auth endpoints
|
||||||
│ │ └── health/ # Health check endpoint
|
│ │ ├── health/ # Health check (Companion validation)
|
||||||
|
│ │ └── inbox/ # API para Companion App
|
||||||
|
│ │ ├── route.ts # POST - Enviar notificação única
|
||||||
|
│ │ └── batch/route.ts # POST - Enviar múltiplas notificações
|
||||||
│ ├── (auth)/ # Rotas públicas de autenticação
|
│ ├── (auth)/ # Rotas públicas de autenticação
|
||||||
│ │ ├── login/ # Página de login
|
│ │ ├── login/ # Página de login
|
||||||
│ │ └── signup/ # Página de cadastro
|
│ │ └── signup/ # Página de cadastro
|
||||||
@@ -877,6 +917,7 @@ opensheets/
|
|||||||
│ │ ├── dashboard/ # Dashboard principal
|
│ │ ├── dashboard/ # Dashboard principal
|
||||||
│ │ │ └── analise-parcelas/ # Análise de parcelas
|
│ │ │ └── analise-parcelas/ # Análise de parcelas
|
||||||
│ │ ├── lancamentos/ # Lançamentos/transações
|
│ │ ├── lancamentos/ # Lançamentos/transações
|
||||||
|
│ │ ├── pre-lancamentos/ # Caixa de entrada (Companion)
|
||||||
│ │ ├── contas/ # Contas bancárias
|
│ │ ├── contas/ # Contas bancárias
|
||||||
│ │ │ └── [contaId]/extrato # Extrato da conta
|
│ │ │ └── [contaId]/extrato # Extrato da conta
|
||||||
│ │ ├── cartoes/ # Cartões de crédito
|
│ │ ├── cartoes/ # Cartões de crédito
|
||||||
@@ -891,7 +932,9 @@ opensheets/
|
|||||||
│ │ │ └── arquivadas/ # Anotações arquivadas
|
│ │ │ └── arquivadas/ # Anotações arquivadas
|
||||||
│ │ ├── insights/ # Insights de IA
|
│ │ ├── insights/ # Insights de IA
|
||||||
│ │ ├── relatorios/ # Relatórios
|
│ │ ├── relatorios/ # Relatórios
|
||||||
│ │ │ └── categorias/ # Relatório de categorias
|
│ │ │ ├── tendencias/ # Tendências de categorias
|
||||||
|
│ │ │ └── uso-cartoes/ # Análise de cartões
|
||||||
|
│ │ ├── top-estabelecimentos/ # Top estabelecimentos
|
||||||
│ │ ├── calendario/ # Visão de calendário
|
│ │ ├── calendario/ # Visão de calendário
|
||||||
│ │ ├── changelog/ # Histórico de mudanças
|
│ │ ├── changelog/ # Histórico de mudanças
|
||||||
│ │ └── ajustes/ # Configurações
|
│ │ └── ajustes/ # Configurações
|
||||||
@@ -923,8 +966,11 @@ opensheets/
|
|||||||
│ ├── anotacoes/ # Componentes de anotações
|
│ ├── anotacoes/ # Componentes de anotações
|
||||||
│ ├── insights/ # Componentes de insights IA
|
│ ├── insights/ # Componentes de insights IA
|
||||||
│ ├── relatorios/ # Componentes de relatórios
|
│ ├── relatorios/ # Componentes de relatórios
|
||||||
|
│ ├── pre-lancamentos/ # Componentes da caixa de entrada
|
||||||
|
│ ├── top-estabelecimentos/ # Top estabelecimentos
|
||||||
│ ├── calendario/ # Componentes de calendário
|
│ ├── calendario/ # Componentes de calendário
|
||||||
│ ├── calculadora/ # Calculadora integrada
|
│ ├── calculadora/ # Calculadora integrada
|
||||||
|
│ ├── ajustes/ # Configurações e Companion
|
||||||
│ ├── sidebar/ # Sidebar de navegação
|
│ ├── sidebar/ # Sidebar de navegação
|
||||||
│ ├── skeletons/ # Estados de loading
|
│ ├── skeletons/ # Estados de loading
|
||||||
│ └── month-picker/ # Seletor de mês/período
|
│ └── month-picker/ # Seletor de mês/período
|
||||||
@@ -1003,7 +1049,8 @@ opensheets/
|
|||||||
|
|
||||||
| Diretório | Descrição | Arquivos |
|
| Diretório | Descrição | Arquivos |
|
||||||
| ------------------ | ------------------------------------------- | -------- |
|
| ------------------ | ------------------------------------------- | -------- |
|
||||||
| `app/(dashboard)/` | Páginas protegidas da aplicação | ~50 |
|
| `app/(dashboard)/` | Páginas protegidas da aplicação | ~60 |
|
||||||
|
| `app/api/` | Endpoints da API (auth, inbox, health) | ~5 |
|
||||||
| `components/` | Componentes React reutilizáveis | ~200 |
|
| `components/` | Componentes React reutilizáveis | ~200 |
|
||||||
| `lib/` | Lógica de negócio, helpers e utilitários | ~80 |
|
| `lib/` | Lógica de negócio, helpers e utilitários | ~80 |
|
||||||
| `db/` | Schema do banco de dados | 1 |
|
| `db/` | Schema do banco de dados | 1 |
|
||||||
@@ -1013,106 +1060,124 @@ opensheets/
|
|||||||
|
|
||||||
### Estrutura do Banco de Dados
|
### Estrutura do Banco de Dados
|
||||||
|
|
||||||
O OpenSheets possui um schema robusto com 15+ tabelas e relações complexas:
|
O OpenSheets possui um schema robusto com 17+ tabelas e relações complexas:
|
||||||
|
|
||||||
```
|
```
|
||||||
┌─────────────────────────────────────────────────────────────────┐
|
┌─────────────────────────────────────────────────────────────────┐
|
||||||
│ TABELAS PRINCIPAIS │
|
│ TABELAS PRINCIPAIS │
|
||||||
├─────────────────────────────────────────────────────────────────┤
|
├─────────────────────────────────────────────────────────────────┤
|
||||||
│ │
|
│ │
|
||||||
│ user user_preferences │
|
│ user preferenciasUsuario │
|
||||||
│ ├── id ├── id │
|
│ ├── id ├── id │
|
||||||
│ ├── name ├── user_id → user.id │
|
│ ├── name ├── userId → user.id │
|
||||||
│ ├── email ├── disable_magnetlines │
|
│ ├── email ├── disableMagnetlines │
|
||||||
│ └── ... └── ... │
|
│ └── ... ├── dashboardWidgets (JSON) │
|
||||||
|
│ └── ... │
|
||||||
│ │
|
│ │
|
||||||
│ contas cartoes │
|
│ contas cartoes │
|
||||||
│ ├── id ├── id │
|
│ ├── id ├── id │
|
||||||
│ ├── user_id → user.id ├── user_id → user.id │
|
│ ├── userId → user.id ├── userId → user.id │
|
||||||
│ ├── nome ├── conta_id → contas.id │
|
│ ├── nome ├── contaId → contas.id │
|
||||||
│ ├── tipo_conta ├── nome │
|
│ ├── tipoConta ├── nome │
|
||||||
│ ├── saldo_inicial ├── bandeira │
|
│ ├── saldoInicial ├── bandeira │
|
||||||
│ └── ... ├── dt_fechamento │
|
│ └── ... ├── dtFechamento │
|
||||||
│ ├── dt_vencimento │
|
│ ├── dtVencimento │
|
||||||
│ └── ... │
|
│ └── ... │
|
||||||
│ │
|
│ │
|
||||||
│ categorias pagadores │
|
│ categorias pagadores │
|
||||||
│ ├── id ├── id │
|
│ ├── id ├── id │
|
||||||
│ ├── user_id → user.id ├── user_id → user.id │
|
│ ├── userId → user.id ├── userId → user.id │
|
||||||
│ ├── nome ├── nome │
|
│ ├── nome ├── nome │
|
||||||
│ ├── tipo ├── email │
|
│ ├── tipo ├── email │
|
||||||
│ ├── icone ├── share_code (único) │
|
│ ├── icone ├── shareCode (único) │
|
||||||
│ └── ... ├── role │
|
│ └── ... ├── isAutoSend │
|
||||||
│ └── ... │
|
│ └── ... │
|
||||||
│ │
|
│ │
|
||||||
│ pagador_shares │
|
│ compartilhamentosPagador │
|
||||||
│ ├── id │
|
│ ├── id │
|
||||||
│ ├── pagador_id → pagadores.id │
|
│ ├── pagadorId → pagadores.id │
|
||||||
│ ├── shared_with_user_id → user.id │
|
│ ├── sharedWithUserId → user.id │
|
||||||
│ ├── created_by_user_id → user.id │
|
│ ├── createdByUserId → user.id │
|
||||||
│ ├── permission (read/write) │
|
│ ├── permission (read/write) │
|
||||||
│ └── ... │
|
│ └── ... │
|
||||||
│ │
|
│ │
|
||||||
│ lancamentos (TABELA PRINCIPAL) │
|
│ lancamentos (TABELA PRINCIPAL) │
|
||||||
│ ├── id │
|
│ ├── id │
|
||||||
│ ├── user_id → user.id │
|
│ ├── userId → user.id │
|
||||||
│ ├── conta_id → contas.id │
|
│ ├── contaId → contas.id │
|
||||||
│ ├── cartao_id → cartoes.id │
|
│ ├── cartaoId → cartoes.id │
|
||||||
│ ├── categoria_id → categorias.id │
|
│ ├── categoriaId → categorias.id │
|
||||||
│ ├── pagador_id → pagadores.id │
|
│ ├── pagadorId → pagadores.id │
|
||||||
│ ├── nome │
|
│ ├── nome │
|
||||||
│ ├── valor │
|
│ ├── valor │
|
||||||
│ ├── tipo_transacao (receita/despesa/transferencia) │
|
│ ├── tipoTransacao (receita/despesa/transferencia) │
|
||||||
│ ├── forma_pagamento │
|
│ ├── formaPagamento │
|
||||||
│ ├── condicao (aberto/realizado/cancelado) │
|
│ ├── condicao (aberto/realizado/cancelado) │
|
||||||
│ ├── data_compra │
|
│ ├── dataCompra │
|
||||||
│ ├── periodo (YYYY-MM) │
|
│ ├── periodo (YYYY-MM) │
|
||||||
│ ├── qtde_parcela │
|
│ ├── qtdeParcela │
|
||||||
│ ├── parcela_atual │
|
│ ├── parcelaAtual │
|
||||||
│ ├── series_id (agrupa parcelas) │
|
│ ├── seriesId (agrupa parcelas) │
|
||||||
│ ├── transfer_id (agrupa transferências) │
|
│ ├── transferId (agrupa transferências) │
|
||||||
│ ├── antecipado (boolean) │
|
│ ├── antecipado (boolean) │
|
||||||
│ ├── antecipacao_id → installment_anticipations.id │
|
│ ├── antecipacaoId → antecipacoesParcelas.id │
|
||||||
│ └── ... │
|
│ └── ... │
|
||||||
│ │
|
│ │
|
||||||
│ installment_anticipations │
|
│ antecipacoesParcelas │
|
||||||
│ ├── id │
|
│ ├── id │
|
||||||
│ ├── user_id → user.id │
|
│ ├── userId → user.id │
|
||||||
│ ├── series_id │
|
│ ├── seriesId │
|
||||||
│ ├── lancamento_id → lancamentos.id │
|
│ ├── lancamentoId → lancamentos.id │
|
||||||
│ ├── periodo_antecipacao │
|
│ ├── periodoAntecipacao │
|
||||||
│ ├── parcelas_antecipadas (JSONB array) │
|
│ ├── parcelasAntecipadas (JSONB array) │
|
||||||
│ ├── valor_total │
|
│ ├── valorTotal │
|
||||||
│ ├── desconto │
|
│ ├── desconto │
|
||||||
│ └── ... │
|
│ └── ... │
|
||||||
│ │
|
│ │
|
||||||
│ faturas orcamentos │
|
│ faturas orcamentos │
|
||||||
│ ├── id ├── id │
|
│ ├── id ├── id │
|
||||||
│ ├── user_id → user.id ├── user_id → user.id │
|
│ ├── userId → user.id ├── userId → user.id │
|
||||||
│ ├── cartao_id → cartoes ├── categoria_id → categorias.id │
|
│ ├── cartaoId → cartoes ├── categoriaId → categorias.id │
|
||||||
│ ├── periodo ├── valor │
|
│ ├── periodo ├── valor │
|
||||||
│ ├── status_pagamento ├── periodo │
|
│ ├── statusPagamento ├── periodo │
|
||||||
│ └── ... └── ... │
|
│ └── ... └── ... │
|
||||||
│ │
|
│ │
|
||||||
│ anotacoes saved_insights │
|
│ anotacoes insightsSalvos │
|
||||||
│ ├── id ├── id │
|
│ ├── id ├── id │
|
||||||
│ ├── user_id → user.id ├── user_id → user.id │
|
│ ├── userId → user.id ├── userId → user.id │
|
||||||
│ ├── titulo ├── period │
|
│ ├── titulo ├── period │
|
||||||
│ ├── descricao ├── model_id │
|
│ ├── descricao ├── modelId │
|
||||||
│ ├── tipo (nota/tarefa) ├── data (JSON) │
|
│ ├── tipo (nota/tarefa) ├── data (JSON) │
|
||||||
│ ├── tasks (JSON) ├── created_at │
|
│ ├── tasks (JSON) └── createdAt │
|
||||||
│ ├── arquivada └── updated_at │
|
│ ├── arquivada │
|
||||||
|
│ └── ... │
|
||||||
|
│ │
|
||||||
|
│ ══════════════════ COMPANION APP ══════════════════ │
|
||||||
|
│ │
|
||||||
|
│ tokensApi preLancamentos │
|
||||||
|
│ ├── id ├── id │
|
||||||
|
│ ├── userId → user.id ├── userId → user.id │
|
||||||
|
│ ├── name ├── sourceApp │
|
||||||
|
│ ├── tokenHash (SHA-256) ├── sourceAppName │
|
||||||
|
│ ├── tokenPrefix ├── originalTitle │
|
||||||
|
│ ├── lastUsedAt ├── originalText │
|
||||||
|
│ ├── lastUsedIp ├── parsedName │
|
||||||
|
│ ├── expiresAt ├── parsedAmount │
|
||||||
|
│ ├── revokedAt ├── status (pending/processed) │
|
||||||
|
│ └── createdAt ├── lancamentoId → lancamentos.id │
|
||||||
│ └── ... │
|
│ └── ... │
|
||||||
│ │
|
│ │
|
||||||
└─────────────────────────────────────────────────────────────────┘
|
└─────────────────────────────────────────────────────────────────┘
|
||||||
|
|
||||||
ÍNDICES OTIMIZADOS:
|
ÍNDICES OTIMIZADOS:
|
||||||
• user_id + period (queries do dashboard)
|
• userId + periodo (queries do dashboard)
|
||||||
• user_id + purchase_date (ordenação por data)
|
• userId + dataCompra (ordenação por data)
|
||||||
• series_id (agrupamento de parcelas)
|
• seriesId (agrupamento de parcelas)
|
||||||
• cartao_id + period (faturas)
|
• cartaoId + periodo (faturas)
|
||||||
• user_id + condition (filtros de condição)
|
• userId + condicao (filtros de condição)
|
||||||
• share_code (compartilhamento)
|
• shareCode (compartilhamento)
|
||||||
|
• tokenHash (autenticação API)
|
||||||
|
• userId + status (inbox filtering)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Fluxo de Autenticação
|
### Fluxo de Autenticação
|
||||||
@@ -1181,6 +1246,20 @@ O OpenSheets possui um schema robusto com 15+ tabelas e relações complexas:
|
|||||||
|
|
||||||
O OpenSheets está em desenvolvimento ativo. Aqui estão algumas das funcionalidades mais interessantes já implementadas:
|
O OpenSheets está em desenvolvimento ativo. Aqui estão algumas das funcionalidades mais interessantes já implementadas:
|
||||||
|
|
||||||
|
### 📲 OpenSheets Companion
|
||||||
|
|
||||||
|
Integração completa com app Android para captura automática de notificações bancárias:
|
||||||
|
|
||||||
|
- **Captura automática:** Nubank, Itaú, Bradesco, Inter, C6 e outros bancos
|
||||||
|
- **Caixa de entrada:** Notificações chegam como pré-lançamentos para revisão
|
||||||
|
- **Edição inline:** Ajuste valores e descrições antes de aprovar
|
||||||
|
- **Tokens seguros:** Autenticação via tokens com hash SHA-256
|
||||||
|
- **Rate limiting:** Proteção contra abuso (100 req/min)
|
||||||
|
- **API batch:** Envio de múltiplas notificações de uma vez
|
||||||
|
- **Health check:** Endpoint para validação de conectividade
|
||||||
|
|
||||||
|
O app Companion está disponível em: [github.com/felipegcoutinho/opensheets-companion](https://github.com/felipegcoutinho/opensheets-companion)
|
||||||
|
|
||||||
### 💸 Sistema Avançado de Parcelamentos
|
### 💸 Sistema Avançado de Parcelamentos
|
||||||
|
|
||||||
O controle de parcelamentos vai além do básico:
|
O controle de parcelamentos vai além do básico:
|
||||||
@@ -1215,7 +1294,10 @@ Analytics poderosos para entender suas finanças:
|
|||||||
|
|
||||||
- **Dashboard interativo:** 20+ widgets com diferentes visualizações
|
- **Dashboard interativo:** 20+ widgets com diferentes visualizações
|
||||||
- **Relatórios de categorias:** Análise profunda por categoria com histórico
|
- **Relatórios de categorias:** Análise profunda por categoria com histórico
|
||||||
- **Comparativos mensais:** Veja a evolução dos seus gastos ao longo do tempo
|
- **Tendências:** Visualize a evolução de categorias ao longo de 3, 6 ou 12 meses
|
||||||
|
- **Uso de cartões:** Análise detalhada de gastos por cartão de crédito
|
||||||
|
- **Top estabelecimentos:** Veja onde você mais gasta
|
||||||
|
- **Comparativos mensais:** Evolução dos seus gastos ao longo do tempo
|
||||||
- **Exportações:** PDF e Excel para análise externa
|
- **Exportações:** PDF e Excel para análise externa
|
||||||
- **Gráficos interativos:** Recharts com dados em tempo real
|
- **Gráficos interativos:** Recharts com dados em tempo real
|
||||||
|
|
||||||
@@ -1258,6 +1340,7 @@ Feito por desenvolvedores, para desenvolvedores:
|
|||||||
- **Migrations automáticas:** Schema sync simplificado
|
- **Migrations automáticas:** Schema sync simplificado
|
||||||
- **Docker completo:** Ambiente reproduzível em qualquer lugar
|
- **Docker completo:** Ambiente reproduzível em qualquer lugar
|
||||||
- **Scripts facilitados:** Comandos npm para tudo
|
- **Scripts facilitados:** Comandos npm para tudo
|
||||||
|
- **Biome:** Linting e formatting unificados em uma ferramenta
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -1356,6 +1439,7 @@ Para o texto legal completo, consulte o arquivo [LICENSE](LICENSE) ou visite [cr
|
|||||||
- [Better Auth](https://better-auth.com/)
|
- [Better Auth](https://better-auth.com/)
|
||||||
- [Drizzle ORM](https://orm.drizzle.team/)
|
- [Drizzle ORM](https://orm.drizzle.team/)
|
||||||
- [shadcn/ui](https://ui.shadcn.com/)
|
- [shadcn/ui](https://ui.shadcn.com/)
|
||||||
|
- [Biome](https://biomejs.dev/)
|
||||||
- [Vercel](https://vercel.com/)
|
- [Vercel](https://vercel.com/)
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
|
import type { ReactNode } from "react";
|
||||||
import {
|
import {
|
||||||
RiAndroidLine,
|
RiAndroidLine,
|
||||||
RiDownload2Line,
|
RiDownload2Line,
|
||||||
|
RiExternalLinkLine,
|
||||||
RiNotification3Line,
|
RiNotification3Line,
|
||||||
RiQrCodeLine,
|
RiQrCodeLine,
|
||||||
RiShieldCheckLine,
|
RiShieldCheckLine,
|
||||||
@@ -25,11 +27,28 @@ interface CompanionTabProps {
|
|||||||
tokens: ApiToken[];
|
tokens: ApiToken[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const steps = [
|
const steps: {
|
||||||
|
icon: typeof RiDownload2Line;
|
||||||
|
title: string;
|
||||||
|
description: ReactNode;
|
||||||
|
}[] = [
|
||||||
{
|
{
|
||||||
icon: RiDownload2Line,
|
icon: RiDownload2Line,
|
||||||
title: "Instale o app",
|
title: "Instale o app",
|
||||||
description: "Instale o APK.",
|
description: (
|
||||||
|
<>
|
||||||
|
Baixe o APK no{" "}
|
||||||
|
<a
|
||||||
|
href="https://github.com/felipegcoutinho/opensheets-companion"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="inline-flex items-center gap-0.5 text-primary hover:underline"
|
||||||
|
>
|
||||||
|
GitHub
|
||||||
|
<RiExternalLinkLine className="h-3 w-3" />
|
||||||
|
</a>
|
||||||
|
</>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
icon: RiQrCodeLine,
|
icon: RiQrCodeLine,
|
||||||
|
|||||||
@@ -1,12 +1,18 @@
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { cn } from "@/lib/utils/ui";
|
import { cn } from "@/lib/utils/ui";
|
||||||
|
import { version } from "@/package.json";
|
||||||
|
|
||||||
interface LogoProps {
|
interface LogoProps {
|
||||||
variant?: "full" | "small";
|
variant?: "full" | "small";
|
||||||
className?: string;
|
className?: string;
|
||||||
|
showVersion?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Logo({ variant = "full", className }: LogoProps) {
|
export function Logo({
|
||||||
|
variant = "full",
|
||||||
|
className,
|
||||||
|
showVersion = false,
|
||||||
|
}: LogoProps) {
|
||||||
if (variant === "small") {
|
if (variant === "small") {
|
||||||
return (
|
return (
|
||||||
<Image
|
<Image
|
||||||
@@ -21,7 +27,7 @@ export function Logo({ variant = "full", className }: LogoProps) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn("flex items-center py-4", className)}>
|
<div className={cn("flex items-center gap-1.5 py-4", className)}>
|
||||||
<Image
|
<Image
|
||||||
src="/logo_small.png"
|
src="/logo_small.png"
|
||||||
alt="Opensheets"
|
alt="Opensheets"
|
||||||
@@ -38,6 +44,11 @@ export function Logo({ variant = "full", className }: LogoProps) {
|
|||||||
className="object-contain dark:invert"
|
className="object-contain dark:invert"
|
||||||
priority
|
priority
|
||||||
/>
|
/>
|
||||||
|
{showVersion && (
|
||||||
|
<span className="text-[10px] font-medium text-muted-foreground">
|
||||||
|
v{version}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,13 +11,13 @@ interface ReturnButtonProps {
|
|||||||
const ReturnButton = React.memo(({ disabled, onClick }: ReturnButtonProps) => {
|
const ReturnButton = React.memo(({ disabled, onClick }: ReturnButtonProps) => {
|
||||||
return (
|
return (
|
||||||
<Button
|
<Button
|
||||||
className="w-28 h-6 rounded-sm"
|
className="w-32 h-6 rounded-sm lowercase"
|
||||||
size="sm"
|
size="sm"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
onClick={onClick}
|
onClick={onClick}
|
||||||
aria-label="Retornar para o mês atual"
|
aria-label="Retornar para o mês atual"
|
||||||
>
|
>
|
||||||
Mês Atual
|
Ir para Mês Atual
|
||||||
</Button>
|
</Button>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export function AppSidebar({
|
|||||||
<SidebarMenuItem>
|
<SidebarMenuItem>
|
||||||
<SidebarMenuButton
|
<SidebarMenuButton
|
||||||
asChild
|
asChild
|
||||||
className="data-[slot=sidebar-menu-button]:px-1.5! hover:bg-transparent active:bg-transparent pt-4 justify-center hover:scale-105 transition-all duration-200"
|
className="data-[slot=sidebar-menu-button]:px-1.5! hover:bg-transparent active:bg-transparent pt-8 py-6 justify-center hover:scale-120 scale-110 transition-all duration-200"
|
||||||
>
|
>
|
||||||
<a href="/dashboard">
|
<a href="/dashboard">
|
||||||
<LogoContent />
|
<LogoContent />
|
||||||
@@ -77,5 +77,10 @@ function LogoContent() {
|
|||||||
const { state } = useSidebar();
|
const { state } = useSidebar();
|
||||||
const isCollapsed = state === "collapsed";
|
const isCollapsed = state === "collapsed";
|
||||||
|
|
||||||
return <Logo variant={isCollapsed ? "small" : "full"} />;
|
return (
|
||||||
|
<Logo
|
||||||
|
variant={isCollapsed ? "small" : "full"}
|
||||||
|
showVersion={!isCollapsed}
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
RiInboxLine,
|
RiInboxLine,
|
||||||
RiNoCreditCardLine,
|
RiNoCreditCardLine,
|
||||||
RiPriceTag3Line,
|
RiPriceTag3Line,
|
||||||
RiSettingsLine,
|
RiSettings2Line,
|
||||||
RiSparklingLine,
|
RiSparklingLine,
|
||||||
RiTodoLine,
|
RiTodoLine,
|
||||||
} from "@remixicon/react";
|
} from "@remixicon/react";
|
||||||
@@ -199,7 +199,7 @@ export function createSidebarNavData(
|
|||||||
{
|
{
|
||||||
title: "Ajustes",
|
title: "Ajustes",
|
||||||
url: "/ajustes",
|
url: "/ajustes",
|
||||||
icon: RiSettingsLine,
|
icon: RiSettings2Line,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
16
package.json
16
package.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "opensheets",
|
"name": "opensheets",
|
||||||
"version": "1.0.0",
|
"version": "1.2.5",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev --turbopack",
|
"dev": "next dev --turbopack",
|
||||||
@@ -27,9 +27,9 @@
|
|||||||
"docker:rebuild": "docker compose up --build --force-recreate"
|
"docker:rebuild": "docker compose up --build --force-recreate"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ai-sdk/anthropic": "^3.0.31",
|
"@ai-sdk/anthropic": "^3.0.35",
|
||||||
"@ai-sdk/google": "^3.0.18",
|
"@ai-sdk/google": "^3.0.20",
|
||||||
"@ai-sdk/openai": "^3.0.23",
|
"@ai-sdk/openai": "^3.0.25",
|
||||||
"@dnd-kit/core": "^6.3.1",
|
"@dnd-kit/core": "^6.3.1",
|
||||||
"@dnd-kit/sortable": "^10.0.0",
|
"@dnd-kit/sortable": "^10.0.0",
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
@@ -59,7 +59,7 @@
|
|||||||
"@tanstack/react-table": "8.21.3",
|
"@tanstack/react-table": "8.21.3",
|
||||||
"@vercel/analytics": "^1.6.1",
|
"@vercel/analytics": "^1.6.1",
|
||||||
"@vercel/speed-insights": "^1.3.1",
|
"@vercel/speed-insights": "^1.3.1",
|
||||||
"ai": "^6.0.62",
|
"ai": "^6.0.67",
|
||||||
"babel-plugin-react-compiler": "^1.0.0",
|
"babel-plugin-react-compiler": "^1.0.0",
|
||||||
"better-auth": "1.4.18",
|
"better-auth": "1.4.18",
|
||||||
"class-variance-authority": "0.7.1",
|
"class-variance-authority": "0.7.1",
|
||||||
@@ -69,10 +69,9 @@
|
|||||||
"drizzle-orm": "0.45.1",
|
"drizzle-orm": "0.45.1",
|
||||||
"jspdf": "^4.0.0",
|
"jspdf": "^4.0.0",
|
||||||
"jspdf-autotable": "^5.0.7",
|
"jspdf-autotable": "^5.0.7",
|
||||||
"motion": "^12.29.2",
|
|
||||||
"next": "16.1.6",
|
"next": "16.1.6",
|
||||||
"next-themes": "0.4.6",
|
"next-themes": "0.4.6",
|
||||||
"pg": "8.17.2",
|
"pg": "8.18.0",
|
||||||
"react": "19.2.4",
|
"react": "19.2.4",
|
||||||
"react-day-picker": "^9.13.0",
|
"react-day-picker": "^9.13.0",
|
||||||
"react-dom": "19.2.4",
|
"react-dom": "19.2.4",
|
||||||
@@ -87,13 +86,10 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "2.3.13",
|
"@biomejs/biome": "2.3.13",
|
||||||
"@tailwindcss/postcss": "4.1.18",
|
"@tailwindcss/postcss": "4.1.18",
|
||||||
"@types/d3-array": "^3.2.2",
|
|
||||||
"@types/node": "25.1.0",
|
"@types/node": "25.1.0",
|
||||||
"@types/pg": "^8.16.0",
|
"@types/pg": "^8.16.0",
|
||||||
"@types/react": "19.2.10",
|
"@types/react": "19.2.10",
|
||||||
"@types/react-dom": "19.2.3",
|
"@types/react-dom": "19.2.3",
|
||||||
"baseline-browser-mapping": "^2.9.19",
|
|
||||||
"depcheck": "^1.4.7",
|
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"drizzle-kit": "0.31.8",
|
"drizzle-kit": "0.31.8",
|
||||||
"tailwindcss": "4.1.18",
|
"tailwindcss": "4.1.18",
|
||||||
|
|||||||
978
pnpm-lock.yaml
generated
978
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 30 KiB |
BIN
public/logos/creditas.png
Normal file
BIN
public/logos/creditas.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
Reference in New Issue
Block a user