refactor: alinha features financeiras ao novo naming

This commit is contained in:
Felipe Coutinho
2026-03-14 12:50:55 +00:00
parent ef918a3667
commit 67ad4b9d02
51 changed files with 876 additions and 898 deletions

View File

@@ -2,7 +2,7 @@
import { and, eq, inArray } from "drizzle-orm";
import { z } from "zod";
import { preLancamentos } from "@/db/schema";
import { inboxItems } from "@/db/schema";
import {
handleActionError,
revalidateForEntity,
@@ -52,12 +52,12 @@ export async function markInboxAsProcessedAction(
// Verificar se item existe e pertence ao usuário
const [item] = await db
.select()
.from(preLancamentos)
.from(inboxItems)
.where(
and(
eq(preLancamentos.id, data.inboxItemId),
eq(preLancamentos.userId, user.id),
eq(preLancamentos.status, "pending"),
eq(inboxItems.id, data.inboxItemId),
eq(inboxItems.userId, user.id),
eq(inboxItems.status, "pending"),
),
)
.limit(1);
@@ -68,7 +68,7 @@ export async function markInboxAsProcessedAction(
// Marcar item como processado
await db
.update(preLancamentos)
.update(inboxItems)
.set({
status: "processed",
processedAt: new Date(),
@@ -76,8 +76,8 @@ export async function markInboxAsProcessedAction(
})
.where(
and(
eq(preLancamentos.id, data.inboxItemId),
eq(preLancamentos.userId, user.id),
eq(inboxItems.id, data.inboxItemId),
eq(inboxItems.userId, user.id),
),
);
@@ -99,12 +99,12 @@ export async function discardInboxItemAction(
// Verificar se item existe e pertence ao usuário
const [item] = await db
.select()
.from(preLancamentos)
.from(inboxItems)
.where(
and(
eq(preLancamentos.id, data.inboxItemId),
eq(preLancamentos.userId, user.id),
eq(preLancamentos.status, "pending"),
eq(inboxItems.id, data.inboxItemId),
eq(inboxItems.userId, user.id),
eq(inboxItems.status, "pending"),
),
)
.limit(1);
@@ -115,7 +115,7 @@ export async function discardInboxItemAction(
// Marcar item como descartado
await db
.update(preLancamentos)
.update(inboxItems)
.set({
status: "discarded",
discardedAt: new Date(),
@@ -123,8 +123,8 @@ export async function discardInboxItemAction(
})
.where(
and(
eq(preLancamentos.id, data.inboxItemId),
eq(preLancamentos.userId, user.id),
eq(inboxItems.id, data.inboxItemId),
eq(inboxItems.userId, user.id),
),
);
@@ -145,7 +145,7 @@ export async function bulkDiscardInboxItemsAction(
// Marcar todos os itens como descartados
await db
.update(preLancamentos)
.update(inboxItems)
.set({
status: "discarded",
discardedAt: new Date(),
@@ -153,9 +153,9 @@ export async function bulkDiscardInboxItemsAction(
})
.where(
and(
inArray(preLancamentos.id, data.inboxItemIds),
eq(preLancamentos.userId, user.id),
eq(preLancamentos.status, "pending"),
inArray(inboxItems.id, data.inboxItemIds),
eq(inboxItems.userId, user.id),
eq(inboxItems.status, "pending"),
),
);
@@ -178,13 +178,13 @@ export async function restoreDiscardedInboxItemAction(
const data = restoreDiscardedInboxSchema.parse(input);
const [item] = await db
.select({ id: preLancamentos.id })
.from(preLancamentos)
.select({ id: inboxItems.id })
.from(inboxItems)
.where(
and(
eq(preLancamentos.id, data.inboxItemId),
eq(preLancamentos.userId, user.id),
eq(preLancamentos.status, "discarded"),
eq(inboxItems.id, data.inboxItemId),
eq(inboxItems.userId, user.id),
eq(inboxItems.status, "discarded"),
),
)
.limit(1);
@@ -197,7 +197,7 @@ export async function restoreDiscardedInboxItemAction(
}
await db
.update(preLancamentos)
.update(inboxItems)
.set({
status: "pending",
discardedAt: null,
@@ -205,8 +205,8 @@ export async function restoreDiscardedInboxItemAction(
})
.where(
and(
eq(preLancamentos.id, data.inboxItemId),
eq(preLancamentos.userId, user.id),
eq(inboxItems.id, data.inboxItemId),
eq(inboxItems.userId, user.id),
),
);
@@ -226,12 +226,12 @@ export async function deleteInboxItemAction(
const data = deleteInboxSchema.parse(input);
const [item] = await db
.select({ status: preLancamentos.status })
.from(preLancamentos)
.select({ status: inboxItems.status })
.from(inboxItems)
.where(
and(
eq(preLancamentos.id, data.inboxItemId),
eq(preLancamentos.userId, user.id),
eq(inboxItems.id, data.inboxItemId),
eq(inboxItems.userId, user.id),
),
)
.limit(1);
@@ -248,11 +248,11 @@ export async function deleteInboxItemAction(
}
await db
.delete(preLancamentos)
.delete(inboxItems)
.where(
and(
eq(preLancamentos.id, data.inboxItemId),
eq(preLancamentos.userId, user.id),
eq(inboxItems.id, data.inboxItemId),
eq(inboxItems.userId, user.id),
),
);
@@ -272,14 +272,11 @@ export async function bulkDeleteInboxItemsAction(
const data = bulkDeleteInboxSchema.parse(input);
const result = await db
.delete(preLancamentos)
.delete(inboxItems)
.where(
and(
eq(preLancamentos.userId, user.id),
eq(preLancamentos.status, data.status),
),
and(eq(inboxItems.userId, user.id), eq(inboxItems.status, data.status)),
)
.returning({ id: preLancamentos.id });
.returning({ id: inboxItems.id });
revalidateInbox();

View File

@@ -10,7 +10,7 @@ import {
markInboxAsProcessedAction,
restoreDiscardedInboxItemAction,
} from "@/features/inbox/actions";
import { LancamentoDialog } from "@/features/transactions/components/dialogs/transaction-dialog/transaction-dialog";
import { TransactionDialog } from "@/features/transactions/components/dialogs/transaction-dialog/transaction-dialog";
import { ConfirmActionDialog } from "@/shared/components/confirm-action-dialog";
import { EmptyState } from "@/shared/components/empty-state";
import { Button } from "@/shared/components/ui/button";
@@ -29,12 +29,12 @@ interface InboxPageProps {
pendingItems: InboxItem[];
processedItems: InboxItem[];
discardedItems: InboxItem[];
pagadorOptions: SelectOption[];
splitPagadorOptions: SelectOption[];
defaultPagadorId: string | null;
contaOptions: SelectOption[];
cartaoOptions: SelectOption[];
categoriaOptions: SelectOption[];
payerOptions: SelectOption[];
splitPayerOptions: SelectOption[];
defaultPayerId: string | null;
accountOptions: SelectOption[];
cardOptions: SelectOption[];
categoryOptions: SelectOption[];
estabelecimentos: string[];
appLogoMap: Record<string, string>;
}
@@ -43,12 +43,12 @@ export function InboxPage({
pendingItems,
processedItems,
discardedItems,
pagadorOptions,
splitPagadorOptions,
defaultPagadorId,
contaOptions,
cartaoOptions,
categoriaOptions,
payerOptions,
splitPayerOptions,
defaultPayerId,
accountOptions,
cardOptions,
categoryOptions,
estabelecimentos,
appLogoMap,
}: InboxPageProps) {
@@ -272,14 +272,14 @@ export function InboxPage({
const appName = itemToProcess?.sourceAppName?.toLowerCase();
if (!appName) return null;
for (const option of cartaoOptions) {
for (const option of cardOptions) {
const label = option.label.toLowerCase();
if (label.includes(appName) || appName.includes(label)) {
return option.value;
}
}
return null;
}, [itemToProcess?.sourceAppName, cartaoOptions]);
}, [itemToProcess?.sourceAppName, cardOptions]);
const renderEmptyState = (message: string) => (
<Card className="flex min-h-[50vh] w-full items-center justify-center py-12">
@@ -378,21 +378,21 @@ export function InboxPage({
</TabsContent>
</Tabs>
<LancamentoDialog
<TransactionDialog
mode="create"
open={processOpen}
onOpenChange={handleProcessOpenChange}
pagadorOptions={pagadorOptions}
splitPagadorOptions={splitPagadorOptions}
defaultPagadorId={defaultPagadorId}
contaOptions={contaOptions}
cartaoOptions={cartaoOptions}
categoriaOptions={categoriaOptions}
payerOptions={payerOptions}
splitPayerOptions={splitPayerOptions}
defaultPayerId={defaultPayerId}
accountOptions={accountOptions}
cardOptions={cardOptions}
categoryOptions={categoryOptions}
estabelecimentos={estabelecimentos}
defaultPurchaseDate={defaultPurchaseDate}
defaultName={defaultName}
defaultAmount={defaultAmount}
defaultCartaoId={matchedCartaoId}
defaultCardId={matchedCartaoId}
defaultPaymentMethod={matchedCartaoId ? "Cartão de crédito" : null}
forceShowTransactionType
onSuccess={handleLancamentoSuccess}

View File

@@ -10,7 +10,7 @@ export interface InboxItem {
parsedName: string | null;
parsedAmount: string | null;
status: string;
lancamentoId: string | null;
transactionId: string | null;
processedAt: Date | null;
discardedAt: Date | null;
createdAt: Date;

View File

@@ -1,5 +1,5 @@
import { and, desc, eq } from "drizzle-orm";
import { cartoes, categorias, contas, preLancamentos } from "@/db/schema";
import { cards, categories, financialAccounts, inboxItems } from "@/db/schema";
import type {
InboxItem,
SelectOption,
@@ -9,8 +9,8 @@ import {
buildSluggedFilters,
} from "@/features/transactions/page-helpers";
import {
fetchLancamentoFilterSources,
fetchRecentEstablishments,
fetchTransactionFilterSources,
} from "@/features/transactions/queries";
import { db } from "@/shared/lib/db";
@@ -20,11 +20,9 @@ export async function fetchInboxItems(
): Promise<InboxItem[]> {
const items = await db
.select()
.from(preLancamentos)
.where(
and(eq(preLancamentos.userId, userId), eq(preLancamentos.status, status)),
)
.orderBy(desc(preLancamentos.createdAt));
.from(inboxItems)
.where(and(eq(inboxItems.userId, userId), eq(inboxItems.status, status)))
.orderBy(desc(inboxItems.createdAt));
return items;
}
@@ -35,54 +33,57 @@ export async function fetchInboxItemById(
): Promise<InboxItem | null> {
const [item] = await db
.select()
.from(preLancamentos)
.where(
and(eq(preLancamentos.id, itemId), eq(preLancamentos.userId, userId)),
)
.from(inboxItems)
.where(and(eq(inboxItems.id, itemId), eq(inboxItems.userId, userId)))
.limit(1);
return item ?? null;
}
export async function fetchCategoriasForSelect(
export async function fetchCategoriesForSelect(
userId: string,
type?: string,
): Promise<SelectOption[]> {
const query = db
.select({ id: categorias.id, name: categorias.name })
.from(categorias)
const rows = await db
.select({ id: categories.id, name: categories.name })
.from(categories)
.where(
type
? and(eq(categorias.userId, userId), eq(categorias.type, type))
: eq(categorias.userId, userId),
? and(eq(categories.userId, userId), eq(categories.type, type))
: eq(categories.userId, userId),
)
.orderBy(categorias.name);
.orderBy(categories.name);
return query;
return rows.map((row) => ({ value: row.id, label: row.name }));
}
export async function fetchContasForSelect(
export async function fetchAccountsForSelect(
userId: string,
): Promise<SelectOption[]> {
const items = await db
.select({ id: contas.id, name: contas.name })
.from(contas)
.where(and(eq(contas.userId, userId), eq(contas.status, "ativo")))
.orderBy(contas.name);
const rows = await db
.select({ id: financialAccounts.id, name: financialAccounts.name })
.from(financialAccounts)
.where(
and(
eq(financialAccounts.userId, userId),
eq(financialAccounts.status, "ativo"),
),
)
.orderBy(financialAccounts.name);
return items;
return rows.map((row) => ({ value: row.id, label: row.name }));
}
export async function fetchCartoesForSelect(
export async function fetchCardsForSelect(
userId: string,
): Promise<(SelectOption & { lastDigits?: string })[]> {
const items = await db
.select({ id: cartoes.id, name: cartoes.name })
.from(cartoes)
.where(and(eq(cartoes.userId, userId), eq(cartoes.status, "ativo")))
.orderBy(cartoes.name);
const rows = await db
.select({ id: cards.id, name: cards.name })
.from(cards)
.where(and(eq(cards.userId, userId), eq(cards.status, "ativo")))
.orderBy(cards.name);
return items;
return rows.map((row) => ({ value: row.id, label: row.name }));
}
export async function fetchAppLogoMap(
@@ -90,13 +91,13 @@ export async function fetchAppLogoMap(
): Promise<Record<string, string>> {
const [userCartoes, userContas] = await Promise.all([
db
.select({ name: cartoes.name, logo: cartoes.logo })
.from(cartoes)
.where(eq(cartoes.userId, userId)),
.select({ name: cards.name, logo: cards.logo })
.from(cards)
.where(eq(cards.userId, userId)),
db
.select({ name: contas.name, logo: contas.logo })
.from(contas)
.where(eq(contas.userId, userId)),
.select({ name: financialAccounts.name, logo: financialAccounts.logo })
.from(financialAccounts)
.where(eq(financialAccounts.userId, userId)),
]);
const logoMap: Record<string, string> = {};
@@ -112,54 +113,51 @@ export async function fetchAppLogoMap(
export async function fetchPendingInboxCount(userId: string): Promise<number> {
const items = await db
.select({ id: preLancamentos.id })
.from(preLancamentos)
.select({ id: inboxItems.id })
.from(inboxItems)
.where(
and(
eq(preLancamentos.userId, userId),
eq(preLancamentos.status, "pending"),
),
and(eq(inboxItems.userId, userId), eq(inboxItems.status, "pending")),
);
return items.length;
}
/**
* Fetch all data needed for the LancamentoDialog in inbox context
* Fetch all data needed for the TransactionDialog in inbox context
*/
export async function fetchInboxDialogData(userId: string): Promise<{
pagadorOptions: SelectOption[];
splitPagadorOptions: SelectOption[];
defaultPagadorId: string | null;
contaOptions: SelectOption[];
cartaoOptions: SelectOption[];
categoriaOptions: SelectOption[];
payerOptions: SelectOption[];
splitPayerOptions: SelectOption[];
defaultPayerId: string | null;
accountOptions: SelectOption[];
cardOptions: SelectOption[];
categoryOptions: SelectOption[];
estabelecimentos: string[];
}> {
const filterSources = await fetchLancamentoFilterSources(userId);
const filterSources = await fetchTransactionFilterSources(userId);
const sluggedFilters = buildSluggedFilters(filterSources);
const {
pagadorOptions,
splitPagadorOptions,
defaultPagadorId,
contaOptions,
cartaoOptions,
categoriaOptions,
payerOptions,
splitPayerOptions,
defaultPayerId,
accountOptions,
cardOptions,
categoryOptions,
} = buildOptionSets({
...sluggedFilters,
pagadorRows: filterSources.pagadorRows,
payerRows: filterSources.payerRows,
});
const estabelecimentos = await fetchRecentEstablishments(userId);
return {
pagadorOptions,
splitPagadorOptions,
defaultPagadorId,
contaOptions,
cartaoOptions,
categoriaOptions,
payerOptions,
splitPayerOptions,
defaultPayerId,
accountOptions,
cardOptions,
categoryOptions,
estabelecimentos,
};
}