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

@@ -26,6 +26,8 @@ import {
import { Label } from "@/components/ui/label";
import { useControlledState } from "@/hooks/use-controlled-state";
import { useFormState } from "@/hooks/use-form-state";
import type { PeriodPreferences } from "@/lib/user-preferences/period";
import { createMonthOptions } from "@/lib/utils/period";
import {
useCallback,
useEffect,
@@ -43,64 +45,11 @@ interface BudgetDialogProps {
budget?: Budget;
categories: BudgetCategory[];
defaultPeriod: string;
periodPreferences: PeriodPreferences;
open?: boolean;
onOpenChange?: (open: boolean) => void;
}
type SelectOption = {
value: string;
label: string;
};
const monthFormatter = new Intl.DateTimeFormat("pt-BR", {
month: "long",
year: "numeric",
});
const formatPeriodLabel = (period: string) => {
const [year, month] = period.split("-").map(Number);
if (!year || !month) {
return period;
}
const date = new Date(year, month - 1, 1);
if (Number.isNaN(date.getTime())) {
return period;
}
const label = monthFormatter.format(date);
return label.charAt(0).toUpperCase() + label.slice(1);
};
const buildPeriodOptions = (currentValue?: string): SelectOption[] => {
const now = new Date();
const options: SelectOption[] = [];
for (let offset = -3; offset <= 3; offset += 1) {
const date = new Date(now.getFullYear(), now.getMonth() + offset, 1);
const value = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(
2,
"0"
)}`;
options.push({ value, label: formatPeriodLabel(value) });
}
if (
currentValue &&
!options.some((option) => option.value === currentValue)
) {
options.push({
value: currentValue,
label: formatPeriodLabel(currentValue),
});
}
return options
.sort((a, b) => a.value.localeCompare(b.value))
.map((option) => ({
value: option.value,
label: option.label,
}));
};
const buildInitialValues = ({
budget,
defaultPeriod,
@@ -119,6 +68,7 @@ export function BudgetDialog({
budget,
categories,
defaultPeriod,
periodPreferences,
open,
onOpenChange,
}: BudgetDialogProps) {
@@ -161,8 +111,13 @@ export function BudgetDialog({
}, [dialogOpen]);
const periodOptions = useMemo(
() => buildPeriodOptions(formState.period),
[formState.period]
() =>
createMonthOptions(
formState.period,
periodPreferences.monthsBefore,
periodPreferences.monthsAfter
),
[formState.period, periodPreferences.monthsBefore, periodPreferences.monthsAfter]
);
const handleSubmit = useCallback(