refactor(ui): unificar páginas ativas/arquivadas com tabs (v1.3.1)

Substitui rotas separadas de inativos/arquivados por tabs inline em
Cartões, Contas e Anotações, seguindo o padrão já usado em Categorias.
Remove sub-links da sidebar e padroniza nomenclatura para "Arquivados".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Felipe Coutinho
2026-02-06 13:20:15 +00:00
parent 6f5c41a4cf
commit 4152a27f4d
15 changed files with 330 additions and 278 deletions

View File

@@ -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/), 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/). 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 ## [1.3.0] - 2026-02-06
### Adicionado ### Adicionado

View File

@@ -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 (
<main className="flex flex-col items-start gap-6">
<NotesPage notes={notes} isArquivadas={true} />
</main>
);
}

View File

@@ -52,6 +52,17 @@ export async function fetchNotesForUser(userId: string): Promise<NoteData[]> {
}); });
} }
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( export async function fetchArquivadasForUser(
userId: string, userId: string,
): Promise<NoteData[]> { ): Promise<NoteData[]> {

View File

@@ -1,14 +1,14 @@
import { NotesPage } from "@/components/anotacoes/notes-page"; import { NotesPage } from "@/components/anotacoes/notes-page";
import { getUserId } from "@/lib/auth/server"; import { getUserId } from "@/lib/auth/server";
import { fetchNotesForUser } from "./data"; import { fetchAllNotesForUser } from "./data";
export default async function Page() { export default async function Page() {
const userId = await getUserId(); const userId = await getUserId();
const notes = await fetchNotesForUser(userId); const { activeNotes, archivedNotes } = await fetchAllNotesForUser(userId);
return ( return (
<main className="flex flex-col items-start gap-6"> <main className="flex flex-col items-start gap-6">
<NotesPage notes={notes} /> <NotesPage notes={activeNotes} archivedNotes={archivedNotes} />
</main> </main>
); );
} }

View File

@@ -211,3 +211,22 @@ export async function fetchInativosForUser(userId: string): Promise<{
return { cards, accounts, logoOptions }; 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,
};
}

View File

@@ -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 (
<main className="flex flex-col items-start gap-6">
<CardsPage
cards={cards}
accounts={accounts}
logoOptions={logoOptions}
isInativos={true}
/>
</main>
);
}

View File

@@ -1,14 +1,20 @@
import { CardsPage } from "@/components/cartoes/cards-page"; import { CardsPage } from "@/components/cartoes/cards-page";
import { getUserId } from "@/lib/auth/server"; import { getUserId } from "@/lib/auth/server";
import { fetchCardsForUser } from "./data"; import { fetchAllCardsForUser } from "./data";
export default async function Page() { export default async function Page() {
const userId = await getUserId(); const userId = await getUserId();
const { cards, accounts, logoOptions } = await fetchCardsForUser(userId); const { activeCards, archivedCards, accounts, logoOptions } =
await fetchAllCardsForUser(userId);
return ( return (
<main className="flex flex-col items-start gap-6"> <main className="flex flex-col items-start gap-6">
<CardsPage cards={cards} accounts={accounts} logoOptions={logoOptions} /> <CardsPage
cards={activeCards}
archivedCards={archivedCards}
accounts={accounts}
logoOptions={logoOptions}
/>
</main> </main>
); );
} }

View File

@@ -169,3 +169,20 @@ export async function fetchInativosForUser(
return { accounts, logoOptions }; 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,
};
}

View File

@@ -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 (
<main className="flex flex-col items-start gap-6">
<AccountsPage
accounts={accounts}
logoOptions={logoOptions}
isInativos={true}
/>
</main>
);
}

View File

@@ -1,14 +1,19 @@
import { AccountsPage } from "@/components/contas/accounts-page"; import { AccountsPage } from "@/components/contas/accounts-page";
import { getUserId } from "@/lib/auth/server"; import { getUserId } from "@/lib/auth/server";
import { fetchAccountsForUser } from "./data"; import { fetchAllAccountsForUser } from "./data";
export default async function Page() { export default async function Page() {
const userId = await getUserId(); const userId = await getUserId();
const { accounts, logoOptions } = await fetchAccountsForUser(userId); const { activeAccounts, archivedAccounts, logoOptions } =
await fetchAllAccountsForUser(userId);
return ( return (
<main className="flex flex-col items-start gap-6"> <main className="flex flex-col items-start gap-6">
<AccountsPage accounts={accounts} logoOptions={logoOptions} /> <AccountsPage
accounts={activeAccounts}
archivedAccounts={archivedAccounts}
logoOptions={logoOptions}
/>
</main> </main>
); );
} }

