"use client"; 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"; import { InboxDetailsDialog } from "./inbox-details-dialog"; import type { InboxItem, SelectOption } from "./types"; interface InboxPageProps { pendingItems: InboxItem[]; processedItems: InboxItem[]; discardedItems: InboxItem[]; pagadorOptions: SelectOption[]; splitPagadorOptions: SelectOption[]; defaultPagadorId: string | null; contaOptions: SelectOption[]; cartaoOptions: SelectOption[]; categoriaOptions: SelectOption[]; estabelecimentos: string[]; appLogoMap: Record; } export function InboxPage({ pendingItems, processedItems, discardedItems, pagadorOptions, splitPagadorOptions, defaultPagadorId, contaOptions, cartaoOptions, categoriaOptions, estabelecimentos, appLogoMap, }: InboxPageProps) { const [processOpen, setProcessOpen] = useState(false); const [itemToProcess, setItemToProcess] = useState(null); const [detailsOpen, setDetailsOpen] = useState(false); const [itemDetails, setItemDetails] = useState(null); 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( (a, b) => new Date(b.notificationTimestamp).getTime() - new Date(a.notificationTimestamp).getTime(), ), [], ); const sortedPending = useMemo( () => sortByTimestamp(pendingItems), [pendingItems, sortByTimestamp], ); const sortedProcessed = useMemo( () => sortByTimestamp(processedItems), [processedItems, sortByTimestamp], ); const sortedDiscarded = useMemo( () => sortByTimestamp(discardedItems), [discardedItems, sortByTimestamp], ); const handleProcessOpenChange = useCallback((open: boolean) => { setProcessOpen(open); if (!open) { setItemToProcess(null); } }, []); const handleDetailsOpenChange = useCallback((open: boolean) => { setDetailsOpen(open); if (!open) { setItemDetails(null); } }, []); const handleDiscardOpenChange = useCallback((open: boolean) => { setDiscardOpen(open); if (!open) { setItemToDiscard(null); } }, []); const handleProcessRequest = useCallback((item: InboxItem) => { setItemToProcess(item); setProcessOpen(true); }, []); const handleDetailsRequest = useCallback((item: InboxItem) => { setItemDetails(item); setDetailsOpen(true); }, []); const handleDiscardRequest = useCallback((item: InboxItem) => { setItemToDiscard(item); setDiscardOpen(true); }, []); const handleDiscardConfirm = useCallback(async () => { if (!itemToDiscard) return; const result = await discardInboxItemAction({ inboxItemId: itemToDiscard.id, }); if (result.success) { toast.success(result.message); return; } toast.error(result.error); 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; const result = await markInboxAsProcessedAction({ inboxItemId: itemToProcess.id, }); if (result.success) { toast.success("Notificação processada!"); } else { toast.error(result.error); } }, [itemToProcess]); // Prepare default values from inbox item 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?.notificationTimestamp) ?? null; const defaultName = itemToProcess?.parsedName ? itemToProcess.parsedName .toLowerCase() .replace(/\b\w/g, (char) => char.toUpperCase()) : null; const defaultAmount = itemToProcess?.parsedAmount ? String(Math.abs(Number(itemToProcess.parsedAmount))) : null; // Match sourceAppName with a cartão to pre-fill card select const matchedCartaoId = useMemo(() => { const appName = itemToProcess?.sourceAppName?.toLowerCase(); if (!appName) return null; for (const option of cartaoOptions) { const label = option.label.toLowerCase(); if (label.includes(appName) || appName.includes(label)) { return option.value; } } return null; }, [itemToProcess?.sourceAppName, cartaoOptions]); const renderEmptyState = (message: string) => ( } title={message} description="As notificações capturadas pelo app OpenMonetis Companion aparecerão aqui. Saiba mais em Ajustes > Companion." /> ); const renderGrid = (list: InboxItem[], readonly?: boolean) => list.length === 0 ? ( renderEmptyState( readonly ? "Nenhuma notificação nesta aba" : "Nenhum pré-lançamento pendente", ) ) : (
{list.map((item) => ( ))}
); return ( <> Pendentes ({pendingItems.length}) Processados ({processedItems.length}) Descartados ({discardedItems.length}) {renderGrid(sortedPending)} {sortedProcessed.length > 0 && (
)} {renderGrid(sortedProcessed, true)}
{sortedDiscarded.length > 0 && (
)} {renderGrid(sortedDiscarded, true)}
); }