"use client"; import { RiAtLine, RiDeleteBinLine } from "@remixicon/react"; import { useMemo, useState } from "react"; import { toast } from "sonner"; import { bulkDeleteInboxItemsAction, bulkDeleteSelectedInboxItemsAction, bulkDiscardInboxItemsAction, deleteInboxItemAction, discardInboxItemAction, markInboxAsProcessedAction, restoreDiscardedInboxItemAction, } from "@/features/inbox/actions"; import { TransactionDialog } from "@/features/transactions/components/dialogs/transaction-dialog/transaction-dialog"; import { ConfirmActionDialog } from "@/shared/components/confirm-action-dialog"; import { EmptyState } from "@/shared/components/empty-state"; import { Button } from "@/shared/components/ui/button"; import { Card } from "@/shared/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger, } from "@/shared/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[]; payerOptions: SelectOption[]; splitPayerOptions: SelectOption[]; defaultPayerId: string | null; accountOptions: SelectOption[]; cardOptions: SelectOption[]; categoryOptions: SelectOption[]; estabelecimentos: string[]; appLogoMap: Record; } export function InboxPage({ pendingItems, processedItems, discardedItems, payerOptions, splitPayerOptions, defaultPayerId, accountOptions, cardOptions, categoryOptions, 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 [restoreOpen, setRestoreOpen] = useState(false); const [itemToRestore, setItemToRestore] = useState(null); const [bulkDeleteOpen, setBulkDeleteOpen] = useState(false); const [bulkDeleteStatus, setBulkDeleteStatus] = useState< "processed" | "discarded" >("processed"); const [selectedPendingIds, setSelectedPendingIds] = useState([]); const [selectedProcessedIds, setSelectedProcessedIds] = useState( [], ); const [selectedDiscardedIds, setSelectedDiscardedIds] = useState( [], ); const [selectionBulkOpen, setSelectionBulkOpen] = useState(false); const [selectionBulkStatus, setSelectionBulkStatus] = useState< "pending" | "processed" | "discarded" >("pending"); const sortedPending = useMemo( () => [...pendingItems].sort( (a, b) => new Date(b.notificationTimestamp).getTime() - new Date(a.notificationTimestamp).getTime(), ), [pendingItems], ); const sortedProcessed = useMemo( () => [...processedItems].sort( (a, b) => new Date(b.notificationTimestamp).getTime() - new Date(a.notificationTimestamp).getTime(), ), [processedItems], ); const sortedDiscarded = useMemo( () => [...discardedItems].sort( (a, b) => new Date(b.notificationTimestamp).getTime() - new Date(a.notificationTimestamp).getTime(), ), [discardedItems], ); const handleProcessOpenChange = (open: boolean) => { setProcessOpen(open); if (!open) { setItemToProcess(null); } }; const handleDetailsOpenChange = (open: boolean) => { setDetailsOpen(open); if (!open) { setItemDetails(null); } }; const handleDiscardOpenChange = (open: boolean) => { setDiscardOpen(open); if (!open) { setItemToDiscard(null); } }; const handleProcessRequest = (item: InboxItem) => { setItemToProcess(item); setProcessOpen(true); }; const handleDetailsRequest = (item: InboxItem) => { setItemDetails(item); setDetailsOpen(true); }; const handleDiscardRequest = (item: InboxItem) => { setItemToDiscard(item); setDiscardOpen(true); }; const handleDiscardConfirm = 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); }; const handleDeleteOpenChange = (open: boolean) => { setDeleteOpen(open); if (!open) { setItemToDelete(null); } }; const handleDeleteRequest = (item: InboxItem) => { setItemToDelete(item); setDeleteOpen(true); }; const handleDeleteConfirm = 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); }; const handleRestoreOpenChange = (open: boolean) => { setRestoreOpen(open); if (!open) { setItemToRestore(null); } }; const handleRestoreRequest = (item: InboxItem) => { setItemToRestore(item); setRestoreOpen(true); }; const handleRestoreToPendingConfirm = async () => { if (!itemToRestore) return; const result = await restoreDiscardedInboxItemAction({ inboxItemId: itemToRestore.id, }); if (result.success) { toast.success(result.message); return; } toast.error(result.error); throw new Error(result.error); }; const toggleSelection = ( ids: string[], setIds: (v: string[]) => void, id: string, ) => { setIds(ids.includes(id) ? ids.filter((x) => x !== id) : [...ids, id]); }; const allPendingSelected = sortedPending.length > 0 && selectedPendingIds.length === sortedPending.length; const allProcessedSelected = sortedProcessed.length > 0 && selectedProcessedIds.length === sortedProcessed.length; const allDiscardedSelected = sortedDiscarded.length > 0 && selectedDiscardedIds.length === sortedDiscarded.length; const toggleSelectAllPending = () => { if (allPendingSelected) setSelectedPendingIds([]); else setSelectedPendingIds(sortedPending.map((item) => item.id)); }; const toggleSelectAllProcessed = () => { if (allProcessedSelected) setSelectedProcessedIds([]); else setSelectedProcessedIds(sortedProcessed.map((item) => item.id)); }; const toggleSelectAllDiscarded = () => { if (allDiscardedSelected) setSelectedDiscardedIds([]); else setSelectedDiscardedIds(sortedDiscarded.map((item) => item.id)); }; const handleSelectionBulkRequest = ( status: "pending" | "processed" | "discarded", ) => { setSelectionBulkStatus(status); setSelectionBulkOpen(true); }; const handleSelectionBulkConfirm = async () => { if (selectionBulkStatus === "pending") { const result = await bulkDiscardInboxItemsAction({ inboxItemIds: selectedPendingIds, }); if (result.success) { toast.success(result.message); setSelectedPendingIds([]); return; } toast.error(result.error); throw new Error(result.error); } else { const ids = selectionBulkStatus === "processed" ? selectedProcessedIds : selectedDiscardedIds; const result = await bulkDeleteSelectedInboxItemsAction({ inboxItemIds: ids, }); if (result.success) { toast.success(result.message); if (selectionBulkStatus === "processed") setSelectedProcessedIds([]); else setSelectedDiscardedIds([]); return; } toast.error(result.error); throw new Error(result.error); } }; const handleBulkDeleteOpenChange = (open: boolean) => { setBulkDeleteOpen(open); }; const handleBulkDeleteRequest = (status: "processed" | "discarded") => { setBulkDeleteStatus(status); setBulkDeleteOpen(true); }; const handleBulkDeleteConfirm = async () => { const result = await bulkDeleteInboxItemsAction({ status: bulkDeleteStatus, }); if (result.success) { toast.success(result.message); return; } toast.error(result.error); throw new Error(result.error); }; const handleLancamentoSuccess = async () => { if (!itemToProcess) return; const result = await markInboxAsProcessedAction({ inboxItemId: itemToProcess.id, }); if (result.success) { toast.success("Notificação processada!"); } else { toast.error(result.error); } }; // 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 cardOptions) { const label = option.label.toLowerCase(); if (label.includes(appName) || appName.includes(label)) { return option.value; } } return null; }, [itemToProcess?.sourceAppName, cardOptions]); 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, selectedIds?: string[], onToggle?: (id: string) => void, ) => 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}) {sortedPending.length > 0 && (
{selectedPendingIds.length > 0 && ( )}
)} {renderGrid(sortedPending, false, selectedPendingIds, (id) => toggleSelection(selectedPendingIds, setSelectedPendingIds, id), )}
{sortedProcessed.length > 0 && (
{selectedProcessedIds.length > 0 && ( )}
)} {renderGrid(sortedProcessed, true, selectedProcessedIds, (id) => toggleSelection(selectedProcessedIds, setSelectedProcessedIds, id), )}
{sortedDiscarded.length > 0 && (
{selectedDiscardedIds.length > 0 && ( )}
)} {renderGrid(sortedDiscarded, true, selectedDiscardedIds, (id) => toggleSelection(selectedDiscardedIds, setSelectedDiscardedIds, id), )}
); }