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

@@ -9,6 +9,7 @@ import {
fetchPagadorHistory,
fetchPagadorMonthlyBreakdown,
} from "@/lib/pagadores/details";
import { displayPeriod } from "@/lib/utils/period";
import { and, desc, eq } from "drizzle-orm";
import { revalidatePath } from "next/cache";
import { Resend } from "resend";
@@ -32,17 +33,6 @@ const formatCurrency = (value: number) =>
maximumFractionDigits: 2,
});
const formatPeriodLabel = (period: string) => {
const [yearStr, monthStr] = period.split("-");
const year = Number.parseInt(yearStr, 10);
const month = Number.parseInt(monthStr, 10) - 1;
const date = new Date(year, month, 1);
return date.toLocaleDateString("pt-BR", {
month: "long",
year: "numeric",
});
};
const formatDate = (value: Date | null | undefined) => {
if (!value) return "—";
return value.toLocaleDateString("pt-BR", {
@@ -560,7 +550,7 @@ export async function sendPagadorSummaryAction(
const html = buildSummaryHtml({
pagadorName: pagadorRow.name,
periodLabel: formatPeriodLabel(period),
periodLabel: displayPeriod(period),
monthlyBreakdown,
historyData,
cardUsage,
@@ -573,7 +563,7 @@ export async function sendPagadorSummaryAction(
await resend.emails.send({
from: resendFrom,
to: pagadorRow.email,
subject: `Resumo Financeiro | ${formatPeriodLabel(period)}`,
subject: `Resumo Financeiro | ${displayPeriod(period)}`,
html,
});

View File

@@ -1,3 +1,4 @@
import { getRecentEstablishmentsAction } from "@/app/(dashboard)/lancamentos/actions";
import { PagadorCardUsageCard } from "@/components/pagadores/details/pagador-card-usage-card";
import { PagadorHistoryCard } from "@/components/pagadores/details/pagador-history-card";
import { PagadorInfoCard } from "@/components/pagadores/details/pagador-info-card";
@@ -29,6 +30,7 @@ import {
type SlugMaps,
type SluggedFilters,
} from "@/lib/lancamentos/page-helpers";
import { fetchUserPeriodPreferences } from "@/lib/user-preferences/period";
import { parsePeriodParam } from "@/lib/utils/period";
import { getPagadorAccess } from "@/lib/pagadores/access";
import {
@@ -134,6 +136,8 @@ export default async function Page({ params, searchParams }: PageProps) {
cardUsage,
boletoStats,
shareRows,
estabelecimentos,
periodPreferences,
] = await Promise.all([
fetchPagadorLancamentos(filters),
fetchPagadorMonthlyBreakdown({
@@ -157,6 +161,8 @@ export default async function Page({ params, searchParams }: PageProps) {
period: selectedPeriod,
}),
sharesPromise,
getRecentEstablishmentsAction(),
fetchUserPeriodPreferences(dataOwnerId),
]);
const mappedLancamentos = mapLancamentosData(lancamentoRows);
@@ -289,6 +295,8 @@ export default async function Page({ params, searchParams }: PageProps) {
categoriaFilterOptions={optionSets.categoriaFilterOptions}
contaCartaoFilterOptions={optionSets.contaCartaoFilterOptions}
selectedPeriod={selectedPeriod}
estabelecimentos={estabelecimentos}
periodPreferences={periodPreferences}
allowCreate={canEdit}
/>
</section>