mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-05-09 19:01:47 +00:00
refactor: atualiza transacoes dashboard e relatorios
This commit is contained in:
@@ -10,9 +10,9 @@ const LEGEND_ITEMS: Array<{
|
||||
label: string;
|
||||
dotColor?: string;
|
||||
}> = [
|
||||
{ type: "lancamento", label: "Lançamentos" },
|
||||
{ type: "transaction", label: "Lançamentos" },
|
||||
{ type: "boleto", label: "Boleto com vencimento" },
|
||||
{ type: "cartao", label: "Vencimento de cartão" },
|
||||
{ type: "card", label: "Vencimento de cartão" },
|
||||
{ label: "Pagamento fatura", dotColor: "bg-success" },
|
||||
];
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ export const EVENT_TYPE_STYLES: Record<
|
||||
CalendarEvent["type"],
|
||||
{ wrapper: string; dot: string; accent?: string }
|
||||
> = {
|
||||
lancamento: {
|
||||
transaction: {
|
||||
wrapper:
|
||||
"bg-warning/10 text-warning dark:bg-warning/5 dark:text-warning border-l-4 border-warning",
|
||||
dot: "bg-warning",
|
||||
@@ -26,7 +26,7 @@ export const EVENT_TYPE_STYLES: Record<
|
||||
"bg-info/10 text-info dark:bg-info/5 dark:text-info border-l-4 border-info",
|
||||
dot: "bg-info",
|
||||
},
|
||||
cartao: {
|
||||
card: {
|
||||
wrapper:
|
||||
"bg-violet-100 text-violet-600 dark:bg-violet-900/10 dark:text-violet-50 border-l-4 border-violet-500",
|
||||
dot: "bg-violet-600",
|
||||
@@ -38,18 +38,18 @@ const eventStyles = EVENT_TYPE_STYLES;
|
||||
const formatCurrencyValue = (value: number | null | undefined) =>
|
||||
currencyFormatter.format(Math.abs(value ?? 0));
|
||||
|
||||
const formatAmount = (event: Extract<CalendarEvent, { type: "lancamento" }>) =>
|
||||
formatCurrencyValue(event.lancamento.amount);
|
||||
const formatAmount = (event: Extract<CalendarEvent, { type: "transaction" }>) =>
|
||||
formatCurrencyValue(event.transaction.amount);
|
||||
|
||||
const buildEventLabel = (event: CalendarEvent) => {
|
||||
switch (event.type) {
|
||||
case "lancamento": {
|
||||
return event.lancamento.name;
|
||||
case "transaction": {
|
||||
return event.transaction.name;
|
||||
}
|
||||
case "boleto": {
|
||||
return event.lancamento.name;
|
||||
return event.transaction.name;
|
||||
}
|
||||
case "cartao": {
|
||||
case "card": {
|
||||
return event.card.name;
|
||||
}
|
||||
default:
|
||||
@@ -59,13 +59,13 @@ const buildEventLabel = (event: CalendarEvent) => {
|
||||
|
||||
const buildEventComplement = (event: CalendarEvent) => {
|
||||
switch (event.type) {
|
||||
case "lancamento": {
|
||||
case "transaction": {
|
||||
return formatAmount(event);
|
||||
}
|
||||
case "boleto": {
|
||||
return formatCurrencyValue(event.lancamento.amount);
|
||||
return formatCurrencyValue(event.transaction.amount);
|
||||
}
|
||||
case "cartao": {
|
||||
case "card": {
|
||||
if (event.card.totalDue !== null) {
|
||||
return formatCurrencyValue(event.card.totalDue);
|
||||
}
|
||||
@@ -78,8 +78,8 @@ const buildEventComplement = (event: CalendarEvent) => {
|
||||
|
||||
const isPagamentoFatura = (event: CalendarEvent) => {
|
||||
return (
|
||||
event.type === "lancamento" &&
|
||||
event.lancamento.name.startsWith("Pagamento fatura -")
|
||||
event.type === "transaction" &&
|
||||
event.transaction.name.startsWith("Pagamento fatura -")
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -50,14 +50,14 @@ const EventCard = ({
|
||||
};
|
||||
|
||||
const renderLancamento = (
|
||||
event: Extract<CalendarEvent, { type: "lancamento" }>,
|
||||
event: Extract<CalendarEvent, { type: "transaction" }>,
|
||||
) => {
|
||||
const isReceita = event.lancamento.transactionType === "Receita";
|
||||
const isReceita = event.transaction.transactionType === "Receita";
|
||||
const isPagamentoFatura =
|
||||
event.lancamento.name.startsWith("Pagamento fatura -");
|
||||
event.transaction.name.startsWith("Pagamento fatura -");
|
||||
|
||||
return (
|
||||
<EventCard type="lancamento" isPagamentoFatura={isPagamentoFatura}>
|
||||
<EventCard type="transaction" isPagamentoFatura={isPagamentoFatura}>
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="flex flex-col gap-1">
|
||||
<span
|
||||
@@ -65,13 +65,13 @@ const renderLancamento = (
|
||||
isPagamentoFatura && "text-success"
|
||||
}`}
|
||||
>
|
||||
{event.lancamento.name}
|
||||
{event.transaction.name}
|
||||
</span>
|
||||
|
||||
<div className="flex gap-1">
|
||||
<Badge variant={"outline"}>{event.lancamento.condition}</Badge>
|
||||
<Badge variant={"outline"}>{event.lancamento.paymentMethod}</Badge>
|
||||
<Badge variant={"outline"}>{event.lancamento.categoriaName}</Badge>
|
||||
<Badge variant={"outline"}>{event.transaction.condition}</Badge>
|
||||
<Badge variant={"outline"}>{event.transaction.paymentMethod}</Badge>
|
||||
<Badge variant={"outline"}>{event.transaction.categoriaName}</Badge>
|
||||
</div>
|
||||
</div>
|
||||
<span
|
||||
@@ -83,7 +83,7 @@ const renderLancamento = (
|
||||
<MoneyValues
|
||||
showPositiveSign
|
||||
className="text-base"
|
||||
amount={event.lancamento.amount}
|
||||
amount={event.transaction.amount}
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
@@ -92,8 +92,8 @@ const renderLancamento = (
|
||||
};
|
||||
|
||||
const renderBoleto = (event: Extract<CalendarEvent, { type: "boleto" }>) => {
|
||||
const isPaid = Boolean(event.lancamento.isSettled);
|
||||
const dueDate = event.lancamento.dueDate;
|
||||
const isPaid = Boolean(event.transaction.isSettled);
|
||||
const dueDate = event.transaction.dueDate;
|
||||
const dueDateLabel = formatFinancialDateLabel(dueDate, "Vence em", {
|
||||
day: "2-digit",
|
||||
month: "2-digit",
|
||||
@@ -106,7 +106,7 @@ const renderBoleto = (event: Extract<CalendarEvent, { type: "boleto" }>) => {
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex gap-1 items-center">
|
||||
<span className="text-sm font-semibold leading-tight">
|
||||
{event.lancamento.name}
|
||||
{event.transaction.name}
|
||||
</span>
|
||||
|
||||
{dueDateLabel && (
|
||||
@@ -119,24 +119,24 @@ const renderBoleto = (event: Extract<CalendarEvent, { type: "boleto" }>) => {
|
||||
<Badge variant={"outline"}>{isPaid ? "Pago" : "Pendente"}</Badge>
|
||||
</div>
|
||||
<span className="font-semibold">
|
||||
<MoneyValues amount={event.lancamento.amount} />
|
||||
<MoneyValues amount={event.transaction.amount} />
|
||||
</span>
|
||||
</div>
|
||||
</EventCard>
|
||||
);
|
||||
};
|
||||
|
||||
const renderCard = (event: Extract<CalendarEvent, { type: "cartao" }>) => (
|
||||
<EventCard type="cartao">
|
||||
const renderCard = (event: Extract<CalendarEvent, { type: "card" }>) => (
|
||||
<EventCard type="card">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex gap-1 items-center">
|
||||
<span className="text-sm font-semibold leading-tight">
|
||||
Vencimento Fatura - {event.card.name}
|
||||
Vencimento Invoice - {event.card.name}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<Badge variant={"outline"}>{event.card.status ?? "Fatura"}</Badge>
|
||||
<Badge variant={"outline"}>{event.card.status ?? "Invoice"}</Badge>
|
||||
</div>
|
||||
{event.card.totalDue !== null ? (
|
||||
<span className="font-semibold">
|
||||
@@ -149,11 +149,11 @@ const renderCard = (event: Extract<CalendarEvent, { type: "cartao" }>) => (
|
||||
|
||||
const renderEvent = (event: CalendarEvent) => {
|
||||
switch (event.type) {
|
||||
case "lancamento":
|
||||
case "transaction":
|
||||
return renderLancamento(event);
|
||||
case "boleto":
|
||||
return renderBoleto(event);
|
||||
case "cartao":
|
||||
case "card":
|
||||
return renderCard(event);
|
||||
default:
|
||||
return null;
|
||||
|
||||
@@ -4,7 +4,7 @@ import { useMemo, useState } from "react";
|
||||
import { CalendarGrid } from "@/features/calendar/components/calendar-grid";
|
||||
import { CalendarLegend } from "@/features/calendar/components/calendar-legend";
|
||||
import { EventModal } from "@/features/calendar/components/event-modal";
|
||||
import { LancamentoDialog } from "@/features/transactions/components/dialogs/transaction-dialog/transaction-dialog";
|
||||
import { TransactionDialog } from "@/features/transactions/components/dialogs/transaction-dialog/transaction-dialog";
|
||||
import type {
|
||||
CalendarDay,
|
||||
CalendarEvent,
|
||||
@@ -93,16 +93,16 @@ export function MonthlyCalendar({
|
||||
onCreate={handleOpenCreate}
|
||||
/>
|
||||
|
||||
<LancamentoDialog
|
||||
<TransactionDialog
|
||||
mode="create"
|
||||
open={createOpen}
|
||||
onOpenChange={handleCreateDialogChange}
|
||||
pagadorOptions={formOptions.pagadorOptions}
|
||||
splitPagadorOptions={formOptions.splitPagadorOptions}
|
||||
defaultPagadorId={formOptions.defaultPagadorId}
|
||||
contaOptions={formOptions.contaOptions}
|
||||
cartaoOptions={formOptions.cartaoOptions}
|
||||
categoriaOptions={formOptions.categoriaOptions}
|
||||
payerOptions={formOptions.payerOptions}
|
||||
splitPayerOptions={formOptions.splitPayerOptions}
|
||||
defaultPayerId={formOptions.defaultPayerId}
|
||||
accountOptions={formOptions.accountOptions}
|
||||
cardOptions={formOptions.cardOptions}
|
||||
categoryOptions={formOptions.categoryOptions}
|
||||
estabelecimentos={formOptions.estabelecimentos}
|
||||
defaultPeriod={period.period}
|
||||
defaultPurchaseDate={createDate ?? undefined}
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import { and, eq, gte, lte, ne, or } from "drizzle-orm";
|
||||
import { cartoes, lancamentos } from "@/db/schema";
|
||||
import { cards, transactions } from "@/db/schema";
|
||||
import {
|
||||
buildOptionSets,
|
||||
buildSluggedFilters,
|
||||
mapLancamentosData,
|
||||
mapTransactionsData,
|
||||
} from "@/features/transactions/page-helpers";
|
||||
import {
|
||||
fetchLancamentoFilterSources,
|
||||
fetchRecentEstablishments,
|
||||
fetchTransactionFilterSources,
|
||||
} from "@/features/transactions/queries";
|
||||
import { db } from "@/shared/lib/db";
|
||||
import { PAGADOR_ROLE_ADMIN } from "@/shared/lib/payers/constants";
|
||||
import { PAYER_ROLE_ADMIN } from "@/shared/lib/payers/constants";
|
||||
import type { CalendarData, CalendarEvent } from "@/shared/lib/types/calendar";
|
||||
import { formatDateKey } from "@/shared/utils/calendar";
|
||||
import { parsePeriod } from "@/shared/utils/period";
|
||||
@@ -46,65 +46,62 @@ export const fetchCalendarData = async ({
|
||||
const rangeStartKey = formatDateKey(rangeStart);
|
||||
const rangeEndKey = formatDateKey(rangeEnd);
|
||||
|
||||
const [lancamentoRows, cardRows, filterSources] = await Promise.all([
|
||||
db.query.lancamentos.findMany({
|
||||
const [transactionRows, cardRows, filterSources] = await Promise.all([
|
||||
db.query.transactions.findMany({
|
||||
where: and(
|
||||
eq(lancamentos.userId, userId),
|
||||
ne(lancamentos.transactionType, TRANSACTION_TYPE_TRANSFERENCIA),
|
||||
eq(transactions.userId, userId),
|
||||
ne(transactions.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),
|
||||
gte(transactions.purchaseDate, rangeStart),
|
||||
lte(transactions.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),
|
||||
eq(transactions.paymentMethod, PAYMENT_METHOD_BOLETO),
|
||||
gte(transactions.dueDate, rangeStart),
|
||||
lte(transactions.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),
|
||||
eq(transactions.period, period),
|
||||
ne(transactions.paymentMethod, PAYMENT_METHOD_BOLETO),
|
||||
),
|
||||
),
|
||||
),
|
||||
with: {
|
||||
pagador: true,
|
||||
conta: true,
|
||||
cartao: true,
|
||||
categoria: true,
|
||||
payer: true,
|
||||
financialAccount: true,
|
||||
card: true,
|
||||
category: true,
|
||||
},
|
||||
}),
|
||||
db.query.cartoes.findMany({
|
||||
where: eq(cartoes.userId, userId),
|
||||
db.query.cards.findMany({
|
||||
where: eq(cards.userId, userId),
|
||||
}),
|
||||
fetchLancamentoFilterSources(userId),
|
||||
fetchTransactionFilterSources(userId),
|
||||
]);
|
||||
|
||||
const lancamentosData = mapLancamentosData(lancamentoRows);
|
||||
const transactionData = mapTransactionsData(transactionRows);
|
||||
const events: CalendarEvent[] = [];
|
||||
|
||||
const cardTotals = new Map<string, number>();
|
||||
for (const item of lancamentosData) {
|
||||
for (const item of transactionData) {
|
||||
if (
|
||||
!item.cartaoId ||
|
||||
!item.cardId ||
|
||||
item.period !== period ||
|
||||
item.pagadorRole !== PAGADOR_ROLE_ADMIN
|
||||
item.pagadorRole !== PAYER_ROLE_ADMIN
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
const amount = Math.abs(item.amount ?? 0);
|
||||
cardTotals.set(
|
||||
item.cartaoId,
|
||||
(cardTotals.get(item.cartaoId) ?? 0) + amount,
|
||||
);
|
||||
cardTotals.set(item.cardId, (cardTotals.get(item.cardId) ?? 0) + amount);
|
||||
}
|
||||
|
||||
for (const item of lancamentosData) {
|
||||
for (const item of transactionData) {
|
||||
const isBoleto = item.paymentMethod === PAYMENT_METHOD_BOLETO;
|
||||
const isAdminPagador = item.pagadorRole === PAGADOR_ROLE_ADMIN;
|
||||
const isAdminPagador = item.pagadorRole === PAYER_ROLE_ADMIN;
|
||||
|
||||
// Para boletos, exibir apenas na data de vencimento e apenas se for pagador admin
|
||||
if (isBoleto) {
|
||||
@@ -117,7 +114,7 @@ export const fetchCalendarData = async ({
|
||||
id: `${item.id}:boleto`,
|
||||
type: "boleto",
|
||||
date: item.dueDate,
|
||||
lancamento: item,
|
||||
transaction: item,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@@ -129,9 +126,9 @@ export const fetchCalendarData = async ({
|
||||
if (isWithinRange(purchaseDateKey, rangeStartKey, rangeEndKey)) {
|
||||
events.push({
|
||||
id: item.id,
|
||||
type: "lancamento",
|
||||
type: "transaction",
|
||||
date: purchaseDateKey,
|
||||
lancamento: item,
|
||||
transaction: item,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -155,7 +152,7 @@ export const fetchCalendarData = async ({
|
||||
|
||||
events.push({
|
||||
id: `${card.id}:cartao`,
|
||||
type: "cartao",
|
||||
type: "card",
|
||||
date: dueDateKey,
|
||||
card: {
|
||||
id: card.id,
|
||||
@@ -171,9 +168,9 @@ export const fetchCalendarData = async ({
|
||||
}
|
||||
|
||||
const typePriority: Record<CalendarEvent["type"], number> = {
|
||||
lancamento: 0,
|
||||
transaction: 0,
|
||||
boleto: 1,
|
||||
cartao: 2,
|
||||
card: 2,
|
||||
};
|
||||
|
||||
events.sort((a, b) => {
|
||||
@@ -186,7 +183,7 @@ export const fetchCalendarData = async ({
|
||||
const sluggedFilters = buildSluggedFilters(filterSources);
|
||||
const optionSets = buildOptionSets({
|
||||
...sluggedFilters,
|
||||
pagadorRows: filterSources.pagadorRows,
|
||||
payerRows: filterSources.payerRows,
|
||||
});
|
||||
|
||||
const estabelecimentos = await fetchRecentEstablishments(userId);
|
||||
@@ -194,12 +191,12 @@ export const fetchCalendarData = async ({
|
||||
return {
|
||||
events,
|
||||
formOptions: {
|
||||
pagadorOptions: optionSets.pagadorOptions,
|
||||
splitPagadorOptions: optionSets.splitPagadorOptions,
|
||||
defaultPagadorId: optionSets.defaultPagadorId,
|
||||
contaOptions: optionSets.contaOptions,
|
||||
cartaoOptions: optionSets.cartaoOptions,
|
||||
categoriaOptions: optionSets.categoriaOptions,
|
||||
payerOptions: optionSets.payerOptions,
|
||||
splitPayerOptions: optionSets.splitPayerOptions,
|
||||
defaultPayerId: optionSets.defaultPayerId,
|
||||
accountOptions: optionSets.accountOptions,
|
||||
cardOptions: optionSets.cardOptions,
|
||||
categoryOptions: optionSets.categoryOptions,
|
||||
estabelecimentos,
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user