diff --git a/src/app/(dashboard)/inbox/loading.tsx b/src/app/(dashboard)/inbox/loading.tsx index 44335fc..dc9653a 100644 --- a/src/app/(dashboard)/inbox/loading.tsx +++ b/src/app/(dashboard)/inbox/loading.tsx @@ -4,26 +4,30 @@ import { Skeleton } from "@/shared/components/ui/skeleton"; export default function Loading() { return (
-
-
- - -
+
+ {/* Tabs */} + + + {/* Grid de cards */}
{Array.from({ length: 6 }).map((_, i) => ( - -
-
- - -
- - -
- - + +
+
+ +
+
+ + + +
+
+ + + +
))}
diff --git a/src/features/inbox/components/inbox-card.tsx b/src/features/inbox/components/inbox-card.tsx index 1539877..5f9b153 100644 --- a/src/features/inbox/components/inbox-card.tsx +++ b/src/features/inbox/components/inbox-card.tsx @@ -5,7 +5,6 @@ import { RiCheckLine, RiDeleteBinLine, RiFileList2Line, - RiMoreLine, } from "@remixicon/react"; import { format, formatDistanceToNow } from "date-fns"; import { ptBR } from "date-fns/locale"; @@ -15,22 +14,42 @@ import { Badge } from "@/shared/components/ui/badge"; import { Button } from "@/shared/components/ui/button"; import { Card, - CardAction, CardContent, CardFooter, CardHeader, CardTitle, } from "@/shared/components/ui/card"; import { Checkbox } from "@/shared/components/ui/checkbox"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from "@/shared/components/ui/dropdown-menu"; import { resolveLogoSrc } from "@/shared/lib/logo"; import type { InboxItem } from "./types"; +// O timestamp vem do app Android em horário local mas salvo como UTC. +// Adicionamos o offset de Brasília para corrigir o cálculo de "há X tempo". +const BRASILIA_OFFSET_MS = 3 * 60 * 60 * 1000; + +function adjustToBrasilia(date: Date): Date { + return new Date(date.getTime() + BRASILIA_OFFSET_MS); +} + +function findMatchingLogo( + sourceAppName: string | null, + appLogoMap: Record, +): string | null { + if (!sourceAppName) return null; + + const appName = sourceAppName.toLowerCase(); + + if (appLogoMap[appName]) return resolveLogoSrc(appLogoMap[appName]); + + for (const [name, logo] of Object.entries(appLogoMap)) { + if (name.includes(appName) || appName.includes(name)) { + return resolveLogoSrc(logo); + } + } + + return null; +} + interface InboxCardProps { item: InboxItem; readonly?: boolean; @@ -44,27 +63,6 @@ interface InboxCardProps { onSelectToggle?: (id: string) => void; } -function findMatchingLogo( - sourceAppName: string | null, - appLogoMap: Record, -): string | null { - if (!sourceAppName) return null; - - const appName = sourceAppName.toLowerCase(); - - // Exact match first - if (appLogoMap[appName]) return resolveLogoSrc(appLogoMap[appName]); - - // Partial match: card/account name contains app name or vice versa - for (const [name, logo] of Object.entries(appLogoMap)) { - if (name.includes(appName) || appName.includes(name)) { - return resolveLogoSrc(logo); - } - } - - return null; -} - export function InboxCard({ item, readonly, @@ -83,28 +81,14 @@ export function InboxCard({ const amount = item.parsedAmount ? parseFloat(item.parsedAmount) : null; - // 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 notificationDate = adjustToBrasilia(rawDate); 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); - const statusDate = item.status === "processed" ? item.processedAt @@ -118,12 +102,11 @@ export function InboxCard({ return ( - {/* Header com app e valor */} -
- +
+ {matchedLogo && ( )} - {item.sourceAppName || item.sourceApp} - {" "} - + + {item.sourceAppName || item.sourceApp} + + {timeAgo} {amount !== null && ( - + )}
- - {!readonly && ( - - - - - - - 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 ou badge de status */} {readonly ? ( - + @@ -201,7 +150,7 @@ export function InboxCard({ {formattedStatusDate} )} -
+
{item.status === "discarded" && onRestoreToPending && ( @@ -234,7 +184,7 @@ export function InboxCard({
) : ( - + + + )} - + diff --git a/src/features/inbox/components/inbox-page.tsx b/src/features/inbox/components/inbox-page.tsx index c7795c6..c89c615 100644 --- a/src/features/inbox/components/inbox-page.tsx +++ b/src/features/inbox/components/inbox-page.tsx @@ -231,6 +231,29 @@ export function InboxPage({ 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", ) => { @@ -412,16 +435,27 @@ export function InboxPage({ - {selectedPendingIds.length > 0 && ( -
+ {sortedPending.length > 0 && ( +
+ {selectedPendingIds.length > 0 && ( + + )}
)} {renderGrid(sortedPending, false, selectedPendingIds, (id) => @@ -431,6 +465,15 @@ export function InboxPage({ {sortedProcessed.length > 0 && (
+ {selectedProcessedIds.length > 0 && ( {selectedDiscardedIds.length > 0 && (