7 Commits
v2.5.3 ... main

Author SHA1 Message Date
Felipe Coutinho
467f71493d fix: ajusta label da opção 'period' no BulkActionDialog para recorrência
Em recorrência, currentInstallment é undefined e o label usava 'parcela',
gerando 'Todas as pessoas desta parcela (undefined/3)'. Adiciona helpers
getPeriodLabel/getPeriodDescription que adaptam o texto para installment
vs recurring, seguindo o padrão das outras opções.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 15:12:35 +00:00
Felipe Coutinho
0cec10ede3 fix: corrige formatação no bulk-action-dialog para passar no biome
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 13:27:38 +00:00
Felipe Coutinho
a6fba5f953 chore: prepara versão 2.5.5
Filtros multi-seleção em lançamentos (condição, forma de pagamento, pessoa,
categoria, conta/cartão), changelog redesenhado como timeline colapsável com
detecção de bump e resumo, e diálogos migrados para as animações utilitárias
do tw-animate-css. Inclui ajustes de label no BulkActionDialog, refinamentos
visuais na landing page e atualização da navbar.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 13:11:59 +00:00
Felipe Coutinho
18893bfe02 docs: atualiza árvore de diretórios em README e CLAUDE com estrutura pós-refatoração
- Adiciona subpastas novas em shared/components/ (brand, widgets, feedback)
- Documenta o padrão interno de feature: actions.ts, queries.ts, actions/,
  components/, hooks/, lib/
- Atualiza shared/lib/ com pastas que já existiam mas faltavam listar
  (import, notifications, storage, version)
- Atualiza shared/utils/ com fetch-json.ts (movido do shared/lib) e id.ts
- Inclui lib/ no checklist de criação de nova feature

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-06 18:49:44 +00:00
Felipe Coutinho
7fdf9e2876 chore: prepara versão 2.5.4
Bump de patch refletindo a refatoração estrutural do commit anterior.
Sem mudanças funcionais — usuário não percebe nenhuma diferença.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-06 18:43:01 +00:00
Felipe Coutinho
7d0781b035 refactor: faxina arquitetural — código morto, identificadores em inglês e estrutura padronizada
Refatoração estrutural sem mudanças funcionais. Saldo líquido: −428 linhas.

Removido:
- 14 funções/constantes mortas verificadas via grep no repo todo: validateCategoriaOwnership,
  getInstallmentAnticipationsAction, getAnticipationDetailsAction, formatDecimalForDb,
  currencyFormatterNoCents, optionalDecimalSchema, formatMonthLabel,
  getGoalProgressStatusColorClass, MONTH_PERIOD_PARAM, calculateRemainingInstallments,
  e 5 funções fetch* não usadas em inbox/queries.ts.
- 1 tipo morto (ImportRow) + 2 órfãos consequentes (InstallmentAnticipationWithRelations,
  GoalProgressStatus convertido em interno).
- ~30 export keywords desnecessários (símbolos usados apenas no próprio arquivo).
- Re-exports mortos em barrels: EstablishmentLogoPicker, CategoryReportSkeleton,
  WidgetSkeleton, toNameKey.
- Arquivo features/reports/types.ts (barrel inteiro era órfão).

Padronizado (PT-BR→EN em identificadores expostos):
- 4 constantes globais (LANCAMENTOS_* → TRANSACTIONS_*).
- 12 tipos/interfaces (Lancamento*/Pagador*/Estabelecimento* → equivalentes EN).
- 13 funções/components exportados (fetchPagador*, EstabelecimentoInput, PagadorInfoCard, etc.).
- 5 props cross-file (preLancamentosCount → inboxPendingCount, pagadorAvatarUrl → payerAvatarUrl, etc.).
- Mantidas em PT-BR conforme exceção do CLAUDE.md: variáveis locais (pagador, categoria,
  lancamento), accessor key pagadorName (persistida em preferências), strings de UI.

Reorganizado:
- transactions/: 14 helpers soltos na raiz movidos para lib/; barrel actions.ts reduzido
  de 76 linhas de wrappers para 14 linhas de re-exports puros; anticipation-actions.ts
  movido para actions/anticipation.ts.
- dashboard/: 8 helpers soltos consolidados em dashboard/lib/.
- reports/: 5 query files na raiz consolidados em reports/lib/.
- payers/: detail-actions.ts (21KB) e detail-queries.ts movidos para payers/lib/.
- shared/components/: 9 dos 16 componentes soltos agrupados em brand/, widgets/, feedback/.
- shared/lib/fetch-json.ts movido para shared/utils/fetch-json.ts.

Validação: pnpm exec tsc --noEmit (0 erros), biome check (0 issues), knip (sem unused).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-06 18:42:54 +00:00
Felipe Coutinho
b9b843b9db Revise README for .env setup and remove acknowledgments
Updated instructions for creating a .env file and removed acknowledgments section.
2026-05-05 14:51:36 -03:00
240 changed files with 1405 additions and 1407 deletions

View File

