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)
128 lines
3.5 KiB
TypeScript
128 lines
3.5 KiB
TypeScript
"use client";
|
|
|
|
import { useCallback, useMemo, useState } from "react";
|
|
|
|
import { LancamentoDialog } from "@/components/lancamentos/dialogs/lancamento-dialog/lancamento-dialog";
|
|
|
|
import type {
|
|
CalendarDay,
|
|
CalendarEvent,
|
|
CalendarFormOptions,
|
|
CalendarPeriod,
|
|
} from "@/components/calendario/types";
|
|
import { buildCalendarDays } from "@/components/calendario/utils";
|
|
import { CalendarGrid } from "@/components/calendario/calendar-grid";
|
|
import { CalendarLegend } from "@/components/calendario/calendar-legend";
|
|
import { EventModal } from "@/components/calendario/event-modal";
|
|
|
|
type MonthlyCalendarProps = {
|
|
period: CalendarPeriod;
|
|
events: CalendarEvent[];
|
|
formOptions: CalendarFormOptions;
|
|
};
|
|
|
|
const parsePeriod = (period: string) => {
|
|
const [yearStr, monthStr] = period.split("-");
|
|
const year = Number.parseInt(yearStr ?? "", 10);
|
|
const month = Number.parseInt(monthStr ?? "", 10);
|
|
|
|
return { year, monthIndex: month - 1 };
|
|
};
|
|
|
|
export function MonthlyCalendar({
|
|
period,
|
|
events,
|
|
formOptions,
|
|
}: MonthlyCalendarProps) {
|
|
const { year, monthIndex } = useMemo(
|
|
() => parsePeriod(period.period),
|
|
[period.period]
|
|
);
|
|
|
|
const eventsByDay = useMemo(() => {
|
|
const map = new Map<string, CalendarEvent[]>();
|
|
events.forEach((event) => {
|
|
const list = map.get(event.date) ?? [];
|
|
list.push(event);
|
|
map.set(event.date, list);
|
|
});
|
|
return map;
|
|
}, [events]);
|
|
|
|
const days = useMemo(
|
|
() => buildCalendarDays({ year, monthIndex, events: eventsByDay }),
|
|
[eventsByDay, monthIndex, year]
|
|
);
|
|
|
|
const [selectedDay, setSelectedDay] = useState<CalendarDay | null>(null);
|
|
const [isModalOpen, setModalOpen] = useState(false);
|
|
const [createOpen, setCreateOpen] = useState(false);
|
|
const [createDate, setCreateDate] = useState<string | null>(null);
|
|
|
|
const handleOpenCreate = useCallback((date: string) => {
|
|
setCreateDate(date);
|
|
setModalOpen(false);
|
|
setCreateOpen(true);
|
|
}, []);
|
|
|
|
const handleDaySelect = useCallback((day: CalendarDay) => {
|
|
setSelectedDay(day);
|
|
setModalOpen(true);
|
|
}, []);
|
|
|
|
const handleCreateFromCell = useCallback(
|
|
(day: CalendarDay) => {
|
|
handleOpenCreate(day.date);
|
|
},
|
|
[handleOpenCreate]
|
|
);
|
|
|
|
const handleModalClose = useCallback(() => {
|
|
setModalOpen(false);
|
|
setSelectedDay(null);
|
|
}, []);
|
|
|
|
const handleCreateDialogChange = useCallback((open: boolean) => {
|
|
setCreateOpen(open);
|
|
if (!open) {
|
|
setCreateDate(null);
|
|
}
|
|
}, []);
|
|
|
|
return (
|
|
<>
|
|
<div className="space-y-3">
|
|
<CalendarLegend />
|
|
<CalendarGrid
|
|
days={days}
|
|
onSelectDay={handleDaySelect}
|
|
onCreateDay={handleCreateFromCell}
|
|
/>
|
|
</div>
|
|
|
|
<EventModal
|
|
open={isModalOpen}
|
|
day={selectedDay}
|
|
onClose={handleModalClose}
|
|
onCreate={handleOpenCreate}
|
|
/>
|
|
|
|
<LancamentoDialog
|
|
mode="create"
|
|
open={createOpen}
|
|
onOpenChange={handleCreateDialogChange}
|
|
pagadorOptions={formOptions.pagadorOptions}
|
|
splitPagadorOptions={formOptions.splitPagadorOptions}
|
|
defaultPagadorId={formOptions.defaultPagadorId}
|
|
contaOptions={formOptions.contaOptions}
|
|
cartaoOptions={formOptions.cartaoOptions}
|
|
categoriaOptions={formOptions.categoriaOptions}
|
|
estabelecimentos={formOptions.estabelecimentos}
|
|
periodPreferences={formOptions.periodPreferences}
|
|
defaultPeriod={period.period}
|
|
defaultPurchaseDate={createDate ?? undefined}
|
|
/>
|
|
</>
|
|
);
|
|
}
|