From 8ffe61c59b95ffe7539b90e3d7c38d07be54eefa Mon Sep 17 00:00:00 2001 From: Felipe Coutinho Date: Mon, 26 Jan 2026 17:05:55 +0000 Subject: [PATCH] =?UTF-8?q?refactor(inbox):=20rename=20caixa-de-entrada=20?= =?UTF-8?q?to=20pre-lancamentos=20e=20remove=20colunas=20n=C3=A3o=20utiliz?= =?UTF-8?q?adas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BREAKING CHANGES: - Renomeia rota /caixa-de-entrada para /pre-lancamentos - Remove colunas device_id, parsed_date e discard_reason da tabela inbox_items Mudanças: - Move componentes de caixa-de-entrada para pre-lancamentos - Atualiza sidebar e navegação para nova rota - Remove campos não utilizados do schema, types e APIs - Adiciona migration 0011 para remover colunas do banco - Simplifica lógica de data padrão usando notificationTimestamp --- app/(dashboard)/layout.tsx | 5 + .../actions.ts | 24 +- .../data.ts | 4 +- .../layout.tsx | 10 +- .../loading.tsx | 0 .../page.tsx | 4 +- app/api/inbox/batch/route.ts | 31 +- app/api/inbox/route.ts | 31 +- components/caixa-de-entrada/inbox-card.tsx | 122 - .../dialogs/lancamento-details-dialog.tsx | 21 +- components/pre-lancamentos/inbox-card.tsx | 153 ++ .../inbox-details-dialog.tsx | 92 +- .../inbox-page.tsx | 13 +- .../types.ts | 6 +- components/sidebar/app-sidebar.tsx | 8 +- components/sidebar/nav-link.tsx | 23 +- components/sidebar/nav-main.tsx | 7 + db/schema.ts | 13 +- drizzle/0011_remove_unused_inbox_columns.sql | 39 + drizzle/meta/0011_snapshot.json | 2261 +++++++++++++++++ drizzle/meta/_journal.json | 7 + lib/actions/helpers.ts | 2 +- lib/schemas/inbox.ts | 2 - 23 files changed, 2606 insertions(+), 272 deletions(-) rename app/(dashboard)/{caixa-de-entrada => pre-lancamentos}/actions.ts (89%) rename app/(dashboard)/{caixa-de-entrada => pre-lancamentos}/data.ts (96%) rename app/(dashboard)/{caixa-de-entrada => pre-lancamentos}/layout.tsx (57%) rename app/(dashboard)/{caixa-de-entrada => pre-lancamentos}/loading.tsx (100%) rename app/(dashboard)/{caixa-de-entrada => pre-lancamentos}/page.tsx (85%) delete mode 100644 components/caixa-de-entrada/inbox-card.tsx create mode 100644 components/pre-lancamentos/inbox-card.tsx rename components/{caixa-de-entrada => pre-lancamentos}/inbox-details-dialog.tsx (63%) rename components/{caixa-de-entrada => pre-lancamentos}/inbox-page.tsx (94%) rename components/{caixa-de-entrada => pre-lancamentos}/types.ts (86%) create mode 100644 drizzle/0011_remove_unused_inbox_columns.sql create mode 100644 drizzle/meta/0011_snapshot.json diff --git a/app/(dashboard)/layout.tsx b/app/(dashboard)/layout.tsx index 77af2e2..d78b1b2 100644 --- a/app/(dashboard)/layout.tsx +++ b/app/(dashboard)/layout.tsx @@ -7,6 +7,7 @@ import { fetchDashboardNotifications } from "@/lib/dashboard/notifications"; import { fetchPagadoresWithAccess } from "@/lib/pagadores/access"; import { PAGADOR_ROLE_ADMIN } from "@/lib/pagadores/constants"; import { parsePeriodParam } from "@/lib/utils/period"; +import { fetchPendingInboxCount } from "./pre-lancamentos/data"; export default async function DashboardLayout({ children, @@ -40,6 +41,9 @@ export default async function DashboardLayout({ currentPeriod, ); + // Buscar contagem de pré-lançamentos pendentes + const preLancamentosCount = await fetchPendingInboxCount(session.user.id); + return ( @@ -52,6 +56,7 @@ export default async function DashboardLayout({ avatarUrl: item.avatarUrl, canEdit: item.canEdit, }))} + preLancamentosCount={preLancamentosCount} variant="sidebar" /> diff --git a/app/(dashboard)/caixa-de-entrada/actions.ts b/app/(dashboard)/pre-lancamentos/actions.ts similarity index 89% rename from app/(dashboard)/caixa-de-entrada/actions.ts rename to app/(dashboard)/pre-lancamentos/actions.ts index c2dc5bd..7f560d0 100644 --- a/app/(dashboard)/caixa-de-entrada/actions.ts +++ b/app/(dashboard)/pre-lancamentos/actions.ts @@ -3,8 +3,8 @@ import { inboxItems } from "@/db/schema"; import { handleActionError } from "@/lib/actions/helpers"; import type { ActionResult } from "@/lib/actions/types"; -import { db } from "@/lib/db"; import { getUser } from "@/lib/auth/server"; +import { db } from "@/lib/db"; import { and, eq, inArray } from "drizzle-orm"; import { revalidatePath } from "next/cache"; import { z } from "zod"; @@ -15,7 +15,6 @@ const markProcessedSchema = z.object({ const discardInboxSchema = z.object({ inboxItemId: z.string().uuid("ID do item inválido"), - reason: z.string().optional(), }); const bulkDiscardSchema = z.object({ @@ -23,7 +22,7 @@ const bulkDiscardSchema = z.object({ }); function revalidateInbox() { - revalidatePath("/caixa-de-entrada"); + revalidatePath("/pre-lancamentos"); revalidatePath("/lancamentos"); revalidatePath("/dashboard"); } @@ -32,7 +31,7 @@ function revalidateInbox() { * Mark an inbox item as processed after a lancamento was created */ export async function markInboxAsProcessedAction( - input: z.infer + input: z.infer, ): Promise { try { const user = await getUser(); @@ -46,8 +45,8 @@ export async function markInboxAsProcessedAction( and( eq(inboxItems.id, data.inboxItemId), eq(inboxItems.userId, user.id), - eq(inboxItems.status, "pending") - ) + eq(inboxItems.status, "pending"), + ), ) .limit(1); @@ -74,7 +73,7 @@ export async function markInboxAsProcessedAction( } export async function discardInboxItemAction( - input: z.infer + input: z.infer, ): Promise { try { const user = await getUser(); @@ -88,8 +87,8 @@ export async function discardInboxItemAction( and( eq(inboxItems.id, data.inboxItemId), eq(inboxItems.userId, user.id), - eq(inboxItems.status, "pending") - ) + eq(inboxItems.status, "pending"), + ), ) .limit(1); @@ -103,7 +102,6 @@ export async function discardInboxItemAction( .set({ status: "discarded", discardedAt: new Date(), - discardReason: data.reason, updatedAt: new Date(), }) .where(eq(inboxItems.id, data.inboxItemId)); @@ -117,7 +115,7 @@ export async function discardInboxItemAction( } export async function bulkDiscardInboxItemsAction( - input: z.infer + input: z.infer, ): Promise { try { const user = await getUser(); @@ -135,8 +133,8 @@ export async function bulkDiscardInboxItemsAction( and( inArray(inboxItems.id, data.inboxItemIds), eq(inboxItems.userId, user.id), - eq(inboxItems.status, "pending") - ) + eq(inboxItems.status, "pending"), + ), ); revalidateInbox(); diff --git a/app/(dashboard)/caixa-de-entrada/data.ts b/app/(dashboard)/pre-lancamentos/data.ts similarity index 96% rename from app/(dashboard)/caixa-de-entrada/data.ts rename to app/(dashboard)/pre-lancamentos/data.ts index 0df1a70..4533982 100644 --- a/app/(dashboard)/caixa-de-entrada/data.ts +++ b/app/(dashboard)/pre-lancamentos/data.ts @@ -1,11 +1,11 @@ /** - * Data fetching functions for Caixa de Entrada + * Data fetching functions for Pré-Lançamentos */ import { db } from "@/lib/db"; import { inboxItems, categorias, contas, cartoes, lancamentos } from "@/db/schema"; import { eq, desc, and, gte } from "drizzle-orm"; -import type { InboxItem, SelectOption } from "@/components/caixa-de-entrada/types"; +import type { InboxItem, SelectOption } from "@/components/pre-lancamentos/types"; import { fetchLancamentoFilterSources, buildSluggedFilters, diff --git a/app/(dashboard)/caixa-de-entrada/layout.tsx b/app/(dashboard)/pre-lancamentos/layout.tsx similarity index 57% rename from app/(dashboard)/caixa-de-entrada/layout.tsx rename to app/(dashboard)/pre-lancamentos/layout.tsx index 5c58ae3..1579c32 100644 --- a/app/(dashboard)/caixa-de-entrada/layout.tsx +++ b/app/(dashboard)/pre-lancamentos/layout.tsx @@ -1,8 +1,8 @@ import PageDescription from "@/components/page-description"; -import { RiInbox2Line } from "@remixicon/react"; +import { RiInboxLine } from "@remixicon/react"; export const metadata = { - title: "Caixa de Entrada | Opensheets", + title: "Pré-Lançamentos | Opensheets", }; export default function RootLayout({ @@ -13,9 +13,9 @@ export default function RootLayout({ return (
} - title="Caixa de Entrada" - subtitle="Visialize seus lançamentos pendentes" + icon={} + title="Pré-Lançamentos" + subtitle="Notificações capturadas aguardando processamento" /> {children}
diff --git a/app/(dashboard)/caixa-de-entrada/loading.tsx b/app/(dashboard)/pre-lancamentos/loading.tsx similarity index 100% rename from app/(dashboard)/caixa-de-entrada/loading.tsx rename to app/(dashboard)/pre-lancamentos/loading.tsx diff --git a/app/(dashboard)/caixa-de-entrada/page.tsx b/app/(dashboard)/pre-lancamentos/page.tsx similarity index 85% rename from app/(dashboard)/caixa-de-entrada/page.tsx rename to app/(dashboard)/pre-lancamentos/page.tsx index 8bc683a..b1d3279 100644 --- a/app/(dashboard)/caixa-de-entrada/page.tsx +++ b/app/(dashboard)/pre-lancamentos/page.tsx @@ -1,6 +1,6 @@ -import { InboxPage } from "@/components/caixa-de-entrada/inbox-page"; +import { InboxPage } from "@/components/pre-lancamentos/inbox-page"; import { getUserId } from "@/lib/auth/server"; -import { fetchInboxItems, fetchInboxDialogData } from "./data"; +import { fetchInboxDialogData, fetchInboxItems } from "./data"; export default async function Page() { const userId = await getUserId(); diff --git a/app/api/inbox/batch/route.ts b/app/api/inbox/batch/route.ts index 235263d..58e57be 100644 --- a/app/api/inbox/batch/route.ts +++ b/app/api/inbox/batch/route.ts @@ -5,12 +5,12 @@ * Requer autenticação via API token (formato os_xxx). */ +import { apiTokens, inboxItems } from "@/db/schema"; import { extractBearerToken, hashToken } from "@/lib/auth/api-token"; import { db } from "@/lib/db"; -import { apiTokens, inboxItems } from "@/db/schema"; -import { eq, and, isNull } from "drizzle-orm"; -import { NextResponse } from "next/server"; import { inboxBatchSchema } from "@/lib/schemas/inbox"; +import { and, eq, isNull } from "drizzle-orm"; +import { NextResponse } from "next/server"; import { z } from "zod"; // Rate limiting simples em memória @@ -51,7 +51,7 @@ export async function POST(request: Request) { if (!token) { return NextResponse.json( { error: "Token não fornecido" }, - { status: 401 } + { status: 401 }, ); } @@ -59,7 +59,7 @@ export async function POST(request: Request) { if (!token.startsWith("os_")) { return NextResponse.json( { error: "Formato de token inválido" }, - { status: 401 } + { status: 401 }, ); } @@ -69,14 +69,14 @@ export async function POST(request: Request) { const tokenRecord = await db.query.apiTokens.findFirst({ where: and( eq(apiTokens.tokenHash, tokenHash), - isNull(apiTokens.revokedAt) + isNull(apiTokens.revokedAt), ), }); if (!tokenRecord) { return NextResponse.json( { error: "Token inválido ou revogado" }, - { status: 401 } + { status: 401 }, ); } @@ -84,7 +84,7 @@ export async function POST(request: Request) { if (!checkRateLimit(tokenRecord.userId)) { return NextResponse.json( { error: "Limite de requisições excedido", retryAfter: 60 }, - { status: 429 } + { status: 429 }, ); } @@ -103,13 +103,11 @@ export async function POST(request: Request) { userId: tokenRecord.userId, sourceApp: item.sourceApp, sourceAppName: item.sourceAppName, - deviceId: item.deviceId, originalTitle: item.originalTitle, originalText: item.originalText, notificationTimestamp: item.notificationTimestamp, parsedName: item.parsedName, parsedAmount: item.parsedAmount?.toString(), - parsedDate: item.parsedDate, parsedTransactionType: item.parsedTransactionType, status: "pending", }) @@ -130,9 +128,10 @@ export async function POST(request: Request) { } // Atualizar último uso do token - const clientIp = request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() - || request.headers.get("x-real-ip") - || null; + const clientIp = + request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || + request.headers.get("x-real-ip") || + null; await db .update(apiTokens) @@ -153,20 +152,20 @@ export async function POST(request: Request) { failed: failCount, results, }, - { status: 201 } + { status: 201 }, ); } catch (error) { if (error instanceof z.ZodError) { return NextResponse.json( { error: error.issues[0]?.message ?? "Dados inválidos" }, - { status: 400 } + { status: 400 }, ); } console.error("[API] Error creating batch inbox items:", error); return NextResponse.json( { error: "Erro ao processar notificações" }, - { status: 500 } + { status: 500 }, ); } } diff --git a/app/api/inbox/route.ts b/app/api/inbox/route.ts index 46938e4..a13f8c9 100644 --- a/app/api/inbox/route.ts +++ b/app/api/inbox/route.ts @@ -5,12 +5,12 @@ * Requer autenticação via API token (formato os_xxx). */ +import { apiTokens, inboxItems } from "@/db/schema"; import { extractBearerToken, hashToken } from "@/lib/auth/api-token"; import { db } from "@/lib/db"; -import { apiTokens, inboxItems } from "@/db/schema"; -import { eq, and, isNull } from "drizzle-orm"; -import { NextResponse } from "next/server"; import { inboxItemSchema } from "@/lib/schemas/inbox"; +import { and, eq, isNull } from "drizzle-orm"; +import { NextResponse } from "next/server"; import { z } from "zod"; // Rate limiting simples em memória (em produção, use Redis) @@ -44,7 +44,7 @@ export async function POST(request: Request) { if (!token) { return NextResponse.json( { error: "Token não fornecido" }, - { status: 401 } + { status: 401 }, ); } @@ -52,7 +52,7 @@ export async function POST(request: Request) { if (!token.startsWith("os_")) { return NextResponse.json( { error: "Formato de token inválido" }, - { status: 401 } + { status: 401 }, ); } @@ -62,14 +62,14 @@ export async function POST(request: Request) { const tokenRecord = await db.query.apiTokens.findFirst({ where: and( eq(apiTokens.tokenHash, tokenHash), - isNull(apiTokens.revokedAt) + isNull(apiTokens.revokedAt), ), }); if (!tokenRecord) { return NextResponse.json( { error: "Token inválido ou revogado" }, - { status: 401 } + { status: 401 }, ); } @@ -77,7 +77,7 @@ export async function POST(request: Request) { if (!checkRateLimit(tokenRecord.userId)) { return NextResponse.json( { error: "Limite de requisições excedido", retryAfter: 60 }, - { status: 429 } + { status: 429 }, ); } @@ -92,22 +92,21 @@ export async function POST(request: Request) { userId: tokenRecord.userId, sourceApp: data.sourceApp, sourceAppName: data.sourceAppName, - deviceId: data.deviceId, originalTitle: data.originalTitle, originalText: data.originalText, notificationTimestamp: data.notificationTimestamp, parsedName: data.parsedName, parsedAmount: data.parsedAmount?.toString(), - parsedDate: data.parsedDate, parsedTransactionType: data.parsedTransactionType, status: "pending", }) .returning({ id: inboxItems.id }); // Atualizar último uso do token - const clientIp = request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() - || request.headers.get("x-real-ip") - || null; + const clientIp = + request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || + request.headers.get("x-real-ip") || + null; await db .update(apiTokens) @@ -123,20 +122,20 @@ export async function POST(request: Request) { clientId: data.clientId, message: "Notificação recebida", }, - { status: 201 } + { status: 201 }, ); } catch (error) { if (error instanceof z.ZodError) { return NextResponse.json( { error: error.issues[0]?.message ?? "Dados inválidos" }, - { status: 400 } + { status: 400 }, ); } console.error("[API] Error creating inbox item:", error); return NextResponse.json( { error: "Erro ao processar notificação" }, - { status: 500 } + { status: 500 }, ); } } diff --git a/components/caixa-de-entrada/inbox-card.tsx b/components/caixa-de-entrada/inbox-card.tsx deleted file mode 100644 index 8493a88..0000000 --- a/components/caixa-de-entrada/inbox-card.tsx +++ /dev/null @@ -1,122 +0,0 @@ -"use client"; - -import { Badge } from "@/components/ui/badge"; -import { Button } from "@/components/ui/button"; -import { Card, CardContent, CardHeader } from "@/components/ui/card"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; -import { - RiCheckLine, - RiDeleteBinLine, - RiEyeLine, - RiMoreLine, - RiSmartphoneLine, -} from "@remixicon/react"; -import { formatDistanceToNow } from "date-fns"; -import { ptBR } from "date-fns/locale"; -import type { InboxItem } from "./types"; - -interface InboxCardProps { - item: InboxItem; - onProcess: (item: InboxItem) => void; - onDiscard: (item: InboxItem) => void; - onViewDetails: (item: InboxItem) => void; -} - -export function InboxCard({ - item, - onProcess, - onDiscard, - onViewDetails, -}: InboxCardProps) { - const formattedAmount = item.parsedAmount - ? new Intl.NumberFormat("pt-BR", { - style: "currency", - currency: "BRL", - }).format(parseFloat(item.parsedAmount)) - : null; - - const timeAgo = formatDistanceToNow(new Date(item.notificationTimestamp), { - addSuffix: true, - locale: ptBR, - }); - - return ( - - -
- - - {item.sourceAppName || item.sourceApp} - -
-
- {formattedAmount && ( - - {formattedAmount} - - )} - - - - - - onViewDetails(item)}> - - Ver detalhes - - onProcess(item)}> - - Processar - - onDiscard(item)} - className="text-destructive" - > - - Descartar - - - -
-
- - -
- {item.originalTitle && ( -

{item.originalTitle}

- )} -

