mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-05-09 11:01:45 +00:00
refactor(lista): componentizar inbox e tabela de lançamentos
This commit is contained in:
133
src/features/inbox/components/inbox-items-list.tsx
Normal file
133
src/features/inbox/components/inbox-items-list.tsx
Normal file
@@ -0,0 +1,133 @@
|
||||
import { RiAtLine, RiCalendarEventLine } from "@remixicon/react";
|
||||
import { format } from "date-fns";
|
||||
import { ptBR } from "date-fns/locale";
|
||||
import { EmptyState } from "@/shared/components/empty-state";
|
||||
import { Card } from "@/shared/components/ui/card";
|
||||
import { InboxCard } from "./inbox-card";
|
||||
import type { InboxItem } from "./types";
|
||||
|
||||
// O Companion envia hora local de Brasília com 'Z' literal (não converte para UTC).
|
||||
// Por isso, o timestamp armazenado no DB já tem a data correta de Brasília como componente UTC.
|
||||
// Basta fatiar o ISO string sem nenhum ajuste para obter a data de Brasília do item.
|
||||
function getItemDateKey(date: Date): string {
|
||||
return date.toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
// Para "hoje" e "ontem", precisamos da data real de Brasília (UTC-3).
|
||||
function getBrasiliaDateKey(date: Date): string {
|
||||
const BRASILIA_OFFSET_MS = 3 * 60 * 60 * 1000;
|
||||
return new Date(date.getTime() - BRASILIA_OFFSET_MS)
|
||||
.toISOString()
|
||||
.slice(0, 10);
|
||||
}
|
||||
|
||||
function getGroupLabel(dateKey: string): string {
|
||||
const now = new Date();
|
||||
const todayKey = getBrasiliaDateKey(now);
|
||||
const yesterdayKey = getBrasiliaDateKey(
|
||||
new Date(now.getTime() - 24 * 60 * 60 * 1000),
|
||||
);
|
||||
if (dateKey === todayKey) return "Hoje";
|
||||
if (dateKey === yesterdayKey) return "Ontem";
|
||||
const [year, month, day] = dateKey.split("-").map(Number);
|
||||
return format(new Date(year, (month ?? 1) - 1, day), "d 'de' MMMM", {
|
||||
locale: ptBR,
|
||||
});
|
||||
}
|
||||
|
||||
function groupItemsByDay(
|
||||
items: InboxItem[],
|
||||
): { label: string; items: InboxItem[] }[] {
|
||||
const groups = new Map<string, InboxItem[]>();
|
||||
for (const item of items) {
|
||||
const key = getItemDateKey(new Date(item.notificationTimestamp));
|
||||
const group = groups.get(key);
|
||||
if (group) {
|
||||
group.push(item);
|
||||
} else {
|
||||
groups.set(key, [item]);
|
||||
}
|
||||
}
|
||||
const sortedKeys = [...groups.keys()].sort((a, b) => b.localeCompare(a));
|
||||
return sortedKeys.map((key) => ({
|
||||
label: getGroupLabel(key),
|
||||
items: groups.get(key) ?? [],
|
||||
}));
|
||||
}
|
||||
|
||||
type InboxItemsListProps = {
|
||||
items: InboxItem[];
|
||||
readonly?: boolean;
|
||||
activeApp: string | null;
|
||||
appLogoMap: Record<string, string>;
|
||||
selectedIds: string[];
|
||||
onProcess?: (item: InboxItem) => void;
|
||||
onDiscard?: (item: InboxItem) => void;
|
||||
onViewDetails?: (item: InboxItem) => void;
|
||||
onDelete?: (item: InboxItem) => void;
|
||||
onRestoreToPending?: (item: InboxItem) => void;
|
||||
onSelectToggle: (id: string) => void;
|
||||
};
|
||||
|
||||
export function InboxItemsList({
|
||||
items,
|
||||
readonly,
|
||||
activeApp,
|
||||
appLogoMap,
|
||||
selectedIds,
|
||||
onProcess,
|
||||
onDiscard,
|
||||
onViewDetails,
|
||||
onDelete,
|
||||
onRestoreToPending,
|
||||
onSelectToggle,
|
||||
}: InboxItemsListProps) {
|
||||
if (items.length === 0) {
|
||||
const message = activeApp
|
||||
? "Nenhuma notificação deste app"
|
||||
: readonly
|
||||
? "Nenhuma notificação nesta aba"
|
||||
: "Nenhum pré-lançamento pendente";
|
||||
return (
|
||||
<Card className="flex min-h-[50vh] w-full items-center justify-center py-12">
|
||||
<EmptyState
|
||||
media={<RiAtLine className="size-6 text-primary" />}
|
||||
title={message}
|
||||
description="As notificações capturadas pelo app OpenMonetis Companion aparecerão aqui. Saiba mais em Ajustes > Companion."
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
const groups = groupItemsByDay(items);
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
{groups.map((group) => (
|
||||
<div key={group.label}>
|
||||
<div className="mb-3 flex items-center gap-1 text-muted-foreground">
|
||||
<RiCalendarEventLine className="size-3.5 shrink-0" />
|
||||
<p className="text-sm font-medium">{group.label}</p>
|
||||
</div>
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
{group.items.map((item) => (
|
||||
<InboxCard
|
||||
key={item.id}
|
||||
item={item}
|
||||
readonly={readonly}
|
||||
appLogoMap={appLogoMap}
|
||||
onProcess={readonly ? undefined : onProcess}
|
||||
onDiscard={readonly ? undefined : onDiscard}
|
||||
onViewDetails={readonly ? undefined : onViewDetails}
|
||||
onDelete={readonly ? onDelete : undefined}
|
||||
onRestoreToPending={readonly ? onRestoreToPending : undefined}
|
||||
selected={selectedIds.includes(item.id)}
|
||||
onSelectToggle={onSelectToggle}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user