mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-05-10 11:21:45 +00:00
fix(financeiro): alinhar saldo, métricas e relatórios
This commit is contained in:
@@ -1,12 +1,18 @@
|
||||
import { RiCheckboxCircleFill } from "@remixicon/react";
|
||||
import {
|
||||
buildBillStatusLabel,
|
||||
buildBillWidgetStatusLabel,
|
||||
isBillOverdue,
|
||||
} from "@/features/dashboard/bills-helpers";
|
||||
import type { DashboardBill } from "@/features/dashboard/bills-queries";
|
||||
import { EstablishmentLogo } from "@/shared/components/entity-avatar";
|
||||
import MoneyValues from "@/shared/components/money-values";
|
||||
import { Button } from "@/shared/components/ui/button";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@/shared/components/ui/tooltip";
|
||||
import { cn } from "@/shared/utils/ui";
|
||||
|
||||
type BillListItemProps = {
|
||||
@@ -15,8 +21,13 @@ type BillListItemProps = {
|
||||
};
|
||||
|
||||
export function BillListItem({ bill, onPay }: BillListItemProps) {
|
||||
const statusLabel = buildBillStatusLabel(bill);
|
||||
const statusLabel = buildBillWidgetStatusLabel(bill);
|
||||
const absoluteStatusLabel = buildBillStatusLabel(bill);
|
||||
const overdue = isBillOverdue(bill);
|
||||
const statusTooltipLabel =
|
||||
statusLabel && statusLabel !== absoluteStatusLabel
|
||||
? absoluteStatusLabel
|
||||
: null;
|
||||
|
||||
return (
|
||||
<li className="flex items-center justify-between transition-all duration-300 py-1.5">
|
||||
@@ -29,14 +40,32 @@ export function BillListItem({ bill, onPay }: BillListItemProps) {
|
||||
</span>
|
||||
<div className="flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
|
||||
{statusLabel ? (
|
||||
<span
|
||||
className={cn(
|
||||
"rounded-full py-0.5",
|
||||
bill.isSettled && "text-success",
|
||||
)}
|
||||
>
|
||||
{statusLabel}
|
||||
</span>
|
||||
statusTooltipLabel ? (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span
|
||||
className={cn(
|
||||
"cursor-help rounded-full py-0.5",
|
||||
bill.isSettled && "text-success",
|
||||
)}
|
||||
>
|
||||
{statusLabel}
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top">
|
||||
{statusTooltipLabel}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<span
|
||||
className={cn(
|
||||
"rounded-full py-0.5",
|
||||
bill.isSettled && "text-success",
|
||||
)}
|
||||
>
|
||||
{statusLabel}
|
||||
</span>
|
||||
)
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -56,7 +56,7 @@ export function BillPaymentDialog({
|
||||
}}
|
||||
>
|
||||
<DialogContent
|
||||
className="max-w-[calc(100%-2rem)] sm:max-w-md"
|
||||
className="max-w-[calc(100%-2rem)] sm:max-w-md sm:p-8"
|
||||
onEscapeKeyDown={(event) => {
|
||||
if (isProcessing) {
|
||||
event.preventDefault();
|
||||
@@ -93,7 +93,7 @@ export function BillPaymentDialog({
|
||||
{bill ? (
|
||||
<div className="space-y-3">
|
||||
{/* Card principal */}
|
||||
<div className="rounded-xl border bg-muted/30 p-4">
|
||||
<div className="rounded-xl border p-3">
|
||||
<p className="mb-1 text-xs font-medium text-muted-foreground uppercase tracking-wide">
|
||||
Boleto
|
||||
</p>
|
||||
|
||||
@@ -76,6 +76,9 @@ export function DashboardGridEditable({
|
||||
const [hiddenWidgets, setHiddenWidgets] = useState<string[]>(
|
||||
initialPreferences?.hidden ?? [],
|
||||
);
|
||||
const [myAccountsShowExcluded, setMyAccountsShowExcluded] = useState(
|
||||
initialPreferences?.myAccountsShowExcluded ?? true,
|
||||
);
|
||||
|
||||
// Keep track of original state for cancel
|
||||
const [originalOrder, setOriginalOrder] = useState(widgetOrder);
|
||||
@@ -186,6 +189,7 @@ export function DashboardGridEditable({
|
||||
if (result.success) {
|
||||
setWidgetOrder(DEFAULT_WIDGET_ORDER);
|
||||
setHiddenWidgets([]);
|
||||
setMyAccountsShowExcluded(true);
|
||||
toast.success("Preferências restauradas!");
|
||||
} else {
|
||||
toast.error(result.error ?? "Erro ao restaurar");
|
||||
@@ -361,7 +365,16 @@ export function DashboardGridEditable({
|
||||
icon={widget.icon}
|
||||
action={widget.action}
|
||||
>
|
||||
{widget.component({ data, period })}
|
||||
{widget.component({
|
||||
data,
|
||||
period,
|
||||
widgetPreferences: {
|
||||
order: widgetOrder,
|
||||
hidden: hiddenWidgets,
|
||||
myAccountsShowExcluded,
|
||||
},
|
||||
onMyAccountsShowExcludedChange: setMyAccountsShowExcluded,
|
||||
})}
|
||||
</ExpandableWidgetCard>
|
||||
</div>
|
||||
</SortableWidget>
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
RiScalesLine,
|
||||
RiSubtractLine,
|
||||
} from "@remixicon/react";
|
||||
import { MetricsCardInfoButton } from "@/features/dashboard/components/metrics-card-info-button";
|
||||
import type { DashboardCardMetrics } from "@/features/dashboard/dashboard-metrics-queries";
|
||||
import MoneyValues from "@/shared/components/money-values";
|
||||
import {
|
||||
@@ -36,6 +37,14 @@ const CARDS = [
|
||||
icon: RiArrowDownLine,
|
||||
invertTrend: false,
|
||||
iconClass: "text-success",
|
||||
helpTitle: "Como calculamos receitas",
|
||||
helpLines: [
|
||||
"Somamos os lançamentos do tipo Receita no período selecionado.",
|
||||
"Consideramos lançamentos efetivados e não efetivados do pagador principal (admin).",
|
||||
"Movimentações de contas marcadas como não consideradas no saldo total ficam fora deste card.",
|
||||
"Não entram transferências internas nem lançamentos automáticos de fatura.",
|
||||
"Saldo inicial também fica fora quando a conta está marcada para desconsiderá-lo das receitas.",
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Despesas",
|
||||
@@ -44,14 +53,29 @@ const CARDS = [
|
||||
icon: RiArrowUpLine,
|
||||
invertTrend: true,
|
||||
iconClass: "text-destructive",
|
||||
helpTitle: "Como calculamos despesas",
|
||||
helpLines: [
|
||||
"Somamos os lançamentos do tipo Despesa no período selecionado.",
|
||||
"Consideramos lançamentos efetivados e não efetivados do pagador principal (admin).",
|
||||
"Movimentações de contas marcadas como não consideradas no saldo total ficam fora deste card.",
|
||||
"Não entram transferências internas nem lançamentos automáticos de fatura.",
|
||||
"O valor mostrado é a saída efetiva do período, sempre em número positivo no card.",
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Balanço",
|
||||
subtitle: "Receitas menos despesas",
|
||||
subtitle: "Receitas, despesas e ajustes entre contas",
|
||||
key: "balanco",
|
||||
icon: RiScalesLine,
|
||||
invertTrend: false,
|
||||
iconClass: "text-warning",
|
||||
helpTitle: "Como calculamos o balanço",
|
||||
helpLines: [
|
||||
"Partimos de receitas menos despesas do período.",
|
||||
"Receitas e despesas de contas marcadas como não consideradas no saldo total ficam fora do cálculo base.",
|
||||
"Depois aplicamos ajustes de transferências entre contas consideradas e não consideradas no saldo total.",
|
||||
"Se a transferência entra em conta considerada, soma. Se sai de conta considerada para conta não considerada, subtrai.",
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Previsto",
|
||||
@@ -60,6 +84,13 @@ const CARDS = [
|
||||
icon: RiCalendarCheckLine,
|
||||
invertTrend: false,
|
||||
iconClass: "text-cyan-600",
|
||||
helpTitle: "Como calculamos o previsto",
|
||||
helpLines: [
|
||||
"Acumulamos o balanço mês a mês até o período atual.",
|
||||
"Ele usa a mesma regra do card de balanço em cada mês do histórico.",
|
||||
"Receitas e despesas de contas marcadas como não consideradas no saldo total ficam fora desse acumulado.",
|
||||
"Por isso também reflete ajustes de transferências entre contas consideradas e não consideradas.",
|
||||
],
|
||||
},
|
||||
] as const;
|
||||
|
||||
@@ -104,7 +135,16 @@ export function DashboardMetricsCards({ metrics }: DashboardMetricsCardsProps) {
|
||||
return (
|
||||
<div className="grid grid-cols-1 gap-3 @xl/main:grid-cols-2 @5xl/main:grid-cols-4">
|
||||
{CARDS.map(
|
||||
({ label, subtitle, key, icon: Icon, invertTrend, iconClass }) => {
|
||||
({
|
||||
label,
|
||||
subtitle,
|
||||
key,
|
||||
icon: Icon,
|
||||
invertTrend,
|
||||
iconClass,
|
||||
helpTitle,
|
||||
helpLines,
|
||||
}) => {
|
||||
const metric = metrics[key];
|
||||
const trend = getTrend(metric.current, metric.previous);
|
||||
const TrendIcon = TREND_ICONS[trend];
|
||||
@@ -119,9 +159,14 @@ export function DashboardMetricsCards({ metrics }: DashboardMetricsCardsProps) {
|
||||
<CardHeader>
|
||||
<div className="flex items-start justify-between">
|
||||
<div>
|
||||
<CardTitle className="flex items-center gap-1 tracking-tight">
|
||||
<CardTitle className="flex items-center gap-1.5 tracking-tight">
|
||||
<Icon className={cn("size-4", iconClass)} aria-hidden />
|
||||
{label}
|
||||
<MetricsCardInfoButton
|
||||
label={label}
|
||||
helpTitle={helpTitle}
|
||||
helpLines={helpLines}
|
||||
/>
|
||||
</CardTitle>
|
||||
<CardDescription className="mt-1.5 tracking-tight">
|
||||
{subtitle}
|
||||
|
||||
@@ -4,8 +4,10 @@ import {
|
||||
buildInvoiceDetailsHref,
|
||||
buildInvoiceInitials,
|
||||
formatInvoicePaymentDate,
|
||||
formatInvoiceWidgetPaymentDate,
|
||||
getInvoiceShareLabel,
|
||||
parseInvoiceDueDate,
|
||||
parseInvoiceWidgetDueDate,
|
||||
} from "@/features/dashboard/invoices-helpers";
|
||||
import type { DashboardInvoice } from "@/features/dashboard/invoices-queries";
|
||||
import MoneyValues from "@/shared/components/money-values";
|
||||
@@ -20,6 +22,11 @@ import {
|
||||
HoverCardContent,
|
||||
HoverCardTrigger,
|
||||
} from "@/shared/components/ui/hover-card";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@/shared/components/ui/tooltip";
|
||||
import { INVOICE_PAYMENT_STATUS } from "@/shared/lib/invoices";
|
||||
import { getAvatarSrc } from "@/shared/lib/payers/utils";
|
||||
import { isDateOnlyPast } from "@/shared/utils/date";
|
||||
@@ -31,14 +38,22 @@ type InvoiceListItemProps = {
|
||||
};
|
||||
|
||||
export function InvoiceListItem({ invoice, onPay }: InvoiceListItemProps) {
|
||||
const dueInfo = parseInvoiceDueDate(invoice.period, invoice.dueDay);
|
||||
const dueInfo = parseInvoiceWidgetDueDate(invoice.period, invoice.dueDay);
|
||||
const absoluteDueInfo = parseInvoiceDueDate(invoice.period, invoice.dueDay);
|
||||
const isPaid = invoice.paymentStatus === INVOICE_PAYMENT_STATUS.PAID;
|
||||
const isOverdue =
|
||||
!isPaid && dueInfo.date !== null && isDateOnlyPast(dueInfo.date);
|
||||
const paymentInfo = formatInvoicePaymentDate(invoice.paidAt);
|
||||
const paymentInfo = formatInvoiceWidgetPaymentDate(invoice.paidAt);
|
||||
const absolutePaymentInfo = formatInvoicePaymentDate(invoice.paidAt);
|
||||
const breakdown = invoice.pagadorBreakdown ?? [];
|
||||
const hasBreakdown = breakdown.length > 0;
|
||||
const detailHref = buildInvoiceDetailsHref(invoice.cardId, invoice.period);
|
||||
const dueTooltipLabel =
|
||||
dueInfo.label !== absoluteDueInfo.label ? absoluteDueInfo.label : null;
|
||||
const paymentTooltipLabel =
|
||||
paymentInfo?.label && paymentInfo.label !== absolutePaymentInfo?.label
|
||||
? absolutePaymentInfo?.label
|
||||
: null;
|
||||
|
||||
const linkNode = (
|
||||
<Link
|
||||
@@ -113,9 +128,33 @@ export function InvoiceListItem({ invoice, onPay }: InvoiceListItemProps) {
|
||||
)}
|
||||
|
||||
<div className="flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
|
||||
{!isPaid ? <span>{dueInfo.label}</span> : null}
|
||||
{!isPaid ? (
|
||||
dueTooltipLabel ? (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span className="cursor-help">{dueInfo.label}</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top">{dueTooltipLabel}</TooltipContent>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<span>{dueInfo.label}</span>
|
||||
)
|
||||
) : null}
|
||||
{isPaid && paymentInfo ? (
|
||||
<span className="text-success">{paymentInfo.label}</span>
|
||||
paymentTooltipLabel ? (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span className="cursor-help text-success">
|
||||
{paymentInfo.label}
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top">
|
||||
{paymentTooltipLabel}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
) : (
|
||||
<span className="text-success">{paymentInfo.label}</span>
|
||||
)
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -63,7 +63,7 @@ export function InvoicePaymentDialog({
|
||||
}}
|
||||
>
|
||||
<DialogContent
|
||||
className="max-w-[calc(100%-2rem)] sm:max-w-md"
|
||||
className="max-w-[calc(100%-2rem)] sm:max-w-md sm:p-8"
|
||||
onEscapeKeyDown={(event) => {
|
||||
if (isProcessing) {
|
||||
event.preventDefault();
|
||||
@@ -100,7 +100,7 @@ export function InvoicePaymentDialog({
|
||||
{invoice ? (
|
||||
<div className="space-y-3">
|
||||
{/* Card principal */}
|
||||
<div className="flex items-center gap-3 rounded-xl border bg-muted/30 p-4">
|
||||
<div className="flex items-center gap-3 rounded-xl border p-4">
|
||||
<InvoiceLogo
|
||||
cardName={invoice.cardName}
|
||||
logo={invoice.logo}
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
"use client";
|
||||
|
||||
import { RiInformationLine } from "@remixicon/react";
|
||||
import {
|
||||
HoverCard,
|
||||
HoverCardContent,
|
||||
HoverCardTrigger,
|
||||
} from "@/shared/components/ui/hover-card";
|
||||
|
||||
type MetricsCardInfoButtonProps = {
|
||||
label: string;
|
||||
helpTitle: string;
|
||||
helpLines: readonly string[];
|
||||
};
|
||||
|
||||
export function MetricsCardInfoButton({
|
||||
label,
|
||||
helpTitle,
|
||||
helpLines,
|
||||
}: MetricsCardInfoButtonProps) {
|
||||
return (
|
||||
<HoverCard openDelay={150}>
|
||||
<HoverCardTrigger asChild>
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex items-center rounded-full text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/60"
|
||||
aria-label={`Entenda como ${label.toLowerCase()} é calculado`}
|
||||
>
|
||||
<RiInformationLine className="size-4" aria-hidden />
|
||||
</button>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent align="start" className="w-80 space-y-3">
|
||||
<div className="space-y-1">
|
||||
<p className="text-sm font-medium text-foreground">{helpTitle}</p>
|
||||
</div>
|
||||
<ul className="space-y-2 text-xs text-muted-foreground">
|
||||
{helpLines.map((line) => (
|
||||
<li key={`${label}-${line}`}>{line}</li>
|
||||
))}
|
||||
</ul>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
);
|
||||
}
|
||||
@@ -1,39 +1,123 @@
|
||||
import { RiBarChartBoxLine, RiExternalLinkLine } from "@remixicon/react";
|
||||
"use client";
|
||||
|
||||
import {
|
||||
RiBarChartBoxLine,
|
||||
RiExternalLinkLine,
|
||||
RiEyeLine,
|
||||
RiEyeOffLine,
|
||||
} from "@remixicon/react";
|
||||
import Image from "next/image";
|
||||
import Link from "next/link";
|
||||
import { useTransition } from "react";
|
||||
import { toast } from "sonner";
|
||||
import type { DashboardAccount } from "@/features/dashboard/accounts-queries";
|
||||
import { updateMyAccountsWidgetPreference } from "@/features/dashboard/widgets/actions";
|
||||
import MoneyValues from "@/shared/components/money-values";
|
||||
import { Badge } from "@/shared/components/ui/badge";
|
||||
import { Button } from "@/shared/components/ui/button";
|
||||
import { CardFooter } from "@/shared/components/ui/card";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@/shared/components/ui/tooltip";
|
||||
import { WidgetEmptyState } from "@/shared/components/widget-empty-state";
|
||||
import { resolveLogoSrc } from "@/shared/lib/logo";
|
||||
import { formatPeriodForUrl } from "@/shared/utils/period";
|
||||
|
||||
type MyAccountsWidgetProps = {
|
||||
accounts: DashboardAccount[];
|
||||
showExcludedAccounts: boolean;
|
||||
onShowExcludedAccountsChange?: (value: boolean) => void;
|
||||
totalBalance: number;
|
||||
period: string;
|
||||
};
|
||||
|
||||
export function MyAccountsWidget({
|
||||
accounts,
|
||||
showExcludedAccounts,
|
||||
onShowExcludedAccountsChange,
|
||||
totalBalance,
|
||||
period,
|
||||
}: MyAccountsWidgetProps) {
|
||||
const visibleAccounts = accounts.filter(
|
||||
(account) => !account.excludeFromBalance,
|
||||
);
|
||||
const [isPending, startTransition] = useTransition();
|
||||
|
||||
const excludedAccountsCount = accounts.filter(
|
||||
(account) => account.excludeFromBalance,
|
||||
).length;
|
||||
const visibleAccounts = showExcludedAccounts
|
||||
? accounts
|
||||
: accounts.filter((account) => !account.excludeFromBalance);
|
||||
const displayedAccounts = visibleAccounts.slice(0, 5);
|
||||
const remainingCount = visibleAccounts.length - displayedAccounts.length;
|
||||
const hiddenExcludedAccountsCount = showExcludedAccounts
|
||||
? 0
|
||||
: excludedAccountsCount;
|
||||
const toggleButtonLabel = showExcludedAccounts
|
||||
? "Ocultar contas não consideradas"
|
||||
: "Mostrar contas não consideradas";
|
||||
|
||||
const handleToggleExcludedAccounts = () => {
|
||||
const nextShowExcludedAccounts = !showExcludedAccounts;
|
||||
onShowExcludedAccountsChange?.(nextShowExcludedAccounts);
|
||||
|
||||
startTransition(async () => {
|
||||
const result = await updateMyAccountsWidgetPreference({
|
||||
showExcludedAccounts: nextShowExcludedAccounts,
|
||||
});
|
||||
|
||||
if (!result.success) {
|
||||
onShowExcludedAccountsChange?.(!nextShowExcludedAccounts);
|
||||
toast.error(result.error ?? "Erro ao salvar preferência");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex justify-between py-1">
|
||||
Saldo Total
|
||||
<MoneyValues className="text-2xl" amount={totalBalance} />
|
||||
<div className="flex items-start justify-between gap-3 py-1">
|
||||
<div className="space-y-1">
|
||||
<p className="text-sm text-muted-foreground">Saldo Total</p>
|
||||
<MoneyValues className="text-2xl" amount={totalBalance} />
|
||||
</div>
|
||||
|
||||
{excludedAccountsCount > 0 ? (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
disabled={isPending}
|
||||
className="mt-0.5 text-muted-foreground"
|
||||
aria-label={toggleButtonLabel}
|
||||
onClick={handleToggleExcludedAccounts}
|
||||
>
|
||||
{showExcludedAccounts ? (
|
||||
<RiEyeOffLine className="size-4" aria-hidden />
|
||||
) : (
|
||||
<RiEyeLine className="size-4" aria-hidden />
|
||||
)}
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="left" className="max-w-xs">
|
||||
<p className="text-xs">{toggleButtonLabel}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{hiddenExcludedAccountsCount > 0 ? (
|
||||
<p className="pb-2 text-xs text-muted-foreground">
|
||||
{hiddenExcludedAccountsCount}{" "}
|
||||
{hiddenExcludedAccountsCount === 1
|
||||
? "conta não considerada oculta"
|
||||
: "contas não consideradas ocultas"}
|
||||
</p>
|
||||
) : null}
|
||||
|
||||
<div>
|
||||
{displayedAccounts.length === 0 ? (
|
||||
{accounts.length === 0 ? (
|
||||
<div className="-mt-10">
|
||||
<WidgetEmptyState
|
||||
icon={
|
||||
@@ -43,6 +127,14 @@ export function MyAccountsWidget({
|
||||
description="Cadastre suas contas bancárias para acompanhar os saldos e movimentações."
|
||||
/>
|
||||
</div>
|
||||
) : displayedAccounts.length === 0 ? (
|
||||
<div className="-mt-10">
|
||||
<WidgetEmptyState
|
||||
icon={<RiEyeOffLine className="size-6 text-muted-foreground" />}
|
||||
title="As contas não consideradas estão ocultas"
|
||||
description="Use o botão no topo do widget para mostrá-las novamente."
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<ul className="flex flex-col">
|
||||
{displayedAccounts.map((account) => {
|
||||
@@ -60,6 +152,7 @@ export function MyAccountsWidget({
|
||||
src={logoSrc}
|
||||
alt={`Logo da conta ${account.name}`}
|
||||
fill
|
||||
sizes="38px"
|
||||
className="object-contain rounded-full"
|
||||
/>
|
||||
) : null}
|
||||
@@ -79,6 +172,26 @@ export function MyAccountsWidget({
|
||||
aria-hidden
|
||||
/>
|
||||
</Link>
|
||||
|
||||
{account.excludeFromBalance ? (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<span className="inline-flex cursor-help ml-2">
|
||||
<Badge className="font-normal" variant="info">
|
||||
Não considerada
|
||||
</Badge>
|
||||
</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="top" className="max-w-xs">
|
||||
<p className="text-xs">
|
||||
Esta conta aparece na lista, mas não entra no
|
||||
cálculo do saldo total porque está marcada para
|
||||
desconsiderar do saldo total.
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
) : null}
|
||||
|
||||
<div className="flex flex-wrap items-center gap-2 text-xs text-muted-foreground">
|
||||
<span className="truncate">{account.accountType}</span>
|
||||
</div>
|
||||
@@ -95,7 +208,7 @@ export function MyAccountsWidget({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{visibleAccounts.length > displayedAccounts.length ? (
|
||||
{remainingCount > 0 ? (
|
||||
<CardFooter className="border-border/60 border-t pt-4 text-sm text-muted-foreground">
|
||||
+{remainingCount} contas não exibidas
|
||||
</CardFooter>
|
||||
|
||||
Reference in New Issue
Block a user