- {item.originalText} -

-
- -
- {timeAgo} -
- -
- - -
-
-
- ); -} diff --git a/components/lancamentos/dialogs/lancamento-details-dialog.tsx b/components/lancamentos/dialogs/lancamento-details-dialog.tsx index ee13ae7..36b917f 100644 --- a/components/lancamentos/dialogs/lancamento-details-dialog.tsx +++ b/components/lancamentos/dialogs/lancamento-details-dialog.tsx @@ -2,12 +2,7 @@ import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; -import { - Card, - CardContent, - CardDescription, - CardHeader, -} from "@/components/ui/card"; +import { CardContent, CardDescription, CardHeader } from "@/components/ui/card"; import { Dialog, DialogClose, @@ -16,8 +11,6 @@ import { DialogTitle, } from "@/components/ui/dialog"; import { Separator } from "@/components/ui/separator"; -import { getPaymentMethodIcon } from "@/lib/utils/icons"; -import { parseLocalDateString } from "@/lib/utils/date"; import { currencyFormatter, formatCondition, @@ -25,6 +18,8 @@ import { formatPeriod, getTransactionBadgeVariant, } from "@/lib/lancamentos/formatting-helpers"; +import { parseLocalDateString } from "@/lib/utils/date"; +import { getPaymentMethodIcon } from "@/lib/utils/icons"; import { InstallmentTimeline } from "../shared/installment-timeline"; import type { LancamentoItem } from "../types"; @@ -59,7 +54,7 @@ export function LancamentoDetailsDialog({ return ( - +
@@ -112,7 +107,7 @@ export function LancamentoDetailsDialog({ variant={getTransactionBadgeVariant( lancamento.categoriaName === "Saldo inicial" ? "Saldo inicial" - : lancamento.transactionType + : lancamento.transactionType, )} > {lancamento.categoriaName === "Saldo inicial" @@ -148,7 +143,9 @@ export function LancamentoDetailsDialog({ {isInstallment && (
  • - +
  • ); diff --git a/components/pre-lancamentos/inbox-card.tsx b/components/pre-lancamentos/inbox-card.tsx new file mode 100644 index 0000000..5fbdbc4 --- /dev/null +++ b/components/pre-lancamentos/inbox-card.tsx @@ -0,0 +1,153 @@ +"use client"; + +import MoneyValues from "@/components/money-values"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardAction, + CardContent, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { cn } from "@/lib/utils/ui"; +import { + RiCheckLine, + RiDeleteBinLine, + RiEyeLine, + RiMoreLine, +} from "@remixicon/react"; +import { formatDistanceToNow } from "date-fns"; +import { ptBR } from "date-fns/locale"; +import type { InboxItem } from "./types"; + +interface InboxCardProps { + item: InboxItem; + onProcess: (item: InboxItem) => void; + onDiscard: (item: InboxItem) => void; + onViewDetails: (item: InboxItem) => void; +} + +export function InboxCard({ + item, + onProcess, + onDiscard, + onViewDetails, +}: InboxCardProps) { + const amount = item.parsedAmount ? parseFloat(item.parsedAmount) : null; + const isReceita = item.parsedTransactionType === "Receita"; + + // O timestamp vem do app Android em horário local mas salvo como UTC + // Precisamos interpretar o valor UTC como se fosse horário de Brasília + const rawDate = new Date(item.notificationTimestamp); + + // Ajusta adicionando o offset de Brasília (3 horas) para corrigir o cálculo do "há X tempo" + const BRASILIA_OFFSET_MS = 3 * 60 * 60 * 1000; + const notificationDate = new Date(rawDate.getTime() + BRASILIA_OFFSET_MS); + + const timeAgo = formatDistanceToNow(notificationDate, { + addSuffix: true, + locale: ptBR, + }); + + // Para exibição, usa UTC pois o valor já representa horário de Brasília + const formattedTime = new Intl.DateTimeFormat("pt-BR", { + day: "2-digit", + month: "short", + hour: "2-digit", + minute: "2-digit", + timeZone: "UTC", + }).format(rawDate); + + return ( + + {/* Header com app e valor */} + +
    + + {item.sourceAppName || item.sourceApp} + {" "} + + {timeAgo} + + + {amount !== null && ( + + )} +
    + + + + + + + + onViewDetails(item)}> + + Ver detalhes + + onProcess(item)}> + + Processar + + onDiscard(item)} + className="text-destructive" + > + + Descartar + + + + +
    + + {/* Conteúdo da notificação */} + + {item.originalTitle && ( +

    {item.originalTitle}

    + )} +

    + {item.originalText} +

    +
    + + {/* Botões de ação */} + + + + +
    + ); +} diff --git a/components/caixa-de-entrada/inbox-details-dialog.tsx b/components/pre-lancamentos/inbox-details-dialog.tsx similarity index 63% rename from components/caixa-de-entrada/inbox-details-dialog.tsx rename to components/pre-lancamentos/inbox-details-dialog.tsx index a9eaf6a..52a73a1 100644 --- a/components/caixa-de-entrada/inbox-details-dialog.tsx +++ b/components/pre-lancamentos/inbox-details-dialog.tsx @@ -1,5 +1,7 @@ "use client"; +import MoneyValues from "@/components/money-values"; +import { TypeBadge } from "@/components/type-badge"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { @@ -11,6 +13,7 @@ import { DialogTitle, } from "@/components/ui/dialog"; import { Separator } from "@/components/ui/separator"; +import { cn } from "@/lib/utils/ui"; import { format } from "date-fns"; import { ptBR } from "date-fns/locale"; import type { InboxItem } from "./types"; @@ -28,12 +31,8 @@ export function InboxDetailsDialog({ }: InboxDetailsDialogProps) { if (!item) return null; - const formattedAmount = item.parsedAmount - ? new Intl.NumberFormat("pt-BR", { - style: "currency", - currency: "BRL", - }).format(parseFloat(item.parsedAmount)) - : "Não extraído"; + const amount = item.parsedAmount ? parseFloat(item.parsedAmount) : null; + const isReceita = item.parsedTransactionType === "Receita"; return ( @@ -45,10 +44,11 @@ export function InboxDetailsDialog({
    {/* Dados da fonte */}
    -

    - Fonte -

    +
    + ID + {item.id} +
    App {item.sourceAppName || item.sourceApp} @@ -57,12 +57,6 @@ export function InboxDetailsDialog({ Package {item.sourceApp}
    - {item.deviceId && ( -
    - Dispositivo - {item.deviceId} -
    - )}
    @@ -70,58 +64,51 @@ export function InboxDetailsDialog({ {/* Texto original */}
    -

    +

    Notificação Original

    + {item.originalTitle && (

    {item.originalTitle}

    )}

    {item.originalText}

    -

    - Recebida em{" "} - {format(new Date(item.notificationTimestamp), "PPpp", { - locale: ptBR, - })} -

    {/* Dados parseados */}
    -

    - Dados Extraídos -

    Estabelecimento {item.parsedName || "Não extraído"}
    -
    +
    Valor - - {formattedAmount} - + {amount !== null ? ( + + ) : ( + Não extraído + )}
    - {item.parsedDate && ( -
    - Data - - {format(new Date(item.parsedDate), "dd/MM/yyyy", { - locale: ptBR, - })} - -
    - )} -
    +
    Tipo - {item.parsedTransactionType || "Não identificado"} + {item.parsedTransactionType ? ( + + ) : ( + + Não identificado + + )}
    @@ -130,14 +117,7 @@ export function InboxDetailsDialog({ {/* Metadados */}
    -

    - Metadados -

    -
    - ID - {item.id} -
    Status {item.status} @@ -154,7 +134,9 @@ export function InboxDetailsDialog({ - + diff --git a/components/caixa-de-entrada/inbox-page.tsx b/components/pre-lancamentos/inbox-page.tsx similarity index 94% rename from components/caixa-de-entrada/inbox-page.tsx rename to components/pre-lancamentos/inbox-page.tsx index 04e8945..4e64b89 100644 --- a/components/caixa-de-entrada/inbox-page.tsx +++ b/components/pre-lancamentos/inbox-page.tsx @@ -3,7 +3,7 @@ import { discardInboxItemAction, markInboxAsProcessedAction, -} from "@/app/(dashboard)/caixa-de-entrada/actions"; +} from "@/app/(dashboard)/pre-lancamentos/actions"; import { ConfirmActionDialog } from "@/components/confirm-action-dialog"; import { EmptyState } from "@/components/empty-state"; import { LancamentoDialog } from "@/components/lancamentos/dialogs/lancamento-dialog/lancamento-dialog"; @@ -122,17 +122,16 @@ export function InboxPage({ }, [itemToProcess]); // Prepare default values from inbox item - // Use parsedDate if available, otherwise fall back to notificationTimestamp - const getDateString = (date: Date | string | null | undefined): string | null => { + const getDateString = ( + date: Date | string | null | undefined, + ): string | null => { if (!date) return null; if (typeof date === "string") return date.slice(0, 10); return date.toISOString().slice(0, 10); }; const defaultPurchaseDate = - getDateString(itemToProcess?.parsedDate) ?? - getDateString(itemToProcess?.notificationTimestamp) ?? - null; + getDateString(itemToProcess?.notificationTimestamp) ?? null; const defaultName = itemToProcess?.parsedName ?? null; @@ -150,7 +149,7 @@ export function InboxPage({ } - title="Caixa de entrada vazia" + title="Nenhum pré-lançamento" description="As notificações capturadas pelo app OpenSheets Companion aparecerão aqui para você processar." /> diff --git a/components/caixa-de-entrada/types.ts b/components/pre-lancamentos/types.ts similarity index 86% rename from components/caixa-de-entrada/types.ts rename to components/pre-lancamentos/types.ts index 395cebf..4e65ff2 100644 --- a/components/caixa-de-entrada/types.ts +++ b/components/pre-lancamentos/types.ts @@ -1,5 +1,5 @@ /** - * Types for Caixa de Entrada (Inbox) feature + * Types for Pré-Lançamentos feature */ import type { SelectOption as LancamentoSelectOption } from "@/components/lancamentos/types"; @@ -8,19 +8,16 @@ export interface InboxItem { id: string; sourceApp: string; sourceAppName: string | null; - deviceId: string | null; originalTitle: string | null; originalText: string; notificationTimestamp: Date; parsedName: string | null; parsedAmount: string | null; - parsedDate: Date | null; parsedTransactionType: string | null; status: string; lancamentoId: string | null; processedAt: Date | null; discardedAt: Date | null; - discardReason: string | null; createdAt: Date; updatedAt: Date; } @@ -41,7 +38,6 @@ export interface ProcessInboxInput { export interface DiscardInboxInput { inboxItemId: string; - reason?: string; } // Re-export the lancamentos SelectOption for use in inbox components diff --git a/components/sidebar/app-sidebar.tsx b/components/sidebar/app-sidebar.tsx index 4075031..853da2a 100644 --- a/components/sidebar/app-sidebar.tsx +++ b/components/sidebar/app-sidebar.tsx @@ -14,7 +14,7 @@ import { useSidebar, } from "@/components/ui/sidebar"; import * as React from "react"; -import { createSidebarNavData, type PagadorLike } from "./nav-link"; +import { createSidebarNavData, type PagadorLike, type SidebarNavOptions } from "./nav-link"; type AppUser = { id: string; @@ -27,12 +27,14 @@ interface AppSidebarProps extends React.ComponentProps { user: AppUser; pagadorAvatarUrl: string | null; pagadores: PagadorLike[]; + preLancamentosCount?: number; } export function AppSidebar({ user, pagadorAvatarUrl, pagadores, + preLancamentosCount = 0, ...props }: AppSidebarProps) { if (!user) { @@ -40,8 +42,8 @@ export function AppSidebar({ } const navigation = React.useMemo( - () => createSidebarNavData(pagadores), - [pagadores] + () => createSidebarNavData({ pagadores, preLancamentosCount }), + [pagadores, preLancamentosCount] ); return ( diff --git a/components/sidebar/nav-link.tsx b/components/sidebar/nav-link.tsx index 45aec64..e0422a4 100644 --- a/components/sidebar/nav-link.tsx +++ b/components/sidebar/nav-link.tsx @@ -25,6 +25,7 @@ export type SidebarSubItem = { isShared?: boolean; key?: string; icon?: RemixiconComponentType; + badge?: number; }; export type SidebarItem = { @@ -56,7 +57,13 @@ export interface PagadorLike { canEdit?: boolean; } -export function createSidebarNavData(pagadores: PagadorLike[]): SidebarNavData { +export interface SidebarNavOptions { + pagadores: PagadorLike[]; + preLancamentosCount?: number; +} + +export function createSidebarNavData(options: SidebarNavOptions): SidebarNavData { + const { pagadores, preLancamentosCount = 0 } = options; const pagadorItems = pagadores .map((pagador) => ({ title: pagador.name?.trim().length @@ -88,15 +95,19 @@ export function createSidebarNavData(pagadores: PagadorLike[]): SidebarNavData { { title: "Gestão Financeira", items: [ - { - title: "Caixa de Entrada", - url: "/caixa-de-entrada", - icon: RiInboxLine, - }, { title: "Lançamentos", url: "/lancamentos", icon: RiArrowLeftRightLine, + items: [ + { + title: "Pré-Lançamentos", + url: "/pre-lancamentos", + key: "pre-lancamentos", + icon: RiInboxLine, + badge: preLancamentosCount > 0 ? preLancamentosCount : undefined, + }, + ], }, { title: "Calendário", diff --git a/components/sidebar/nav-main.tsx b/components/sidebar/nav-main.tsx index 7e26e25..75cea7f 100644 --- a/components/sidebar/nav-main.tsx +++ b/components/sidebar/nav-main.tsx @@ -1,6 +1,7 @@ "use client"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { Badge } from "@/components/ui/badge"; import { Collapsible, CollapsibleContent, @@ -40,6 +41,7 @@ type NavItem = { isShared?: boolean; key?: string; icon?: RemixiconComponentType; + badge?: number; }[]; }; @@ -181,6 +183,11 @@ export function NavMain({ sections }: { sections: NavSection[] }) { ) : null} {subItem.title} + {subItem.badge ? ( + + {subItem.badge} + + ) : null} {subItem.isShared ? ( ) : null} diff --git a/db/schema.ts b/db/schema.ts index 3054cb2..1757c35 100644 --- a/db/schema.ts +++ b/db/schema.ts @@ -464,7 +464,6 @@ export const inboxItems = pgTable( // Informações da fonte sourceApp: text("source_app").notNull(), // Ex: "com.nu.production" sourceAppName: text("source_app_name"), // Ex: "Nubank" - deviceId: text("device_id"), // Identificador do dispositivo // Dados originais da notificação originalTitle: text("original_title"), @@ -477,7 +476,6 @@ export const inboxItems = pgTable( // Dados parseados (editáveis pelo usuário antes de processar) parsedName: text("parsed_name"), // Nome do estabelecimento parsedAmount: numeric("parsed_amount", { precision: 12, scale: 2 }), - parsedDate: date("parsed_date", { mode: "date" }), parsedTransactionType: text("parsed_transaction_type"), // Despesa, Receita // Status de processamento @@ -489,9 +487,14 @@ export const inboxItems = pgTable( }), // Metadados de processamento - processedAt: timestamp("processed_at", { mode: "date", withTimezone: true }), - discardedAt: timestamp("discarded_at", { mode: "date", withTimezone: true }), - discardReason: text("discard_reason"), + processedAt: timestamp("processed_at", { + mode: "date", + withTimezone: true, + }), + discardedAt: timestamp("discarded_at", { + mode: "date", + withTimezone: true, + }), // Timestamps createdAt: timestamp("created_at", { mode: "date", withTimezone: true }) diff --git a/drizzle/0011_remove_unused_inbox_columns.sql b/drizzle/0011_remove_unused_inbox_columns.sql new file mode 100644 index 0000000..7a994cc --- /dev/null +++ b/drizzle/0011_remove_unused_inbox_columns.sql @@ -0,0 +1,39 @@ +CREATE TABLE "api_tokens" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" text NOT NULL, + "name" text NOT NULL, + "token_hash" text NOT NULL, + "token_prefix" text NOT NULL, + "last_used_at" timestamp with time zone, + "last_used_ip" text, + "expires_at" timestamp with time zone, + "revoked_at" timestamp with time zone, + "created_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +CREATE TABLE "inbox_items" ( + "id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL, + "user_id" text NOT NULL, + "source_app" text NOT NULL, + "source_app_name" text, + "original_title" text, + "original_text" text NOT NULL, + "notification_timestamp" timestamp with time zone NOT NULL, + "parsed_name" text, + "parsed_amount" numeric(12, 2), + "parsed_transaction_type" text, + "status" text DEFAULT 'pending' NOT NULL, + "lancamento_id" uuid, + "processed_at" timestamp with time zone, + "discarded_at" timestamp with time zone, + "created_at" timestamp with time zone DEFAULT now() NOT NULL, + "updated_at" timestamp with time zone DEFAULT now() NOT NULL +); +--> statement-breakpoint +ALTER TABLE "api_tokens" ADD CONSTRAINT "api_tokens_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "inbox_items" ADD CONSTRAINT "inbox_items_user_id_user_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."user"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint +ALTER TABLE "inbox_items" ADD CONSTRAINT "inbox_items_lancamento_id_lancamentos_id_fk" FOREIGN KEY ("lancamento_id") REFERENCES "public"."lancamentos"("id") ON DELETE set null ON UPDATE no action;--> statement-breakpoint +CREATE INDEX "api_tokens_user_id_idx" ON "api_tokens" USING btree ("user_id");--> statement-breakpoint +CREATE UNIQUE INDEX "api_tokens_token_hash_idx" ON "api_tokens" USING btree ("token_hash");--> statement-breakpoint +CREATE INDEX "inbox_items_user_id_status_idx" ON "inbox_items" USING btree ("user_id","status");--> statement-breakpoint +CREATE INDEX "inbox_items_user_id_created_at_idx" ON "inbox_items" USING btree ("user_id","created_at"); \ No newline at end of file diff --git a/drizzle/meta/0011_snapshot.json b/drizzle/meta/0011_snapshot.json new file mode 100644 index 0000000..5329473 --- /dev/null +++ b/drizzle/meta/0011_snapshot.json @@ -0,0 +1,2261 @@ +{ + "id": "f072fd39-3525-4d1f-a396-57aff100b211", + "prevId": "f6c9f7bb-f966-4058-a973-d9db99e509e2", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "accountId": { + "name": "accountId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "providerId": { + "name": "providerId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "accessToken": { + "name": "accessToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "refreshToken": { + "name": "refreshToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "idToken": { + "name": "idToken", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "accessTokenExpiresAt": { + "name": "accessTokenExpiresAt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "refreshTokenExpiresAt": { + "name": "refreshTokenExpiresAt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "account_userId_user_id_fk": { + "name": "account_userId_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.anotacoes": { + "name": "anotacoes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "titulo": { + "name": "titulo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "descricao": { + "name": "descricao", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "tipo": { + "name": "tipo", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'nota'" + }, + "tasks": { + "name": "tasks", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "arquivada": { + "name": "arquivada", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "anotacoes_user_id_user_id_fk": { + "name": "anotacoes_user_id_user_id_fk", + "tableFrom": "anotacoes", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.api_tokens": { + "name": "api_tokens", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_hash": { + "name": "token_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "token_prefix": { + "name": "token_prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "last_used_at": { + "name": "last_used_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "last_used_ip": { + "name": "last_used_ip", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "revoked_at": { + "name": "revoked_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "api_tokens_user_id_idx": { + "name": "api_tokens_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "api_tokens_token_hash_idx": { + "name": "api_tokens_token_hash_idx", + "columns": [ + { + "expression": "token_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "api_tokens_user_id_user_id_fk": { + "name": "api_tokens_user_id_user_id_fk", + "tableFrom": "api_tokens", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.cartoes": { + "name": "cartoes", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "nome": { + "name": "nome", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dt_fechamento": { + "name": "dt_fechamento", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "dt_vencimento": { + "name": "dt_vencimento", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "anotacao": { + "name": "anotacao", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "limite": { + "name": "limite", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": false + }, + "bandeira": { + "name": "bandeira", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "conta_id": { + "name": "conta_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "cartoes_user_id_status_idx": { + "name": "cartoes_user_id_status_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "cartoes_user_id_user_id_fk": { + "name": "cartoes_user_id_user_id_fk", + "tableFrom": "cartoes", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "cartoes_conta_id_contas_id_fk": { + "name": "cartoes_conta_id_contas_id_fk", + "tableFrom": "cartoes", + "tableTo": "contas", + "columnsFrom": [ + "conta_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.categorias": { + "name": "categorias", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "nome": { + "name": "nome", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tipo": { + "name": "tipo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "icone": { + "name": "icone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "categorias_user_id_type_idx": { + "name": "categorias_user_id_type_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "tipo", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "categorias_user_id_user_id_fk": { + "name": "categorias_user_id_user_id_fk", + "tableFrom": "categorias", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.contas": { + "name": "contas", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "nome": { + "name": "nome", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tipo_conta": { + "name": "tipo_conta", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "anotacao": { + "name": "anotacao", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "logo": { + "name": "logo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "saldo_inicial": { + "name": "saldo_inicial", + "type": "numeric(12, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "excluir_do_saldo": { + "name": "excluir_do_saldo", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "excluir_saldo_inicial_receitas": { + "name": "excluir_saldo_inicial_receitas", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "contas_user_id_status_idx": { + "name": "contas_user_id_status_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "contas_user_id_user_id_fk": { + "name": "contas_user_id_user_id_fk", + "tableFrom": "contas", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.faturas": { + "name": "faturas", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "status_pagamento": { + "name": "status_pagamento", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "periodo": { + "name": "periodo", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cartao_id": { + "name": "cartao_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "faturas_user_id_period_idx": { + "name": "faturas_user_id_period_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "periodo", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "faturas_cartao_id_period_idx": { + "name": "faturas_cartao_id_period_idx", + "columns": [ + { + "expression": "cartao_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "periodo", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "faturas_user_id_user_id_fk": { + "name": "faturas_user_id_user_id_fk", + "tableFrom": "faturas", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "faturas_cartao_id_cartoes_id_fk": { + "name": "faturas_cartao_id_cartoes_id_fk", + "tableFrom": "faturas", + "tableTo": "cartoes", + "columnsFrom": [ + "cartao_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.inbox_items": { + "name": "inbox_items", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_app": { + "name": "source_app", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "source_app_name": { + "name": "source_app_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "original_title": { + "name": "original_title", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "original_text": { + "name": "original_text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "notification_timestamp": { + "name": "notification_timestamp", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "parsed_name": { + "name": "parsed_name", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "parsed_amount": { + "name": "parsed_amount", + "type": "numeric(12, 2)", + "primaryKey": false, + "notNull": false + }, + "parsed_transaction_type": { + "name": "parsed_transaction_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "lancamento_id": { + "name": "lancamento_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "processed_at": { + "name": "processed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "discarded_at": { + "name": "discarded_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "inbox_items_user_id_status_idx": { + "name": "inbox_items_user_id_status_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "inbox_items_user_id_created_at_idx": { + "name": "inbox_items_user_id_created_at_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "inbox_items_user_id_user_id_fk": { + "name": "inbox_items_user_id_user_id_fk", + "tableFrom": "inbox_items", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "inbox_items_lancamento_id_lancamentos_id_fk": { + "name": "inbox_items_lancamento_id_lancamentos_id_fk", + "tableFrom": "inbox_items", + "tableTo": "lancamentos", + "columnsFrom": [ + "lancamento_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.installment_anticipations": { + "name": "installment_anticipations", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "series_id": { + "name": "series_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "periodo_antecipacao": { + "name": "periodo_antecipacao", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data_antecipacao": { + "name": "data_antecipacao", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "parcelas_antecipadas": { + "name": "parcelas_antecipadas", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "valor_total": { + "name": "valor_total", + "type": "numeric(12, 2)", + "primaryKey": false, + "notNull": true + }, + "qtde_parcelas": { + "name": "qtde_parcelas", + "type": "smallint", + "primaryKey": false, + "notNull": true + }, + "desconto": { + "name": "desconto", + "type": "numeric(12, 2)", + "primaryKey": false, + "notNull": true, + "default": "'0'" + }, + "lancamento_id": { + "name": "lancamento_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "pagador_id": { + "name": "pagador_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "categoria_id": { + "name": "categoria_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "anotacao": { + "name": "anotacao", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "installment_anticipations_series_id_idx": { + "name": "installment_anticipations_series_id_idx", + "columns": [ + { + "expression": "series_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "installment_anticipations_user_id_idx": { + "name": "installment_anticipations_user_id_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "installment_anticipations_lancamento_id_lancamentos_id_fk": { + "name": "installment_anticipations_lancamento_id_lancamentos_id_fk", + "tableFrom": "installment_anticipations", + "tableTo": "lancamentos", + "columnsFrom": [ + "lancamento_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "installment_anticipations_pagador_id_pagadores_id_fk": { + "name": "installment_anticipations_pagador_id_pagadores_id_fk", + "tableFrom": "installment_anticipations", + "tableTo": "pagadores", + "columnsFrom": [ + "pagador_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "installment_anticipations_categoria_id_categorias_id_fk": { + "name": "installment_anticipations_categoria_id_categorias_id_fk", + "tableFrom": "installment_anticipations", + "tableTo": "categorias", + "columnsFrom": [ + "categoria_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "installment_anticipations_user_id_user_id_fk": { + "name": "installment_anticipations_user_id_user_id_fk", + "tableFrom": "installment_anticipations", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.lancamentos": { + "name": "lancamentos", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "condicao": { + "name": "condicao", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "nome": { + "name": "nome", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "forma_pagamento": { + "name": "forma_pagamento", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "anotacao": { + "name": "anotacao", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "valor": { + "name": "valor", + "type": "numeric(12, 2)", + "primaryKey": false, + "notNull": true + }, + "data_compra": { + "name": "data_compra", + "type": "date", + "primaryKey": false, + "notNull": true + }, + "tipo_transacao": { + "name": "tipo_transacao", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "qtde_parcela": { + "name": "qtde_parcela", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "periodo": { + "name": "periodo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parcela_atual": { + "name": "parcela_atual", + "type": "smallint", + "primaryKey": false, + "notNull": false + }, + "qtde_recorrencia": { + "name": "qtde_recorrencia", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "data_vencimento": { + "name": "data_vencimento", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "dt_pagamento_boleto": { + "name": "dt_pagamento_boleto", + "type": "date", + "primaryKey": false, + "notNull": false + }, + "realizado": { + "name": "realizado", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "dividido": { + "name": "dividido", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "antecipado": { + "name": "antecipado", + "type": "boolean", + "primaryKey": false, + "notNull": false, + "default": false + }, + "antecipacao_id": { + "name": "antecipacao_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "cartao_id": { + "name": "cartao_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "conta_id": { + "name": "conta_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "categoria_id": { + "name": "categoria_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "pagador_id": { + "name": "pagador_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "series_id": { + "name": "series_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "transfer_id": { + "name": "transfer_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "lancamentos_user_id_period_idx": { + "name": "lancamentos_user_id_period_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "periodo", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "lancamentos_user_id_purchase_date_idx": { + "name": "lancamentos_user_id_purchase_date_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "data_compra", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "lancamentos_series_id_idx": { + "name": "lancamentos_series_id_idx", + "columns": [ + { + "expression": "series_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "lancamentos_transfer_id_idx": { + "name": "lancamentos_transfer_id_idx", + "columns": [ + { + "expression": "transfer_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "lancamentos_user_id_condition_idx": { + "name": "lancamentos_user_id_condition_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "condicao", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "lancamentos_cartao_id_period_idx": { + "name": "lancamentos_cartao_id_period_idx", + "columns": [ + { + "expression": "cartao_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "periodo", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "lancamentos_antecipacao_id_installment_anticipations_id_fk": { + "name": "lancamentos_antecipacao_id_installment_anticipations_id_fk", + "tableFrom": "lancamentos", + "tableTo": "installment_anticipations", + "columnsFrom": [ + "antecipacao_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "set null", + "onUpdate": "no action" + }, + "lancamentos_user_id_user_id_fk": { + "name": "lancamentos_user_id_user_id_fk", + "tableFrom": "lancamentos", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "lancamentos_cartao_id_cartoes_id_fk": { + "name": "lancamentos_cartao_id_cartoes_id_fk", + "tableFrom": "lancamentos", + "tableTo": "cartoes", + "columnsFrom": [ + "cartao_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "lancamentos_conta_id_contas_id_fk": { + "name": "lancamentos_conta_id_contas_id_fk", + "tableFrom": "lancamentos", + "tableTo": "contas", + "columnsFrom": [ + "conta_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "lancamentos_categoria_id_categorias_id_fk": { + "name": "lancamentos_categoria_id_categorias_id_fk", + "tableFrom": "lancamentos", + "tableTo": "categorias", + "columnsFrom": [ + "categoria_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + }, + "lancamentos_pagador_id_pagadores_id_fk": { + "name": "lancamentos_pagador_id_pagadores_id_fk", + "tableFrom": "lancamentos", + "tableTo": "pagadores", + "columnsFrom": [ + "pagador_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.orcamentos": { + "name": "orcamentos", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "valor": { + "name": "valor", + "type": "numeric(10, 2)", + "primaryKey": false, + "notNull": true + }, + "periodo": { + "name": "periodo", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "categoria_id": { + "name": "categoria_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "orcamentos_user_id_period_idx": { + "name": "orcamentos_user_id_period_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "periodo", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "orcamentos_user_id_user_id_fk": { + "name": "orcamentos_user_id_user_id_fk", + "tableFrom": "orcamentos", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "orcamentos_categoria_id_categorias_id_fk": { + "name": "orcamentos_categoria_id_categorias_id_fk", + "tableFrom": "orcamentos", + "tableTo": "categorias", + "columnsFrom": [ + "categoria_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "cascade" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pagador_shares": { + "name": "pagador_shares", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "pagador_id": { + "name": "pagador_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "shared_with_user_id": { + "name": "shared_with_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "permission": { + "name": "permission", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'read'" + }, + "created_by_user_id": { + "name": "created_by_user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "pagador_shares_unique": { + "name": "pagador_shares_unique", + "columns": [ + { + "expression": "pagador_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "shared_with_user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pagador_shares_pagador_id_pagadores_id_fk": { + "name": "pagador_shares_pagador_id_pagadores_id_fk", + "tableFrom": "pagador_shares", + "tableTo": "pagadores", + "columnsFrom": [ + "pagador_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pagador_shares_shared_with_user_id_user_id_fk": { + "name": "pagador_shares_shared_with_user_id_user_id_fk", + "tableFrom": "pagador_shares", + "tableTo": "user", + "columnsFrom": [ + "shared_with_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "pagador_shares_created_by_user_id_user_id_fk": { + "name": "pagador_shares_created_by_user_id_user_id_fk", + "tableFrom": "pagador_shares", + "tableTo": "user", + "columnsFrom": [ + "created_by_user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.pagadores": { + "name": "pagadores", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "nome": { + "name": "nome", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "avatar_url": { + "name": "avatar_url", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "anotacao": { + "name": "anotacao", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "role": { + "name": "role", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "is_auto_send": { + "name": "is_auto_send", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "share_code": { + "name": "share_code", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "substr(encode(gen_random_bytes(24), 'base64'), 1, 24)" + }, + "last_mail": { + "name": "last_mail", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "pagadores_share_code_key": { + "name": "pagadores_share_code_key", + "columns": [ + { + "expression": "share_code", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pagadores_user_id_status_idx": { + "name": "pagadores_user_id_status_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "pagadores_user_id_role_idx": { + "name": "pagadores_user_id_role_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "role", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "pagadores_user_id_user_id_fk": { + "name": "pagadores_user_id_user_id_fk", + "tableFrom": "pagadores", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.saved_insights": { + "name": "saved_insights", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "period": { + "name": "period", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "model_id": { + "name": "model_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "data": { + "name": "data", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "saved_insights_user_period_idx": { + "name": "saved_insights_user_period_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "period", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "saved_insights_user_id_user_id_fk": { + "name": "saved_insights_user_id_user_id_fk", + "tableFrom": "saved_insights", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "ipAddress": { + "name": "ipAddress", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userAgent": { + "name": "userAgent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "userId": { + "name": "userId", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "session_userId_user_id_fk": { + "name": "session_userId_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": [ + "userId" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_token_unique": { + "name": "session_token_unique", + "nullsNotDistinct": false, + "columns": [ + "token" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "email": { + "name": "email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "emailVerified": { + "name": "emailVerified", + "type": "boolean", + "primaryKey": false, + "notNull": true + }, + "image": { + "name": "image", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_email_unique": { + "name": "user_email_unique", + "nullsNotDistinct": false, + "columns": [ + "email" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_preferences": { + "name": "user_preferences", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "disable_magnetlines": { + "name": "disable_magnetlines", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "dashboard_widgets": { + "name": "dashboard_widgets", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_preferences_user_id_user_id_fk": { + "name": "user_preferences_user_id_user_id_fk", + "tableFrom": "user_preferences", + "tableTo": "user", + "columnsFrom": [ + "user_id" + ], + "columnsTo": [ + "id" + ], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "user_preferences_user_id_unique": { + "name": "user_preferences_user_id_unique", + "nullsNotDistinct": false, + "columns": [ + "user_id" + ] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expiresAt": { + "name": "expiresAt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "createdAt": { + "name": "createdAt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updatedAt": { + "name": "updatedAt", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": {}, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} \ No newline at end of file diff --git a/drizzle/meta/_journal.json b/drizzle/meta/_journal.json index 021a88b..152f3a5 100644 --- a/drizzle/meta/_journal.json +++ b/drizzle/meta/_journal.json @@ -78,6 +78,13 @@ "when": 1769369834242, "tag": "0010_lame_psynapse", "breakpoints": true + }, + { + "idx": 11, + "version": "7", + "when": 1769447087678, + "tag": "0011_remove_unused_inbox_columns", + "breakpoints": true } ] } \ No newline at end of file diff --git a/lib/actions/helpers.ts b/lib/actions/helpers.ts index 6141ec3..c9b9b55 100644 --- a/lib/actions/helpers.ts +++ b/lib/actions/helpers.ts @@ -32,7 +32,7 @@ export const revalidateConfig = { pagadores: ["/pagadores"], anotacoes: ["/anotacoes", "/anotacoes/arquivadas"], lancamentos: ["/lancamentos", "/contas"], - inbox: ["/caixa-de-entrada", "/lancamentos", "/dashboard"], + inbox: ["/pre-lancamentos", "/lancamentos", "/dashboard"], } as const; /** diff --git a/lib/schemas/inbox.ts b/lib/schemas/inbox.ts index 74d7425..87b5635 100644 --- a/lib/schemas/inbox.ts +++ b/lib/schemas/inbox.ts @@ -7,13 +7,11 @@ import { z } from "zod"; export const inboxItemSchema = z.object({ sourceApp: z.string().min(1, "sourceApp é obrigatório"), sourceAppName: z.string().optional(), - deviceId: z.string().optional(), originalTitle: z.string().optional(), originalText: z.string().min(1, "originalText é obrigatório"), notificationTimestamp: z.string().transform((val) => new Date(val)), parsedName: z.string().optional(), parsedAmount: z.coerce.number().optional(), - parsedDate: z.string().optional().transform((val) => (val ? new Date(val) : undefined)), parsedTransactionType: z.enum(["Despesa", "Receita"]).optional(), clientId: z.string().optional(), // ID local do app para rastreamento });