diff --git a/app/(dashboard)/pre-lancamentos/actions.ts b/app/(dashboard)/pre-lancamentos/actions.ts index 39e7032..6ba2f4b 100644 --- a/app/(dashboard)/pre-lancamentos/actions.ts +++ b/app/(dashboard)/pre-lancamentos/actions.ts @@ -1,10 +1,9 @@ "use server"; import { and, eq, inArray } from "drizzle-orm"; -import { revalidatePath } from "next/cache"; import { z } from "zod"; import { preLancamentos } from "@/db/schema"; -import { handleActionError } from "@/lib/actions/helpers"; +import { handleActionError, revalidateForEntity } from "@/lib/actions/helpers"; import type { ActionResult } from "@/lib/actions/types"; import { getUser } from "@/lib/auth/server"; import { db } from "@/lib/db"; @@ -17,6 +16,10 @@ const discardInboxSchema = z.object({ inboxItemId: z.string().uuid("ID do item inválido"), }); +const restoreDiscardedInboxSchema = z.object({ + inboxItemId: z.string().uuid("ID do item inválido"), +}); + const bulkDiscardSchema = z.object({ inboxItemIds: z.array(z.string().uuid()).min(1, "Selecione ao menos um item"), }); @@ -30,9 +33,7 @@ const bulkDeleteInboxSchema = z.object({ }); function revalidateInbox() { - revalidatePath("/pre-lancamentos"); - revalidatePath("/lancamentos"); - revalidatePath("/dashboard"); + revalidateForEntity("inbox"); } /** @@ -166,6 +167,54 @@ export async function bulkDiscardInboxItemsAction( } } +export async function restoreDiscardedInboxItemAction( + input: z.infer, +): Promise { + try { + const user = await getUser(); + const data = restoreDiscardedInboxSchema.parse(input); + + const [item] = await db + .select({ id: preLancamentos.id }) + .from(preLancamentos) + .where( + and( + eq(preLancamentos.id, data.inboxItemId), + eq(preLancamentos.userId, user.id), + eq(preLancamentos.status, "discarded"), + ), + ) + .limit(1); + + if (!item) { + return { + success: false, + error: "Item não encontrado ou não está descartado.", + }; + } + + await db + .update(preLancamentos) + .set({ + status: "pending", + discardedAt: null, + updatedAt: new Date(), + }) + .where( + and( + eq(preLancamentos.id, data.inboxItemId), + eq(preLancamentos.userId, user.id), + ), + ); + + revalidateInbox(); + + return { success: true, message: "Item voltou para pendentes." }; + } catch (error) { + return handleActionError(error); + } +} + export async function deleteInboxItemAction( input: z.infer, ): Promise { diff --git a/components/pre-lancamentos/inbox-card.tsx b/components/pre-lancamentos/inbox-card.tsx index de09121..b0231e8 100644 --- a/components/pre-lancamentos/inbox-card.tsx +++ b/components/pre-lancamentos/inbox-card.tsx @@ -1,9 +1,10 @@ "use client"; import { + RiArrowGoBackLine, RiCheckLine, RiDeleteBinLine, - RiEyeLine, + RiFileList2Line, RiMoreLine, } from "@remixicon/react"; import { format, formatDistanceToNow } from "date-fns"; @@ -37,6 +38,7 @@ interface InboxCardProps { onDiscard?: (item: InboxItem) => void; onViewDetails?: (item: InboxItem) => void; onDelete?: (item: InboxItem) => void; + onRestoreToPending?: (item: InboxItem) => void | Promise; } function resolveLogoPath(logo: string): string { @@ -79,6 +81,7 @@ export function InboxCard({ onDiscard, onViewDetails, onDelete, + onRestoreToPending, }: InboxCardProps) { const matchedLogo = useMemo( () => @@ -161,7 +164,7 @@ export function InboxCard({ onViewDetails?.(item)}> - + Ver detalhes onProcess?.(item)}> @@ -204,16 +207,30 @@ export function InboxCard({ {formattedStatusDate} )} - {onDelete && ( - - )} +
+ {item.status === "discarded" && onRestoreToPending && ( + + )} + {onDelete && ( + + )} +
) : ( @@ -226,10 +243,12 @@ export function InboxCard({ Processar diff --git a/components/pre-lancamentos/inbox-page.tsx b/components/pre-lancamentos/inbox-page.tsx index 00f6e56..7c32401 100644 --- a/components/pre-lancamentos/inbox-page.tsx +++ b/components/pre-lancamentos/inbox-page.tsx @@ -8,10 +8,11 @@ import { deleteInboxItemAction, discardInboxItemAction, markInboxAsProcessedAction, + restoreDiscardedInboxItemAction, } 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 { EmptyState } from "@/components/shared/empty-state"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; @@ -58,6 +59,9 @@ export function InboxPage({ 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" @@ -166,6 +170,34 @@ export function InboxPage({ throw new Error(result.error); }, [itemToDelete]); + const handleRestoreOpenChange = useCallback((open: boolean) => { + setRestoreOpen(open); + if (!open) { + setItemToRestore(null); + } + }, []); + + const handleRestoreRequest = useCallback((item: InboxItem) => { + setItemToRestore(item); + setRestoreOpen(true); + }, []); + + const handleRestoreToPendingConfirm = useCallback(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); + }, [itemToRestore]); + const handleBulkDeleteOpenChange = useCallback((open: boolean) => { setBulkDeleteOpen(open); }, []); @@ -271,6 +303,7 @@ export function InboxPage({ onDiscard={readonly ? undefined : handleDiscardRequest} onViewDetails={readonly ? undefined : handleDetailsRequest} onDelete={readonly ? handleDeleteRequest : undefined} + onRestoreToPending={readonly ? handleRestoreRequest : undefined} /> ))} @@ -279,15 +312,27 @@ export function InboxPage({ return ( <> - - - Pendentes ({pendingItems.length}) + + + Pendentes + ({pendingItems.length}) - - Processados ({processedItems.length}) + + Processados + ({processedItems.length}) - - Descartados ({discardedItems.length}) + + Descartados + ({discardedItems.length}) @@ -374,6 +419,16 @@ export function InboxPage({ onConfirm={handleDeleteConfirm} /> + +