View File

@@ -10,6 +10,7 @@ import {
import { ConfirmActionDialog } from "@/components/confirm-action-dialog"; import { ConfirmActionDialog } from "@/components/confirm-action-dialog";
import { EmptyState } from "@/components/empty-state"; import { EmptyState } from "@/components/empty-state";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Card } from "../ui/card"; import { Card } from "../ui/card";
import { NoteCard } from "./note-card"; import { NoteCard } from "./note-card";
import { NoteDetailsDialog } from "./note-details-dialog"; import { NoteDetailsDialog } from "./note-details-dialog";
@@ -18,10 +19,11 @@ import type { Note } from "./types";
interface NotesPageProps { interface NotesPageProps {
notes: Note[]; 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 [createOpen, setCreateOpen] = useState(false);
const [editOpen, setEditOpen] = useState(false); const [editOpen, setEditOpen] = useState(false);
const [noteToEdit, setNoteToEdit] = useState<Note | null>(null); const [noteToEdit, setNoteToEdit] = useState<Note | null>(null);
@@ -32,15 +34,23 @@ export function NotesPage({ notes, isArquivadas = false }: NotesPageProps) {
const [arquivarOpen, setArquivarOpen] = useState(false); const [arquivarOpen, setArquivarOpen] = useState(false);
const [noteToArquivar, setNoteToArquivar] = useState<Note | null>(null); const [noteToArquivar, setNoteToArquivar] = useState<Note | null>(null);
const sortedNotes = useMemo( const sortNotes = useCallback(
() => (list: Note[]) =>
[...notes].sort( [...list].sort(
(a, b) => (a, b) =>
new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(), 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) => { const handleCreateOpenChange = useCallback((open: boolean) => {
setCreateOpen(open); setCreateOpen(open);
}, []); }, []);
@@ -146,56 +156,75 @@ export function NotesPage({ notes, isArquivadas = false }: NotesPageProps) {
? "Desarquivar anotação?" ? "Desarquivar anotação?"
: "Arquivar anotação?"; : "Arquivar anotação?";
const renderNoteList = (list: Note[], isArchived: boolean) => {
if (list.length === 0) {
return (
<Card className="flex min-h-[50vh] w-full items-center justify-center py-12">
<EmptyState
media={<RiTodoLine className="size-6 text-primary" />}
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."
}
/>
</Card>
);
}
return (
<div className="flex flex-wrap gap-4">
{list.map((note) => (
<NoteCard
key={note.id}
note={note}
onEdit={handleEditRequest}
onDetails={handleDetailsRequest}
onRemove={handleRemoveRequest}
onArquivar={handleArquivarRequest}
isArquivadas={isArchived}
/>
))}
</div>
);
};
return ( return (
<> <>
<div className="flex w-full flex-col gap-6"> <div className="flex w-full flex-col gap-6">
{!isArquivadas && ( <div className="flex justify-start">
<div className="flex justify-start"> <NoteDialog
<NoteDialog mode="create"
mode="create" open={createOpen}
open={createOpen} onOpenChange={handleCreateOpenChange}
onOpenChange={handleCreateOpenChange} trigger={
trigger={ <Button>
<Button> <RiAddCircleLine className="size-4" />
<RiAddCircleLine className="size-4" /> Nova anotação
Nova anotação </Button>
</Button> }
} />
/> </div>
</div>
)}
{sortedNotes.length === 0 ? ( <Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
<Card className="flex min-h-[50vh] w-full items-center justify-center py-12"> <TabsList>
<EmptyState <TabsTrigger value="ativas">Ativas</TabsTrigger>
media={<RiTodoLine className="size-6 text-primary" />} <TabsTrigger value="arquivadas">Arquivadas</TabsTrigger>
title={ </TabsList>
isArquivadas
? "Nenhuma anotação arquivada" <TabsContent value="ativas" className="mt-4">
: "Nenhuma anotação registrada" {renderNoteList(sortedNotes, false)}
} </TabsContent>
description={
isArquivadas <TabsContent value="arquivadas" className="mt-4">
? "As anotações arquivadas aparecerão aqui." {renderNoteList(sortedArchivedNotes, true)}
: "Crie anotações personalizadas para acompanhar lembretes, decisões ou observações financeiras importantes." </TabsContent>
} </Tabs>
/>
</Card>
) : (
<div className="flex flex-wrap gap-4">
{sortedNotes.map((note) => (
<NoteCard
key={note.id}
note={note}
onEdit={handleEditRequest}
onDetails={handleDetailsRequest}
onRemove={handleRemoveRequest}
onArquivar={handleArquivarRequest}
isArquivadas={isArquivadas}
/>
))}
</div>
)}
</div> </div>
<NoteDialog <NoteDialog

View File

@@ -9,6 +9,7 @@ import { ConfirmActionDialog } from "@/components/confirm-action-dialog";
import { EmptyState } from "@/components/empty-state"; import { EmptyState } from "@/components/empty-state";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Card } from "@/components/ui/card"; import { Card } from "@/components/ui/card";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { CardDialog } from "./card-dialog"; import { CardDialog } from "./card-dialog";
import { CardItem } from "./card-item"; import { CardItem } from "./card-item";
@@ -19,39 +20,36 @@ type AccountOption = {
interface CardsPageProps { interface CardsPageProps {
cards: Card[]; cards: Card[];
archivedCards: Card[];
accounts: AccountOption[]; accounts: AccountOption[];
logoOptions: string[]; logoOptions: string[];
isInativos?: boolean;
} }
export function CardsPage({ export function CardsPage({
cards, cards,
archivedCards,
accounts, accounts,
logoOptions, logoOptions,
isInativos = false,
}: CardsPageProps) { }: CardsPageProps) {
const router = useRouter(); const router = useRouter();
const [activeTab, setActiveTab] = useState("ativos");
const [editOpen, setEditOpen] = useState(false); const [editOpen, setEditOpen] = useState(false);
const [selectedCard, setSelectedCard] = useState<Card | null>(null); const [selectedCard, setSelectedCard] = useState<Card | null>(null);
const [removeOpen, setRemoveOpen] = useState(false); const [removeOpen, setRemoveOpen] = useState(false);
const [cardToRemove, setCardToRemove] = useState<Card | null>(null); const [cardToRemove, setCardToRemove] = useState<Card | null>(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( const orderedCards = useMemo(() => sortCards(cards), [cards, sortCards]);
() => const orderedArchivedCards = useMemo(
[...cards].sort((a, b) => { () => sortCards(archivedCards),
// Coloca inativos no final [archivedCards, sortCards],
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 handleEdit = useCallback((card: Card) => { const handleEdit = useCallback((card: Card) => {
@@ -105,64 +103,83 @@ export function CardsPage({
? `Remover cartão "${cardToRemove.name}"?` ? `Remover cartão "${cardToRemove.name}"?`
: "Remover cartão?"; : "Remover cartão?";
const renderCardList = (list: Card[], isArchived: boolean) => {
if (list.length === 0) {
return (
<Card className="flex w-full items-center justify-center py-12">
<EmptyState
media={<RiBankCard2Line className="size-6 text-primary" />}
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."
}
/>
</Card>
);
}
return (
<div className="flex flex-wrap gap-4">
{list.map((card) => (
<CardItem
key={card.id}
name={card.name}
brand={card.brand}
status={card.status}
closingDay={card.closingDay}
dueDay={card.dueDay}
limit={card.limit}
limitInUse={card.limitInUse ?? null}
limitAvailable={card.limitAvailable ?? card.limit ?? null}
contaName={card.contaName}
logo={card.logo}
note={card.note}
onEdit={() => handleEdit(card)}
onInvoice={() => handleInvoice(card)}
onRemove={() => handleRemoveRequest(card)}
/>
))}
</div>
);
};
return ( return (
<> <>
<div className="flex w-full flex-col gap-6"> <div className="flex w-full flex-col gap-6">
{!isInativos && ( <div className="flex justify-start">
<div className="flex justify-start"> <CardDialog
<CardDialog mode="create"
mode="create" accounts={accounts}
accounts={accounts} logoOptions={logoOptions}
logoOptions={logoOptions} trigger={
trigger={ <Button>
<Button> <RiAddCircleLine className="size-4" />
<RiAddCircleLine className="size-4" /> Novo cartão
Novo cartão </Button>
</Button> }
} />
/> </div>
</div>
)}
{hasCards ? ( <Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
<div className="flex flex-wrap gap-4"> <TabsList>
{orderedCards.map((card) => ( <TabsTrigger value="ativos">Ativos</TabsTrigger>
<CardItem <TabsTrigger value="arquivados">Arquivados</TabsTrigger>
key={card.id} </TabsList>
name={card.name}
brand={card.brand} <TabsContent value="ativos" className="mt-4">
status={card.status} {renderCardList(orderedCards, false)}
closingDay={card.closingDay} </TabsContent>
dueDay={card.dueDay}
limit={card.limit} <TabsContent value="arquivados" className="mt-4">
limitInUse={card.limitInUse ?? null} {renderCardList(orderedArchivedCards, true)}
limitAvailable={card.limitAvailable ?? card.limit ?? null} </TabsContent>
contaName={card.contaName} </Tabs>
logo={card.logo}
note={card.note}
onEdit={() => handleEdit(card)}
onInvoice={() => handleInvoice(card)}
onRemove={() => handleRemoveRequest(card)}
/>
))}
</div>
) : (
<Card className="flex w-full items-center justify-center py-12">
<EmptyState
media={<RiBankCard2Line className="size-6 text-primary" />}
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."
}
/>
</Card>
)}
</div> </div>
<CardDialog <CardDialog

View File

@@ -10,6 +10,7 @@ import { ConfirmActionDialog } from "@/components/confirm-action-dialog";
import { AccountCard } from "@/components/contas/account-card"; import { AccountCard } from "@/components/contas/account-card";
import { EmptyState } from "@/components/empty-state"; import { EmptyState } from "@/components/empty-state";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { getCurrentPeriod } from "@/lib/utils/period"; import { getCurrentPeriod } from "@/lib/utils/period";
import { Card } from "../ui/card"; import { Card } from "../ui/card";
import { AccountDialog } from "./account-dialog"; import { AccountDialog } from "./account-dialog";
@@ -18,8 +19,8 @@ import type { Account } from "./types";
interface AccountsPageProps { interface AccountsPageProps {
accounts: Account[]; accounts: Account[];
archivedAccounts: Account[];
logoOptions: string[]; logoOptions: string[];
isInativos?: boolean;
} }
const resolveLogoSrc = (logo: string | null) => { const resolveLogoSrc = (logo: string | null) => {
@@ -33,10 +34,11 @@ const resolveLogoSrc = (logo: string | null) => {
export function AccountsPage({ export function AccountsPage({
accounts, accounts,
archivedAccounts,
logoOptions, logoOptions,
isInativos = false,
}: AccountsPageProps) { }: AccountsPageProps) {
const router = useRouter(); const router = useRouter();
const [activeTab, setActiveTab] = useState("ativos");
const [editOpen, setEditOpen] = useState(false); const [editOpen, setEditOpen] = useState(false);
const [selectedAccount, setSelectedAccount] = useState<Account | null>(null); const [selectedAccount, setSelectedAccount] = useState<Account | null>(null);
const [removeOpen, setRemoveOpen] = useState(false); const [removeOpen, setRemoveOpen] = useState(false);
@@ -45,19 +47,13 @@ export function AccountsPage({
const [transferFromAccount, setTransferFromAccount] = const [transferFromAccount, setTransferFromAccount] =
useState<Account | null>(null); useState<Account | null>(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) => { const orderedAccounts = sortAccounts(accounts);
// Coloca inativas no final const orderedArchivedAccounts = sortAccounts(archivedAccounts);
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 handleEdit = (account: Account) => { const handleEdit = (account: Account) => {
setSelectedAccount(account); setSelectedAccount(account);
@@ -115,6 +111,67 @@ export function AccountsPage({
? `Remover conta "${accountToRemove.name}"?` ? `Remover conta "${accountToRemove.name}"?`
: "Remover conta?"; : "Remover conta?";
const renderAccountList = (list: Account[], isArchived: boolean) => {
if (list.length === 0) {
return (
<Card className="flex min-h-[50vh] w-full items-center justify-center py-12">
<EmptyState
media={<RiBankLine className="size-6 text-primary" />}
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."
}
/>
</Card>
);
}
return (
<div className="flex flex-wrap gap-4">
{list.map((account) => {
const logoSrc = resolveLogoSrc(account.logo);
return (
<AccountCard
key={account.id}
accountName={account.name}
accountType={`${account.accountType}`}
balance={account.balance ?? account.initialBalance ?? 0}
status={account.status}
excludeFromBalance={account.excludeFromBalance}
excludeInitialBalanceFromIncome={
account.excludeInitialBalanceFromIncome
}
icon={
logoSrc ? (
<Image
src={logoSrc}
alt={`Logo da conta ${account.name}`}
width={42}
height={42}
className="rounded-lg"
/>
) : undefined
}
onEdit={() => handleEdit(account)}
onRemove={() => handleRemoveRequest(account)}
onTransfer={() => handleTransferRequest(account)}
onViewStatement={() =>
router.push(`/contas/${account.id}/extrato`)
}
/>
);
})}
</div>
);
};
return ( return (
<> <>
<div className="flex w-full flex-col gap-6"> <div className="flex w-full flex-col gap-6">
@@ -131,60 +188,20 @@ export function AccountsPage({
/> />
</div> </div>
{hasAccounts ? ( <Tabs value={activeTab} onValueChange={setActiveTab} className="w-full">
<div className="flex flex-wrap gap-4"> <TabsList>
{orderedAccounts.map((account) => { <TabsTrigger value="ativos">Ativas</TabsTrigger>
const logoSrc = resolveLogoSrc(account.logo); <TabsTrigger value="arquivados">Arquivadas</TabsTrigger>
</TabsList>
return ( <TabsContent value="ativos" className="mt-4">
<AccountCard {renderAccountList(orderedAccounts, false)}
key={account.id} </TabsContent>
accountName={account.name}
accountType={`${account.accountType}`} <TabsContent value="arquivados" className="mt-4">
balance={account.balance ?? account.initialBalance ?? 0} {renderAccountList(orderedArchivedAccounts, true)}
status={account.status} </TabsContent>
excludeFromBalance={account.excludeFromBalance} </Tabs>
excludeInitialBalanceFromIncome={
account.excludeInitialBalanceFromIncome
}
icon={
logoSrc ? (
<Image
src={logoSrc}
alt={`Logo da conta ${account.name}`}
width={42}
height={42}
className="rounded-lg"
/>
) : undefined
}
onEdit={() => handleEdit(account)}
onRemove={() => handleRemoveRequest(account)}
onTransfer={() => handleTransferRequest(account)}
onViewStatement={() =>
router.push(`/contas/${account.id}/extrato`)
}
/>
);
})}
</div>
) : (
<Card className="flex min-h-[50vh] w-full items-center justify-center py-12">
<EmptyState
media={<RiBankLine className="size-6 text-primary" />}
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."
}
/>
</Card>
)}
</div> </div>
<AccountDialog <AccountDialog

View File

@@ -1,17 +1,14 @@
import { import {
type RemixiconComponentType, type RemixiconComponentType,
RiArchiveLine,
RiArrowLeftRightLine, RiArrowLeftRightLine,
RiBankCard2Line, RiBankCard2Line,
RiBankLine, RiBankLine,
RiCalendarEventLine, RiCalendarEventLine,
RiDashboardLine, RiDashboardLine,
RiEyeOffLine,
RiFileChartLine, RiFileChartLine,
RiFundsLine, RiFundsLine,
RiGroupLine, RiGroupLine,
RiInboxLine, RiInboxLine,
RiNoCreditCardLine,
RiPriceTag3Line, RiPriceTag3Line,
RiSettings2Line, RiSettings2Line,
RiSparklingLine, RiSparklingLine,
@@ -116,27 +113,11 @@ export function createSidebarNavData(
title: "Cartões", title: "Cartões",
url: "/cartoes", url: "/cartoes",
icon: RiBankCard2Line, icon: RiBankCard2Line,
items: [
{
title: "Inativos",
url: "/cartoes/inativos",
key: "cartoes-inativos",
icon: RiNoCreditCardLine,
},
],
}, },
{ {
title: "Contas", title: "Contas",
url: "/contas", url: "/contas",
icon: RiBankLine, icon: RiBankLine,
items: [
{
title: "Inativas",
url: "/contas/inativos",
key: "contas-inativos",
icon: RiEyeOffLine,
},
],
}, },
{ {
title: "Orçamentos", title: "Orçamentos",
@@ -163,14 +144,6 @@ export function createSidebarNavData(
title: "Anotações", title: "Anotações",
url: "/anotacoes", url: "/anotacoes",
icon: RiTodoLine, icon: RiTodoLine,
items: [
{
title: "Arquivadas",
url: "/anotacoes/arquivadas",
key: "anotacoes-arquivadas",
icon: RiArchiveLine,
},
],
}, },
], ],
}, },

View File

@@ -1,6 +1,6 @@
{ {
"name": "opensheets", "name": "opensheets",
"version": "1.3.0", "version": "1.3.1",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev --turbopack", "dev": "next dev --turbopack",