forked from git.gladyson/openmonetis
feat: implementar melhorias em importação, compartilhamento e contas inativas
- Corrigir cálculo de valor na importação de lançamentos parcelados
- Exibir valor total (parcela × quantidade) ao invés do valor da parcela individual
- Permite recriar parcelamentos importados com valor correto
- Permitir que usuários compartilhados se descompartilhem de pagadores
- Adicionar componente PagadorLeaveShareCard na aba Perfil
- Usuário filho pode sair do compartilhamento sem precisar do usuário pai
- Manter autorização bidirecionada na action de remoção de share
- Implementar submenu "Inativos" para contas bancárias
- Criar página /contas/inativos seguindo padrão de cartões
- Filtrar contas ativas e inativas em páginas separadas
- Adicionar ícone e navegação no sidebar
This commit is contained in:
@@ -5,7 +5,7 @@ import { ConfirmActionDialog } from "@/components/confirm-action-dialog";
|
||||
import { EmptyState } from "@/components/empty-state";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card } from "@/components/ui/card";
|
||||
import { RiAddCircleLine, RiBankCardLine } from "@remixicon/react";
|
||||
import { RiAddCircleLine, RiBankCard2Line } from "@remixicon/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
@@ -21,9 +21,15 @@ interface CardsPageProps {
|
||||
cards: Card[];
|
||||
accounts: AccountOption[];
|
||||
logoOptions: string[];
|
||||
isInativos?: boolean;
|
||||
}
|
||||
|
||||
export function CardsPage({ cards, accounts, logoOptions }: CardsPageProps) {
|
||||
export function CardsPage({
|
||||
cards,
|
||||
accounts,
|
||||
logoOptions,
|
||||
isInativos = false,
|
||||
}: CardsPageProps) {
|
||||
const router = useRouter();
|
||||
const [editOpen, setEditOpen] = useState(false);
|
||||
const [selectedCard, setSelectedCard] = useState<Card | null>(null);
|
||||
@@ -102,19 +108,21 @@ export function CardsPage({ cards, accounts, logoOptions }: CardsPageProps) {
|
||||
return (
|
||||
<>
|
||||
<div className="flex w-full flex-col gap-6">
|
||||
<div className="flex justify-start">
|
||||
<CardDialog
|
||||
mode="create"
|
||||
accounts={accounts}
|
||||
logoOptions={logoOptions}
|
||||
trigger={
|
||||
<Button>
|
||||
<RiAddCircleLine className="size-4" />
|
||||
Novo cartão
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{!isInativos && (
|
||||
<div className="flex justify-start">
|
||||
<CardDialog
|
||||
mode="create"
|
||||
accounts={accounts}
|
||||
logoOptions={logoOptions}
|
||||
trigger={
|
||||
<Button>
|
||||
<RiAddCircleLine className="size-4" />
|
||||
Novo cartão
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{hasCards ? (
|
||||
<div className="flex flex-wrap gap-4">
|
||||
@@ -141,9 +149,17 @@ export function CardsPage({ cards, accounts, logoOptions }: CardsPageProps) {
|
||||
) : (
|
||||
<Card className="flex w-full items-center justify-center py-12">
|
||||
<EmptyState
|
||||
media={<RiBankCardLine className="size-6 text-primary" />}
|
||||
title="Nenhum cartão cadastrado"
|
||||
description="Adicione seu primeiro cartão para acompanhar limites e faturas com mais controle."
|
||||
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>
|
||||
)}
|
||||
|
||||
@@ -19,6 +19,7 @@ import type { Account } from "./types";
|
||||
interface AccountsPageProps {
|
||||
accounts: Account[];
|
||||
logoOptions: string[];
|
||||
isInativos?: boolean;
|
||||
}
|
||||
|
||||
const resolveLogoSrc = (logo: string | null) => {
|
||||
@@ -30,7 +31,7 @@ const resolveLogoSrc = (logo: string | null) => {
|
||||
return `/logos/${fileName}`;
|
||||
};
|
||||
|
||||
export function AccountsPage({ accounts, logoOptions }: AccountsPageProps) {
|
||||
export function AccountsPage({ accounts, logoOptions, isInativos = false }: AccountsPageProps) {
|
||||
const router = useRouter();
|
||||
const [editOpen, setEditOpen] = useState(false);
|
||||
const [selectedAccount, setSelectedAccount] = useState<Account | null>(null);
|
||||
@@ -169,8 +170,8 @@ export function AccountsPage({ accounts, logoOptions }: AccountsPageProps) {
|
||||
<Card className="flex min-h-[50vh] w-full items-center justify-center py-12">
|
||||
<EmptyState
|
||||
media={<RiBankLine className="size-6 text-primary" />}
|
||||
title="Nenhuma conta cadastrada"
|
||||
description="Cadastre sua primeira conta para começar a organizar os lançamentos."
|
||||
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>
|
||||
)}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import MoneyValues from "@/components/money-values";
|
||||
import type { PaymentMethodsData } from "@/lib/dashboard/payments/payment-methods";
|
||||
import { getPaymentMethodIcon } from "@/lib/utils/icons";
|
||||
import { RiBankCardLine, RiMoneyDollarCircleLine } from "@remixicon/react";
|
||||
import { RiBankCard2Line, RiMoneyDollarCircleLine } from "@remixicon/react";
|
||||
import { Progress } from "../ui/progress";
|
||||
import { WidgetEmptyState } from "../widget-empty-state";
|
||||
|
||||
@@ -28,7 +28,7 @@ const resolveIcon = (paymentMethod: string | null | undefined) => {
|
||||
return icon;
|
||||
}
|
||||
|
||||
return <RiBankCardLine className="size-5" aria-hidden />;
|
||||
return <RiBankCard2Line className="size-5" aria-hidden />;
|
||||
};
|
||||
|
||||
export function PaymentMethodsWidget({ data }: PaymentMethodsWidgetProps) {
|
||||
|
||||
@@ -130,6 +130,7 @@ interface LancamentosFiltersProps {
|
||||
contaCartaoOptions: ContaCartaoFilterOption[];
|
||||
className?: string;
|
||||
exportButton?: ReactNode;
|
||||
hideAdvancedFilters?: boolean;
|
||||
}
|
||||
|
||||
export function LancamentosFilters({
|
||||
@@ -138,6 +139,7 @@ export function LancamentosFilters({
|
||||
contaCartaoOptions,
|
||||
className,
|
||||
exportButton,
|
||||
hideAdvancedFilters = false,
|
||||
}: LancamentosFiltersProps) {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
@@ -277,22 +279,31 @@ export function LancamentosFilters({
|
||||
|
||||
return (
|
||||
<div className={cn("flex flex-wrap items-center gap-2", className)}>
|
||||
<Input
|
||||
value={searchValue}
|
||||
onChange={(event) => setSearchValue(event.target.value)}
|
||||
placeholder="Buscar"
|
||||
aria-label="Buscar lançamentos"
|
||||
className="w-[250px] text-sm border-dashed"
|
||||
/>
|
||||
|
||||
{exportButton}
|
||||
|
||||
<Drawer direction="right" open={drawerOpen} onOpenChange={setDrawerOpen}>
|
||||
<DrawerTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="text-sm border-dashed relative"
|
||||
aria-label="Abrir filtros"
|
||||
>
|
||||
<RiFilter3Line className="size-4" />
|
||||
Filtros
|
||||
{hasActiveFilters && (
|
||||
<span className="absolute -top-1 -right-1 size-2 rounded-full bg-primary" />
|
||||
)}
|
||||
</Button>
|
||||
</DrawerTrigger>
|
||||
{!hideAdvancedFilters && (
|
||||
<Drawer direction="right" open={drawerOpen} onOpenChange={setDrawerOpen}>
|
||||
<DrawerTrigger asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="text-sm border-dashed relative"
|
||||
aria-label="Abrir filtros"
|
||||
>
|
||||
<RiFilter3Line className="size-4" />
|
||||
Filtros
|
||||
{hasActiveFilters && (
|
||||
<span className="absolute -top-1 -right-1 size-2 rounded-full bg-primary" />
|
||||
)}
|
||||
</Button>
|
||||
</DrawerTrigger>
|
||||
<DrawerContent>
|
||||
<DrawerHeader>
|
||||
<DrawerTitle>Filtros</DrawerTitle>
|
||||
@@ -319,7 +330,9 @@ export function LancamentosFilters({
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">Condição</label>
|
||||
<label className="text-sm font-medium">
|
||||
Condição de Lançamento
|
||||
</label>
|
||||
<FilterSelect
|
||||
param="condicao"
|
||||
placeholder="Todas"
|
||||
@@ -335,7 +348,7 @@ export function LancamentosFilters({
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium">Pagamento</label>
|
||||
<label className="text-sm font-medium">Forma de Pagamento</label>
|
||||
<FilterSelect
|
||||
param="pagamento"
|
||||
placeholder="Todos"
|
||||
@@ -532,15 +545,8 @@ export function LancamentosFilters({
|
||||
</Button>
|
||||
</DrawerFooter>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
|
||||
<Input
|
||||
value={searchValue}
|
||||
onChange={(event) => setSearchValue(event.target.value)}
|
||||
placeholder="Buscar"
|
||||
aria-label="Buscar lançamentos"
|
||||
className="w-[250px] text-sm border-dashed"
|
||||
/>
|
||||
</Drawer>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -734,9 +734,8 @@ export function LancamentosTable({
|
||||
0
|
||||
);
|
||||
|
||||
// Check if all data belongs to current user to determine if filters should be shown
|
||||
const isOwnData = data.every((item) => item.userId === currentUserId);
|
||||
const shouldShowFilters = showFilters && isOwnData;
|
||||
// Check if there's any data from other users
|
||||
const hasOtherUserData = data.some((item) => item.userId !== currentUserId);
|
||||
|
||||
const handleBulkDelete = () => {
|
||||
if (onBulkDelete && selectedCount > 0) {
|
||||
@@ -755,7 +754,7 @@ export function LancamentosTable({
|
||||
};
|
||||
|
||||
const showTopControls =
|
||||
Boolean(onCreate) || Boolean(onMassAdd) || shouldShowFilters;
|
||||
Boolean(onCreate) || Boolean(onMassAdd) || showFilters;
|
||||
|
||||
return (
|
||||
<TooltipProvider>
|
||||
@@ -791,15 +790,16 @@ export function LancamentosTable({
|
||||
) : null}
|
||||
</div>
|
||||
) : (
|
||||
<span className={shouldShowFilters ? "hidden sm:block" : ""} />
|
||||
<span className={showFilters ? "hidden sm:block" : ""} />
|
||||
)}
|
||||
|
||||
{shouldShowFilters ? (
|
||||
{showFilters ? (
|
||||
<LancamentosFilters
|
||||
pagadorOptions={pagadorFilterOptions}
|
||||
categoriaOptions={categoriaFilterOptions}
|
||||
contaCartaoOptions={contaCartaoFilterOptions}
|
||||
className="w-full lg:flex-1 lg:justify-end"
|
||||
hideAdvancedFilters={hasOtherUserData}
|
||||
exportButton={
|
||||
selectedPeriod ? (
|
||||
<LancamentosExport
|
||||
|
||||
@@ -2,7 +2,7 @@ import MoneyValues from "@/components/money-values";
|
||||
import { WidgetEmptyState } from "@/components/widget-empty-state";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import type { PagadorCardUsageItem } from "@/lib/pagadores/details";
|
||||
import { RiBankCardLine } from "@remixicon/react";
|
||||
import { RiBankCard2Line } from "@remixicon/react";
|
||||
import Image from "next/image";
|
||||
|
||||
const resolveLogoPath = (logo?: string | null) => {
|
||||
@@ -36,7 +36,7 @@ export function PagadorCardUsageCard({ items }: PagadorCardUsageCardProps) {
|
||||
<CardContent className="space-y-3 pt-2">
|
||||
{items.length === 0 ? (
|
||||
<WidgetEmptyState
|
||||
icon={<RiBankCardLine className="size-6 text-muted-foreground" />}
|
||||
icon={<RiBankCard2Line className="size-6 text-muted-foreground" />}
|
||||
title="Nenhum lançamento com cartão de crédito"
|
||||
description="Quando houver despesas registradas com cartão, elas aparecerão aqui."
|
||||
/>
|
||||
|
||||
114
components/pagadores/details/pagador-leave-share-card.tsx
Normal file
114
components/pagadores/details/pagador-leave-share-card.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
"use client";
|
||||
|
||||
import { deletePagadorShareAction } from "@/app/(dashboard)/pagadores/actions";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { RiLogoutBoxLine } from "@remixicon/react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { useState, useTransition } from "react";
|
||||
import { toast } from "sonner";
|
||||
|
||||
interface PagadorLeaveShareCardProps {
|
||||
shareId: string;
|
||||
pagadorName: string;
|
||||
createdAt: string;
|
||||
}
|
||||
|
||||
export function PagadorLeaveShareCard({
|
||||
shareId,
|
||||
pagadorName,
|
||||
createdAt,
|
||||
}: PagadorLeaveShareCardProps) {
|
||||
const router = useRouter();
|
||||
const [isPending, startTransition] = useTransition();
|
||||
const [showConfirm, setShowConfirm] = useState(false);
|
||||
|
||||
const handleLeave = () => {
|
||||
startTransition(async () => {
|
||||
const result = await deletePagadorShareAction({ shareId });
|
||||
|
||||
if (!result.success) {
|
||||
toast.error(result.error);
|
||||
return;
|
||||
}
|
||||
|
||||
toast.success("Você saiu do compartilhamento.");
|
||||
router.push("/pagadores");
|
||||
});
|
||||
};
|
||||
|
||||
const formattedDate = new Date(createdAt).toLocaleDateString("pt-BR", {
|
||||
day: "2-digit",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
});
|
||||
|
||||
return (
|
||||
<Card className="border">
|
||||
<CardHeader>
|
||||
<CardTitle className="text-base font-semibold">
|
||||
Acesso Compartilhado
|
||||
</CardTitle>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Você tem acesso somente leitura aos dados de{" "}
|
||||
<strong>{pagadorName}</strong>.
|
||||
</p>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="flex flex-col gap-2 rounded-lg border border-dashed p-4 text-sm">
|
||||
<span className="text-xs font-semibold uppercase text-muted-foreground/80">
|
||||
Informações do compartilhamento
|
||||
</span>
|
||||
<div className="flex flex-col gap-1">
|
||||
<p className="text-sm">
|
||||
<span className="text-muted-foreground">Acesso desde:</span>{" "}
|
||||
<strong>{formattedDate}</strong>
|
||||
</p>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Você pode visualizar os lançamentos, mas não pode criar ou editar.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{!showConfirm ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={() => setShowConfirm(true)}
|
||||
className="w-full"
|
||||
>
|
||||
<RiLogoutBoxLine className="size-4" />
|
||||
Sair do compartilhamento
|
||||
</Button>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm font-medium text-destructive">
|
||||
Tem certeza que deseja sair? Você perderá o acesso aos dados de{" "}
|
||||
{pagadorName}.
|
||||
</p>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={() => setShowConfirm(false)}
|
||||
disabled={isPending}
|
||||
className="flex-1"
|
||||
>
|
||||
Cancelar
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="destructive"
|
||||
onClick={handleLeave}
|
||||
disabled={isPending}
|
||||
className="flex-1"
|
||||
>
|
||||
{isPending ? "Saindo..." : "Confirmar saída"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@@ -1,46 +1,46 @@
|
||||
"use client";
|
||||
|
||||
import { formatCurrency, formatPercentageChange } from "@/lib/relatorios/utils";
|
||||
import { RiArrowDownLine, RiArrowUpLine } from "@remixicon/react";
|
||||
import { cn } from "@/lib/utils/ui";
|
||||
import { RiArrowDownLine, RiArrowUpLine } from "@remixicon/react";
|
||||
|
||||
interface CategoryCellProps {
|
||||
value: number;
|
||||
previousValue: number;
|
||||
categoryType: "despesa" | "receita";
|
||||
isFirstMonth: boolean;
|
||||
value: number;
|
||||
previousValue: number;
|
||||
categoryType: "despesa" | "receita";
|
||||
isFirstMonth: boolean;
|
||||
}
|
||||
|
||||
export function CategoryCell({
|
||||
value,
|
||||
previousValue,
|
||||
categoryType,
|
||||
isFirstMonth,
|
||||
value,
|
||||
previousValue,
|
||||
categoryType,
|
||||
isFirstMonth,
|
||||
}: CategoryCellProps) {
|
||||
const percentageChange =
|
||||
!isFirstMonth && previousValue !== 0
|
||||
? ((value - previousValue) / previousValue) * 100
|
||||
: null;
|
||||
const percentageChange =
|
||||
!isFirstMonth && previousValue !== 0
|
||||
? ((value - previousValue) / previousValue) * 100
|
||||
: null;
|
||||
|
||||
const isIncrease = percentageChange !== null && percentageChange > 0;
|
||||
const isDecrease = percentageChange !== null && percentageChange < 0;
|
||||
const isIncrease = percentageChange !== null && percentageChange > 0;
|
||||
const isDecrease = percentageChange !== null && percentageChange < 0;
|
||||
|
||||
return (
|
||||
<div className="flex flex-col items-end gap-0.5">
|
||||
<span className="font-medium">{formatCurrency(value)}</span>
|
||||
{!isFirstMonth && percentageChange !== null && (
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center gap-0.5 text-xs",
|
||||
isIncrease && "text-red-600 dark:text-red-400",
|
||||
isDecrease && "text-green-600 dark:text-green-400"
|
||||
)}
|
||||
>
|
||||
{isIncrease && <RiArrowUpLine className="h-3 w-3" />}
|
||||
{isDecrease && <RiArrowDownLine className="h-3 w-3" />}
|
||||
<span>{formatPercentageChange(percentageChange)}</span>
|
||||
</div>
|
||||
)}
|
||||
return (
|
||||
<div className="flex flex-col items-end gap-0.5 min-h-9">
|
||||
<span className="font-medium">{formatCurrency(value)}</span>
|
||||
{!isFirstMonth && percentageChange !== null && (
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center gap-0.5 text-xs",
|
||||
isIncrease && "text-red-600 dark:text-red-400",
|
||||
isDecrease && "text-green-600 dark:text-green-400"
|
||||
)}
|
||||
>
|
||||
{isIncrease && <RiArrowUpLine className="h-3 w-3" />}
|
||||
{isDecrease && <RiArrowDownLine className="h-3 w-3" />}
|
||||
<span>{formatPercentageChange(percentageChange)}</span>
|
||||
</div>
|
||||
);
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -9,13 +9,12 @@ import {
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { getIconComponent } from "@/lib/utils/icons";
|
||||
import { formatPeriodLabel } from "@/lib/relatorios/utils";
|
||||
import type { CategoryReportData } from "@/lib/relatorios/types";
|
||||
import { CategoryCell } from "./category-cell";
|
||||
import { formatCurrency } from "@/lib/relatorios/utils";
|
||||
import { Card } from "../ui/card";
|
||||
import { formatCurrency, formatPeriodLabel } from "@/lib/relatorios/utils";
|
||||
import { getIconComponent } from "@/lib/utils/icons";
|
||||
import DotIcon from "../dot-icon";
|
||||
import { Card } from "../ui/card";
|
||||
import { CategoryCell } from "./category-cell";
|
||||
|
||||
interface CategoryReportTableProps {
|
||||
data: CategoryReportData;
|
||||
@@ -88,16 +87,19 @@ export function CategoryReportTable({ data }: CategoryReportTableProps) {
|
||||
|
||||
<TableFooter>
|
||||
<TableRow>
|
||||
<TableCell>Total Geral</TableCell>
|
||||
<TableCell className="min-h-[2.5rem]">Total Geral</TableCell>
|
||||
{periods.map((period) => {
|
||||
const periodTotal = totals.get(period) ?? 0;
|
||||
return (
|
||||
<TableCell key={period} className="text-right font-semibold">
|
||||
<TableCell
|
||||
key={period}
|
||||
className="text-right font-semibold min-h-8"
|
||||
>
|
||||
{formatCurrency(periodTotal)}
|
||||
</TableCell>
|
||||
);
|
||||
})}
|
||||
<TableCell className="text-right font-semibold">
|
||||
<TableCell className="text-right font-semibold min-h-8">
|
||||
{formatCurrency(grandTotal)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
import {
|
||||
RiArchiveLine,
|
||||
RiArrowLeftRightLine,
|
||||
RiBankCardLine,
|
||||
RiBankCard2Line,
|
||||
RiBankLine,
|
||||
RiCalendarEventLine,
|
||||
RiDashboardLine,
|
||||
RiFileChartLine,
|
||||
RiFundsLine,
|
||||
RiGroupLine,
|
||||
RiNoCreditCardLine,
|
||||
RiPriceTag3Line,
|
||||
RiSettingsLine,
|
||||
RiSparklingLine,
|
||||
RiTodoLine,
|
||||
RiEyeOffLine,
|
||||
type RemixiconComponentType,
|
||||
} from "@remixicon/react";
|
||||
|
||||
@@ -98,12 +100,28 @@ export function createSidebarNavData(pagadores: PagadorLike[]): SidebarNavData {
|
||||
{
|
||||
title: "Cartões",
|
||||
url: "/cartoes",
|
||||
icon: RiBankCardLine,
|
||||
icon: RiBankCard2Line,
|
||||
items: [
|
||||
{
|
||||
title: "Inativos",
|
||||
url: "/cartoes/inativos",
|
||||
key: "cartoes-inativos",
|
||||
icon: RiNoCreditCardLine,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Contas",
|
||||
url: "/contas",
|
||||
icon: RiBankLine,
|
||||
items: [
|
||||
{
|
||||
title: "Inativas",
|
||||
url: "/contas/inativos",
|
||||
key: "contas-inativos",
|
||||
icon: RiEyeOffLine,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Orçamentos",
|
||||
|
||||
@@ -21,7 +21,7 @@ import {
|
||||
import { getAvatarSrc } from "@/lib/pagadores/utils";
|
||||
import {
|
||||
RiArrowRightSLine,
|
||||
RiStackshareLine,
|
||||
RiUserSharedLine,
|
||||
type RemixiconComponentType,
|
||||
} from "@remixicon/react";
|
||||
import Link from "next/link";
|
||||
@@ -182,7 +182,7 @@ export function NavMain({ sections }: { sections: NavSection[] }) {
|
||||
) : null}
|
||||
<span>{subItem.title}</span>
|
||||
{subItem.isShared ? (
|
||||
<RiStackshareLine className="size-3.5 text-muted-foreground" />
|
||||
<RiUserSharedLine className="size-3.5 text-muted-foreground" />
|
||||
) : null}
|
||||
</Link>
|
||||
</SidebarMenuSubButton>
|
||||
|
||||
Reference in New Issue
Block a user