diff --git a/app/(dashboard)/categorias/loading.tsx b/app/(dashboard)/categorias/loading.tsx index 5ecb500..044b0b1 100644 --- a/app/(dashboard)/categorias/loading.tsx +++ b/app/(dashboard)/categorias/loading.tsx @@ -1,3 +1,4 @@ +import { Card, CardContent } from "@/components/ui/card"; import { Skeleton } from "@/components/ui/skeleton"; export default function CategoriasLoading() { @@ -21,32 +22,40 @@ export default function CategoriasLoading() { ))} - {/* Grid de cards de categorias */} -
- {Array.from({ length: 8 }).map((_, i) => ( -
- {/* Ícone + Nome */} -
- -
- - + {/* Tabela de categorias */} + + +
+ {/* Header da tabela */} +
+ + +
+ +
+ + {/* Linhas da tabela */} + {Array.from({ length: 8 }).map((_, i) => ( +
+ + +
+
+ + + +
-
- - {/* Descrição */} - {i % 3 === 0 && ( - - )} - - {/* Botões de ação */} -
- - -
+ ))}
- ))} -
+
+
diff --git a/app/(dashboard)/orcamentos/layout.tsx b/app/(dashboard)/orcamentos/layout.tsx index b71baf1..2ea292c 100644 --- a/app/(dashboard)/orcamentos/layout.tsx +++ b/app/(dashboard)/orcamentos/layout.tsx @@ -1,4 +1,4 @@ -import { RiFundsLine } from "@remixicon/react"; +import { RiBarChart2Line } from "@remixicon/react"; import PageDescription from "@/components/page-description"; export const metadata = { @@ -13,7 +13,7 @@ export default function RootLayout({ return (
} + icon={} title="Orçamentos" subtitle="Gerencie seus orçamentos mensais por categorias. Acompanhe o progresso do seu orçamento e faça ajustes conforme necessário." /> diff --git a/app/(dashboard)/pre-lancamentos/actions.ts b/app/(dashboard)/pre-lancamentos/actions.ts index 927d386..39e7032 100644 --- a/app/(dashboard)/pre-lancamentos/actions.ts +++ b/app/(dashboard)/pre-lancamentos/actions.ts @@ -21,6 +21,14 @@ const bulkDiscardSchema = z.object({ inboxItemIds: z.array(z.string().uuid()).min(1, "Selecione ao menos um item"), }); +const deleteInboxSchema = z.object({ + inboxItemId: z.string().uuid("ID do item inválido"), +}); + +const bulkDeleteInboxSchema = z.object({ + status: z.enum(["processed", "discarded"]), +}); + function revalidateInbox() { revalidatePath("/pre-lancamentos"); revalidatePath("/lancamentos"); @@ -157,3 +165,78 @@ export async function bulkDiscardInboxItemsAction( return handleActionError(error); } } + +export async function deleteInboxItemAction( + input: z.infer, +): Promise { + try { + const user = await getUser(); + const data = deleteInboxSchema.parse(input); + + const [item] = await db + .select({ status: preLancamentos.status }) + .from(preLancamentos) + .where( + and( + eq(preLancamentos.id, data.inboxItemId), + eq(preLancamentos.userId, user.id), + ), + ) + .limit(1); + + if (!item) { + return { success: false, error: "Item não encontrado." }; + } + + if (item.status === "pending") { + return { + success: false, + error: "Não é possível excluir itens pendentes.", + }; + } + + await db + .delete(preLancamentos) + .where( + and( + eq(preLancamentos.id, data.inboxItemId), + eq(preLancamentos.userId, user.id), + ), + ); + + revalidateInbox(); + + return { success: true, message: "Item excluído." }; + } catch (error) { + return handleActionError(error); + } +} + +export async function bulkDeleteInboxItemsAction( + input: z.infer, +): Promise { + try { + const user = await getUser(); + const data = bulkDeleteInboxSchema.parse(input); + + const result = await db + .delete(preLancamentos) + .where( + and( + eq(preLancamentos.userId, user.id), + eq(preLancamentos.status, data.status), + ), + ) + .returning({ id: preLancamentos.id }); + + revalidateInbox(); + + const count = result.length; + return { + success: true, + message: `${count} item(s) excluído(s).`, + }; + } catch (error) { + return handleActionError(error); + } +} diff --git a/app/(dashboard)/pre-lancamentos/layout.tsx b/app/(dashboard)/pre-lancamentos/layout.tsx index 312beff..1d01634 100644 --- a/app/(dashboard)/pre-lancamentos/layout.tsx +++ b/app/(dashboard)/pre-lancamentos/layout.tsx @@ -1,4 +1,4 @@ -import { RiInboxLine } from "@remixicon/react"; +import { RiAtLine } from "@remixicon/react"; import PageDescription from "@/components/page-description"; export const metadata = { @@ -13,7 +13,7 @@ export default function RootLayout({ return (
} + icon={} title="Pré-Lançamentos" subtitle="Notificações capturadas pelo Companion" /> diff --git a/app/globals.css b/app/globals.css index 33211e4..0025c16 100644 --- a/app/globals.css +++ b/app/globals.css @@ -343,3 +343,34 @@ [data-slot="dialog-content"][data-state="closed"] { animation: dialog-out 0.15s ease-in; } + +/* Overdue blink: alternates two stacked labels with a smooth crossfade */ +@keyframes blink-in { + 0%, 40% { opacity: 1; } + 50%, 90% { opacity: 0; } + 100% { opacity: 1; } +} + +@keyframes blink-out { + 0%, 40% { opacity: 0; } + 50%, 90% { opacity: 1; } + 100% { opacity: 0; } +} + +.overdue-blink { + position: relative; + display: inline-flex; +} + +.overdue-blink-primary { + animation: blink-in 6s ease-in-out infinite; +} + +.overdue-blink-secondary { + position: absolute; + inset: 0; + display: inline-flex; + align-items: center; + justify-content: flex-end; + animation: blink-out 6s ease-in-out infinite; +} diff --git a/components/categorias/categories-page.tsx b/components/categorias/categories-page.tsx index d88f8ca..1b7ed3e 100644 --- a/components/categorias/categories-page.tsx +++ b/components/categorias/categories-page.tsx @@ -1,20 +1,41 @@ "use client"; -import { RiAddCircleLine } from "@remixicon/react"; +import { + RiAddCircleLine, + RiDeleteBin5Line, + RiExternalLinkLine, + RiPencilLine, +} from "@remixicon/react"; +import Link from "next/link"; import { useCallback, useMemo, useState } from "react"; import { toast } from "sonner"; import { deleteCategoryAction } from "@/app/(dashboard)/categorias/actions"; import { ConfirmActionDialog } from "@/components/confirm-action-dialog"; import { Button } from "@/components/ui/button"; +import { Card, CardContent } from "@/components/ui/card"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { CATEGORY_TYPE_LABEL, CATEGORY_TYPES, } from "@/lib/categorias/constants"; -import { CategoryCard } from "./category-card"; import { CategoryDialog } from "./category-dialog"; +import { CategoryIconBadge } from "./category-icon-badge"; import type { Category, CategoryType } from "./types"; +const CATEGORIAS_PROTEGIDAS = [ + "Transferência interna", + "Saldo inicial", + "Pagamentos", +]; + interface CategoriesPageProps { categories: Category[]; } @@ -129,17 +150,83 @@ export function CategoriesPage({ categories }: CategoriesPageProps) { {CATEGORY_TYPE_LABEL[type].toLowerCase()}.
) : ( -
- {categoriesByType[type].map((category, index) => ( - - ))} -
+ + + + + + + Nome + Ações + + + + {categoriesByType[type].map((category, index) => { + const isProtegida = CATEGORIAS_PROTEGIDAS.includes( + category.name, + ); + + return ( + + + + + + + {category.name} + + + + +
+ {!isProtegida && ( + + )} + {!isProtegida && ( + + )} +
+
+
+ ); + })} +
+
+
+
)} ))} diff --git a/components/dashboard/boletos-widget.tsx b/components/dashboard/boletos-widget.tsx index 0b205df..aea2a48 100644 --- a/components/dashboard/boletos-widget.tsx +++ b/components/dashboard/boletos-widget.tsx @@ -5,6 +5,7 @@ import { RiCheckboxCircleFill, RiCheckboxCircleLine, RiLoader4Line, + RiMoneyDollarCircleLine, } from "@remixicon/react"; import { useRouter } from "next/navigation"; import { useEffect, useMemo, useState, useTransition } from "react"; @@ -165,6 +166,12 @@ export function BoletosWidget({ boletos }: BoletosWidgetProps) {
) : ( <> - + Confirmar pagamento do boleto Confirme os dados para registrar o pagamento. Você poderá @@ -280,47 +294,59 @@ export function BoletosWidget({ boletos }: BoletosWidgetProps) { {selectedBoleto ? ( -
-
-
- -
-
-

- {selectedBoleto.name} -

+
+
+
+
+
+ +
+
+

+ Boleto +

+

+ {selectedBoleto.name} +

+
+
{selectedBoletoDueLabel ? ( -

- {selectedBoletoDueLabel} -

+
+

+ {selectedBoletoDueLabel} +

+
) : null}
-
-
- - Valor do boleto - +
+
+
+ + + Valor do Boleto + +
-
- - Status atual - - - - {selectedBoleto.isSettled ? "Pago" : "Pendente"} - - +
+
+ + + Status + +
+ + {selectedBoleto.isSettled ? "Pago" : "Pendente"} +
diff --git a/components/dashboard/dashboard-welcome.tsx b/components/dashboard/dashboard-welcome.tsx index dbc6184..95395ae 100644 --- a/components/dashboard/dashboard-welcome.tsx +++ b/components/dashboard/dashboard-welcome.tsx @@ -17,6 +17,7 @@ const formatCurrentDate = (date = new Date()) => { day: "numeric", month: "long", year: "numeric", + hour12: false, timeZone: "America/Sao_Paulo", }).format(date); @@ -68,7 +69,7 @@ export function DashboardWelcome({ />
-

+

{greeting}, {displayName}!

{formattedDate}

diff --git a/components/dashboard/invoices-widget.tsx b/components/dashboard/invoices-widget.tsx index 2c89663..cedfc72 100644 --- a/components/dashboard/invoices-widget.tsx +++ b/components/dashboard/invoices-widget.tsx @@ -5,6 +5,7 @@ import { RiCheckboxCircleLine, RiExternalLinkLine, RiLoader4Line, + RiMoneyDollarCircleLine, } from "@remixicon/react"; import Image from "next/image"; import Link from "next/link"; @@ -87,12 +88,14 @@ const parseDueDate = (period: string, dueDay: string) => { ) { return { label: `Vence dia ${dueDay}`, + date: null, }; } const date = new Date(Date.UTC(year, month - 1, dayNumber)); return { label: `Vence em ${DUE_DATE_FORMATTER.format(date)}`, + date, }; }; @@ -251,6 +254,8 @@ export function InvoicesWidget({ invoices }: InvoicesWidgetProps) { const dueInfo = parseDueDate(invoice.period, invoice.dueDay); const isPaid = invoice.paymentStatus === INVOICE_PAYMENT_STATUS.PAID; + const isOverdue = + !isPaid && dueInfo.date !== null && dueInfo.date < new Date(); const paymentInfo = formatPaymentDate(invoice.paidAt); return ( @@ -381,6 +386,15 @@ export function InvoicesWidget({ invoices }: InvoicesWidgetProps) { Pago + ) : isOverdue ? ( + + + Atrasado + + + Pagar + + ) : ( Pagar )} @@ -445,7 +459,7 @@ export function InvoicesWidget({ invoices }: InvoicesWidgetProps) {
) : ( <> - + Confirmar pagamento Revise os dados antes de confirmar. Vamos registrar a fatura @@ -454,72 +468,83 @@ export function InvoicesWidget({ invoices }: InvoicesWidgetProps) { {selectedInvoice ? ( -
-
-
- {selectedLogo ? ( - {`Logo - ) : ( - - {buildInitials(selectedInvoice.cardName)} - - )} -
-
-

Cartão

-

- {selectedInvoice.cardName} -

- {selectedInvoice.paymentStatus !== - INVOICE_PAYMENT_STATUS.PAID ? ( -

- { - parseDueDate( - selectedInvoice.period, - selectedInvoice.dueDay, - ).label - } -

- ) : null} - {selectedInvoice.paymentStatus === - INVOICE_PAYMENT_STATUS.PAID && selectedPaymentInfo ? ( -

- {selectedPaymentInfo.label} -

- ) : null} +
+
+
+
+
+ {selectedLogo ? ( + {`Logo + ) : ( + + {buildInitials(selectedInvoice.cardName)} + + )} +
+
+

+ Cartão +

+

+ {selectedInvoice.cardName} +

+
+
+
+ {selectedInvoice.paymentStatus !== + INVOICE_PAYMENT_STATUS.PAID ? ( +

+ { + parseDueDate( + selectedInvoice.period, + selectedInvoice.dueDay, + ).label + } +

+ ) : null} + {selectedInvoice.paymentStatus === + INVOICE_PAYMENT_STATUS.PAID && selectedPaymentInfo ? ( +

+ {selectedPaymentInfo.label} +

+ ) : null} +
-
-
- - Valor da fatura - +
+
+
+ + + Valor da Fatura + +
-
- - Status atual - - - - {INVOICE_STATUS_LABEL[selectedInvoice.paymentStatus]} - - +
+
+ + + Status + +
+ + {INVOICE_STATUS_LABEL[selectedInvoice.paymentStatus]} +
diff --git a/components/navbar/nav-items.tsx b/components/navbar/nav-items.tsx index c2bfe6a..b937bb3 100644 --- a/components/navbar/nav-items.tsx +++ b/components/navbar/nav-items.tsx @@ -1,12 +1,12 @@ import { RiArrowLeftRightLine, + RiAtLine, RiBankCard2Line, RiBankLine, + RiBarChart2Line, RiCalendarEventLine, RiFileChartLine, - RiFundsLine, RiGroupLine, - RiInboxLine, RiPriceTag3Line, RiSparklingLine, RiTodoLine, @@ -39,7 +39,7 @@ export const NAV_SECTIONS: NavSection[] = [ { href: "/pre-lancamentos", label: "pré-lançamentos", - icon: , + icon: , }, { href: "/calendario", @@ -65,7 +65,7 @@ export const NAV_SECTIONS: NavSection[] = [ { href: "/orcamentos", label: "orçamentos", - icon: , + icon: , preservePeriod: true, }, ], diff --git a/components/notificacoes/notification-bell.tsx b/components/notificacoes/notification-bell.tsx index 20c75f5..1bc07b3 100644 --- a/components/notificacoes/notification-bell.tsx +++ b/components/notificacoes/notification-bell.tsx @@ -3,15 +3,16 @@ import { RiAlertFill, RiArrowRightLine, + RiAtLine, RiBankCardLine, RiBarChart2Line, RiCheckboxCircleFill, RiErrorWarningLine, RiFileListLine, - RiInboxLine, RiNotification3Line, RiTimeLine, } from "@remixicon/react"; +import Image from "next/image"; import Link from "next/link"; import { useState } from "react"; import { Badge } from "@/components/ui/badge"; @@ -46,6 +47,12 @@ type NotificationBellProps = { preLancamentosCount?: number; }; +const resolveLogoPath = (logo: string | null | undefined) => { + if (!logo) return null; + if (/^(https?:\/\/|data:)/.test(logo)) return logo; + return logo.startsWith("/") ? logo : `/logos/${logo}`; +}; + function formatDate(dateString: string): string { const [year, month, day] = dateString.split("-").map(Number); const date = new Date(Date.UTC(year, month - 1, day)); @@ -72,10 +79,8 @@ function SectionLabel({ }) { return (
- {icon} - - {title} - + {icon} + {title}
); } @@ -174,7 +179,7 @@ export function NotificationBell({ {preLancamentosCount > 0 && (
} + icon={} title="Pré-lançamentos" /> setOpen(false)} className="group mx-1 mb-1 flex items-center gap-2 rounded-md px-2 py-2 transition-colors hover:bg-accent/60" > +

{preLancamentosCount === 1 ? "1 pré-lançamento aguardando revisão" @@ -206,9 +212,9 @@ export function NotificationBell({ className="flex items-start gap-2 px-2 py-2" > {n.status === "exceeded" ? ( - + ) : ( - + )}

{n.status === "exceeded" ? ( @@ -243,43 +249,54 @@ export function NotificationBell({ title="Cartão de Crédito" />

- {invoiceNotifications.map((n) => ( -
- {n.status === "overdue" ? ( - - ) : ( - - )} -

- {n.status === "overdue" ? ( - <> - A fatura de {n.name} venceu em{" "} - {formatDate(n.dueDate)} - {n.showAmount && n.amount > 0 && ( - <> - {" "} - — {formatCurrency(n.amount)} - - )} - + {invoiceNotifications.map((n) => { + const logo = resolveLogoPath(n.cardLogo); + return ( +

+ {logo ? ( + + ) : n.status === "overdue" ? ( + ) : ( - <> - A fatura de {n.name} vence em{" "} - {formatDate(n.dueDate)} - {n.showAmount && n.amount > 0 && ( - <> - {" "} - — {formatCurrency(n.amount)} - - )} - + )} -

-
- ))} +

+ {n.status === "overdue" ? ( + <> + A fatura de {n.name} venceu em{" "} + {formatDate(n.dueDate)} + {n.showAmount && n.amount > 0 && ( + <> + {" "} + — {formatCurrency(n.amount)} + + )} + + ) : ( + <> + A fatura de {n.name} vence em{" "} + {formatDate(n.dueDate)} + {n.showAmount && n.amount > 0 && ( + <> + {" "} + — {formatCurrency(n.amount)} + + )} + + )} +

+
+ ); + })}
)} @@ -297,11 +314,14 @@ export function NotificationBell({ key={n.id} className="flex items-start gap-2 px-2 py-2" > - {n.status === "overdue" ? ( - - ) : ( - - )} +

{n.status === "overdue" ? ( <> diff --git a/components/pagadores/details/pagador-info-card.tsx b/components/pagadores/details/pagador-info-card.tsx index ba0d537..602f694 100644 --- a/components/pagadores/details/pagador-info-card.tsx +++ b/components/pagadores/details/pagador-info-card.tsx @@ -292,7 +292,7 @@ export function PagadorInfoCard({

{/* Total Geral */} -
+
@@ -318,7 +318,7 @@ export function PagadorInfoCard({ {/* Grid de Formas de Pagamento */}
{/* Cartões */} -
+
@@ -331,7 +331,7 @@ export function PagadorInfoCard({
{/* Boletos */} -
+
@@ -344,7 +344,7 @@ export function PagadorInfoCard({
{/* Instantâneo */} -
+
@@ -361,7 +361,7 @@ export function PagadorInfoCard({
{/* Cartões Utilizados */} {summary.cardUsage.length > 0 && ( -
+
@@ -387,7 +387,7 @@ export function PagadorInfoCard({ {/* Status de Boletos */} {(summary.boletoStats.paidCount > 0 || summary.boletoStats.pendingCount > 0) && ( -
+
diff --git a/components/pre-lancamentos/inbox-card.tsx b/components/pre-lancamentos/inbox-card.tsx index 1b36115..de09121 100644 --- a/components/pre-lancamentos/inbox-card.tsx +++ b/components/pre-lancamentos/inbox-card.tsx @@ -36,6 +36,7 @@ interface InboxCardProps { onProcess?: (item: InboxItem) => void; onDiscard?: (item: InboxItem) => void; onViewDetails?: (item: InboxItem) => void; + onDelete?: (item: InboxItem) => void; } function resolveLogoPath(logo: string): string { @@ -77,6 +78,7 @@ export function InboxCard({ onProcess, onDiscard, onViewDetails, + onDelete, }: InboxCardProps) { const matchedLogo = useMemo( () => @@ -202,6 +204,16 @@ export function InboxCard({ {formattedStatusDate} )} + {onDelete && ( + + )} ) : ( diff --git a/components/pre-lancamentos/inbox-page.tsx b/components/pre-lancamentos/inbox-page.tsx index 799f035..00f6e56 100644 --- a/components/pre-lancamentos/inbox-page.tsx +++ b/components/pre-lancamentos/inbox-page.tsx @@ -1,15 +1,18 @@ "use client"; -import { RiInboxLine } from "@remixicon/react"; +import { RiAtLine, RiDeleteBinLine } from "@remixicon/react"; import { useCallback, useMemo, useState } from "react"; import { toast } from "sonner"; import { + bulkDeleteInboxItemsAction, + deleteInboxItemAction, discardInboxItemAction, markInboxAsProcessedAction, } 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"; +import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { InboxCard } from "./inbox-card"; @@ -52,6 +55,14 @@ export function InboxPage({ const [discardOpen, setDiscardOpen] = useState(false); const [itemToDiscard, setItemToDiscard] = useState(null); + const [deleteOpen, setDeleteOpen] = useState(false); + const [itemToDelete, setItemToDelete] = useState(null); + + const [bulkDeleteOpen, setBulkDeleteOpen] = useState(false); + const [bulkDeleteStatus, setBulkDeleteStatus] = useState< + "processed" | "discarded" + >("processed"); + const sortByTimestamp = useCallback( (list: InboxItem[]) => [...list].sort( @@ -127,6 +138,60 @@ export function InboxPage({ throw new Error(result.error); }, [itemToDiscard]); + const handleDeleteOpenChange = useCallback((open: boolean) => { + setDeleteOpen(open); + if (!open) { + setItemToDelete(null); + } + }, []); + + const handleDeleteRequest = useCallback((item: InboxItem) => { + setItemToDelete(item); + setDeleteOpen(true); + }, []); + + const handleDeleteConfirm = useCallback(async () => { + if (!itemToDelete) return; + + const result = await deleteInboxItemAction({ + inboxItemId: itemToDelete.id, + }); + + if (result.success) { + toast.success(result.message); + return; + } + + toast.error(result.error); + throw new Error(result.error); + }, [itemToDelete]); + + const handleBulkDeleteOpenChange = useCallback((open: boolean) => { + setBulkDeleteOpen(open); + }, []); + + const handleBulkDeleteRequest = useCallback( + (status: "processed" | "discarded") => { + setBulkDeleteStatus(status); + setBulkDeleteOpen(true); + }, + [], + ); + + const handleBulkDeleteConfirm = useCallback(async () => { + const result = await bulkDeleteInboxItemsAction({ + status: bulkDeleteStatus, + }); + + if (result.success) { + toast.success(result.message); + return; + } + + toast.error(result.error); + throw new Error(result.error); + }, [bulkDeleteStatus]); + const handleLancamentoSuccess = useCallback(async () => { if (!itemToProcess) return; @@ -180,7 +245,7 @@ export function InboxPage({ const renderEmptyState = (message: string) => ( } + media={} title={message} description="As notificações capturadas pelo app OpenMonetis Companion aparecerão aqui. Saiba mais em Ajustes > Companion." /> @@ -205,6 +270,7 @@ export function InboxPage({ onProcess={readonly ? undefined : handleProcessRequest} onDiscard={readonly ? undefined : handleDiscardRequest} onViewDetails={readonly ? undefined : handleDetailsRequest} + onDelete={readonly ? handleDeleteRequest : undefined} /> ))}
@@ -229,9 +295,33 @@ export function InboxPage({ {renderGrid(sortedPending)} + {sortedProcessed.length > 0 && ( +
+ +
+ )} {renderGrid(sortedProcessed, true)}
+ {sortedDiscarded.length > 0 && ( +
+ +
+ )} {renderGrid(sortedDiscarded, true)}
@@ -272,6 +362,28 @@ export function InboxPage({ pendingLabel="Descartando..." onConfirm={handleDiscardConfirm} /> + + + + ); } diff --git a/components/sidebar/nav-link.tsx b/components/sidebar/nav-link.tsx index 268eec0..14de25c 100644 --- a/components/sidebar/nav-link.tsx +++ b/components/sidebar/nav-link.tsx @@ -1,6 +1,7 @@ import { type RemixiconComponentType, RiArrowLeftRightLine, + RiAtLine, RiBankCard2Line, RiBankLine, RiCalendarEventLine, @@ -8,7 +9,6 @@ import { RiFileChartLine, RiFundsLine, RiGroupLine, - RiInboxLine, RiPriceTag3Line, RiSettings2Line, RiSparklingLine, @@ -98,7 +98,7 @@ export function createSidebarNavData( title: "Pré-Lançamentos", url: "/pre-lancamentos", key: "pre-lancamentos", - icon: RiInboxLine, + icon: RiAtLine, badge: preLancamentosCount > 0 ? preLancamentosCount : undefined, }, diff --git a/lib/dashboard/notifications.ts b/lib/dashboard/notifications.ts index 045ac84..b6819c1 100644 --- a/lib/dashboard/notifications.ts +++ b/lib/dashboard/notifications.ts @@ -23,6 +23,7 @@ export type DashboardNotification = { amount: number; period?: string; showAmount: boolean; + cardLogo?: string | null; }; export type BudgetStatus = "exceeded" | "critical"; @@ -160,6 +161,7 @@ export async function fetchDashboardNotifications( invoiceId: faturas.id, cardId: cartoes.id, cardName: cartoes.name, + cardLogo: cartoes.logo, dueDay: cartoes.dueDay, period: faturas.period, totalAmount: sql` @@ -189,6 +191,7 @@ export async function fetchDashboardNotifications( invoiceId: faturas.id, cardId: cartoes.id, cardName: cartoes.name, + cardLogo: cartoes.logo, dueDay: cartoes.dueDay, period: sql`COALESCE(${faturas.period}, ${currentPeriod})`, paymentStatus: faturas.paymentStatus, @@ -219,6 +222,7 @@ export async function fetchDashboardNotifications( faturas.id, cartoes.id, cartoes.name, + cartoes.logo, cartoes.dueDay, faturas.period, faturas.paymentStatus, @@ -296,6 +300,7 @@ export async function fetchDashboardNotifications( amount: Math.abs(amount), period: invoice.period, showAmount: true, + cardLogo: invoice.cardLogo, }); } @@ -332,6 +337,7 @@ export async function fetchDashboardNotifications( amount: Math.abs(amount), period: invoice.period, showAmount: invoiceIsOverdue, + cardLogo: invoice.cardLogo, }); }