diff --git a/CHANGELOG.md b/CHANGELOG.md index 26fb4da..a8bca48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,15 @@ Todas as mudanças notáveis deste projeto serão documentadas neste arquivo. O formato é baseado em [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/), e este projeto adere ao [Versionamento Semântico](https://semver.org/lang/pt-BR/). +## [1.3.1] - 2026-02-06 + +### Alterado + +- Unificadas páginas de itens ativos e arquivados em Cartões, Contas e Anotações com sistema de tabs (padrão Categorias) +- Removidas rotas separadas `/cartoes/inativos`, `/contas/inativos` e `/anotacoes/arquivadas` +- Removidos sub-links de inativos/arquivados da sidebar +- Padronizada nomenclatura para "Arquivados"/"Arquivadas" em todas as entidades + ## [1.3.0] - 2026-02-06 ### Adicionado diff --git a/app/(dashboard)/anotacoes/arquivadas/page.tsx b/app/(dashboard)/anotacoes/arquivadas/page.tsx deleted file mode 100644 index a1ce516..0000000 --- a/app/(dashboard)/anotacoes/arquivadas/page.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { NotesPage } from "@/components/anotacoes/notes-page"; -import { getUserId } from "@/lib/auth/server"; -import { fetchArquivadasForUser } from "../data"; - -export default async function ArquivadasPage() { - const userId = await getUserId(); - const notes = await fetchArquivadasForUser(userId); - - return ( -
- -
- ); -} diff --git a/app/(dashboard)/anotacoes/data.ts b/app/(dashboard)/anotacoes/data.ts index 91b3f14..4697516 100644 --- a/app/(dashboard)/anotacoes/data.ts +++ b/app/(dashboard)/anotacoes/data.ts @@ -52,6 +52,17 @@ export async function fetchNotesForUser(userId: string): Promise { }); } +export async function fetchAllNotesForUser( + userId: string, +): Promise<{ activeNotes: NoteData[]; archivedNotes: NoteData[] }> { + const [activeNotes, archivedNotes] = await Promise.all([ + fetchNotesForUser(userId), + fetchArquivadasForUser(userId), + ]); + + return { activeNotes, archivedNotes }; +} + export async function fetchArquivadasForUser( userId: string, ): Promise { diff --git a/app/(dashboard)/anotacoes/page.tsx b/app/(dashboard)/anotacoes/page.tsx index e9cf42e..d13e33c 100644 --- a/app/(dashboard)/anotacoes/page.tsx +++ b/app/(dashboard)/anotacoes/page.tsx @@ -1,14 +1,14 @@ import { NotesPage } from "@/components/anotacoes/notes-page"; import { getUserId } from "@/lib/auth/server"; -import { fetchNotesForUser } from "./data"; +import { fetchAllNotesForUser } from "./data"; export default async function Page() { const userId = await getUserId(); - const notes = await fetchNotesForUser(userId); + const { activeNotes, archivedNotes } = await fetchAllNotesForUser(userId); return (
- +
); } diff --git a/app/(dashboard)/cartoes/data.ts b/app/(dashboard)/cartoes/data.ts index 0af7535..e9d55ce 100644 --- a/app/(dashboard)/cartoes/data.ts +++ b/app/(dashboard)/cartoes/data.ts @@ -211,3 +211,22 @@ export async function fetchInativosForUser(userId: string): Promise<{ return { cards, accounts, logoOptions }; } + +export async function fetchAllCardsForUser(userId: string): Promise<{ + activeCards: CardData[]; + archivedCards: CardData[]; + accounts: AccountSimple[]; + logoOptions: LogoOption[]; +}> { + const [activeData, archivedData] = await Promise.all([ + fetchCardsForUser(userId), + fetchInativosForUser(userId), + ]); + + return { + activeCards: activeData.cards, + archivedCards: archivedData.cards, + accounts: activeData.accounts, + logoOptions: activeData.logoOptions, + }; +} diff --git a/app/(dashboard)/cartoes/inativos/page.tsx b/app/(dashboard)/cartoes/inativos/page.tsx deleted file mode 100644 index 61a514d..0000000 --- a/app/(dashboard)/cartoes/inativos/page.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import { CardsPage } from "@/components/cartoes/cards-page"; -import { getUserId } from "@/lib/auth/server"; -import { fetchInativosForUser } from "../data"; - -export default async function InativosPage() { - const userId = await getUserId(); - const { cards, accounts, logoOptions } = await fetchInativosForUser(userId); - - return ( -
- -
- ); -} diff --git a/app/(dashboard)/cartoes/page.tsx b/app/(dashboard)/cartoes/page.tsx index 8ce99cf..3cbae11 100644 --- a/app/(dashboard)/cartoes/page.tsx +++ b/app/(dashboard)/cartoes/page.tsx @@ -1,14 +1,20 @@ import { CardsPage } from "@/components/cartoes/cards-page"; import { getUserId } from "@/lib/auth/server"; -import { fetchCardsForUser } from "./data"; +import { fetchAllCardsForUser } from "./data"; export default async function Page() { const userId = await getUserId(); - const { cards, accounts, logoOptions } = await fetchCardsForUser(userId); + const { activeCards, archivedCards, accounts, logoOptions } = + await fetchAllCardsForUser(userId); return (
- +
); } diff --git a/app/(dashboard)/contas/data.ts b/app/(dashboard)/contas/data.ts index 81995da..b1b0d60 100644 --- a/app/(dashboard)/contas/data.ts +++ b/app/(dashboard)/contas/data.ts @@ -169,3 +169,20 @@ export async function fetchInativosForUser( return { accounts, logoOptions }; } + +export async function fetchAllAccountsForUser(userId: string): Promise<{ + activeAccounts: AccountData[]; + archivedAccounts: AccountData[]; + logoOptions: LogoOption[]; +}> { + const [activeData, archivedData] = await Promise.all([ + fetchAccountsForUser(userId), + fetchInativosForUser(userId), + ]); + + return { + activeAccounts: activeData.accounts, + archivedAccounts: archivedData.accounts, + logoOptions: activeData.logoOptions, + }; +} diff --git a/app/(dashboard)/contas/inativos/page.tsx b/app/(dashboard)/contas/inativos/page.tsx deleted file mode 100644 index 8ac4753..0000000 --- a/app/(dashboard)/contas/inativos/page.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { AccountsPage } from "@/components/contas/accounts-page"; -import { getUserId } from "@/lib/auth/server"; -import { fetchInativosForUser } from "../data"; - -export default async function InativosPage() { - const userId = await getUserId(); - const { accounts, logoOptions } = await fetchInativosForUser(userId); - - return ( -
- -
- ); -} diff --git a/app/(dashboard)/contas/page.tsx b/app/(dashboard)/contas/page.tsx index 359395a..972ff5a 100644 --- a/app/(dashboard)/contas/page.tsx +++ b/app/(dashboard)/contas/page.tsx @@ -1,14 +1,19 @@ import { AccountsPage } from "@/components/contas/accounts-page"; import { getUserId } from "@/lib/auth/server"; -import { fetchAccountsForUser } from "./data"; +import { fetchAllAccountsForUser } from "./data"; export default async function Page() { const userId = await getUserId(); - const { accounts, logoOptions } = await fetchAccountsForUser(userId); + const { activeAccounts, archivedAccounts, logoOptions } = + await fetchAllAccountsForUser(userId); return (
- +
); } diff --git a/components/anotacoes/notes-page.tsx b/components/anotacoes/notes-page.tsx index a6a3533..da58bcd 100644 --- a/components/anotacoes/notes-page.tsx +++ b/components/anotacoes/notes-page.tsx @@ -10,6 +10,7 @@ import { import { ConfirmActionDialog } from "@/components/confirm-action-dialog"; import { EmptyState } from "@/components/empty-state"; import { Button } from "@/components/ui/button"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Card } from "../ui/card"; import { NoteCard } from "./note-card"; import { NoteDetailsDialog } from "./note-details-dialog"; @@ -18,10 +19,11 @@ import type { Note } from "./types"; interface NotesPageProps { notes: Note[]; - isArquivadas?: boolean; + archivedNotes: Note[]; } -export function NotesPage({ notes, isArquivadas = false }: NotesPageProps) { +export function NotesPage({ notes, archivedNotes }: NotesPageProps) { + const [activeTab, setActiveTab] = useState("ativas"); const [createOpen, setCreateOpen] = useState(false); const [editOpen, setEditOpen] = useState(false); const [noteToEdit, setNoteToEdit] = useState(null); @@ -32,15 +34,23 @@ export function NotesPage({ notes, isArquivadas = false }: NotesPageProps) { const [arquivarOpen, setArquivarOpen] = useState(false); const [noteToArquivar, setNoteToArquivar] = useState(null); - const sortedNotes = useMemo( - () => - [...notes].sort( + const sortNotes = useCallback( + (list: Note[]) => + [...list].sort( (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(), ), - [notes], + [], ); + const sortedNotes = useMemo(() => sortNotes(notes), [notes, sortNotes]); + const sortedArchivedNotes = useMemo( + () => sortNotes(archivedNotes), + [archivedNotes, sortNotes], + ); + + const isArquivadas = activeTab === "arquivadas"; + const handleCreateOpenChange = useCallback((open: boolean) => { setCreateOpen(open); }, []); @@ -146,56 +156,75 @@ export function NotesPage({ notes, isArquivadas = false }: NotesPageProps) { ? "Desarquivar anotação?" : "Arquivar anotação?"; + const renderNoteList = (list: Note[], isArchived: boolean) => { + if (list.length === 0) { + return ( + + } + title={ + isArchived + ? "Nenhuma anotação arquivada" + : "Nenhuma anotação registrada" + } + description={ + isArchived + ? "As anotações arquivadas aparecerão aqui." + : "Crie anotações personalizadas para acompanhar lembretes, decisões ou observações financeiras importantes." + } + /> + + ); + } + + return ( +
+ {list.map((note) => ( + + ))} +
+ ); + }; + return ( <>
- {!isArquivadas && ( -
- - - Nova anotação - - } - /> -
- )} +
+ + + Nova anotação + + } + /> +
- {sortedNotes.length === 0 ? ( - - } - title={ - isArquivadas - ? "Nenhuma anotação arquivada" - : "Nenhuma anotação registrada" - } - description={ - isArquivadas - ? "As anotações arquivadas aparecerão aqui." - : "Crie anotações personalizadas para acompanhar lembretes, decisões ou observações financeiras importantes." - } - /> - - ) : ( -
- {sortedNotes.map((note) => ( - - ))} -
- )} + + + Ativas + Arquivadas + + + + {renderNoteList(sortedNotes, false)} + + + + {renderNoteList(sortedArchivedNotes, true)} + +
(null); const [removeOpen, setRemoveOpen] = useState(false); const [cardToRemove, setCardToRemove] = useState(null); - const hasCards = cards.length > 0; + const sortCards = useCallback( + (list: Card[]) => + [...list].sort((a, b) => + a.name.localeCompare(b.name, "pt-BR", { sensitivity: "base" }), + ), + [], + ); - const orderedCards = useMemo( - () => - [...cards].sort((a, b) => { - // Coloca inativos no final - const aIsInactive = a.status?.toLowerCase() === "inativo"; - const bIsInactive = b.status?.toLowerCase() === "inativo"; - - if (aIsInactive && !bIsInactive) return 1; - if (!aIsInactive && bIsInactive) return -1; - - // Mesma ordem alfabética dentro de cada grupo - return a.name.localeCompare(b.name, "pt-BR", { sensitivity: "base" }); - }), - [cards], + const orderedCards = useMemo(() => sortCards(cards), [cards, sortCards]); + const orderedArchivedCards = useMemo( + () => sortCards(archivedCards), + [archivedCards, sortCards], ); const handleEdit = useCallback((card: Card) => { @@ -105,64 +103,83 @@ export function CardsPage({ ? `Remover cartão "${cardToRemove.name}"?` : "Remover cartão?"; + const renderCardList = (list: Card[], isArchived: boolean) => { + if (list.length === 0) { + return ( + + } + title={ + isArchived + ? "Nenhum cartão arquivado" + : "Nenhum cartão cadastrado" + } + description={ + isArchived + ? "Os cartões arquivados aparecerão aqui." + : "Adicione seu primeiro cartão para acompanhar limites e faturas com mais controle." + } + /> + + ); + } + + return ( +
+ {list.map((card) => ( + handleEdit(card)} + onInvoice={() => handleInvoice(card)} + onRemove={() => handleRemoveRequest(card)} + /> + ))} +
+ ); + }; + return ( <>
- {!isInativos && ( -
- - - Novo cartão - - } - /> -
- )} +
+ + + Novo cartão + + } + /> +
- {hasCards ? ( -
- {orderedCards.map((card) => ( - handleEdit(card)} - onInvoice={() => handleInvoice(card)} - onRemove={() => handleRemoveRequest(card)} - /> - ))} -
- ) : ( - - } - title={ - isInativos - ? "Nenhum cartão inativo" - : "Nenhum cartão cadastrado" - } - description={ - isInativos - ? "Os cartões inativos aparecerão aqui." - : "Adicione seu primeiro cartão para acompanhar limites e faturas com mais controle." - } - /> - - )} + + + Ativos + Arquivados + + + + {renderCardList(orderedCards, false)} + + + + {renderCardList(orderedArchivedCards, true)} + +
{ @@ -33,10 +34,11 @@ const resolveLogoSrc = (logo: string | null) => { export function AccountsPage({ accounts, + archivedAccounts, logoOptions, - isInativos = false, }: AccountsPageProps) { const router = useRouter(); + const [activeTab, setActiveTab] = useState("ativos"); const [editOpen, setEditOpen] = useState(false); const [selectedAccount, setSelectedAccount] = useState(null); const [removeOpen, setRemoveOpen] = useState(false); @@ -45,19 +47,13 @@ export function AccountsPage({ const [transferFromAccount, setTransferFromAccount] = useState(null); - const hasAccounts = accounts.length > 0; + const sortAccounts = (list: Account[]) => + [...list].sort((a, b) => + a.name.localeCompare(b.name, "pt-BR", { sensitivity: "base" }), + ); - const orderedAccounts = [...accounts].sort((a, b) => { - // Coloca inativas no final - const aIsInactive = a.status?.toLowerCase() === "inativa"; - const bIsInactive = b.status?.toLowerCase() === "inativa"; - - if (aIsInactive && !bIsInactive) return 1; - if (!aIsInactive && bIsInactive) return -1; - - // Mesma ordem alfabética dentro de cada grupo - return a.name.localeCompare(b.name, "pt-BR", { sensitivity: "base" }); - }); + const orderedAccounts = sortAccounts(accounts); + const orderedArchivedAccounts = sortAccounts(archivedAccounts); const handleEdit = (account: Account) => { setSelectedAccount(account); @@ -115,6 +111,67 @@ export function AccountsPage({ ? `Remover conta "${accountToRemove.name}"?` : "Remover conta?"; + const renderAccountList = (list: Account[], isArchived: boolean) => { + if (list.length === 0) { + return ( + + } + title={ + isArchived + ? "Nenhuma conta arquivada" + : "Nenhuma conta cadastrada" + } + description={ + isArchived + ? "As contas arquivadas aparecerão aqui." + : "Cadastre sua primeira conta para começar a organizar os lançamentos." + } + /> + + ); + } + + return ( +
+ {list.map((account) => { + const logoSrc = resolveLogoSrc(account.logo); + + return ( + + ) : undefined + } + onEdit={() => handleEdit(account)} + onRemove={() => handleRemoveRequest(account)} + onTransfer={() => handleTransferRequest(account)} + onViewStatement={() => + router.push(`/contas/${account.id}/extrato`) + } + /> + ); + })} +
+ ); + }; + return ( <>
@@ -131,60 +188,20 @@ export function AccountsPage({ />
- {hasAccounts ? ( -
- {orderedAccounts.map((account) => { - const logoSrc = resolveLogoSrc(account.logo); + + + Ativas + Arquivadas + - return ( - - ) : undefined - } - onEdit={() => handleEdit(account)} - onRemove={() => handleRemoveRequest(account)} - onTransfer={() => handleTransferRequest(account)} - onViewStatement={() => - router.push(`/contas/${account.id}/extrato`) - } - /> - ); - })} -
- ) : ( - - } - title={ - isInativos - ? "Nenhuma conta inativa" - : "Nenhuma conta cadastrada" - } - description={ - isInativos - ? "Não há contas inativas no momento." - : "Cadastre sua primeira conta para começar a organizar os lançamentos." - } - /> - - )} + + {renderAccountList(orderedAccounts, false)} + + + + {renderAccountList(orderedArchivedAccounts, true)} + +