chore: ajustes de componentes, estilos, dependências e métricas do dashboard

- dashboard: melhorias em métricas, filtros de transações e overview de período
- transactions: colunas, tabela e página com novos campos e ajustes de exibição
- ui: card, table, navigation-menu, navbar, month-picker, logo-picker, theme-toggler
- calculator: ajustes de display, keypad e estado
- calendar: melhorias de grid e day-cell
- insights: atualização de constantes
- settings: pequenos ajustes
- pnpm-lock: atualização de dependências
- pdf.worker: atualização do worker

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Felipe Coutinho
2026-05-02 22:08:53 +00:00
parent d55173e8c1
commit 94bf93194f
40 changed files with 4699 additions and 477 deletions

View File

@@ -0,0 +1,178 @@
"use server";
import { and, eq } from "drizzle-orm";
import { z } from "zod";
import { cards, categories, transactions } from "@/db/schema";
import {
buildRefundNote,
isRefundNote,
} from "@/shared/lib/accounts/constants";
import { getUser } from "@/shared/lib/auth/server";
import { db } from "@/shared/lib/db";
import { PERIOD_FORMAT_REGEX } from "@/shared/lib/invoices";
import type { ActionResult } from "@/shared/lib/types/actions";
import { formatDecimalForDbRequired } from "@/shared/utils/currency";
import { parseLocalDateString } from "@/shared/utils/date";
import {
formatPaidInvoicePeriods,
getPaidInvoicePeriods,
revalidate,
} from "./core";
const refundSchema = z.object({
originalTransactionId: z
.string({ message: "Lançamento inválido." })
.uuid("Lançamento inválido."),
refundDate: z
.string({ message: "Data inválida." })
.refine(
(value) => !Number.isNaN(parseLocalDateString(value).getTime()),
"Data inválida.",
),
refundPeriod: z
.string({ message: "Período inválido." })
.regex(PERIOD_FORMAT_REGEX, "Período inválido."),
});
type RefundInput = z.infer<typeof refundSchema>;
export async function refundTransactionAction(
input: RefundInput,
): Promise<ActionResult<{ refundId: string }>> {
try {
const user = await getUser();
const data = refundSchema.parse(input);
const original = await db.query.transactions.findFirst({
where: and(
eq(transactions.id, data.originalTransactionId),
eq(transactions.userId, user.id),
),
});
if (!original) {
return { success: false, error: "Lançamento não encontrado." };
}
if (original.transactionType !== "Despesa") {
return {
success: false,
error: "Apenas despesas podem ser estornadas.",
};
}
if (original.condition !== "À vista") {
return {
success: false,
error: "Apenas lançamentos à vista podem ser estornados.",
};
}
if (original.splitGroupId) {
return {
success: false,
error: "Lançamentos divididos não podem ser estornados.",
};
}
if (isRefundNote(original.note)) {
return {
success: false,
error: "Este lançamento já é um reembolso.",
};
}
const [existingRefund, card, paidPeriods, refundCategory] =
await Promise.all([
db.query.transactions.findFirst({
columns: { id: true },
where: and(
eq(transactions.userId, user.id),
eq(transactions.note, buildRefundNote(original.id)),
),
}),
original.cardId
? db.query.cards.findFirst({
columns: { id: true },
where: and(
eq(cards.id, original.cardId),
eq(cards.userId, user.id),
),
})
: Promise.resolve(null),
original.cardId
? getPaidInvoicePeriods(user.id, original.cardId, [data.refundPeriod])
: Promise.resolve([] as string[]),
db.query.categories.findFirst({
columns: { id: true },
where: and(
eq(categories.userId, user.id),
eq(categories.name, "Reembolso"),
),
}),
]);
if (existingRefund) {
return {
success: false,
error: "Este lançamento já foi estornado.",
};
}
if (original.cardId && !card) {
return { success: false, error: "Cartão não encontrado." };
}
if (paidPeriods.length > 0) {
return {
success: false,
error: `A fatura de ${formatPaidInvoicePeriods(
paidPeriods,
)} já está paga. Desfaça o pagamento antes de lançar o reembolso.`,
};
}
const amountAbs = Math.abs(Number(original.amount));
const refundDate = parseLocalDateString(data.refundDate);
const [inserted] = await db
.insert(transactions)
.values({
name: `Reembolso de: ${original.name}`,
condition: "À vista",
paymentMethod: original.paymentMethod,
note: buildRefundNote(original.id),
amount: formatDecimalForDbRequired(amountAbs),
purchaseDate: refundDate,
transactionType: "Receita",
period: data.refundPeriod,
isSettled: false,
userId: user.id,
cardId: original.cardId,
accountId: original.accountId,
categoryId: refundCategory?.id ?? null,
payerId: original.payerId,
})
.returning({ id: transactions.id });
revalidate(user.id);
return {
success: true,
message: "Reembolso registrado.",
data: { refundId: inserted?.id ?? "" },
};
} catch (error) {
if (error instanceof z.ZodError) {
return {
success: false,
error: error.issues[0]?.message ?? "Dados inválidos.",
};
}
return {
success: false,
error: error instanceof Error ? error.message : "Erro inesperado.",
};
}
}