mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-05-10 03:11:46 +00:00
fix(financeiro): alinhar saldo, métricas e relatórios
This commit is contained in:
@@ -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