feat: implementar sistema de preferências do usuário e refatorar changelog

Adiciona sistema completo de preferências de usuário:
  - Cria tabela userPreferences no schema com campos disableMagnetlines, periodMonthsBefore e periodMonthsAfter
  - Implementa página de Ajustes com abas (Preferências, Alterar nome, Senha, E-mail, Deletar conta)
  - Adiciona componente PreferencesForm para configuração de magnetlines e períodos de exibição
  - Propaga periodPreferences para todos os componentes de lançamentos e calendário

  Refatora sistema de changelog:
  - Remove implementação anterior baseada em JSON estático
  - Adiciona nova página de changelog dinâmica em app/(dashboard)/changelog
  - Adiciona componente changelog-list.tsx
  - Remove arquivos obsoletos (changelog-notification, actions, data, utils, scripts)

  Adiciona controle de saldo inicial em contas:
  - Novo campo excludeInitialBalanceFromIncome em contas
  - Permite excluir saldo inicial do cálculo de receitas
  - Atualiza queries de lançamentos para respeitar esta configuração

  Melhorias adicionais:
  - Adiciona componente ui/accordion.tsx do shadcn/ui
  - Refatora formatPeriodLabel para displayPeriod centralizado
  - Propaga estabelecimentos para componentes de lançamentos
  - Remove variável DB_PROVIDER obsoleta do .env.example e documentação
  - Adiciona 6 migrações de banco de dados (0003-0008)
This commit is contained in:
Felipe Coutinho
2026-01-03 14:18:03 +00:00
parent 3eca48c71a
commit fd817683ca
87 changed files with 13582 additions and 1445 deletions

View File

@@ -7,6 +7,7 @@ import {
fetchLancamentoFilterSources,
mapLancamentosData,
} from "@/lib/lancamentos/page-helpers";
import { fetchUserPeriodPreferences } from "@/lib/user-preferences/period";
import { PAGADOR_ROLE_ADMIN } from "@/lib/pagadores/constants";
import { and, eq, gte, lte, ne, or } from "drizzle-orm";
@@ -59,42 +60,44 @@ export const fetchCalendarData = async ({
const rangeStartKey = toDateKey(rangeStart);
const rangeEndKey = toDateKey(rangeEnd);
const [lancamentoRows, cardRows, filterSources] = await Promise.all([
db.query.lancamentos.findMany({
where: and(
eq(lancamentos.userId, userId),
ne(lancamentos.transactionType, TRANSACTION_TYPE_TRANSFERENCIA),
or(
// Lançamentos cuja data de compra esteja no período do calendário
and(
gte(lancamentos.purchaseDate, rangeStart),
lte(lancamentos.purchaseDate, rangeEnd)
),
// Boletos cuja data de vencimento esteja no período do calendário
and(
eq(lancamentos.paymentMethod, PAYMENT_METHOD_BOLETO),
gte(lancamentos.dueDate, rangeStart),
lte(lancamentos.dueDate, rangeEnd)
),
// Lançamentos de cartão do período (para calcular totais de vencimento)
and(
eq(lancamentos.period, period),
ne(lancamentos.paymentMethod, PAYMENT_METHOD_BOLETO)
const [lancamentoRows, cardRows, filterSources, periodPreferences] =
await Promise.all([
db.query.lancamentos.findMany({
where: and(
eq(lancamentos.userId, userId),
ne(lancamentos.transactionType, TRANSACTION_TYPE_TRANSFERENCIA),
or(
// Lançamentos cuja data de compra esteja no período do calendário
and(
gte(lancamentos.purchaseDate, rangeStart),
lte(lancamentos.purchaseDate, rangeEnd)
),
// Boletos cuja data de vencimento esteja no período do calendário
and(
eq(lancamentos.paymentMethod, PAYMENT_METHOD_BOLETO),
gte(lancamentos.dueDate, rangeStart),
lte(lancamentos.dueDate, rangeEnd)
),
// Lançamentos de cartão do período (para calcular totais de vencimento)
and(
eq(lancamentos.period, period),
ne(lancamentos.paymentMethod, PAYMENT_METHOD_BOLETO)
)
)
)
),
with: {
pagador: true,
conta: true,
cartao: true,
categoria: true,
},
}),
db.query.cartoes.findMany({
where: eq(cartoes.userId, userId),
}),
fetchLancamentoFilterSources(userId),
]);
),
with: {
pagador: true,
conta: true,
cartao: true,
categoria: true,
},
}),
db.query.cartoes.findMany({
where: eq(cartoes.userId, userId),
}),
fetchLancamentoFilterSources(userId),
fetchUserPeriodPreferences(userId),
]);
const lancamentosData = mapLancamentosData(lancamentoRows);
const events: CalendarEvent[] = [];
@@ -214,6 +217,7 @@ export const fetchCalendarData = async ({
cartaoOptions: optionSets.cartaoOptions,
categoriaOptions: optionSets.categoriaOptions,
estabelecimentos,
periodPreferences,
},
};
};