@@ -5,6 +5,47 @@ Todas as mudanças notáveis deste projeto serão documentadas neste arquivo.
O formato é baseado em [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/), O formato é baseado em [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/),
e este projeto adere ao [Versionamento Semântico](https://semver.org/lang/pt-BR/). e este projeto adere ao [Versionamento Semântico](https://semver.org/lang/pt-BR/).
## [2.5.5] - 2026-05-06
Esta versão melhora a navegação por históricos e lançamentos. O changelog ganhou uma linha do tempo mais leve, colapsável e fácil de escanear; os filtros de lançamentos passam a aceitar múltiplas pessoas, categorias, formas de pagamento, condições e contas/cartões na mesma busca; e os diálogos adotam as animações compartilhadas do design system. Também há pequenos polimentos de texto e layout para deixar a interface mais consistente.
### Adicionado
- Lançamentos: filtros multi-seleção para condição, forma de pagamento, pessoa, categoria e conta/cartão, permitindo combinar vários valores no mesmo filtro (query string passa a aceitar múltiplos valores por chave).
- Changelog: parser passou a inferir o tipo de bump (major/minor/patch) a partir da numeração e a extrair o parágrafo de resumo abaixo do cabeçalho de versão; novo arquivo `src/features/settings/lib/changelog-types.ts` consolidando os tipos compartilhados.
- UI: dependência `tw-animate-css` para usar as mesmas animações utilitárias já presentes nos componentes shadcn/ui.
### Alterado
- Changelog: visual da página reformulado para linha do tempo com resumo sempre visível, detalhes colapsáveis por versão, agrupamento por mês e marcadores visuais por tipo de bump; componente migrado para `"use client"` com `Collapsible` e abertura via âncora (`#vX-Y-Z`).
- Lançamentos: botões "Nova Receita" e "Nova Despesa" agora usam os próprios triggers do `TransactionDialog` (via prop `createSlot`), reduzindo estado manual na página e eliminando o fluxo `setCreateOpen` + `transactionTypeForCreate`.
- Diálogos: animações customizadas em CSS (`@keyframes dialog-in/out` e `overlay-in/out`) substituídas pelas classes utilitárias compartilhadas em `Dialog`/`DialogOverlay` (`data-[state=open]:animate-in`, `zoom-in-95`, `fade-in-0`).
- BulkActionDialog: label do escopo "Todas as pessoas" passa a indicar a parcela atual (`Todas as pessoas desta parcela (N/Total)`) com descrição mais clara sobre o efeito da ação.
- Checkbox: `RiCheckLine`/`RiSubtractLine` agora herdam `text-current` para alinhar com a cor do indicator nativo.
- Landing page: remoção de fundos alternados (`bg-muted/40`) nas seções "Funcionalidades", "Stack" e "Para quem é" para uma leitura visual mais limpa.
- Navbar: aviso de atualização passa a usar o texto "Versão X disponível".
## [2.5.4] - 2026-05-06
Esta versão é uma faxina arquitetural de larga escala sem nenhuma mudança visível ao usuário. Removido código morto, padronizamos identificadores em inglês conforme a convenção do projeto, simplificamos o barrel de Server Actions e consolidamos os arquivos de helpers/queries soltos nas raízes das features dentro de pastas `lib/`. O resultado é uma estrutura previsível e consistente entre features (`actions.ts`, `queries.ts`, `actions/`, `components/`, `hooks/`, `lib/`) e um saldo líquido de 428 linhas de código com zero impacto em comportamento, performance ou banco de dados.
### Alterado
- Padronização da estrutura de `transactions/`: 14 helpers soltos na raiz movidos para `lib/`; barrel `actions.ts` reduzido de 76 linhas de wrappers redundantes para 14 linhas de re-exports puros; `anticipation-actions.ts` movido para `actions/anticipation.ts`.
- Reorganização de `dashboard/`: 8 helpers soltos consolidados em `dashboard/lib/`; orquestradores (`fetch-dashboard-data.ts`, `page-data-queries.ts`) permanecem na raiz como entry points.
- Reorganização de `reports/`: 5 query files na raiz consolidados em `reports/lib/`.
- Reorganização de `payers/`: god file `detail-actions.ts` (21KB) e `detail-queries.ts` movidos para `payers/lib/`.
- `shared/components/`: 9 dos 16 componentes soltos agrupados em 3 novas subpastas temáticas (`brand/`, `widgets/`, `feedback/`).
- `shared/lib/fetch-json.ts` movido para `shared/utils/fetch-json.ts` (categorização correta — utilitário genérico de transporte HTTP).
- Padronização EN dos identificadores remanescentes: 4 constantes globais (`LANCAMENTOS_*``TRANSACTIONS_*`), 12 tipos/interfaces (`Lancamento*`/`Pagador*`/`Estabelecimento*` → equivalentes em EN), 13 funções/components exportados (`fetchPagador*`, `EstabelecimentoInput`, `PagadorInfoCard`, etc.), 5 props cross-file (`preLancamentosCount``inboxPendingCount`, etc.).
- Server Actions de `insights/` simplificadas: barrel reduzido para re-exports puros.
- Mantidas intencionalmente em PT-BR conforme exceção do `CLAUDE.md`: variáveis locais (`pagador`, `categoria`, `lancamento`), accessor key `pagadorName` (persistida em preferências do usuário), strings de UI.
### Removido
- 14 funções/constantes mortas verificadas via `grep` em todo o repo: `validateCategoriaOwnership`, `getInstallmentAnticipationsAction`, `getAnticipationDetailsAction`, `formatDecimalForDb`, `currencyFormatterNoCents`, `optionalDecimalSchema`, `formatMonthLabel`, `getGoalProgressStatusColorClass`, `MONTH_PERIOD_PARAM`, `calculateRemainingInstallments`, e 5 funções `fetch*` não usadas em `inbox/queries.ts`.
- 1 tipo morto: `ImportRow` em `transactions/actions/import-action.ts`.
- 2 tipos órfãos consequentes: `InstallmentAnticipationWithRelations`, `GoalProgressStatus` (este último convertido em interno).
- ~30 `export` keywords desnecessários (símbolos usados apenas no próprio arquivo) — visibilidade reduzida sem mudar comportamento.
- Re-exports mortos em barrels: `EstablishmentLogoPicker` em `entity-avatar/index.ts`, `CategoryReportSkeleton` e `WidgetSkeleton` em `skeletons/index.ts`, `toNameKey` em `establishment-logo-queries.ts`.
- Arquivo `features/reports/types.ts` (barrel inteiro era órfão — todos os 5 tipos eram importados direto de `@/shared/lib/types/reports`).
## [2.5.3] - 2026-05-05 ## [2.5.3] - 2026-05-05
Esta versão foca em polimento do diálogo de detalhes do lançamento, refresh visual da linha do tempo de parcelas e limpeza terminológica em torno de contas/cartões inativos. O diálogo de detalhes ganhou logo da conta/cartão, ícone colorido por categoria e avatar do responsável; a barra de progresso de parcelas foi redesenhada num layout horizontal compacto; e o widget "Minhas Contas" do dashboard passou a ocultar automaticamente contas marcadas como inativas. Internamente, o termo "arquivadas" foi padronizado como "inativas" nas tabs de contas e cartões, surgiram constantes compartilhadas para formas de pagamento liquidáveis e um helper `isAccountInactive`, e o seed de mock data ganhou cobertura mais realista (novas pessoas, contas, cartões e assinaturas recorrentes). Esta versão foca em polimento do diálogo de detalhes do lançamento, refresh visual da linha do tempo de parcelas e limpeza terminológica em torno de contas/cartões inativos. O diálogo de detalhes ganhou logo da conta/cartão, ícone colorido por categoria e avatar do responsável; a barra de progresso de parcelas foi redesenhada num layout horizontal compacto; e o widget "Minhas Contas" do dashboard passou a ocultar automaticamente contas marcadas como inativas. Internamente, o termo "arquivadas" foi padronizado como "inativas" nas tabs de contas e cartões, surgiram constantes compartilhadas para formas de pagamento liquidáveis e um helper `isAccountInactive`, e o seed de mock data ganhou cobertura mais realista (novas pessoas, contas, cartões e assinaturas recorrentes).
@@ -197,6 +238,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [2.4.1] - 2026-04-16 ## [2.4.1] - 2026-04-16
Versão pequena com refresh visual nas telas de autenticação (efeito blob com três círculos coloridos em movimento e card com glassmorphism), capitalização dos labels da navbar para melhor legibilidade e otimização do banco com 17 índices novos em foreign keys — evitando sequential scans em deletes em tabelas grandes como `lancamentos`. Corrigida regressão no `postgres:18-alpine` que recusava iniciar em instalações existentes; adicionada variável `PGDATA` no compose para preservar dados de quem já tinha o volume populado.
### Adicionado ### Adicionado
- UI/Auth: layout animado nas páginas de login e signup com efeito blob (3 círculos coloridos em movimento) e card com glassmorphism; layout compartilhado extraído para `app/(auth)/layout.tsx` eliminando duplicação (PR #42) - UI/Auth: layout animado nas páginas de login e signup com efeito blob (3 círculos coloridos em movimento) e card com glassmorphism; layout compartilhado extraído para `app/(auth)/layout.tsx` eliminando duplicação (PR #42)
@@ -217,6 +260,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [2.4.0] - 2026-04-13 ## [2.4.0] - 2026-04-13
Esta versão integra o serviço Logo.dev para exibir automaticamente logos de marcas na coluna de estabelecimentos dos lançamentos, com picker manual para fixar o domínio quando a sugestão automática não acerta. As consultas vão por novas rotas de API (`/api/logo/search` e `/api/logo/mapping`) que servem como proxy seguro — a secret key fica server-side. Inclui também tabela própria `establishment_logos` com PK composta `(user_id, name_key)` para persistir as preferências por usuário.
### Adicionado ### Adicionado
- Estabelecimentos: integração com Logo.dev — logos automáticos de marcas exibidos na coluna de estabelecimentos nos lançamentos - Estabelecimentos: integração com Logo.dev — logos automáticos de marcas exibidos na coluna de estabelecimentos nos lançamentos
@@ -236,6 +281,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [2.3.8] - 2026-04-12 ## [2.3.8] - 2026-04-12
Refatoração do `docker-compose.yml` para virar standalone — agora basta um `curl` + `docker compose up -d`, sem dependências de arquivos externos ou profiles complexos. README reescrito em dois perfis claros (Usar com Docker e Desenvolver com hot-reload) e scripts npm reduzidos de 10 para 5.
### Alterado ### Alterado
- Docker: `docker-compose.yml` refatorado — removidos profiles, build e dependência de arquivo externo; compose agora é standalone (basta `curl` + `docker compose up -d`) - Docker: `docker-compose.yml` refatorado — removidos profiles, build e dependência de arquivo externo; compose agora é standalone (basta `curl` + `docker compose up -d`)
@@ -245,6 +292,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [2.3.7] - 2026-04-11 ## [2.3.7] - 2026-04-11
Esta versão amplia significativamente o dashboard com três novos widgets configuráveis (Anexos, Inbox, Tendências de Categoria), adiciona filtros úteis na tabela de lançamentos (por status de pagamento e por presença de anexo) e moderniza a tipografia substituindo a fonte local por Inter (Google Fonts, self-hosted pelo Next.js) — eliminando arquivos `.woff2` do repositório. Pesos tipográficos foram padronizados para `font-semibold` em títulos, rótulos e valores monetários, e o card de grupo de parcelas foi redesenhado expandindo num dialog de detalhes com parcelas pagas/pendentes separadas. No backend, a CSP foi expandida para permitir preview de anexos PDF via S3, e o setup ganhou script `install-deps.sh` pra preparar servidores Ubuntu 24.04 limpos.
### Adicionado ### Adicionado
- Dashboard: novos widgets configuráveis — Anexos (resumo de arquivos do período), Inbox (snapshot de pré-lançamentos pendentes) e Tendências de Categoria - Dashboard: novos widgets configuráveis — Anexos (resumo de arquivos do período), Inbox (snapshot de pré-lançamentos pendentes) e Tendências de Categoria
@@ -281,24 +330,32 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [2.3.6] - 2026-04-09 ## [2.3.6] - 2026-04-09
Correção pontual no Docker — adicionado `NODE_PATH=/app/migrate/node_modules` no entrypoint para o `drizzle-kit` resolver corretamente o `drizzle-orm` ao executar as migrations no container.
### Corrigido ### Corrigido
- Docker: adicionado `NODE_PATH=/app/migrate/node_modules` no entrypoint para que o `drizzle-kit` consiga resolver `drizzle-orm` ao executar as migrations no container - Docker: adicionado `NODE_PATH=/app/migrate/node_modules` no entrypoint para que o `drizzle-kit` consiga resolver `drizzle-orm` ao executar as migrations no container
## [2.3.5] - 2026-04-07 ## [2.3.5] - 2026-04-07
Correção crítica na CSP: regra movida do `next.config.ts` (build time) para `proxy.ts` (runtime), desbloqueando uploads de anexos quando o `S3_ENDPOINT` ainda não estava disponível durante o build da imagem Docker.
### Corrigido ### Corrigido
- CSP: movido `Content-Security-Policy` do `next.config.ts` (build time) para `proxy.ts` (runtime), corrigindo bloqueio de upload de anexos quando `S3_ENDPOINT` não estava disponível durante o build do Docker - CSP: movido `Content-Security-Policy` do `next.config.ts` (build time) para `proxy.ts` (runtime), corrigindo bloqueio de upload de anexos quando `S3_ENDPOINT` não estava disponível durante o build do Docker
## [2.3.4] - 2026-04-05 ## [2.3.4] - 2026-04-05
Correção pontual no upload de anexos — a CSP `connect-src` bloqueava o fetch para o storage, gerando `NetworkError` na hora de subir o arquivo.
### Corrigido ### Corrigido
- Anexos: corrigido upload que falhava com `NetworkError` — CSP `connect-src` bloqueava fetch para o Storage - Anexos: corrigido upload que falhava com `NetworkError` — CSP `connect-src` bloqueava fetch para o Storage
## [2.3.3] - 2026-04-05 ## [2.3.3] - 2026-04-05
Correção do fluxo de tokens da API: `/api/auth/device/verify` voltou a aceitar tokens criados pela tela de Settings (revertido de JWT para hash lookup). O prefixo dos tokens também foi renomeado de `os_` para `opm_` (OpenMonetis) e rotas JWT não utilizadas foram removidas — usuários precisam recriar os tokens existentes.
### Corrigido ### Corrigido
- Tokens: corrigido `/api/auth/device/verify` que rejeitava tokens criados via Settings (revertido de JWT para hash lookup) - Tokens: corrigido `/api/auth/device/verify` que rejeitava tokens criados via Settings (revertido de JWT para hash lookup)
@@ -311,6 +368,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [2.3.2] - 2026-04-04 ## [2.3.2] - 2026-04-04
Esta versão concentra hardening de segurança. Tokens da API ganharam expiração obrigatória de 1 ano (sem mais tokens eternos) e o refresh foi corrigido para validar JWT por assinatura. A CSP foi expandida com `default-src`, `script-src`, `style-src`, `img-src`, `font-src` e `connect-src` (no lugar de uma regra única ampla), e foi adicionada mitigação para CVE-2024-44294 desabilitando parsing de fórmulas em `xlsx`. Inclui ainda novos headers (`Referrer-Policy`, `X-Permitted-Cross-Domain-Policies`), respostas `401 JSON` em vez de redirect 302 em rotas autenticadas, `security.txt` (RFC 9116) e correção de URL com protocolo duplicado no sitemap.
### Segurança ### Segurança
- Tokens: removido aceite de tokens sem expiração (`expiresAt NULL`); tokens criados via settings agora expiram em 1 ano - Tokens: removido aceite de tokens sem expiração (`expiresAt NULL`); tokens criados via settings agora expiram em 1 ano
@@ -326,12 +385,16 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [2.3.1] - 2026-04-03 ## [2.3.1] - 2026-04-03
Correção pontual de infraestrutura — dependências do `drizzle-kit` passaram a ser instaladas em `/app/migrate/` separadamente do `node_modules` do build standalone, corrigindo o erro `Cannot find module 'next'` no startup do container.
### Corrigido ### Corrigido
- Infraestrutura: deps do drizzle-kit agora são instaladas em `/app/migrate/` separado do `node_modules` do standalone, corrigindo erro `Cannot find module 'next'` no startup do container - Infraestrutura: deps do drizzle-kit agora são instaladas em `/app/migrate/` separado do `node_modules` do standalone, corrigindo erro `Cannot find module 'next'` no startup do container
## [2.3.0] - 2026-04-03 ## [2.3.0] - 2026-04-03
Esta versão introduz `@tanstack/react-query` no projeto, padronizando cache, deduplicação e invalidação de leituras client-side. Várias features (anexos, insights, antecipação de parcelas) passaram a usar React Query no lugar de `useEffect` manual sobre rotas GET dedicadas. O dashboard ganhou ajuda contextual em cada métrica e configuração persistida pra ocultar contas marcadas como não consideradas no saldo total; o menu do usuário na navbar passou a avisar quando há release nova publicada no GitHub; e o Docker passou a rodar migrations automaticamente no startup via `docker-entrypoint.sh`. Internamente, o `knip` foi adicionado pra auditar arquivos/exports/tipos sem uso, várias rotas e actions ganharam validações extras (filtros por `userId` em joins, rate limits explícitos no Better Auth, headers `Cache-Control: private, no-store` em rotas privadas) e o projeto foi atualizado para Next.js 16.2.2 e Biome 2.4.10.
### Adicionado ### Adicionado
- Dependências: adiciona `@tanstack/react-query` e um provider global para padronizar cache, deduplicação e invalidação de leituras client-side - Dependências: adiciona `@tanstack/react-query` e um provider global para padronizar cache, deduplicação e invalidação de leituras client-side
@@ -367,12 +430,16 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [2.2.1] - 2026-04-01 ## [2.2.1] - 2026-04-01
Correção pontual no build da imagem Docker — removido `chown -R /app` do stage final (que travava o build/push da GitHub Action por lentidão excessiva); permissões agora definidas via `COPY --chown` direto.
### Corrigido ### Corrigido
- Docker: imagem de produção deixa de executar `chown -R /app` no stage final; as permissões passam a ser definidas nos `COPY --chown`, reduzindo o risco de travamento e lentidão excessiva no build/push da GitHub Action - Docker: imagem de produção deixa de executar `chown -R /app` no stage final; as permissões passam a ser definidas nos `COPY --chown`, reduzindo o risco de travamento e lentidão excessiva no build/push da GitHub Action
## [2.2.0] - 2026-04-01 ## [2.2.0] - 2026-04-01
Esta versão entrega uma nova página dedicada de galeria de anexos em `/attachments` com miniaturas, visualização inline (incluindo PDF via `pdfjs-dist`), download direto e acesso a partir do lançamento. As páginas de login e cadastro foram redesenhadas com sidebar mockup de faturas, três blocos de funcionalidade e gradiente decorativo. O dashboard passou a notificar boletos e faturas com vencimento dentro de 5 dias, e o cache do dashboard migrou de `unstable_cache` para a diretiva `use cache` (com `cacheTag` e `cacheLife`), com `cacheComponents: true` no `next.config.ts` e `connection()` em todas as páginas para forçar render dinâmico. A tipografia ganhou peso 500 (Medium) padronizado em títulos, valores e rótulos.
### Adicionado ### Adicionado
- Anexos: nova página de galeria em `/attachments` com miniaturas, visualização inline de imagem e PDF, download direto e acesso a partir do lançamento - Anexos: nova página de galeria em `/attachments` com miniaturas, visualização inline de imagem e PDF, download direto e acesso a partir do lançamento
@@ -393,6 +460,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [2.1.2] - 2026-03-30 ## [2.1.2] - 2026-03-30
Pequena versão de polimento: novo escopo `"period"` na ação em lote de lançamentos (aplica alteração a todos os lançamentos do período sem sobrescrever o pagador individual de cada um), preferência de tamanho máximo por arquivo de anexo (5/10/25/50/100 MB) persistida no banco e respeitada em todos os pontos de upload, e redesign visual da página de Configurações com separadores entre seções e títulos maiores.
### Adicionado ### Adicionado
- Preferências: nova configuração de tamanho máximo por arquivo de anexo (5, 10, 25, 50 ou 100 MB), persistida no banco e respeitada em todos os pontos de upload - Preferências: nova configuração de tamanho máximo por arquivo de anexo (5, 10, 25, 50 ou 100 MB), persistida no banco e respeitada em todos os pontos de upload
@@ -409,6 +478,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [2.1.1] - 2026-03-29 ## [2.1.1] - 2026-03-29
Esta versão extrai a navbar pra um componente `NavbarShell` compartilhado entre app e landing page e cria uma variante `navbar` no Button pra centralizar os estilos antes duplicados em `nav-styles.ts`. A integração com `@vercel/analytics`/`@vercel/speed-insights` foi substituída por Umami self-hosted via script tag no layout raiz.
### Adicionado ### Adicionado
- Navbar: novo componente `NavbarShell` que unifica a estrutura da barra de navegação entre o app e a landing page - Navbar: novo componente `NavbarShell` que unifica a estrutura da barra de navegação entre o app e a landing page
@@ -430,6 +501,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [2.1.0] - 2026-03-28 ## [2.1.0] - 2026-03-28
Esta versão adiciona suporte a anexos em transações, com upload direto para storage compatível com S3, persistência em tabelas dedicadas (`anexos` e `lancamento_anexos`) e ações de visualizar/remover no detalhe do lançamento. O upload exige token assinado por arquivo, valida ownership da transação na leitura/remoção e confere tamanho/tipo do objeto no storage antes de persistir o vínculo no banco. Inclui também novo workflow `release.yml` que cria tag e GitHub Release automaticamente a partir da versão do `package.json` e da entrada correspondente no `CHANGELOG.md`.
### Adicionado ### Adicionado
- Lançamentos: suporte a anexos em transações com upload direto para storage compatível com S3, persistência em tabelas dedicadas (`anexos` e `lancamento_anexos`) e ações de visualizar/remover no detalhe do lançamento - Lançamentos: suporte a anexos em transações com upload direto para storage compatível com S3, persistência em tabelas dedicadas (`anexos` e `lancamento_anexos`) e ações de visualizar/remover no detalhe do lançamento
@@ -445,12 +518,16 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [2.0.3] - 2026-03-26 ## [2.0.3] - 2026-03-26
Correção pontual em `/transactions` — removida dependência de `crypto.randomUUID()` no carregamento inicial, que falhava em ambientes self-hosted sem HTTPS (a API só está disponível em contextos seguros).
### Corrigido ### Corrigido
- Lançamentos: `/transactions` deixa de depender de `crypto.randomUUID()` no carregamento inicial, corrigindo a falha em ambientes self-hosted sem HTTPS ao abrir a página - Lançamentos: `/transactions` deixa de depender de `crypto.randomUUID()` no carregamento inicial, corrigindo a falha em ambientes self-hosted sem HTTPS ao abrir a página
## [2.0.2] - 2026-03-25 ## [2.0.2] - 2026-03-25
Versão focada nas notificações da navbar: novo estado persistido permite marcar alertas de fatura, boleto e orçamento como lidos ou arquivados por usuário; o snapshot global passa a usar o período corrente do negócio (não mais o `periodo` da URL), itens lidos saem do badge e arquivados somem da lista padrão do sino. O filtro foi refinado para um seletor explícito entre `Ativas` e `Arquivadas`. Inclui ajustes pontuais no detalhamento por categoria do dashboard (oculta categorias sem movimentação no período), na arte decorativa do cabeçalho de boas-vindas e na edição em lote de lançamentos em série (que agora propaga também o status de pagamento para transações fora do cartão).
### Adicionado ### Adicionado
- Scripts: novo comando `mockup` no `package.json` para executar `scripts/mock-data.ts` - Scripts: novo comando `mockup` no `package.json` para executar `scripts/mock-data.ts`
@@ -479,6 +556,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [2.0.1] - 2026-03-21 ## [2.0.1] - 2026-03-21
Versão de correções na inbox de pré-lançamentos: filtro por app passa a montar a lista completa a partir de todos os itens do status atual (sem depender da página carregada), notificações de cartões/apps sem logo cadastrado passam a usar `default_icon.png` como fallback, e o select de apps exibe os logos. Inclui também correção de divergência entre a versão exibida no UI e a reportada pelo `/api/health` (que agora reporta a versão atual do `package.json`).
### Corrigido ### Corrigido
- Inbox: filtro por app em `/inbox` agora monta a lista completa de apps da aba a partir de todos os itens do status atual, sem depender apenas da página carregada, e o SSR deixa de quebrar quando `sourceApps` vier inconsistente - Inbox: filtro por app em `/inbox` agora monta a lista completa de apps da aba a partir de todos os itens do status atual, sem depender apenas da página carregada, e o SSR deixa de quebrar quando `sourceApps` vier inconsistente
@@ -489,6 +568,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [2.0.0] - 2026-03-21 ## [2.0.0] - 2026-03-21
Marco importante do projeto. Esta versão consolida ganhos de performance, segurança e organização interna. No backend, paginação server-side real foi implementada em transações, extrato e inbox; o dashboard reduziu de 19 fetchers para 7 blocos com agregações compartilhadas; exportações de PDF/Excel passaram a carregar libs sob demanda apenas no clique; e o cache de dashboard/insights ganhou invalidação segmentada por `userId` (sem fallback global). Internamente, identificadores foram migrados de PT-BR para inglês (`lancamento``transaction`, `pagador``payer`, `conta``account`, etc.) e helpers foram consolidados em módulos de domínio. Visualmente, a navbar e os cards de auth ganharam dot pattern + brilho em primary, faturas tiveram refinamento na hierarquia visual, e a tipografia foi unificada na família America. Inclui ainda script `scripts/backup.sh` para backup automático do PostgreSQL, importação de extratos OFX e XLS/XLSX com tela de revisão e dedup por FITID, e nova opção de zerar dados financeiros sem excluir o usuário.
### Adicionado ### Adicionado
- Infraestrutura: script `scripts/backup.sh` para backup automático do banco PostgreSQL; configuração de destino (rclone, cron, retenção) feita separadamente; passa a gerar também `*.data.sql.gz` com dados puros de todas as tabelas públicas (`--data-only --schema=public`) - Infraestrutura: script `scripts/backup.sh` para backup automático do banco PostgreSQL; configuração de destino (rclone, cron, retenção) feita separadamente; passa a gerar também `*.data.sql.gz` com dados puros de todas as tabelas públicas (`--data-only --schema=public`)
@@ -545,6 +626,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [1.7.7] - 2026-03-05 ## [1.7.7] - 2026-03-05
Versão de organização interna sem mudanças visíveis grandes. Períodos e navegação mensal passaram a usar os helpers centrais de período (`YYYY-MM`), hooks locais (calculadora, month-picker, logo picker) foram movidos pra perto das respectivas features e `components/navbar`/`sidebar` foram consolidados em `components/navigation/*`. Análise de parcelas migrou para `/relatorios/analise-parcelas`, exportações em PDF/CSV/Excel ganharam melhor branding e apresentação, e a calculadora teve ajustes de estabilidade no arrasto.
### Alterado ### Alterado
- Períodos e navegação mensal: `useMonthPeriod` passou a usar os helpers centrais de período (`YYYY-MM`), o month-picker foi simplificado e o rótulo visual agora segue o formato `Março 2026`. - Períodos e navegação mensal: `useMonthPeriod` passou a usar os helpers centrais de período (`YYYY-MM`), o month-picker foi simplificado e o rótulo visual agora segue o formato `Março 2026`.
@@ -559,6 +642,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [1.7.6] - 2026-03-02 ## [1.7.6] - 2026-03-02
Esta versão adiciona suporte completo a Passkeys (WebAuthn) via `@better-auth/passkey`: nova aba em `/ajustes` permite listar, adicionar, renomear e remover credenciais, e a tela de login ganhou ação dedicada para passkey. O dashboard ganhou widget de Anotações e atalhos rápidos na toolbar de widgets pra criar Receita, Despesa ou Anotação direto. Top Estabelecimentos foi unificado num único widget com abas, e o widget "Lançamentos recentes" foi substituído por "Progresso de metas" com lista de orçamentos do período (gasto, limite e percentual de uso por categoria).
### Adicionado ### Adicionado
- Suporte completo a Passkeys (WebAuthn) com plugin `@better-auth/passkey` no servidor e `passkeyClient` no cliente de autenticação - Suporte completo a Passkeys (WebAuthn) com plugin `@better-auth/passkey` no servidor e `passkeyClient` no cliente de autenticação
@@ -593,6 +678,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [1.7.5] - 2026-02-28 ## [1.7.5] - 2026-02-28
Versão pequena de polimento: ações para excluir item individual (processado/descartado) e limpar itens em lote por status na inbox de pré-lançamentos, redesign dos cards e diálogos dos widgets de boletos e faturas com indicação "Atrasado / Pagar" quando vencidos e não pagos, e migração da página de categorias de cards pra layout em tabela com link direto para detalhe e ações inline.
### Adicionado ### Adicionado
- Inbox de pré-lançamentos: ações para excluir item individual (processado/descartado) e limpar itens em lote por status - Inbox de pré-lançamentos: ações para excluir item individual (processado/descartado) e limpar itens em lote por status
@@ -611,6 +698,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [1.7.4] - 2026-02-28 ## [1.7.4] - 2026-02-28
Versão de polimento de responsividade no mobile: 26 componentes ajustados (navbar, filtros, skeletons, widgets, dialogs), card de análise de parcelas empilhado verticalmente em telas pequenas e cards do top estabelecimentos reorganizados em coluna única no mobile. Inclui também regra mais inteligente em "Remover selecionados" — quando todos os itens pertencem à mesma série, abre dialog de escopo com 3 opções; e ajuste no consumo de limite por despesa recorrente no cartão (só consome quando a data já passou).
### Alterado ### Alterado
- Card de análise de parcelas (`/dashboard/analise-parcelas`): layout empilhado no mobile — nome/cartão e valores Total/Pendente em linhas separadas ao invés de lado-a-lado, evitando truncamento - Card de análise de parcelas (`/dashboard/analise-parcelas`): layout empilhado no mobile — nome/cartão e valores Total/Pendente em linhas separadas ao invés de lado-a-lado, evitando truncamento
@@ -622,6 +711,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [1.7.3] - 2026-02-27 ## [1.7.3] - 2026-02-27
Versão pequena com nova prop `compact` no DatePicker (formato abreviado "28 fev", sem "de" e sem ano) e modal de múltiplos lançamentos reformulado: selects de conta e cartão separados por forma de pagamento, InlinePeriodPicker ao escolher cartão de crédito e DatePicker compacto.
### Adicionado ### Adicionado
- Prop `compact` no DatePicker para formato abreviado "28 fev" (sem "de" e sem ano) - Prop `compact` no DatePicker para formato abreviado "28 fev" (sem "de" e sem ano)
@@ -633,6 +724,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [1.7.2] - 2026-02-26 ## [1.7.2] - 2026-02-26
Versão de polimento dos diálogos: padding maior (p-10), largura padronizada em `max-w-xl` e botões do footer com largura igual; o lançamento dialog ganhou seção colapsável "Condições e anotações" e cálculo automático do período da fatura via `deriveCreditCardPeriod()`. Inclui também uma faxina de tipos (non-null assertions removidas, `any` substituído por tipos explícitos em 15+ arquivos) e remoção de 6 componentes e 20+ funções/tipos sem uso.
### Alterado ### Alterado
- Dialogs padronizados: padding maior (p-10), largura max-w-xl, botões do footer com largura igual (flex-1) - Dialogs padronizados: padding maior (p-10), largura max-w-xl, botões do footer com largura igual (flex-1)
@@ -656,6 +749,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [1.7.1] - 2026-02-24 ## [1.7.1] - 2026-02-24
Esta versão substitui o header lateral por uma topbar de navegação com backdrop blur e links agrupados em 5 seções (Dashboard, Lançamentos, Cartões, Relatórios, Ferramentas), expande o sino de notificações pra exibir orçamentos estourados e pré-lançamentos pendentes em seções separadas, e cria página dedicada de changelog em `/changelog` (acessível pelo menu do usuário com a versão atual exibida ao lado).
### Adicionado ### Adicionado
- Topbar de navegação substituindo o header fixo: backdrop blur, links agrupados em 5 seções (Dashboard, Lançamentos, Cartões, Relatórios, Ferramentas) - Topbar de navegação substituindo o header fixo: backdrop blur, links agrupados em 5 seções (Dashboard, Lançamentos, Cartões, Relatórios, Ferramentas)
@@ -679,6 +774,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [1.6.3] - 2026-02-19 ## [1.6.3] - 2026-02-19
Correção pontual: variável `RESEND_FROM_EMAIL` não era lida corretamente do `.env` quando o valor continha espaços (precisa estar entre aspas). Leitura centralizada em `lib/email/resend.ts` com `getResendFromEmail()` e carregamento explícito do `.env` no contexto de Server Actions.
### Corrigido ### Corrigido
- E-mail Resend: variável `RESEND_FROM_EMAIL` não era lida do `.env` (valores com espaço precisam estar entre aspas). Leitura centralizada em `lib/email/resend.ts` com `getResendFromEmail()` e carregamento explícito do `.env` no contexto de Server Actions - E-mail Resend: variável `RESEND_FROM_EMAIL` não era lida do `.env` (valores com espaço precisam estar entre aspas). Leitura centralizada em `lib/email/resend.ts` com `getResendFromEmail()` e carregamento explícito do `.env` no contexto de Server Actions
@@ -690,12 +787,16 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [1.6.2] - 2026-02-19 ## [1.6.2] - 2026-02-19
Correção pontual no mobile: ao selecionar um logo no diálogo de criação de conta/cartão, o diálogo principal fechava inesperadamente. Adicionado `stopPropagation` nos eventos de click/touch dos botões e delay com `requestAnimationFrame` antes de fechar o seletor.
### Corrigido ### Corrigido
- Bug no mobile onde, ao selecionar um logo no diálogo de criação de conta/cartão, o diálogo principal fechava inesperadamente: adicionado `stopPropagation` nos eventos de click/touch dos botões de logo e delay com `requestAnimationFrame` antes de fechar o seletor de logo - Bug no mobile onde, ao selecionar um logo no diálogo de criação de conta/cartão, o diálogo principal fechava inesperadamente: adicionado `stopPropagation` nos eventos de click/touch dos botões de logo e delay com `requestAnimationFrame` antes de fechar o seletor de logo
## [1.6.1] - 2026-02-18 ## [1.6.1] - 2026-02-18
Versão pequena: nome do estabelecimento padronizado para transferências entre contas ("Saída - Transf. entre contas" e "Entrada - Transf. entre contas") com anotação no formato "de {origem} -> {destino}", e correção de avisos `width(-1) and height(-1)` do `ChartContainer` no console.
### Alterado ### Alterado
- Transferências entre contas: nome do estabelecimento passa a ser "Saída - Transf. entre contas" na saída e "Entrada - Transf. entre contas" na entrada e adicionando em anotação no formato "de {conta origem} -> {conta destino}" - Transferências entre contas: nome do estabelecimento passa a ser "Saída - Transf. entre contas" na saída e "Entrada - Transf. entre contas" na entrada e adicionando em anotação no formato "de {conta origem} -> {conta destino}"
@@ -703,6 +804,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [1.6.0] - 2026-02-18 ## [1.6.0] - 2026-02-18
Versão de personalização da tabela de lançamentos. Duas novas preferências em Ajustes > Extrato e lançamentos: "Anotações em coluna" (controla se a anotação aparece como coluna ou tooltip no ícone) e "Ordem das colunas" (lista ordenável por arrasto pra reordenar Estabelecimento, Transação, Valor etc.). Inclui ajustes mobile no header do dashboard (fixo só no mobile) e na rolagem horizontal de tabs e botões de ação.
### Adicionado ### Adicionado
- Preferência "Anotações em coluna" em Ajustes > Extrato e lançamentos: quando ativa, a anotação dos lançamentos aparece em coluna na tabela; quando inativa, permanece no balão (tooltip) no ícone - Preferência "Anotações em coluna" em Ajustes > Extrato e lançamentos: quando ativa, a anotação dos lançamentos aparece em coluna na tabela; quando inativa, permanece no balão (tooltip) no ícone
@@ -723,6 +826,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [1.5.3] - 2026-02-21 ## [1.5.3] - 2026-02-21
Versão focada no painel do pagador (novo card "Status de Pagamento" com totais pagos/pendentes e listagem individual de boletos com data de vencimento, data de pagamento e status), além de SEO completo na landing page (Open Graph, Twitter Card, JSON-LD Schema.org, sitemap.xml e robots.txt) e layout específico com metadados ricos. Imagens da landing convertidas de PNG para WebP para melhor performance.
### Adicionado ### Adicionado
- Painel do pagador: card "Status de Pagamento" com totais pagos/pendentes e listagem individual de boletos com data de vencimento, data de pagamento e status - Painel do pagador: card "Status de Pagamento" com totais pagos/pendentes e listagem individual de boletos com data de vencimento, data de pagamento e status
@@ -744,6 +849,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [1.5.2] - 2026-02-16 ## [1.5.2] - 2026-02-16
Reforma visual da landing page: hero com gradient sutil e tipografia responsiva, dashboard preview sem bordas pra visual mais limpo, seção "Funcionalidades" reorganizada em 6 cards principais + 6 extras compactos, seção "Como usar" com tabs Docker (Recomendado) vs Manual e footer simplificado em 3 colunas. Inclui menu hamburger mobile com Sheet drawer, animações fade-in via Intersection Observer e seção dedicada ao OpenMonetis Companion com screenshots e fluxo de captura.
### Alterado ### Alterado
- Landing page reformulada: visual modernizado, melhor experiência mobile e novas seções - Landing page reformulada: visual modernizado, melhor experiência mobile e novas seções
@@ -766,6 +873,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [1.5.1] - 2026-02-16 ## [1.5.1] - 2026-02-16
Esta versão renomeia o projeto de **OpenSheets** para **OpenMonetis** em todo o codebase (~40 arquivos: package.json, manifests, layouts, componentes, server actions, emails, Docker, docs e landing page). URLs do repositório atualizados de `opensheets-app` para `openmonetis`, image Docker renomeada para `felipegcoutinho/openmonetis` e logo textual atualizado. Inclui também suporte a multi-domínio via `PUBLIC_DOMAIN` (domínio público serve apenas a landing page, com middleware bloqueando rotas do app).
### Alterado ### Alterado
- Projeto renomeado de **OpenSheets** para **OpenMonetis** em todo o codebase (~40 arquivos): package.json, manifests, layouts, componentes, server actions, emails, Docker, docs e landing page - Projeto renomeado de **OpenSheets** para **OpenMonetis** em todo o codebase (~40 arquivos): package.json, manifests, layouts, componentes, server actions, emails, Docker, docs e landing page
@@ -780,6 +889,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [1.5.0] - 2026-02-15 ## [1.5.0] - 2026-02-15
Versão de personalização tipográfica: 13 fontes disponíveis (incluindo SF Pro Display, SF Pro Rounded, Inter, Geist Sans, Roboto, Reddit Sans, JetBrains Mono e outras) configuráveis por usuário tanto pra interface quanto pros valores monetários, com FontProvider que aplica a troca instantaneamente via CSS variables sem necessidade de reload. Fontes Apple SF Pro carregadas localmente com 4 pesos (Regular, Medium, Semibold, Bold) e novas colunas `system_font` e `money_font` na tabela `preferencias_usuario`.
### Adicionado ### Adicionado
- Customização de fontes nas preferências — fonte da interface e fonte de valores monetários configuráveis por usuário - Customização de fontes nas preferências — fonte da interface e fonte de valores monetários configuráveis por usuário
@@ -799,6 +910,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [1.4.1] - 2026-02-15 ## [1.4.1] - 2026-02-15
Versão focada na inbox de pré-lançamentos: novas abas "Pendentes", "Processados" e "Descartados" (antes só pendentes), logo do cartão/conta exibido automaticamente nos cards via matching por nome do app, pre-fill automático do cartão de crédito ao processar e badges de status com data nos itens já processados/descartados em modo readonly. Cor `--warning` ajustada para melhor contraste (mais alaranjada).
### Adicionado ### Adicionado
- Abas "Pendentes", "Processados" e "Descartados" na página de pré-lançamentos (antes exibia apenas pendentes) - Abas "Pendentes", "Processados" e "Descartados" na página de pré-lançamentos (antes exibia apenas pendentes)
@@ -820,6 +933,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [1.4.0] - 2026-02-07 ## [1.4.0] - 2026-02-07
Reforma do design system: ~60+ componentes migrados de cores hardcoded do Tailwind (`green-500`, `red-600`, `amber-500`, `blue-500` etc.) pra tokens semânticos (`success`, `destructive`, `warning`, `info`); adicionados novos tokens `--success`, `--warning`, `--info` (com foregrounds) tanto em light quanto dark mode, novas variantes `success` e `info` no Badge, e cores de chart estendidas de 6 para 10. Inclui também correção do bug de invalidação de cache do dashboard que impedia widgets de boleto/fatura de atualizar após pagamento, e fix de scroll em listas Popover+Command (estabelecimento, categorias, filtros) com a prop `modal`.
### Corrigido ### Corrigido
- Widgets de boleto/fatura não atualizavam após pagamento: actions de fatura (`updateInvoicePaymentStatusAction`, `updatePaymentDateAction`) e antecipação de parcelas não invalidavam o cache do dashboard - Widgets de boleto/fatura não atualizavam após pagamento: actions de fatura (`updateInvoicePaymentStatusAction`, `updatePaymentDateAction`) e antecipação de parcelas não invalidavam o cache do dashboard
@@ -850,6 +965,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [1.3.1] - 2026-02-06 ## [1.3.1] - 2026-02-06
Versão pequena: calculadora arrastável via drag handle no header do dialog, callback `onSelectValue` pra inserir valor diretamente no campo de lançamento, e nova aba "Changelog" em Ajustes com histórico parseado do `CHANGELOG.md`. As páginas de itens ativos e arquivados em Cartões, Contas e Anotações foram unificadas com sistema de tabs (mesmo padrão de Categorias), eliminando rotas separadas e nomenclatura inconsistente.
### Adicionado ### Adicionado
- Calculadora arrastável via drag handle no header do dialog - Calculadora arrastável via drag handle no header do dialog
@@ -865,6 +982,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [1.3.0] - 2026-02-06 ## [1.3.0] - 2026-02-06
Versão de performance no dashboard: indexes compostos em `lancamentos`, cache cross-request via `unstable_cache` com tag `"dashboard"` e TTL de 120s, e invalidação automática em mutations financeiras via `revalidateTag`. Eliminados ~20 JOINs com a tabela `pagadores` (substituídos por filtro direto via `pagadorId`) e queries consolidadas (income-expense-balance: 12→1 com GROUP BY; payment-status: 2→1; expenses/income por categoria: 4→2). Auth session deduplicada por request via `React.cache()` e scan de métricas limitado a 24 meses.
### Adicionado ### Adicionado
- Indexes compostos em `lancamentos`: `(userId, period, transactionType)` e `(pagadorId, period)` - Indexes compostos em `lancamentos`: `(userId, period, transactionType)` e `(pagadorId, period)`
@@ -885,6 +1004,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [1.2.6] - 2025-02-04 ## [1.2.6] - 2025-02-04
Versão de adaptação ao React 19 compiler: removidos ~60 `useCallback`/`useMemo` desnecessários, wrappers `React.memo` redundantes e simplificação de padrões de hidratação com `useSyncExternalStore`. Sem mudanças visíveis ao usuário — só faxina interna alinhada às novas otimizações automáticas do compilador.
### Alterado ### Alterado
- Refatoração para otimização do React 19 compiler - Refatoração para otimização do React 19 compiler
@@ -913,6 +1034,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [1.2.5] - 2025-02-01 ## [1.2.5] - 2025-02-01
Versão pequena: novo widget de pagadores no dashboard com avatares atualizados.
### Adicionado ### Adicionado
- Widget de pagadores no dashboard - Widget de pagadores no dashboard
@@ -920,6 +1043,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [1.2.4] - 2025-01-22 ## [1.2.4] - 2025-01-22
Correção pontual: preservação de formatação nas anotações e ajuste no layout do card de anotações.
### Corrigido ### Corrigido
- Preservar formatação nas anotações - Preservar formatação nas anotações
@@ -927,6 +1052,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [1.2.3] - 2025-01-22 ## [1.2.3] - 2025-01-22
Versão pequena: versão do app passa a aparecer na sidebar e atualização da documentação.
### Adicionado ### Adicionado
- Versão exibida na sidebar - Versão exibida na sidebar
@@ -934,6 +1061,8 @@ Esta versão é quase toda sobre organização e polimento. O código interno do
## [1.2.2] - 2025-01-22 ## [1.2.2] - 2025-01-22
Versão de manutenção: atualização de dependências e formatação aplicada em todo o código.
### Alterado ### Alterado
- Atualização de dependências - Atualização de dependências

View File

@@ -97,7 +97,7 @@ src/
│ ├── api/ │ ├── api/
│ ├── globals.css │ ├── globals.css
│ └── layout.tsx │ └── layout.tsx
├── features/ ├── features/ # cada feature segue: actions.ts, queries.ts, actions/, components/, hooks/, lib/
│ ├── auth/ │ ├── auth/
│ ├── landing/ │ ├── landing/
│ ├── dashboard/ │ ├── dashboard/
@@ -117,9 +117,12 @@ src/
│ └── settings/ │ └── settings/
├── shared/ ├── shared/
│ ├── components/ │ ├── components/
│ │ ├── ui/ │ │ ├── ui/ # shadcn/ui primitives
│ │ ├── navigation/ │ │ ├── navigation/ # navbar, sidebar, breadcrumbs
│ │ ├── providers/ │ │ ├── providers/ # React context providers
│ │ ├── brand/ # logos do app (logo, logo-icon, logo-text)
│ │ ├── widgets/ # widget-card, widget-empty-state, expandable-widget-card
│ │ ├── feedback/ # empty-state, status-dot, payment-success
│ │ ├── month-picker/ │ │ ├── month-picker/
│ │ ├── logo-picker/ │ │ ├── logo-picker/
│ │ ├── calculator/ │ │ ├── calculator/
@@ -134,34 +137,56 @@ src/
│ │ ├── calculator/ │ │ ├── calculator/
│ │ ├── categories/ │ │ ├── categories/
│ │ ├── email/ │ │ ├── email/
│ │ ├── import/
│ │ ├── installments/ │ │ ├── installments/
│ │ ├── invoices/ │ │ ├── invoices/
│ │ ├── logo/ │ │ ├── logo/
│ │ ├── notifications/
│ │ ├── payers/ │ │ ├── payers/
│ │ ├── schemas/ │ │ ├── schemas/
│ │ ├── storage/
│ │ ├── transfers/ │ │ ├── transfers/
│ │ ├── types/ │ │ ├── types/
│ │ ├── version/
│ │ └── db.ts │ │ └── db.ts
│ └── utils/ │ └── utils/
│ ├── period/ │ ├── period/
│ ├── calculator.ts
│ ├── calendar.ts
│ ├── category-colors.ts
│ ├── currency.ts │ ├── currency.ts
│ ├── date.ts │ ├── date.ts
│ ├── export-branding.ts
│ ├── fetch-json.ts
│ ├── financial-dates.ts │ ├── financial-dates.ts
│ ├── percentage.ts │ ├── icons.tsx
│ ├── category-colors.ts │ ├── id.ts
│ ├── calendar.ts │ ├── initials.ts
│ ├── math.ts │ ├── math.ts
│ ├── number.ts │ ├── number.ts
│ ├── percentage.ts
│ ├── string.ts │ ├── string.ts
── initials.ts ── ui.ts
│ ├── icons.tsx
│ ├── export-branding.ts
│ ├── ui.ts
│ └── calculator.ts
└── db/ └── db/
└── schema.ts └── schema.ts
``` ```
### Estrutura interna padrão de uma feature
Toda feature em `src/features/<nome>/` segue:
```text
<feature>/
├── actions.ts # entry point de Server Actions (barrel quando há actions/)
├── queries.ts # entry point de leitura do banco
├── actions/ # (opcional) Server Actions divididas por domínio quando o volume cresce
├── components/ # componentes de UI da feature
├── hooks/ # React hooks específicos da feature
└── lib/ # helpers, types, sub-queries e constantes internas
```
`actions.ts` e `queries.ts` são as portas de entrada da feature. Tudo que é helper interno fica em `lib/`. Componentes e hooks ficam nas pastas com nome óbvio.
--- ---
## Import Patterns ## Import Patterns
@@ -299,9 +324,11 @@ export async function fetchData(userId: string, period: string) {
2. Criar a feature em `src/features/<feature>/` 2. Criar a feature em `src/features/<feature>/`
3. Separar: 3. Separar:
- `components/` - `components/`
- `queries.ts` - `queries.ts` (entry point de leitura)
- `actions.ts` - `actions.ts` (entry point de Server Actions; vira barrel quando crescer e migrar para `actions/`)
- `types.ts` ou `schemas.ts` quando fizer sentido - `lib/` para helpers internos, sub-queries por tópico, types e constantes da feature
- `types.ts` ou `schemas.ts` quando fizer sentido (alternativa a `lib/`)
- `hooks/` quando houver hooks específicos da feature
4. Extrair para `src/shared/` tudo que for reutilizavel 4. Extrair para `src/shared/` tudo que for reutilizavel
5. Atualizar navegacao e `revalidateForEntity()` se a feature tiver CRUD 5. Atualizar navegacao e `revalidateForEntity()` se a feature tiver CRUD
6. Rodar: 6. Rodar:

View File

@@ -8,7 +8,7 @@
> **⚠️ Não há versão online hospedada.** Você precisa clonar o repositório e rodar localmente ou no seu próprio servidor. > **⚠️ Não há versão online hospedada.** Você precisa clonar o repositório e rodar localmente ou no seu próprio servidor.
[![Version](https://img.shields.io/badge/version-2.5.3-blue?style=flat-square)](CHANGELOG.md) [![Version](https://img.shields.io/badge/version-2.5.5-blue?style=flat-square)](CHANGELOG.md)
[![Next.js](https://img.shields.io/badge/Next.js-black?style=flat-square&logo=next.js)](https://nextjs.org/) [![Next.js](https://img.shields.io/badge/Next.js-black?style=flat-square&logo=next.js)](https://nextjs.org/)
[![TypeScript](https://img.shields.io/badge/TypeScript-blue?style=flat-square&logo=typescript)](https://www.typescriptlang.org/) [![TypeScript](https://img.shields.io/badge/TypeScript-blue?style=flat-square&logo=typescript)](https://www.typescriptlang.org/)
[![PostgreSQL](https://img.shields.io/badge/PostgreSQL-blue?style=flat-square&logo=postgresql)](https://www.postgresql.org/) [![PostgreSQL](https://img.shields.io/badge/PostgreSQL-blue?style=flat-square&logo=postgresql)](https://www.postgresql.org/)
@@ -61,7 +61,7 @@ A ideia é simples: ter um lugar onde consigo ver todas as minhas contas, cartõ
### Funcionalidades ### Funcionalidades
💰 **Contas e transações** — Contas bancárias, cartões, dinheiro. Receitas, despesas e transferências. Categorização, extratos detalhados e importação de extratos OFX e XLS/XLSX com detecção automática de categoria. 💰 **Contas e transações** — Contas bancárias, cartões, dinheiro. Receitas, despesas e transferências. Categorização, filtros combináveis, extratos detalhados e importação de extratos OFX e XLS/XLSX com detecção automática de categoria.
📊 **Dashboard e relatórios** — Widgets interativos de métricas, gráficos de evolução, comparativos por categoria, tendências, uso de cartões, top estabelecimentos. Exportação em PDF e Excel. 📊 **Dashboard e relatórios** — Widgets interativos de métricas, gráficos de evolução, comparativos por categoria, tendências, uso de cartões, top estabelecimentos. Exportação em PDF e Excel.
@@ -85,7 +85,7 @@ A ideia é simples: ter um lugar onde consigo ver todas as minhas contas, cartõ
<img src="./public/images/companion-preview-light.webp" alt="OpenMonetis Companion" width="300" height="600" /> <img src="./public/images/companion-preview-light.webp" alt="OpenMonetis Companion" width="300" height="600" />
</p> </p>
⚙️ **Personalização** — Tema dark/light e modo privacidade. ⚙️ **Personalização** — Tema dark/light, modo privacidade e changelog visual para acompanhar as novidades do app.
### Stack técnica ### Stack técnica
@@ -127,21 +127,20 @@ Só quer rodar o OpenMonetis. **Não precisa clonar o repositório nem instalar
# 1. Baixe o compose # 1. Baixe o compose
curl -fsSL https://raw.githubusercontent.com/felipegcoutinho/openmonetis/main/docker-compose.yml -o docker-compose.yml curl -fsSL https://raw.githubusercontent.com/felipegcoutinho/openmonetis/main/docker-compose.yml -o docker-compose.yml
# 2. Suba tudo # 2. Crie um .en na mesma pasta.
# .env mínimo recomendado para produção
BETTER_AUTH_SECRET=gere-um-valor-com-openssl-rand-base64-32
BETTER_AUTH_URL=http://seu-dominio.com
# 3. Suba tudo
docker compose up -d docker compose up -d
``` ```
Acesse em: `http://localhost:3000` Acesse em: `http://localhost:3000`
O banco sobe com credenciais padrão. Para personalizar (senha, Google OAuth, e-mail, IA...), crie um `.env` na mesma pasta **antes** de subir: O banco sobe com credenciais padrão. Para personalizar (senha, Google OAuth, e-mail, IA...), crie um `.env` na mesma pasta **antes** de subir.
```bash Mais sobre .env em [Variáveis de Ambiente](#-variáveis-de-ambiente).
# .env mínimo recomendado para produção
BETTER_AUTH_SECRET=gere-um-valor-com-openssl-rand-base64-32
BETTER_AUTH_URL=https://seu-dominio.com
```
Veja todas as variáveis disponíveis em [Variáveis de Ambiente](#-variáveis-de-ambiente).
**Banco remoto (Supabase, Neon, Railway...):** defina `DATABASE_URL` no `.env` e suba só o app: **Banco remoto (Supabase, Neon, Railway...):** defina `DATABASE_URL` no `.env` e suba só o app:
@@ -508,7 +507,18 @@ openmonetis/
│ │ └── auth/ # Formulários de autenticação │ │ └── auth/ # Formulários de autenticação
│ │ │ │
│ ├── shared/ # Código reutilizado entre features │ ├── shared/ # Código reutilizado entre features
│ │ ├── components/ # UI compartilhada (shadcn/ui, navigation, skeletons...) │ │ ├── components/ # UI compartilhada
│ │ │ ├── ui/ # shadcn/ui primitives
│ │ │ ├── navigation/ # navbar, sidebar, breadcrumbs
│ │ │ ├── brand/ # logos do app
│ │ │ ├── widgets/ # widget-card e variantes
│ │ │ ├── feedback/ # empty-state, status-dot, payment-success
│ │ │ ├── entity-avatar/ # avatares de categoria/estabelecimento
│ │ │ ├── month-picker/ # seletor de período
│ │ │ ├── logo-picker/ # seletor de logos
│ │ │ ├── calculator/ # calculadora de cálculos rápidos
│ │ │ ├── skeletons/ # loading skeletons
│ │ │ └── providers/ # React context providers
│ │ ├── hooks/ # React hooks globais │ │ ├── hooks/ # React hooks globais
│ │ ├── lib/ # Helpers de domínio (auth, db, payers, schemas, email...) │ │ ├── lib/ # Helpers de domínio (auth, db, payers, schemas, email...)
│ │ └── utils/ # Utilitários (currency, date, period, math, string...) │ │ └── utils/ # Utilitários (currency, date, period, math, string...)
@@ -524,6 +534,22 @@ openmonetis/
└── proxy.ts # Middleware (auth + multi-domínio) └── proxy.ts # Middleware (auth + multi-domínio)
``` ```
### Estrutura interna de uma feature
Toda feature em `src/features/<nome>/` segue o mesmo padrão:
```
<feature>/
├── actions.ts # Server Actions (entry point — barrel re-export quando há actions/)
├── queries.ts # Funções de leitura do banco (entry point)
├── actions/ # (opcional) Server Actions divididas por domínio quando o volume cresce
├── components/ # Componentes de UI da feature
├── hooks/ # React hooks específicos da feature
└── lib/ # Helpers, types, sub-queries e constantes
```
A regra é: `actions.ts` e `queries.ts` são as portas de entrada da feature. Tudo que é helper interno fica em `lib/`. Componentes e hooks ficam nas pastas com nome óbvio.
--- ---
## 🤝 Contribuindo ## 🤝 Contribuindo
@@ -563,12 +589,6 @@ Para o texto legal completo, consulte o arquivo [LICENSE](LICENSE) ou visite [cr
--- ---
## 🙏 Agradecimentos
[Next.js](https://nextjs.org/) · [Better Auth](https://better-auth.com/) · [Drizzle ORM](https://orm.drizzle.team/) · [shadcn/ui](https://ui.shadcn.com/) · [Biome](https://biomejs.dev/) · [Vercel](https://vercel.com/)
---
**Desenvolvido por:** Felipe Coutinho — [@felipegcoutinho](https://github.com/felipegcoutinho) **Desenvolvido por:** Felipe Coutinho — [@felipegcoutinho](https://github.com/felipegcoutinho)
<div align="center"> <div align="center">

View File

@@ -12,8 +12,10 @@
], ],
// PostCSS is inferred from the config file, but the project only depends on // PostCSS is inferred from the config file, but the project only depends on
// the Tailwind PostCSS plugin directly. // the Tailwind PostCSS plugin directly.
// `server-only` is provided implicitly by Next.js — no install needed.
"ignoreDependencies": [ "ignoreDependencies": [
"postcss" "postcss",
"server-only"
], ],
"next": true, "next": true,
"postcss": true, "postcss": true,

View File

@@ -21,6 +21,7 @@ const nextConfig: NextConfig = {
experimental: { experimental: {
prefetchInlining: true, prefetchInlining: true,
turbopackFileSystemCacheForDev: true, turbopackFileSystemCacheForDev: true,
optimizePackageImports: ["@remixicon/react"],
}, },
// Headers for Safari compatibility // Headers for Safari compatibility

View File

@@ -1,6 +1,6 @@
{ {
"name": "openmonetis", "name": "openmonetis",
"version": "2.5.3", "version": "2.5.5",
"private": true, "private": true,
"packageManager": "pnpm@10.33.0", "packageManager": "pnpm@10.33.0",
"scripts": { "scripts": {
@@ -88,6 +88,7 @@
"resend": "^6.12.2", "resend": "^6.12.2",
"sonner": "2.0.7", "sonner": "2.0.7",
"tailwind-merge": "3.5.0", "tailwind-merge": "3.5.0",
"tw-animate-css": "^1.4.0",
"vaul": "1.1.2", "vaul": "1.1.2",
"zod": "4.4.3" "zod": "4.4.3"
}, },

8
pnpm-lock.yaml generated
View File

@@ -182,6 +182,9 @@ importers:
tailwind-merge: tailwind-merge:
specifier: 3.5.0 specifier: 3.5.0
version: 3.5.0 version: 3.5.0
tw-animate-css:
specifier: ^1.4.0
version: 1.4.0
vaul: vaul:
specifier: 1.1.2 specifier: 1.1.2
version: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) version: 1.1.2(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
@@ -4211,6 +4214,9 @@ packages:
resolution: {integrity: sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==} resolution: {integrity: sha512-axr3IdNuVIxnaK5XGEUFTu3YmAQ6lllgrvqfEoR16g/HGnYY/6We4oWENtAnzK6/LpJ2ur9PAb80RBt7/U4ugw==}
engines: {node: '>= 6.0.0'} engines: {node: '>= 6.0.0'}
tw-animate-css@1.4.0:
resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==}
typescript@6.0.3: typescript@6.0.3:
resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==}
engines: {node: '>=14.17'} engines: {node: '>=14.17'}
@@ -8398,6 +8404,8 @@ snapshots:
dependencies: dependencies:
tslib: 1.14.1 tslib: 1.14.1
tw-animate-css@1.4.0: {}
typescript@6.0.3: {} typescript@6.0.3: {}
unbash@3.0.0: {} unbash@3.0.0: {}

View File

@@ -21,7 +21,7 @@ import type {
PAYMENT_METHODS, PAYMENT_METHODS,
TRANSACTION_CONDITIONS, TRANSACTION_CONDITIONS,
TRANSACTION_TYPES, TRANSACTION_TYPES,
} from "@/features/transactions/constants"; } from "@/features/transactions/lib/constants";
import { import {
buildInvoicePaymentNote, buildInvoicePaymentNote,
INITIAL_BALANCE_CATEGORY_NAME, INITIAL_BALANCE_CATEGORY_NAME,

View File

@@ -1,4 +1,4 @@
import { Logo } from "@/shared/components/logo"; import { Logo } from "@/shared/components/brand/logo";
export default function AuthLayout({ export default function AuthLayout({
children, children,

View File

@@ -7,8 +7,8 @@ import { AdjustBalanceDialog } from "@/features/accounts/components/adjust-balan
import type { Account } from "@/features/accounts/components/types"; import type { Account } from "@/features/accounts/components/types";
import { import {
fetchAccountData, fetchAccountData,
fetchAccountLancamentosPage,
fetchAccountSummary, fetchAccountSummary,
fetchAccountTransactionsPage,
} from "@/features/accounts/statement-queries"; } from "@/features/accounts/statement-queries";
import { fetchUserPreferences } from "@/features/settings/queries"; import { fetchUserPreferences } from "@/features/settings/queries";
import { TransactionsPage as LancamentosSection } from "@/features/transactions/components/page/transactions-page"; import { TransactionsPage as LancamentosSection } from "@/features/transactions/components/page/transactions-page";
@@ -22,7 +22,7 @@ import {
mapTransactionsData, mapTransactionsData,
type ResolvedSearchParams, type ResolvedSearchParams,
resolveTransactionPagination, resolveTransactionPagination,
} from "@/features/transactions/page-helpers"; } from "@/features/transactions/lib/page-helpers";
import { import {
fetchRecentEstablishments, fetchRecentEstablishments,
fetchTransactionFilterSources, fetchTransactionFilterSources,
@@ -89,7 +89,7 @@ export default async function Page({ params, searchParams }: PageProps) {
accountId: account.id, accountId: account.id,
}); });
const transactionsPage = await fetchAccountLancamentosPage( const transactionsPage = await fetchAccountTransactionsPage(
filters, filters,
pagination, pagination,
); );

View File

@@ -4,7 +4,7 @@ import { fetchCalendarData } from "@/features/calendar/queries";
import { import {
getSingleParam, getSingleParam,
type ResolvedSearchParams, type ResolvedSearchParams,
} from "@/features/transactions/page-helpers"; } from "@/features/transactions/lib/page-helpers";
import MonthNavigation from "@/shared/components/month-picker/month-navigation"; import MonthNavigation from "@/shared/components/month-picker/month-navigation";
import { getUserId } from "@/shared/lib/auth/server"; import { getUserId } from "@/shared/lib/auth/server";
import type { CalendarPeriod } from "@/shared/lib/types/calendar"; import type { CalendarPeriod } from "@/shared/lib/types/calendar";

View File

@@ -21,7 +21,7 @@ import {
getSingleParam, getSingleParam,
mapTransactionsData, mapTransactionsData,
type ResolvedSearchParams, type ResolvedSearchParams,
} from "@/features/transactions/page-helpers"; } from "@/features/transactions/lib/page-helpers";
import { import {
fetchRecentEstablishments, fetchRecentEstablishments,
fetchTransactionFilterSources, fetchTransactionFilterSources,

View File

@@ -7,7 +7,7 @@ import { TransactionsPage } from "@/features/transactions/components/page/transa
import { import {
buildOptionSets, buildOptionSets,
buildSluggedFilters, buildSluggedFilters,
} from "@/features/transactions/page-helpers"; } from "@/features/transactions/lib/page-helpers";
import { import {
fetchRecentEstablishments, fetchRecentEstablishments,
fetchTransactionFilterSources, fetchTransactionFilterSources,

View File

@@ -2,9 +2,9 @@ import { connection } from "next/server";
import { DashboardGridEditable } from "@/features/dashboard/components/dashboard-grid-editable"; import { DashboardGridEditable } from "@/features/dashboard/components/dashboard-grid-editable";
import { DashboardMetricsCards } from "@/features/dashboard/components/dashboard-metrics-cards"; import { DashboardMetricsCards } from "@/features/dashboard/components/dashboard-metrics-cards";
import { DashboardWelcome } from "@/features/dashboard/components/dashboard-welcome"; import { DashboardWelcome } from "@/features/dashboard/components/dashboard-welcome";
import { extractDashboardLogoNames } from "@/features/dashboard/extract-logo-names"; import { extractDashboardLogoNames } from "@/features/dashboard/lib/extract-logo-names";
import { fetchDashboardPageData } from "@/features/dashboard/page-data-queries"; import { fetchDashboardPageData } from "@/features/dashboard/page-data-queries";
import { getSingleParam } from "@/features/transactions/page-helpers"; import { getSingleParam } from "@/features/transactions/lib/page-helpers";
import { LogoPrefetchProvider } from "@/shared/components/entity-avatar"; import { LogoPrefetchProvider } from "@/shared/components/entity-avatar";
import MonthNavigation from "@/shared/components/month-picker/month-navigation"; import MonthNavigation from "@/shared/components/month-picker/month-navigation";
import { getUser } from "@/shared/lib/auth/server"; import { getUser } from "@/shared/lib/auth/server";

View File

@@ -1,5 +1,5 @@
import { connection } from "next/server"; import { connection } from "next/server";
import { fetchDashboardNavbarData } from "@/features/dashboard/navbar-queries"; import { fetchDashboardNavbarData } from "@/features/dashboard/lib/navbar-queries";
import { AppNavbar } from "@/shared/components/navigation/navbar/app-navbar"; import { AppNavbar } from "@/shared/components/navigation/navbar/app-navbar";
import { LogoDevProvider } from "@/shared/components/providers/logo-dev-provider"; import { LogoDevProvider } from "@/shared/components/providers/logo-dev-provider";
import { PrivacyProvider } from "@/shared/components/providers/privacy-provider"; import { PrivacyProvider } from "@/shared/components/providers/privacy-provider";
@@ -21,8 +21,8 @@ export default async function DashboardLayout({
<PrivacyProvider> <PrivacyProvider>
<AppNavbar <AppNavbar
user={{ ...session.user, image: session.user.image ?? null }} user={{ ...session.user, image: session.user.image ?? null }}
pagadorAvatarUrl={navbarData.pagadorAvatarUrl} payerAvatarUrl={navbarData.payerAvatarUrl}
preLancamentosCount={navbarData.preLancamentosCount} inboxPendingCount={navbarData.inboxPendingCount}
notificationsSnapshot={navbarData.notificationsSnapshot} notificationsSnapshot={navbarData.notificationsSnapshot}
/> />
<div className="relative flex flex-1 flex-col pt-16"> <div className="relative flex flex-1 flex-col pt-16">

View File

@@ -4,7 +4,7 @@ import { Skeleton } from "@/shared/components/ui/skeleton";
* Loading state para a página de detalhes do pagador. * Loading state para a página de detalhes do pagador.
* Layout: navegação mensal + tabs com card compartilhado do pagador. * Layout: navegação mensal + tabs com card compartilhado do pagador.
*/ */
export default function PagadorDetailsLoading() { export default function PayerDetailsLoading() {
return ( return (
<main className="flex flex-col gap-6"> <main className="flex flex-col gap-6">
<div className="h-[60px] animate-pulse rounded-md bg-foreground/10" /> <div className="h-[60px] animate-pulse rounded-md bg-foreground/10" />

View File

@@ -8,7 +8,7 @@ import { connection } from "next/server";
import { PayerCardUsageCard } from "@/features/payers/components/details/payer-card-usage-card"; import { PayerCardUsageCard } from "@/features/payers/components/details/payer-card-usage-card";
import { PayerHeaderCard } from "@/features/payers/components/details/payer-header-card"; import { PayerHeaderCard } from "@/features/payers/components/details/payer-header-card";
import { PayerHistoryCard } from "@/features/payers/components/details/payer-history-card"; import { PayerHistoryCard } from "@/features/payers/components/details/payer-history-card";
import { PagadorInfoCard } from "@/features/payers/components/details/payer-info-card"; import { PayerInfoCard } from "@/features/payers/components/details/payer-info-card";
import { PayerLeaveShareCard } from "@/features/payers/components/details/payer-leave-share-card"; import { PayerLeaveShareCard } from "@/features/payers/components/details/payer-leave-share-card";
import { PayerMonthlySummaryCard } from "@/features/payers/components/details/payer-monthly-summary-card"; import { PayerMonthlySummaryCard } from "@/features/payers/components/details/payer-monthly-summary-card";
import { import {
@@ -16,12 +16,12 @@ import {
PayerPaymentStatusCard, PayerPaymentStatusCard,
} from "@/features/payers/components/details/payer-payment-method-cards"; } from "@/features/payers/components/details/payer-payment-method-cards";
import { PayerSharingCard } from "@/features/payers/components/details/payer-sharing-card"; import { PayerSharingCard } from "@/features/payers/components/details/payer-sharing-card";
import { buildReadOnlyOptionSets } from "@/features/payers/lib/build-readonly-option-sets";
import { import {
fetchCurrentUserShare, fetchCurrentUserShare,
fetchPagadorLancamentos,
fetchPayerShares, fetchPayerShares,
} from "@/features/payers/detail-queries"; fetchPayerTransactions,
import { buildReadOnlyOptionSets } from "@/features/payers/lib/build-readonly-option-sets"; } from "@/features/payers/lib/detail-queries";
import { fetchUserPreferences } from "@/features/settings/queries"; import { fetchUserPreferences } from "@/features/settings/queries";
import { TransactionsPage as LancamentosSection } from "@/features/transactions/components/page/transactions-page"; import { TransactionsPage as LancamentosSection } from "@/features/transactions/components/page/transactions-page";
import { import {
@@ -36,13 +36,12 @@ import {
type SluggedFilters, type SluggedFilters,
type SlugMaps, type SlugMaps,
type TransactionSearchFilters, type TransactionSearchFilters,
} from "@/features/transactions/page-helpers"; } from "@/features/transactions/lib/page-helpers";
import { import {
fetchRecentEstablishments, fetchRecentEstablishments,
fetchTransactionFilterSources, fetchTransactionFilterSources,
} from "@/features/transactions/queries"; } from "@/features/transactions/queries";
import { LogoPrefetchProvider } from "@/shared/components/entity-avatar"; import { LogoPrefetchProvider } from "@/shared/components/entity-avatar";
import { ExpandableWidgetCard } from "@/shared/components/expandable-widget-card";
import MonthNavigation from "@/shared/components/month-picker/month-navigation"; import MonthNavigation from "@/shared/components/month-picker/month-navigation";
import { import {
Tabs, Tabs,
@@ -50,16 +49,17 @@ import {
TabsList, TabsList,
TabsTrigger, TabsTrigger,
} from "@/shared/components/ui/tabs"; } from "@/shared/components/ui/tabs";
import { ExpandableWidgetCard } from "@/shared/components/widgets/expandable-widget-card";
import { getUserId } from "@/shared/lib/auth/server"; import { getUserId } from "@/shared/lib/auth/server";
import { prefetchLogoMappings } from "@/shared/lib/logo/prefetch-server"; import { prefetchLogoMappings } from "@/shared/lib/logo/prefetch-server";
import { getPayerAccess } from "@/shared/lib/payers/access"; import { getPayerAccess } from "@/shared/lib/payers/access";
import { import {
fetchPagadorBoletoItems, fetchPayerBoletoItems,
fetchPagadorBoletoStats, fetchPayerBoletoStats,
fetchPagadorCardUsage, fetchPayerCardUsage,
fetchPagadorPaymentStatus,
fetchPayerHistory, fetchPayerHistory,
fetchPayerMonthlyBreakdown, fetchPayerMonthlyBreakdown,
fetchPayerPaymentStatus,
type PayerCardUsageItem, type PayerCardUsageItem,
} from "@/shared/lib/payers/details"; } from "@/shared/lib/payers/details";
import { parsePeriodParam } from "@/shared/utils/period"; import { parsePeriodParam } from "@/shared/utils/period";
@@ -76,11 +76,11 @@ const capitalize = (value: string) =>
const EMPTY_FILTERS: TransactionSearchFilters = { const EMPTY_FILTERS: TransactionSearchFilters = {
transactionFilter: null, transactionFilter: null,
conditionFilter: null, conditionFilters: [],
paymentFilter: null, paymentFilters: [],
payerFilter: null, payerFilters: [],
categoryFilter: null, categoryFilters: [],
accountCardFilter: null, accountCardFilters: [],
searchFilter: null, searchFilter: null,
settledFilter: null, settledFilter: null,
attachmentFilter: null, attachmentFilter: null,
@@ -182,7 +182,7 @@ export default async function Page({ params, searchParams }: PageProps) {
estabelecimentos, estabelecimentos,
userPreferences, userPreferences,
] = await Promise.all([ ] = await Promise.all([
fetchPagadorLancamentos(filters), fetchPayerTransactions(filters),
fetchPayerMonthlyBreakdown({ fetchPayerMonthlyBreakdown({
userId: dataOwnerId, userId: dataOwnerId,
payerId: pagador.id, payerId: pagador.id,
@@ -193,22 +193,22 @@ export default async function Page({ params, searchParams }: PageProps) {
payerId: pagador.id, payerId: pagador.id,
period: selectedPeriod, period: selectedPeriod,
}), }),
fetchPagadorCardUsage({ fetchPayerCardUsage({
userId: dataOwnerId, userId: dataOwnerId,
payerId: pagador.id, payerId: pagador.id,
period: selectedPeriod, period: selectedPeriod,
}), }),
fetchPagadorBoletoStats({ fetchPayerBoletoStats({
userId: dataOwnerId, userId: dataOwnerId,
payerId: pagador.id, payerId: pagador.id,
period: selectedPeriod, period: selectedPeriod,
}), }),
fetchPagadorBoletoItems({ fetchPayerBoletoItems({
userId: dataOwnerId, userId: dataOwnerId,
payerId: pagador.id, payerId: pagador.id,
period: selectedPeriod, period: selectedPeriod,
}), }),
fetchPagadorPaymentStatus({ fetchPayerPaymentStatus({
userId: dataOwnerId, userId: dataOwnerId,
payerId: pagador.id, payerId: pagador.id,
period: selectedPeriod, period: selectedPeriod,
@@ -333,7 +333,7 @@ export default async function Page({ params, searchParams }: PageProps) {
/> />
<TabsContent value="profile" className="space-y-4"> <TabsContent value="profile" className="space-y-4">
<PagadorInfoCard payer={payerData} /> <PayerInfoCard payer={payerData} />
{canEdit && payerData.shareCode ? ( {canEdit && payerData.shareCode ? (
<PayerSharingCard <PayerSharingCard
payerId={pagador.id} payerId={pagador.id}

View File

@@ -4,7 +4,7 @@ import { Skeleton } from "@/shared/components/ui/skeleton";
* Loading state para a página de pessoas * Loading state para a página de pessoas
* Layout: Header + Input de compartilhamento + Grid de cards * Layout: Header + Input de compartilhamento + Grid de cards
*/ */
export default function PagadoresLoading() { export default function PayersLoading() {
return ( return (
<main className="flex flex-col items-start gap-6"> <main className="flex flex-col items-start gap-6">
<div className="w-full space-y-6"> <div className="w-full space-y-6">

View File

@@ -1,11 +1,11 @@
import { RiBankCard2Line } from "@remixicon/react"; import { RiBankCard2Line } from "@remixicon/react";
import { connection } from "next/server"; import { connection } from "next/server";
import { fetchCartoesReportData } from "@/features/reports/cards-report-queries";
import { CardCategoryBreakdown } from "@/features/reports/components/cards/card-category-breakdown"; import { CardCategoryBreakdown } from "@/features/reports/components/cards/card-category-breakdown";
import { CardInvoiceStatus } from "@/features/reports/components/cards/card-invoice-status"; import { CardInvoiceStatus } from "@/features/reports/components/cards/card-invoice-status";
import { CardTopExpenses } from "@/features/reports/components/cards/card-top-expenses"; import { CardTopExpenses } from "@/features/reports/components/cards/card-top-expenses";
import { CardUsageChart } from "@/features/reports/components/cards/card-usage-chart"; import { CardUsageChart } from "@/features/reports/components/cards/card-usage-chart";
import { CardsOverview } from "@/features/reports/components/cards/cards-overview"; import { CardsOverview } from "@/features/reports/components/cards/cards-overview";
import { fetchCartoesReportData } from "@/features/reports/lib/cards-report-queries";
import MonthNavigation from "@/shared/components/month-picker/month-navigation"; import MonthNavigation from "@/shared/components/month-picker/month-navigation";
import { Card } from "@/shared/components/ui/card"; import { Card } from "@/shared/components/ui/card";
import { getUser } from "@/shared/lib/auth/server"; import { getUser } from "@/shared/lib/auth/server";

View File

@@ -1,15 +1,15 @@
import { redirect } from "next/navigation"; import { redirect } from "next/navigation";
import { connection } from "next/server"; import { connection } from "next/server";
import type { Category } from "@/db/schema"; import type { Category } from "@/db/schema";
import { fetchCategoryChartData } from "@/features/reports/category-chart-queries";
import { fetchCategoryReport } from "@/features/reports/category-report-queries";
import { fetchUserCategories } from "@/features/reports/category-trends-queries";
import { CategoryReportPage } from "@/features/reports/components/category-report-page"; import { CategoryReportPage } from "@/features/reports/components/category-report-page";
import type { import type {
CategoryOption, CategoryOption,
FilterState, FilterState,
} from "@/features/reports/components/types"; } from "@/features/reports/components/types";
import { validateDateRange } from "@/features/reports/utils"; import { fetchCategoryChartData } from "@/features/reports/lib/category-chart-queries";
import { fetchCategoryReport } from "@/features/reports/lib/category-report-queries";
import { fetchUserCategories } from "@/features/reports/lib/category-trends-queries";
import { validateDateRange } from "@/features/reports/lib/utils";
import { getUserId } from "@/shared/lib/auth/server"; import { getUserId } from "@/shared/lib/auth/server";
import type { CategoryReportFilters } from "@/shared/lib/types/reports"; import type { CategoryReportFilters } from "@/shared/lib/types/reports";
import { addMonthsToPeriod, getCurrentPeriod } from "@/shared/utils/period"; import { addMonthsToPeriod, getCurrentPeriod } from "@/shared/utils/period";

View File

@@ -34,7 +34,7 @@ const validatePeriodFilter = (value: string | null): PeriodFilter => {
return "6"; return "6";
}; };
export default async function TopEstabelecimentosPage({ export default async function TopEstablishmentsPage({
searchParams, searchParams,
}: PageProps) { }: PageProps) {
await connection(); await connection();

View File

@@ -3,7 +3,7 @@ import { ImportPage } from "@/features/transactions/components/import/import-pag
import { import {
buildOptionSets, buildOptionSets,
buildSluggedFilters, buildSluggedFilters,
} from "@/features/transactions/page-helpers"; } from "@/features/transactions/lib/page-helpers";
import { fetchTransactionFilterSources } from "@/features/transactions/queries"; import { fetchTransactionFilterSources } from "@/features/transactions/queries";
import { getUserId } from "@/shared/lib/auth/server"; import { getUserId } from "@/shared/lib/auth/server";

View File

@@ -8,7 +8,7 @@ import { Skeleton } from "@/shared/components/ui/skeleton";
* Loading state para a página de lançamentos * Loading state para a página de lançamentos
* Mantém o mesmo layout da página final * Mantém o mesmo layout da página final
*/ */
export default function LancamentosLoading() { export default function TransactionsLoading() {
return ( return (
<main className="flex flex-col gap-6"> <main className="flex flex-col gap-6">
{/* Month Picker placeholder */} {/* Month Picker placeholder */}

View File

@@ -11,7 +11,7 @@ import {
mapTransactionsData, mapTransactionsData,
type ResolvedSearchParams, type ResolvedSearchParams,
resolveTransactionPagination, resolveTransactionPagination,
} from "@/features/transactions/page-helpers"; } from "@/features/transactions/lib/page-helpers";
import { import {
fetchRecentEstablishments, fetchRecentEstablishments,
fetchTransactionFilterSources, fetchTransactionFilterSources,

View File

@@ -24,7 +24,7 @@ import {
import { landingImages } from "@/features/landing/images"; import { landingImages } from "@/features/landing/images";
import { fetchGitHubStats } from "@/features/landing/queries"; import { fetchGitHubStats } from "@/features/landing/queries";
import { AnimatedThemeToggler } from "@/shared/components/animated-theme-toggler"; import { AnimatedThemeToggler } from "@/shared/components/animated-theme-toggler";
import { Logo } from "@/shared/components/logo"; import { Logo } from "@/shared/components/brand/logo";
import { NavbarShell } from "@/shared/components/navigation/navbar/navbar-shell"; import { NavbarShell } from "@/shared/components/navigation/navbar/navbar-shell";
import { Badge } from "@/shared/components/ui/badge"; import { Badge } from "@/shared/components/ui/badge";
import { Button } from "@/shared/components/ui/button"; import { Button } from "@/shared/components/ui/button";
@@ -208,7 +208,7 @@ export default async function Page() {
</section> </section>
{/* Features Section */} {/* Features Section */}
<section id="funcionalidades" className="py-12 md:py-24 bg-muted/40"> <section id="funcionalidades" className="py-12 md:py-24">
<div className="max-w-8xl mx-auto px-4"> <div className="max-w-8xl mx-auto px-4">
<div className="mx-auto max-w-6xl"> <div className="mx-auto max-w-6xl">
<AnimateOnScroll> <AnimateOnScroll>
@@ -447,7 +447,7 @@ export default async function Page() {
</section> </section>
{/* Tech Stack Section */} {/* Tech Stack Section */}
<section id="stack" className="py-12 md:py-24 bg-muted/40"> <section id="stack" className="py-12 md:py-24">
<div className="max-w-8xl mx-auto px-4"> <div className="max-w-8xl mx-auto px-4">
<div className="mx-auto max-w-6xl"> <div className="mx-auto max-w-6xl">
<AnimateOnScroll> <AnimateOnScroll>
@@ -535,7 +535,7 @@ export default async function Page() {
</section> </section>
{/* Who is this for Section */} {/* Who is this for Section */}
<section id="para-quem-e" className="py-12 md:py-24 bg-muted/40"> <section id="para-quem-e" className="py-12 md:py-24">
<div className="max-w-8xl mx-auto px-4"> <div className="max-w-8xl mx-auto px-4">
<div className="mx-auto max-w-4xl"> <div className="mx-auto max-w-4xl">
<AnimateOnScroll> <AnimateOnScroll>

View File

@@ -1,5 +1,5 @@
import { NextResponse } from "next/server"; import { NextResponse } from "next/server";
import { fetchTransactionAttachments } from "@/features/transactions/attachment-queries"; import { fetchTransactionAttachments } from "@/features/transactions/lib/attachment-queries";
import { getOptionalUserSession } from "@/shared/lib/auth/server"; import { getOptionalUserSession } from "@/shared/lib/auth/server";
const PRIVATE_RESPONSE_HEADERS = { const PRIVATE_RESPONSE_HEADERS = {

View File

@@ -1,5 +1,5 @@
import { NextResponse } from "next/server"; import { NextResponse } from "next/server";
import { fetchInstallmentAnticipations } from "@/features/transactions/anticipation-queries"; import { fetchInstallmentAnticipations } from "@/features/transactions/lib/anticipation-queries";
import { getOptionalUserSession } from "@/shared/lib/auth/server"; import { getOptionalUserSession } from "@/shared/lib/auth/server";
const PRIVATE_RESPONSE_HEADERS = { const PRIVATE_RESPONSE_HEADERS = {

View File

@@ -1,4 +1,5 @@
@import "tailwindcss"; @import "tailwindcss";
@import "tw-animate-css";
@custom-variant dark (&:is(.dark *)); @custom-variant dark (&:is(.dark *));
@@ -269,54 +270,6 @@
mix-blend-mode: normal; mix-blend-mode: normal;
} }
@keyframes dialog-in {
from {
opacity: 0;
transform: scale(0.96);
}
to {
opacity: 1;
transform: scale(1);
}
}
@keyframes dialog-out {
from {
opacity: 1;
transform: scale(1);
}
to {
opacity: 0;
transform: scale(0.96);
}
}
@keyframes overlay-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes overlay-out {
from { opacity: 1; }
to { opacity: 0; }
}
[data-slot="dialog-overlay"][data-state="open"] {
animation: overlay-in 0.2s ease-out;
}
[data-slot="dialog-overlay"][data-state="closed"] {
animation: overlay-out 0.15s ease-in;
}
[data-slot="dialog-content"][data-state="open"] {
animation: dialog-in 0.25s cubic-bezier(0.16, 1, 0.3, 1);
}
[data-slot="dialog-content"][data-state="closed"] {
animation: dialog-out 0.15s ease-in;
}
@keyframes blink-in { @keyframes blink-in {
0%, 40% { opacity: 1; } 0%, 40% { opacity: 1; }
50%, 90% { opacity: 0; } 50%, 90% { opacity: 0; }

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import StatusDot from "@/shared/components/status-dot"; import StatusDot from "@/shared/components/feedback/status-dot";
import { getAccountTypeIcon } from "@/shared/utils/icons"; import { getAccountTypeIcon } from "@/shared/utils/icons";
export function AccountTypeSelectContent({ label }: { label: string }) { export function AccountTypeSelectContent({ label }: { label: string }) {

View File

@@ -8,7 +8,7 @@ import { toast } from "sonner";
import { deleteAccountAction } from "@/features/accounts/actions"; import { deleteAccountAction } from "@/features/accounts/actions";
import { AccountCard } from "@/features/accounts/components/account-card"; import { AccountCard } from "@/features/accounts/components/account-card";
import { ConfirmActionDialog } from "@/shared/components/confirm-action-dialog"; import { ConfirmActionDialog } from "@/shared/components/confirm-action-dialog";
import { EmptyState } from "@/shared/components/empty-state"; import { EmptyState } from "@/shared/components/feedback/empty-state";
import { Button } from "@/shared/components/ui/button"; import { Button } from "@/shared/components/ui/button";
import { Card } from "@/shared/components/ui/card"; import { Card } from "@/shared/components/ui/card";
import { import {

View File

@@ -99,13 +99,13 @@ async function fetchAccountsByStatus(
return { accounts, logoOptions }; return { accounts, logoOptions };
} }
export async function fetchAccountsForUser( async function fetchAccountsForUser(
userId: string, userId: string,
): Promise<{ accounts: AccountData[]; logoOptions: string[] }> { ): Promise<{ accounts: AccountData[]; logoOptions: string[] }> {
return fetchAccountsByStatus(userId, false); return fetchAccountsByStatus(userId, false);
} }
export async function fetchInactiveForUser( async function fetchInactiveForUser(
userId: string, userId: string,
): Promise<{ accounts: AccountData[]; logoOptions: string[] }> { ): Promise<{ accounts: AccountData[]; logoOptions: string[] }> {
return fetchAccountsByStatus(userId, true); return fetchAccountsByStatus(userId, true);

View File

@@ -154,7 +154,7 @@ export async function fetchAccountSummary(
}; };
} }
export async function fetchAccountLancamentos( export async function fetchAccountTransactions(
filters: SQL[], filters: SQL[],
settledOnly = true, settledOnly = true,
) { ) {
@@ -167,7 +167,7 @@ export async function fetchAccountLancamentos(
}); });
} }
export async function fetchAccountLancamentosPage( export async function fetchAccountTransactionsPage(
filters: SQL[], filters: SQL[],
{ {
page, page,

View File

@@ -18,7 +18,7 @@ import { fetchTransactionDialogOptionsAction } from "@/features/transactions/act
import { TransactionDetailsDialog } from "@/features/transactions/components/dialogs/transaction-details-dialog"; import { TransactionDetailsDialog } from "@/features/transactions/components/dialogs/transaction-details-dialog";
import { TransactionDialog } from "@/features/transactions/components/dialogs/transaction-dialog/transaction-dialog"; import { TransactionDialog } from "@/features/transactions/components/dialogs/transaction-dialog/transaction-dialog";
import type { TransactionItem } from "@/features/transactions/components/types"; import type { TransactionItem } from "@/features/transactions/components/types";
import { EmptyState } from "@/shared/components/empty-state"; import { EmptyState } from "@/shared/components/feedback/empty-state";
import { Card, CardContent } from "@/shared/components/ui/card"; import { Card, CardContent } from "@/shared/components/ui/card";
import { cn } from "@/shared/utils/ui"; import { cn } from "@/shared/utils/ui";

View File

@@ -2,11 +2,11 @@
import { useQuery } from "@tanstack/react-query"; import { useQuery } from "@tanstack/react-query";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { fetchJson } from "@/shared/lib/fetch-json"; import { fetchJson } from "@/shared/utils/fetch-json";
const ATTACHMENT_URL_STALE_TIME = 4 * 60 * 1000; const ATTACHMENT_URL_STALE_TIME = 4 * 60 * 1000;
export const attachmentUrlQueryKey = (attachmentId: string) => const attachmentUrlQueryKey = (attachmentId: string) =>
["attachments", "url", attachmentId] as const; ["attachments", "url", attachmentId] as const;
export function useAttachmentUrlQuery(attachmentId: string, enabled: boolean) { export function useAttachmentUrlQuery(attachmentId: string, enabled: boolean) {

View File

@@ -3,7 +3,7 @@ import {
RiBarChart2Line, RiBarChart2Line,
RiShieldCheckLine, RiShieldCheckLine,
} from "@remixicon/react"; } from "@remixicon/react";
import { Logo } from "@/shared/components/logo"; import { Logo } from "@/shared/components/brand/logo";
import { DotPattern } from "@/shared/components/ui/dot-pattern"; import { DotPattern } from "@/shared/components/ui/dot-pattern";
import { AuthSidebarInvoicesMock } from "./auth-sidebar-invoices-mock"; import { AuthSidebarInvoicesMock } from "./auth-sidebar-invoices-mock";

View File

@@ -8,7 +8,7 @@ import {
duplicatePreviousMonthBudgetsAction, duplicatePreviousMonthBudgetsAction,
} from "@/features/budgets/actions"; } from "@/features/budgets/actions";
import { ConfirmActionDialog } from "@/shared/components/confirm-action-dialog"; import { ConfirmActionDialog } from "@/shared/components/confirm-action-dialog";
import { EmptyState } from "@/shared/components/empty-state"; import { EmptyState } from "@/shared/components/feedback/empty-state";
import { Button } from "@/shared/components/ui/button"; import { Button } from "@/shared/components/ui/button";
import { Card } from "@/shared/components/ui/card"; import { Card } from "@/shared/components/ui/card";
import { BudgetCard } from "./budget-card"; import { BudgetCard } from "./budget-card";

View File

@@ -26,7 +26,7 @@ type BudgetData = {
} | null; } | null;
}; };
export type CategoryOption = { type CategoryOption = {
id: string; id: string;
name: string; name: string;
icon: string | null; icon: string | null;

View File

@@ -4,7 +4,7 @@ import {
buildOptionSets, buildOptionSets,
buildSluggedFilters, buildSluggedFilters,
mapTransactionsData, mapTransactionsData,
} from "@/features/transactions/page-helpers"; } from "@/features/transactions/lib/page-helpers";
import { import {
fetchRecentEstablishments, fetchRecentEstablishments,
fetchTransactionFilterSources, fetchTransactionFilterSources,

View File

@@ -2,7 +2,7 @@
import { RiBankLine } from "@remixicon/react"; import { RiBankLine } from "@remixicon/react";
import Image from "next/image"; import Image from "next/image";
import StatusDot from "@/shared/components/status-dot"; import StatusDot from "@/shared/components/feedback/status-dot";
import { resolveCardBrandLogoSrc } from "@/shared/lib/cards/brand-assets"; import { resolveCardBrandLogoSrc } from "@/shared/lib/cards/brand-assets";
import { resolveLogoSrc } from "@/shared/lib/logo"; import { resolveLogoSrc } from "@/shared/lib/logo";

View File

@@ -6,7 +6,7 @@ import { useMemo, useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import { deleteCardAction } from "@/features/cards/actions"; import { deleteCardAction } from "@/features/cards/actions";
import { ConfirmActionDialog } from "@/shared/components/confirm-action-dialog"; import { ConfirmActionDialog } from "@/shared/components/confirm-action-dialog";
import { EmptyState } from "@/shared/components/empty-state"; import { EmptyState } from "@/shared/components/feedback/empty-state";
import { Button } from "@/shared/components/ui/button"; import { Button } from "@/shared/components/ui/button";
import { Card as UiCard } from "@/shared/components/ui/card"; import { Card as UiCard } from "@/shared/components/ui/card";
import { import {

View File

@@ -19,7 +19,7 @@ type CardData = {
accountName: string; accountName: string;
}; };
export type AccountSimple = { type AccountSimple = {
id: string; id: string;
name: string; name: string;
logo: string | null; logo: string | null;
@@ -121,7 +121,7 @@ async function fetchCardsByStatus(
return { cards: cardList, accounts, logoOptions }; return { cards: cardList, accounts, logoOptions };
} }
export async function fetchCardsForUser(userId: string): Promise<{ async function fetchCardsForUser(userId: string): Promise<{
cards: CardData[]; cards: CardData[];
accounts: AccountSimple[]; accounts: AccountSimple[];
logoOptions: string[]; logoOptions: string[];
@@ -129,7 +129,7 @@ export async function fetchCardsForUser(userId: string): Promise<{
return fetchCardsByStatus(userId, false); return fetchCardsByStatus(userId, false);
} }
export async function fetchInactiveForUser(userId: string): Promise<{ async function fetchInactiveForUser(userId: string): Promise<{
cards: CardData[]; cards: CardData[];
accounts: AccountSimple[]; accounts: AccountSimple[];
logoOptions: string[]; logoOptions: string[];

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import StatusDot from "@/shared/components/status-dot"; import StatusDot from "@/shared/components/feedback/status-dot";
export function TypeSelectContent({ label }: { label: string }) { export function TypeSelectContent({ label }: { label: string }) {
const isReceita = label === "Receita"; const isReceita = label === "Receita";

View File

@@ -3,7 +3,7 @@ import { type Category, categories } from "@/db/schema";
import type { CategoryType } from "@/shared/lib/categories/constants"; import type { CategoryType } from "@/shared/lib/categories/constants";
import { db } from "@/shared/lib/db"; import { db } from "@/shared/lib/db";
export type CategoryData = { type CategoryData = {
id: string; id: string;
name: string; name: string;
type: CategoryType; type: CategoryType;

View File

@@ -8,7 +8,7 @@ import {
} from "@/shared/utils/financial-dates"; } from "@/shared/utils/financial-dates";
export type BillDialogState = PaymentDialogState; export type BillDialogState = PaymentDialogState;
export type BillStatusDateItem = Pick< type BillStatusDateItem = Pick<
DashboardBill, DashboardBill,
"dueDate" | "boletoPaymentDate" | "isSettled" "dueDate" | "boletoPaymentDate" | "isSettled"
>; >;

View File

@@ -1,6 +1,6 @@
import { and, desc, eq, isNull, ne, or, sql } from "drizzle-orm"; import { and, desc, eq, isNull, ne, or, sql } from "drizzle-orm";
import { categories, financialAccounts, transactions } from "@/db/schema"; import { categories, financialAccounts, transactions } from "@/db/schema";
import { mapTransactionsData } from "@/features/transactions/page-helpers"; import { mapTransactionsData } from "@/features/transactions/lib/page-helpers";
import { import {
ACCOUNT_AUTO_INVOICE_NOTE_PREFIX, ACCOUNT_AUTO_INVOICE_NOTE_PREFIX,
INITIAL_BALANCE_NOTE, INITIAL_BALANCE_NOTE,
@@ -17,7 +17,7 @@ import { getPreviousPeriod } from "@/shared/utils/period";
type MappedLancamentos = ReturnType<typeof mapTransactionsData>; type MappedLancamentos = ReturnType<typeof mapTransactionsData>;
export type CategoryDetailData = { type CategoryDetailData = {
category: { category: {
id: string; id: string;
name: string; name: string;

View File

@@ -11,14 +11,14 @@ import {
formatPeriodMonthShort, formatPeriodMonthShort,
} from "@/shared/utils/period"; } from "@/shared/utils/period";
export type CategoryOption = { type CategoryOption = {
id: string; id: string;
name: string; name: string;
icon: string | null; icon: string | null;
type: "receita" | "despesa"; type: "receita" | "despesa";
}; };
export type CategoryHistoryItem = { type CategoryHistoryItem = {
id: string; id: string;
name: string; name: string;
icon: string | null; icon: string | null;

View File

@@ -22,7 +22,7 @@ import {
excludeInitialBalanceWhenConfigured, excludeInitialBalanceWhenConfigured,
excludeRefundEntries, excludeRefundEntries,
excludeTransactionsFromExcludedAccounts, excludeTransactionsFromExcludedAccounts,
} from "@/features/dashboard/transaction-filters"; } from "@/features/dashboard/lib/transaction-filters";
import { db } from "@/shared/lib/db"; import { db } from "@/shared/lib/db";
import { getAdminPayerId } from "@/shared/lib/payers/get-admin-id"; import { getAdminPayerId } from "@/shared/lib/payers/get-admin-id";
import { safeToNumber as toNumber } from "@/shared/utils/number"; import { safeToNumber as toNumber } from "@/shared/utils/number";

View File

@@ -1,10 +1,10 @@
export type CategoryOption = { type CategoryOption = {
id: string; id: string;
name: string; name: string;
type: string; type: string;
}; };
export type CategoryTransaction = { type CategoryTransaction = {
id: string; id: string;
name: string; name: string;
amount: number; amount: number;

View File

@@ -14,8 +14,8 @@ import type {
} from "@/features/dashboard/bills/bills-queries"; } from "@/features/dashboard/bills/bills-queries";
import { AccountCardSelectContent } from "@/features/transactions/components/select-items"; import { AccountCardSelectContent } from "@/features/transactions/components/select-items";
import { EstablishmentLogo } from "@/shared/components/entity-avatar"; import { EstablishmentLogo } from "@/shared/components/entity-avatar";
import { PaymentSuccess } from "@/shared/components/feedback/payment-success";
import MoneyValues from "@/shared/components/money-values"; import MoneyValues from "@/shared/components/money-values";
import { PaymentSuccess } from "@/shared/components/payment-success";
import { Badge } from "@/shared/components/ui/badge"; import { Badge } from "@/shared/components/ui/badge";
import { Button } from "@/shared/components/ui/button"; import { Button } from "@/shared/components/ui/button";
import { Card } from "@/shared/components/ui/card"; import { Card } from "@/shared/components/ui/card";

View File

@@ -1,6 +1,6 @@
import { RiBarcodeFill } from "@remixicon/react"; import { RiBarcodeFill } from "@remixicon/react";
import type { DashboardBill } from "@/features/dashboard/bills/bills-queries"; import type { DashboardBill } from "@/features/dashboard/bills/bills-queries";
import { WidgetEmptyState } from "@/shared/components/widget-empty-state"; import { WidgetEmptyState } from "@/shared/components/widgets/widget-empty-state";
import { BillListItem } from "./bill-list-item"; import { BillListItem } from "./bill-list-item";
type BillsListProps = { type BillsListProps = {

View File

@@ -13,7 +13,7 @@ import {
TabsList, TabsList,
TabsTrigger, TabsTrigger,
} from "@/shared/components/ui/tabs"; } from "@/shared/components/ui/tabs";
import { WidgetEmptyState } from "@/shared/components/widget-empty-state"; import { WidgetEmptyState } from "@/shared/components/widgets/widget-empty-state";
import { formatPeriodForUrl } from "@/shared/utils/period"; import { formatPeriodForUrl } from "@/shared/utils/period";
import { CategoryBreakdownChart } from "./category-breakdown-chart"; import { CategoryBreakdownChart } from "./category-breakdown-chart";
import { CategoryBreakdownList } from "./category-breakdown-list"; import { CategoryBreakdownList } from "./category-breakdown-list";

View File

@@ -40,8 +40,8 @@ import {
} from "@/features/dashboard/widget-registry/widget-config"; } from "@/features/dashboard/widget-registry/widget-config";
import { NoteDialog } from "@/features/notes/components/note-dialog"; import { NoteDialog } from "@/features/notes/components/note-dialog";
import { TransactionDialog } from "@/features/transactions/components/dialogs/transaction-dialog/transaction-dialog"; import { TransactionDialog } from "@/features/transactions/components/dialogs/transaction-dialog/transaction-dialog";
import { ExpandableWidgetCard } from "@/shared/components/expandable-widget-card";
import { Button } from "@/shared/components/ui/button"; import { Button } from "@/shared/components/ui/button";
import { ExpandableWidgetCard } from "@/shared/components/widgets/expandable-widget-card";
type DashboardGridEditableProps = { type DashboardGridEditableProps = {
data: DashboardData; data: DashboardData;

View File

@@ -1,6 +1,6 @@
import { RiFundsLine } from "@remixicon/react"; import { RiFundsLine } from "@remixicon/react";
import type { GoalProgressItem } from "@/features/dashboard/goals-progress/goals-progress-queries"; import type { GoalProgressItem } from "@/features/dashboard/goals-progress/goals-progress-queries";
import { WidgetEmptyState } from "@/shared/components/widget-empty-state"; import { WidgetEmptyState } from "@/shared/components/widgets/widget-empty-state";
import { GoalProgressItem as GoalProgressListItem } from "./goals-progress-item"; import { GoalProgressItem as GoalProgressListItem } from "./goals-progress-item";
type GoalsProgressListProps = { type GoalsProgressListProps = {

View File

@@ -1,6 +1,6 @@
import { RiNumbersLine } from "@remixicon/react"; import { RiNumbersLine } from "@remixicon/react";
import type { InstallmentExpense } from "@/features/dashboard/expenses/installment-expenses-queries"; import type { InstallmentExpense } from "@/features/dashboard/expenses/installment-expenses-queries";
import { WidgetEmptyState } from "@/shared/components/widget-empty-state"; import { WidgetEmptyState } from "@/shared/components/widgets/widget-empty-state";
import { InstallmentExpenseListItem } from "./installment-expense-list-item"; import { InstallmentExpenseListItem } from "./installment-expense-list-item";
type InstallmentExpensesListProps = { type InstallmentExpensesListProps = {

View File

@@ -14,8 +14,8 @@ import type {
InvoicePaymentAccountOption, InvoicePaymentAccountOption,
} from "@/features/dashboard/invoices/invoices-queries"; } from "@/features/dashboard/invoices/invoices-queries";
import { AccountCardSelectContent } from "@/features/transactions/components/select-items"; import { AccountCardSelectContent } from "@/features/transactions/components/select-items";
import { PaymentSuccess } from "@/shared/components/feedback/payment-success";
import MoneyValues from "@/shared/components/money-values"; import MoneyValues from "@/shared/components/money-values";
import { PaymentSuccess } from "@/shared/components/payment-success";
import { Badge } from "@/shared/components/ui/badge"; import { Badge } from "@/shared/components/ui/badge";
import { Button } from "@/shared/components/ui/button"; import { Button } from "@/shared/components/ui/button";
import { Card } from "@/shared/components/ui/card"; import { Card } from "@/shared/components/ui/card";

View File

@@ -1,6 +1,6 @@
import { RiBillLine } from "@remixicon/react"; import { RiBillLine } from "@remixicon/react";
import type { DashboardInvoice } from "@/features/dashboard/invoices/invoices-queries"; import type { DashboardInvoice } from "@/features/dashboard/invoices/invoices-queries";
import { WidgetEmptyState } from "@/shared/components/widget-empty-state"; import { WidgetEmptyState } from "@/shared/components/widgets/widget-empty-state";
import { InvoiceListItem } from "./invoice-list-item"; import { InvoiceListItem } from "./invoice-list-item";
type InvoicesListProps = { type InvoicesListProps = {

View File

@@ -1,6 +1,6 @@
import { RiTodoLine } from "@remixicon/react"; import { RiTodoLine } from "@remixicon/react";
import type { Note } from "@/features/notes/components/types"; import type { Note } from "@/features/notes/components/types";
import { WidgetEmptyState } from "@/shared/components/widget-empty-state"; import { WidgetEmptyState } from "@/shared/components/widgets/widget-empty-state";
import { NoteListItem } from "./note-list-item"; import { NoteListItem } from "./note-list-item";
type NotesListProps = { type NotesListProps = {

View File

@@ -1,5 +1,5 @@
import type { ReactNode } from "react"; import type { ReactNode } from "react";
import { WidgetEmptyState } from "@/shared/components/widget-empty-state"; import { WidgetEmptyState } from "@/shared/components/widgets/widget-empty-state";
import { import {
PaymentBreakdownListItem, PaymentBreakdownListItem,
type PaymentBreakdownListItemData, type PaymentBreakdownListItemData,

View File

@@ -1,5 +1,5 @@
import StatusDot from "@/shared/components/feedback/status-dot";
import MoneyValues from "@/shared/components/money-values"; import MoneyValues from "@/shared/components/money-values";
import StatusDot from "@/shared/components/status-dot";
import { Progress } from "@/shared/components/ui/progress"; import { Progress } from "@/shared/components/ui/progress";
type PaymentStatusCategorySectionProps = { type PaymentStatusCategorySectionProps = {

View File

@@ -1,7 +1,7 @@
import { RiWallet3Line } from "@remixicon/react"; import { RiWallet3Line } from "@remixicon/react";
import type { PaymentStatusData } from "@/features/dashboard/payments/payment-status-queries"; import type { PaymentStatusData } from "@/features/dashboard/payments/payment-status-queries";
import { CardContent } from "@/shared/components/ui/card"; import { CardContent } from "@/shared/components/ui/card";
import { WidgetEmptyState } from "@/shared/components/widget-empty-state"; import { WidgetEmptyState } from "@/shared/components/widgets/widget-empty-state";
import { PaymentStatusCategorySection } from "./payment-status-category-section"; import { PaymentStatusCategorySection } from "./payment-status-category-section";
type PaymentStatusWidgetViewProps = { type PaymentStatusWidgetViewProps = {

View File

@@ -6,7 +6,7 @@ import {
import { formatPercentage } from "@/shared/utils/percentage"; import { formatPercentage } from "@/shared/utils/percentage";
import { cn } from "@/shared/utils/ui"; import { cn } from "@/shared/utils/ui";
export type PercentageChangeTrend = "up" | "down" | "flat"; type PercentageChangeTrend = "up" | "down" | "flat";
type PercentageChangeIndicatorProps = { type PercentageChangeIndicatorProps = {
value?: number | null; value?: number | null;

View File

@@ -14,7 +14,7 @@ import {
TooltipContent, TooltipContent,
TooltipTrigger, TooltipTrigger,
} from "@/shared/components/ui/tooltip"; } from "@/shared/components/ui/tooltip";
import { WidgetEmptyState } from "@/shared/components/widget-empty-state"; import { WidgetEmptyState } from "@/shared/components/widgets/widget-empty-state";
import { formatDateOnly } from "@/shared/utils/date"; import { formatDateOnly } from "@/shared/utils/date";
import { formatBytes } from "@/shared/utils/number"; import { formatBytes } from "@/shared/utils/number";

View File

@@ -26,7 +26,7 @@ import {
PopoverContent, PopoverContent,
PopoverTrigger, PopoverTrigger,
} from "@/shared/components/ui/popover"; } from "@/shared/components/ui/popover";
import { WidgetEmptyState } from "@/shared/components/widget-empty-state"; import { WidgetEmptyState } from "@/shared/components/widgets/widget-empty-state";
import { CATEGORY_COLORS } from "@/shared/utils/category-colors"; import { CATEGORY_COLORS } from "@/shared/utils/category-colors";
import { formatCurrency, formatCurrencyCompact } from "@/shared/utils/currency"; import { formatCurrency, formatCurrencyCompact } from "@/shared/utils/currency";
import { getIconComponent } from "@/shared/utils/icons"; import { getIconComponent } from "@/shared/utils/icons";

View File

@@ -5,7 +5,7 @@ import type { DashboardCategoryBreakdownItem } from "@/features/dashboard/catego
import { PercentageChangeIndicator } from "@/features/dashboard/components/percentage-change-indicator"; import { PercentageChangeIndicator } from "@/features/dashboard/components/percentage-change-indicator";
import { CategoryIconBadge } from "@/shared/components/entity-avatar"; import { CategoryIconBadge } from "@/shared/components/entity-avatar";
import MoneyValues from "@/shared/components/money-values"; import MoneyValues from "@/shared/components/money-values";
import { WidgetEmptyState } from "@/shared/components/widget-empty-state"; import { WidgetEmptyState } from "@/shared/components/widgets/widget-empty-state";
import { formatPercentage } from "@/shared/utils/percentage"; import { formatPercentage } from "@/shared/utils/percentage";
type CategoryTrendsWidgetProps = { type CategoryTrendsWidgetProps = {

View File

@@ -9,7 +9,7 @@ import Image from "next/image";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import { useMemo, useState } from "react"; import { useMemo, useState } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import type { DashboardInboxSnapshot } from "@/features/dashboard/inbox-snapshot-queries"; import type { DashboardInboxSnapshot } from "@/features/dashboard/lib/inbox-snapshot-queries";
import type { DashboardWidgetQuickActionOptions } from "@/features/dashboard/widget-registry/widget-config"; import type { DashboardWidgetQuickActionOptions } from "@/features/dashboard/widget-registry/widget-config";
import { import {
discardInboxItemAction, discardInboxItemAction,
@@ -19,7 +19,7 @@ import { TransactionDialog } from "@/features/transactions/components/dialogs/tr
import { ConfirmActionDialog } from "@/shared/components/confirm-action-dialog"; import { ConfirmActionDialog } from "@/shared/components/confirm-action-dialog";
import MoneyValues from "@/shared/components/money-values"; import MoneyValues from "@/shared/components/money-values";
import { Button } from "@/shared/components/ui/button"; import { Button } from "@/shared/components/ui/button";
import { WidgetEmptyState } from "@/shared/components/widget-empty-state"; import { WidgetEmptyState } from "@/shared/components/widgets/widget-empty-state";
import { resolveLogoSrc } from "@/shared/lib/logo"; import { resolveLogoSrc } from "@/shared/lib/logo";
const DEFAULT_INBOX_APP_LOGO = "/avatars/default_icon.png"; const DEFAULT_INBOX_APP_LOGO = "/avatars/default_icon.png";

View File

@@ -9,7 +9,7 @@ import {
ChartContainer, ChartContainer,
ChartTooltip, ChartTooltip,
} from "@/shared/components/ui/chart"; } from "@/shared/components/ui/chart";
import { WidgetEmptyState } from "@/shared/components/widget-empty-state"; import { WidgetEmptyState } from "@/shared/components/widgets/widget-empty-state";
import { formatCurrency } from "@/shared/utils/currency"; import { formatCurrency } from "@/shared/utils/currency";
type IncomeExpenseBalanceWidgetProps = { type IncomeExpenseBalanceWidgetProps = {

View File

@@ -10,7 +10,7 @@ import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import { useTransition } from "react"; import { useTransition } from "react";
import { toast } from "sonner"; import { toast } from "sonner";
import type { DashboardAccount } from "@/features/dashboard/accounts-queries"; import type { DashboardAccount } from "@/features/dashboard/lib/accounts-queries";
import { updateMyAccountsWidgetPreference } from "@/features/dashboard/widget-registry/widget-actions"; import { updateMyAccountsWidgetPreference } from "@/features/dashboard/widget-registry/widget-actions";
import MoneyValues from "@/shared/components/money-values"; import MoneyValues from "@/shared/components/money-values";
import { Badge } from "@/shared/components/ui/badge"; import { Badge } from "@/shared/components/ui/badge";
@@ -21,7 +21,7 @@ import {
TooltipContent, TooltipContent,
TooltipTrigger, TooltipTrigger,
} from "@/shared/components/ui/tooltip"; } from "@/shared/components/ui/tooltip";
import { WidgetEmptyState } from "@/shared/components/widget-empty-state"; import { WidgetEmptyState } from "@/shared/components/widgets/widget-empty-state";
import { isAccountInactive } from "@/shared/lib/accounts/constants"; import { isAccountInactive } from "@/shared/lib/accounts/constants";
import { resolveLogoSrc } from "@/shared/lib/logo"; import { resolveLogoSrc } from "@/shared/lib/logo";
import { formatPeriodForUrl } from "@/shared/utils/period"; import { formatPeriodForUrl } from "@/shared/utils/period";

View File

@@ -7,14 +7,14 @@ import {
} from "@remixicon/react"; } from "@remixicon/react";
import Link from "next/link"; import Link from "next/link";
import { PercentageChangeIndicator } from "@/features/dashboard/components/percentage-change-indicator"; import { PercentageChangeIndicator } from "@/features/dashboard/components/percentage-change-indicator";
import type { DashboardPagador } from "@/features/dashboard/payers-queries"; import type { DashboardPagador } from "@/features/dashboard/lib/payers-queries";
import MoneyValues from "@/shared/components/money-values"; import MoneyValues from "@/shared/components/money-values";
import { import {
Avatar, Avatar,
AvatarFallback, AvatarFallback,
AvatarImage, AvatarImage,
} from "@/shared/components/ui/avatar"; } from "@/shared/components/ui/avatar";
import { WidgetEmptyState } from "@/shared/components/widget-empty-state"; import { WidgetEmptyState } from "@/shared/components/widgets/widget-empty-state";
import { getAvatarSrc } from "@/shared/lib/payers/utils"; import { getAvatarSrc } from "@/shared/lib/payers/utils";
import { buildInitials } from "@/shared/utils/initials"; import { buildInitials } from "@/shared/utils/initials";

View File

@@ -12,7 +12,7 @@ import {
SelectTrigger, SelectTrigger,
SelectValue, SelectValue,
} from "@/shared/components/ui/select"; } from "@/shared/components/ui/select";
import { WidgetEmptyState } from "@/shared/components/widget-empty-state"; import { WidgetEmptyState } from "@/shared/components/widgets/widget-empty-state";
import { CATEGORY_TYPE_LABEL } from "@/shared/lib/categories/constants"; import { CATEGORY_TYPE_LABEL } from "@/shared/lib/categories/constants";
import { formatTransactionDate } from "@/shared/utils/date"; import { formatTransactionDate } from "@/shared/utils/date";

View File

@@ -2,7 +2,7 @@ import { RiRefreshLine } from "@remixicon/react";
import type { RecurringExpensesData } from "@/features/dashboard/expenses/recurring-expenses-queries"; import type { RecurringExpensesData } from "@/features/dashboard/expenses/recurring-expenses-queries";
import { EstablishmentLogo } from "@/shared/components/entity-avatar"; import { EstablishmentLogo } from "@/shared/components/entity-avatar";
import MoneyValues from "@/shared/components/money-values"; import MoneyValues from "@/shared/components/money-values";
import { WidgetEmptyState } from "@/shared/components/widget-empty-state"; import { WidgetEmptyState } from "@/shared/components/widgets/widget-empty-state";
type RecurringExpensesWidgetProps = { type RecurringExpensesWidgetProps = {
data: RecurringExpensesData; data: RecurringExpensesData;

View File

@@ -3,7 +3,7 @@
import { RiArrowUpDoubleLine, RiStore2Line } from "@remixicon/react"; import { RiArrowUpDoubleLine, RiStore2Line } from "@remixicon/react";
import { useState } from "react"; import { useState } from "react";
import type { TopExpensesData } from "@/features/dashboard/expenses/top-expenses-queries"; import type { TopExpensesData } from "@/features/dashboard/expenses/top-expenses-queries";
import type { TopEstablishmentsData } from "@/features/dashboard/top-establishments-queries"; import type { TopEstablishmentsData } from "@/features/dashboard/lib/top-establishments-queries";
import { import {
Tabs, Tabs,
TabsContent, TabsContent,

View File

@@ -1,8 +1,8 @@
import { RiStore2Line } from "@remixicon/react"; import { RiStore2Line } from "@remixicon/react";
import type { TopEstablishmentsData } from "@/features/dashboard/top-establishments-queries"; import type { TopEstablishmentsData } from "@/features/dashboard/lib/top-establishments-queries";
import { EstablishmentLogo } from "@/shared/components/entity-avatar"; import { EstablishmentLogo } from "@/shared/components/entity-avatar";
import MoneyValues from "@/shared/components/money-values"; import MoneyValues from "@/shared/components/money-values";
import { WidgetEmptyState } from "@/shared/components/widget-empty-state"; import { WidgetEmptyState } from "@/shared/components/widgets/widget-empty-state";
type TopEstablishmentsWidgetProps = { type TopEstablishmentsWidgetProps = {
data: TopEstablishmentsData; data: TopEstablishmentsData;

View File

@@ -9,7 +9,7 @@ import type {
import { EstablishmentLogo } from "@/shared/components/entity-avatar"; import { EstablishmentLogo } from "@/shared/components/entity-avatar";
import MoneyValues from "@/shared/components/money-values"; import MoneyValues from "@/shared/components/money-values";
import { Switch } from "@/shared/components/ui/switch"; import { Switch } from "@/shared/components/ui/switch";
import { WidgetEmptyState } from "@/shared/components/widget-empty-state"; import { WidgetEmptyState } from "@/shared/components/widgets/widget-empty-state";
import { formatTransactionDate } from "@/shared/utils/date"; import { formatTransactionDate } from "@/shared/utils/date";
type TopExpensesWidgetProps = { type TopExpensesWidgetProps = {

View File

@@ -31,7 +31,7 @@ function calculateDueDate(period: string, dueDay: string | null): Date | null {
} }
} }
export type InstallmentDetail = { type InstallmentDetail = {
id: string; id: string;
currentInstallment: number; currentInstallment: number;
amount: number; amount: number;

View File

@@ -4,7 +4,7 @@ import {
formatLastInstallmentDate, formatLastInstallmentDate,
} from "@/shared/lib/installments/utils"; } from "@/shared/lib/installments/utils";
export type InstallmentExpenseDisplay = { type InstallmentExpenseDisplay = {
compactLabel: string | null; compactLabel: string | null;
isLast: boolean; isLast: boolean;
remainingInstallments: number; remainingInstallments: number;
@@ -13,7 +13,7 @@ export type InstallmentExpenseDisplay = {
progress: number; progress: number;
}; };
export const buildInstallmentCompactLabel = ( const buildInstallmentCompactLabel = (
currentInstallment: number | null, currentInstallment: number | null,
installmentCount: number | null, installmentCount: number | null,
) => { ) => {
@@ -24,7 +24,7 @@ export const buildInstallmentCompactLabel = (
return null; return null;
}; };
export const isInstallmentLast = ( const isInstallmentLast = (
currentInstallment: number | null, currentInstallment: number | null,
installmentCount: number | null, installmentCount: number | null,
) => { ) => {
@@ -35,7 +35,7 @@ export const isInstallmentLast = (
return currentInstallment === installmentCount && installmentCount > 1; return currentInstallment === installmentCount && installmentCount > 1;
}; };
export const calculateInstallmentRemainingCount = ( const calculateInstallmentRemainingCount = (
currentInstallment: number | null, currentInstallment: number | null,
installmentCount: number | null, installmentCount: number | null,
) => { ) => {
@@ -46,7 +46,7 @@ export const calculateInstallmentRemainingCount = (
return Math.max(0, installmentCount - currentInstallment); return Math.max(0, installmentCount - currentInstallment);
}; };
export const calculateInstallmentRemainingAmount = ( const calculateInstallmentRemainingAmount = (
amount: number, amount: number,
currentInstallment: number | null, currentInstallment: number | null,
installmentCount: number | null, installmentCount: number | null,
@@ -54,7 +54,7 @@ export const calculateInstallmentRemainingAmount = (
amount * amount *
calculateInstallmentRemainingCount(currentInstallment, installmentCount); calculateInstallmentRemainingCount(currentInstallment, installmentCount);
export const formatInstallmentEndDate = ( const formatInstallmentEndDate = (
period: string, period: string,
currentInstallment: number | null, currentInstallment: number | null,
installmentCount: number | null, installmentCount: number | null,
@@ -72,7 +72,7 @@ export const formatInstallmentEndDate = (
return formatLastInstallmentDate(lastDate); return formatLastInstallmentDate(lastDate);
}; };
export const buildInstallmentProgress = ( const buildInstallmentProgress = (
currentInstallment: number | null, currentInstallment: number | null,
installmentCount: number | null, installmentCount: number | null,
) => { ) => {

View File

@@ -1,4 +1,4 @@
export type RecurringExpense = { type RecurringExpense = {
id: string; id: string;
name: string; name: string;
amount: number; amount: number;

View File

@@ -1,13 +1,13 @@
import { cacheLife, cacheTag } from "next/cache"; import { cacheLife, cacheTag } from "next/cache";
import { fetchAttachmentsForPeriod } from "@/features/attachments/queries"; import { fetchAttachmentsForPeriod } from "@/features/attachments/queries";
import { fetchDashboardAccounts } from "./accounts-queries";
import { fetchDashboardCategoryOverview } from "./categories/category-overview-queries"; import { fetchDashboardCategoryOverview } from "./categories/category-overview-queries";
import { fetchDashboardInboxSnapshot } from "./inbox-snapshot-queries";
import { fetchDashboardInvoices } from "./invoices/invoices-queries"; import { fetchDashboardInvoices } from "./invoices/invoices-queries";
import { fetchDashboardAccounts } from "./lib/accounts-queries";
import { fetchDashboardInboxSnapshot } from "./lib/inbox-snapshot-queries";
import { fetchDashboardPayers } from "./lib/payers-queries";
import { fetchDashboardNotes } from "./notes/notes-queries"; import { fetchDashboardNotes } from "./notes/notes-queries";
import { fetchDashboardCurrentPeriodOverview } from "./overview/current-period-overview-queries"; import { fetchDashboardCurrentPeriodOverview } from "./overview/current-period-overview-queries";
import { fetchDashboardPeriodOverview } from "./overview/period-overview-queries"; import { fetchDashboardPeriodOverview } from "./overview/period-overview-queries";
import { fetchDashboardPayers } from "./payers-queries";
async function fetchDashboardDataInternal(userId: string, period: string) { async function fetchDashboardDataInternal(userId: string, period: string) {
const [ const [

View File

@@ -5,7 +5,6 @@ import type {
import type { import type {
GoalProgressCategory, GoalProgressCategory,
GoalProgressItem, GoalProgressItem,
GoalProgressStatus,
} from "@/features/dashboard/goals-progress/goals-progress-queries"; } from "@/features/dashboard/goals-progress/goals-progress-queries";
import { formatPercentage } from "@/shared/utils/percentage"; import { formatPercentage } from "@/shared/utils/percentage";
@@ -18,9 +17,6 @@ export const formatGoalProgressPercentage = (value: number, withSign = false) =>
signDisplay: withSign ? "always" : "auto", signDisplay: withSign ? "always" : "auto",
}); });
export const getGoalProgressStatusColorClass = (status: GoalProgressStatus) =>
status === "exceeded" ? "text-destructive" : "";
export const mapGoalProgressCategoriesToBudgetCategories = ( export const mapGoalProgressCategoriesToBudgetCategories = (
categories: GoalProgressCategory[], categories: GoalProgressCategory[],
): BudgetCategory[] => ): BudgetCategory[] =>

View File

@@ -1,4 +1,4 @@
export type GoalProgressStatus = "on-track" | "critical" | "exceeded"; type GoalProgressStatus = "on-track" | "critical" | "exceeded";
export type GoalProgressItem = { export type GoalProgressItem = {
id: string; id: string;

View File

@@ -55,7 +55,7 @@ type RawInvoiceBreakdownRow = {
amount: number | string | null; amount: number | string | null;
}; };
export type InvoicePagadorBreakdown = { type InvoicePagadorBreakdown = {
payerId: string | null; payerId: string | null;
pagadorName: string; pagadorName: string;
pagadorAvatar: string | null; pagadorAvatar: string | null;

View File

@@ -1,4 +1,4 @@
import type { DashboardData } from "./fetch-dashboard-data"; import type { DashboardData } from "../fetch-dashboard-data";
/** /**
* Coleta todos os nomes de estabelecimentos exibidos nos widgets do * Coleta todos os nomes de estabelecimentos exibidos nos widgets do

View File

@@ -3,7 +3,7 @@ import { cacheLife, cacheTag } from "next/cache";
import { cards, financialAccounts, inboxItems } from "@/db/schema"; import { cards, financialAccounts, inboxItems } from "@/db/schema";
import { db } from "@/shared/lib/db"; import { db } from "@/shared/lib/db";
export type DashboardInboxItem = { type DashboardInboxItem = {
id: string; id: string;
sourceAppName: string | null; sourceAppName: string | null;
parsedName: string | null; parsedName: string | null;

View File

@@ -8,11 +8,11 @@ import { getBusinessDateString } from "@/shared/utils/date";
import { import {
type DashboardNotificationsSnapshot, type DashboardNotificationsSnapshot,
fetchDashboardNotifications, fetchDashboardNotifications,
} from "./notifications/notifications-queries"; } from "../notifications/notifications-queries";
type DashboardNavbarData = { type DashboardNavbarData = {
pagadorAvatarUrl: string | null; payerAvatarUrl: string | null;
preLancamentosCount: number; inboxPendingCount: number;
notificationsSnapshot: DashboardNotificationsSnapshot; notificationsSnapshot: DashboardNotificationsSnapshot;
}; };
@@ -39,7 +39,7 @@ async function fetchDashboardNavbarDataInternal(
userId: string, userId: string,
): Promise<DashboardNavbarData> { ): Promise<DashboardNavbarData> {
const currentPeriod = getBusinessDateString().slice(0, 7); const currentPeriod = getBusinessDateString().slice(0, 7);
const [pagadorAvatarUrl, notificationsSnapshot, preLancamentosCount] = const [payerAvatarUrl, notificationsSnapshot, inboxPendingCount] =
await Promise.all([ await Promise.all([
fetchAdminPayerAvatarUrl(userId), fetchAdminPayerAvatarUrl(userId),
fetchDashboardNotifications(userId, currentPeriod), fetchDashboardNotifications(userId, currentPeriod),
@@ -47,8 +47,8 @@ async function fetchDashboardNavbarDataInternal(
]); ]);
return { return {
pagadorAvatarUrl, payerAvatarUrl,
preLancamentosCount, inboxPendingCount,
notificationsSnapshot, notificationsSnapshot,
}; };
} }

View File

@@ -1,6 +1,6 @@
import { and, desc, eq, inArray, isNull, or, sql } from "drizzle-orm"; import { and, desc, eq, inArray, isNull, or, sql } from "drizzle-orm";
import { financialAccounts, payers, transactions } from "@/db/schema"; import { financialAccounts, payers, transactions } from "@/db/schema";
import { excludeTransactionsFromExcludedAccounts } from "@/features/dashboard/transaction-filters"; import { excludeTransactionsFromExcludedAccounts } from "@/features/dashboard/lib/transaction-filters";
import { ACCOUNT_AUTO_INVOICE_NOTE_PREFIX } from "@/shared/lib/accounts/constants"; import { ACCOUNT_AUTO_INVOICE_NOTE_PREFIX } from "@/shared/lib/accounts/constants";
import { db } from "@/shared/lib/db"; import { db } from "@/shared/lib/db";
import { PAYER_ROLE_ADMIN } from "@/shared/lib/payers/constants"; import { PAYER_ROLE_ADMIN } from "@/shared/lib/payers/constants";

View File

@@ -1,4 +1,4 @@
export type TopEstablishment = { type TopEstablishment = {
id: string; id: string;
name: string; name: string;
amount: number; amount: number;

View File

@@ -1,7 +1,7 @@
import type { DashboardNote } from "@/features/dashboard/notes/notes-queries"; import type { DashboardNote } from "@/features/dashboard/notes/notes-queries";
import type { Note } from "@/features/notes/components/types"; import type { Note } from "@/features/notes/components/types";
export const mapDashboardNoteToNote = (note: DashboardNote): Note => ({ const mapDashboardNoteToNote = (note: DashboardNote): Note => ({
id: note.id, id: note.id,
title: note.title, title: note.title,
description: note.description, description: note.description,

View File

@@ -2,7 +2,7 @@ import { and, eq } from "drizzle-orm";
import { notes } from "@/db/schema"; import { notes } from "@/db/schema";
import { db } from "@/shared/lib/db"; import { db } from "@/shared/lib/db";
export type DashboardTask = { type DashboardTask = {
id: string; id: string;
text: string; text: string;
completed: boolean; completed: boolean;

View File

@@ -31,13 +31,7 @@ import {
getNextPeriod, getNextPeriod,
} from "@/shared/utils/period"; } from "@/shared/utils/period";
export type { export type { DashboardNotificationsSnapshot } from "@/shared/lib/types/notifications";
BudgetNotification,
BudgetStatus,
DashboardNotification,
DashboardNotificationsSnapshot,
NotificationType,
} from "@/shared/lib/types/notifications";
const PAYMENT_METHOD_BOLETO = "Boleto"; const PAYMENT_METHOD_BOLETO = "Boleto";
const BUDGET_CRITICAL_THRESHOLD = 80; const BUDGET_CRITICAL_THRESHOLD = 80;

View File

@@ -13,11 +13,11 @@ import type {
TopExpense, TopExpense,
TopExpensesData, TopExpensesData,
} from "@/features/dashboard/expenses/top-expenses-queries"; } from "@/features/dashboard/expenses/top-expenses-queries";
import type { TopEstablishmentsData } from "@/features/dashboard/lib/top-establishments-queries";
import { excludeTransactionsFromExcludedAccounts } from "@/features/dashboard/lib/transaction-filters";
import type { PaymentConditionsData } from "@/features/dashboard/payments/payment-conditions-queries"; import type { PaymentConditionsData } from "@/features/dashboard/payments/payment-conditions-queries";
import type { PaymentMethodsData } from "@/features/dashboard/payments/payment-methods-queries"; import type { PaymentMethodsData } from "@/features/dashboard/payments/payment-methods-queries";
import type { PaymentStatusData } from "@/features/dashboard/payments/payment-status-queries"; import type { PaymentStatusData } from "@/features/dashboard/payments/payment-status-queries";
import type { TopEstablishmentsData } from "@/features/dashboard/top-establishments-queries";
import { excludeTransactionsFromExcludedAccounts } from "@/features/dashboard/transaction-filters";
import { import {
ACCOUNT_AUTO_INVOICE_NOTE_PREFIX, ACCOUNT_AUTO_INVOICE_NOTE_PREFIX,
INITIAL_BALANCE_NOTE, INITIAL_BALANCE_NOTE,

View File

@@ -1,16 +1,16 @@
import { and, asc, eq, gte, inArray, lte, sql } from "drizzle-orm"; import { and, asc, eq, gte, inArray, lte, sql } from "drizzle-orm";
import { financialAccounts, transactions } from "@/db/schema"; import { financialAccounts, transactions } from "@/db/schema";
import type { DashboardCardMetrics } from "@/features/dashboard/overview/dashboard-metrics-queries";
import type {
IncomeExpenseBalanceData,
MonthData,
} from "@/features/dashboard/overview/income-expense-balance-queries";
import { import {
buildDashboardAdminFilters, buildDashboardAdminFilters,
excludeAutoInvoiceEntries, excludeAutoInvoiceEntries,
excludeInitialBalanceWhenConfigured, excludeInitialBalanceWhenConfigured,
excludeTransactionsFromExcludedAccounts, excludeTransactionsFromExcludedAccounts,
} from "@/features/dashboard/transaction-filters"; } from "@/features/dashboard/lib/transaction-filters";
import type { DashboardCardMetrics } from "@/features/dashboard/overview/dashboard-metrics-queries";
import type {
IncomeExpenseBalanceData,
MonthData,
} from "@/features/dashboard/overview/income-expense-balance-queries";
import { REFUND_NOTE_PREFIX } from "@/shared/lib/accounts/constants"; import { REFUND_NOTE_PREFIX } from "@/shared/lib/accounts/constants";
import { db } from "@/shared/lib/db"; import { db } from "@/shared/lib/db";
import { getAdminPayerId } from "@/shared/lib/payers/get-admin-id"; import { getAdminPayerId } from "@/shared/lib/payers/get-admin-id";

View File

@@ -1,10 +1,10 @@
import { cacheLife, cacheTag } from "next/cache"; import { cacheLife, cacheTag } from "next/cache";
import { fetchDashboardData } from "@/features/dashboard/fetch-dashboard-data"; import { fetchDashboardData } from "@/features/dashboard/fetch-dashboard-data";
import { fetchUserDashboardPreferences } from "@/features/dashboard/preferences-queries"; import { fetchUserDashboardPreferences } from "@/features/dashboard/lib/preferences-queries";
import { import {
buildOptionSets, buildOptionSets,
buildSluggedFilters, buildSluggedFilters,
} from "@/features/transactions/page-helpers"; } from "@/features/transactions/lib/page-helpers";
import { import {
fetchRecentEstablishments, fetchRecentEstablishments,
fetchTransactionFilterSources, fetchTransactionFilterSources,
@@ -52,7 +52,7 @@ async function fetchDashboardQuickActionOptionsInternal(
}; };
} }
export async function fetchDashboardQuickActionOptions(userId: string) { async function fetchDashboardQuickActionOptions(userId: string) {
"use cache"; "use cache";
cacheTag(`dashboard-${userId}`); cacheTag(`dashboard-${userId}`);
cacheLife({ revalidate: 3 }); cacheLife({ revalidate: 3 });

View File

@@ -1,4 +1,4 @@
export type PaymentConditionSummary = { type PaymentConditionSummary = {
condition: string; condition: string;
amount: number; amount: number;
percentage: number; percentage: number;

View File

@@ -1,4 +1,4 @@
export type PaymentMethodSummary = { type PaymentMethodSummary = {
paymentMethod: string; paymentMethod: string;
amount: number; amount: number;
percentage: number; percentage: number;

View File

@@ -1,4 +1,4 @@
export type PaymentStatusCategory = { type PaymentStatusCategory = {
total: number; total: number;
confirmed: number; confirmed: number;
pending: number; pending: number;

View File

@@ -1,7 +1,7 @@
import { RiAtLine, RiCalendarEventLine } from "@remixicon/react"; import { RiAtLine, RiCalendarEventLine } from "@remixicon/react";
import { format } from "date-fns"; import { format } from "date-fns";
import { ptBR } from "date-fns/locale"; import { ptBR } from "date-fns/locale";
import { EmptyState } from "@/shared/components/empty-state"; import { EmptyState } from "@/shared/components/feedback/empty-state";
import { Card } from "@/shared/components/ui/card"; import { Card } from "@/shared/components/ui/card";
import { InboxCard } from "./inbox-card"; import { InboxCard } from "./inbox-card";
import type { InboxItem } from "./types"; import type { InboxItem } from "./types";

View File

@@ -4,10 +4,7 @@ import {
RiArrowRightDoubleLine, RiArrowRightDoubleLine,
RiArrowRightSLine, RiArrowRightSLine,
} from "@remixicon/react"; } from "@remixicon/react";
import { import { INBOX_PAGE_SIZE_OPTIONS } from "@/features/inbox/page-helpers";
INBOX_DEFAULT_PAGE_SIZE,
INBOX_PAGE_SIZE_OPTIONS,
} from "@/features/inbox/page-helpers";
import { Button } from "@/shared/components/ui/button"; import { Button } from "@/shared/components/ui/button";
import { import {
Select, Select,
@@ -117,6 +114,3 @@ export function InboxPagination({
</div> </div>
); );
} }
// Re-export para facilitar uso externo
export { INBOX_DEFAULT_PAGE_SIZE };

View File

@@ -1,5 +1,5 @@
import { TabsList, TabsTrigger } from "@/shared/components/ui/tabs"; import { TabsList, TabsTrigger } from "@/shared/components/ui/tabs";
import type { InboxStatus, InboxStatusCounts } from "./types"; import type { InboxStatusCounts } from "./types";
type InboxTabsProps = { type InboxTabsProps = {
counts: InboxStatusCounts; counts: InboxStatusCounts;
@@ -36,5 +36,3 @@ export function InboxTabs({ counts, isPending }: InboxTabsProps) {
</TabsList> </TabsList>
); );
} }
export type { InboxStatus, InboxStatusCounts };

Some files were not shown because too many files have changed in this diff Show More