mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-06-10 07:16:01 +00:00
feat(relatorios): refina indicadores e filtros
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { RiExternalLinkLine, RiWallet3Line } from "@remixicon/react";
|
||||
import { RiExternalLinkLine } from "@remixicon/react";
|
||||
import Link from "next/link";
|
||||
import type { DashboardCategoryBreakdownItem } from "@/features/dashboard/categories/category-breakdown-helpers";
|
||||
import { PercentageChangeIndicator } from "@/features/dashboard/components/percentage-change-indicator";
|
||||
@@ -63,7 +63,7 @@ export function CategoryBreakdownListItem({
|
||||
/>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-muted-foreground">
|
||||
<div className="flex flex-wrap items-center gap-x-1 text-xs text-muted-foreground">
|
||||
<span>
|
||||
{formatPercentage(
|
||||
category.percentageOfTotal,
|
||||
@@ -77,10 +77,9 @@ export function CategoryBreakdownListItem({
|
||||
<span
|
||||
className={`flex items-center gap-1 ${budgetExceeded ? "text-destructive" : "text-info"}`}
|
||||
>
|
||||
<RiWallet3Line className="size-3 shrink-0" />
|
||||
{budgetExceeded ? (
|
||||
<>
|
||||
excedeu{" "}
|
||||
Excedeu{" "}
|
||||
<span className="font-medium">
|
||||
{formatCurrency(exceededAmount)}
|
||||
</span>
|
||||
|
||||
@@ -20,6 +20,7 @@ export function InstallmentExpenseListItem({
|
||||
const {
|
||||
compactLabel,
|
||||
isLast,
|
||||
remainingLabel,
|
||||
remainingInstallments,
|
||||
remainingAmount,
|
||||
endDate,
|
||||
@@ -65,15 +66,22 @@ export function InstallmentExpenseListItem({
|
||||
/>
|
||||
</div>
|
||||
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{endDate ? `Termina em ${endDate}` : null}
|
||||
{" · Restante "}
|
||||
<MoneyValues
|
||||
amount={remainingAmount}
|
||||
className="inline-block font-semibold"
|
||||
/>{" "}
|
||||
({remainingInstallments})
|
||||
</p>
|
||||
{remainingInstallments === 0 ? (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{endDate ? `Termina em ${endDate}` : null}
|
||||
{" · Quitado"}
|
||||
</p>
|
||||
) : (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{endDate ? `Termina em ${endDate}` : null}
|
||||
{` · ${remainingLabel}: `}
|
||||
<MoneyValues
|
||||
amount={remainingAmount}
|
||||
className="inline-block font-semibold"
|
||||
/>{" "}
|
||||
({remainingInstallments}x)
|
||||
</p>
|
||||
)}
|
||||
|
||||
<Progress value={progress} className="mt-1 h-2" />
|
||||
</div>
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
import { RiInformationLine } from "@remixicon/react";
|
||||
import {
|
||||
HoverCard,
|
||||
HoverCardContent,
|
||||
HoverCardTrigger,
|
||||
} from "@/shared/components/ui/hover-card";
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@/shared/components/ui/tooltip";
|
||||
|
||||
type MetricsCardInfoButtonProps = {
|
||||
label: string;
|
||||
@@ -19,8 +19,8 @@ export function MetricsCardInfoButton({
|
||||
helpLines,
|
||||
}: MetricsCardInfoButtonProps) {
|
||||
return (
|
||||
<HoverCard openDelay={150}>
|
||||
<HoverCardTrigger asChild>
|
||||
<Tooltip>
|
||||
<TooltipTrigger 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"
|
||||
@@ -28,17 +28,22 @@ export function MetricsCardInfoButton({
|
||||
>
|
||||
<RiInformationLine className="size-4" aria-hidden />
|
||||
</button>
|
||||
</HoverCardTrigger>
|
||||
<HoverCardContent align="start" className="w-80 space-y-3">
|
||||
</TooltipTrigger>
|
||||
<TooltipContent
|
||||
align="start"
|
||||
side="bottom"
|
||||
sideOffset={8}
|
||||
className="max-w-80 space-y-3 p-3 text-left"
|
||||
>
|
||||
<div className="space-y-1">
|
||||
<p className="text-sm font-medium text-foreground">{helpTitle}</p>
|
||||
<p className="text-sm font-medium text-background">{helpTitle}</p>
|
||||
</div>
|
||||
<ul className="space-y-2 text-xs text-muted-foreground">
|
||||
<ul className="space-y-2 text-xs text-background/80">
|
||||
{helpLines.map((line) => (
|
||||
<li key={`${label}-${line}`}>{line}</li>
|
||||
))}
|
||||
</ul>
|
||||
</HoverCardContent>
|
||||
</HoverCard>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -73,6 +73,7 @@ export function WidgetSettingsDialog({
|
||||
</div>
|
||||
</div>
|
||||
<Switch
|
||||
aria-label={`${isVisible ? "Ocultar" : "Exibir"} widget ${widget.title}`}
|
||||
checked={isVisible}
|
||||
onCheckedChange={() => onToggleWidget(widget.id)}
|
||||
/>
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import type { InstallmentExpense } from "@/features/dashboard/expenses/installment-expenses-queries";
|
||||
import {
|
||||
calculateLastInstallmentDate,
|
||||
formatLastInstallmentDate,
|
||||
} from "@/shared/lib/installments/utils";
|
||||
import { calculateLastInstallmentDate } from "@/shared/lib/installments/utils";
|
||||
import { capitalize } from "@/shared/utils/string";
|
||||
|
||||
type InstallmentExpenseDisplay = {
|
||||
compactLabel: string | null;
|
||||
isLast: boolean;
|
||||
remainingLabel: "Próx." | "Aberto";
|
||||
remainingInstallments: number;
|
||||
remainingAmount: number;
|
||||
endDate: string | null;
|
||||
@@ -38,21 +37,30 @@ const isInstallmentLast = (
|
||||
const calculateInstallmentRemainingCount = (
|
||||
currentInstallment: number | null,
|
||||
installmentCount: number | null,
|
||||
isSettled: boolean | null,
|
||||
) => {
|
||||
if (!currentInstallment || !installmentCount) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return Math.max(0, installmentCount - currentInstallment);
|
||||
const includeCurrentInstallment = isSettled !== true;
|
||||
const currentOffset = includeCurrentInstallment ? 1 : 0;
|
||||
|
||||
return Math.max(0, installmentCount - currentInstallment + currentOffset);
|
||||
};
|
||||
|
||||
const calculateInstallmentRemainingAmount = (
|
||||
amount: number,
|
||||
currentInstallment: number | null,
|
||||
installmentCount: number | null,
|
||||
isSettled: boolean | null,
|
||||
) =>
|
||||
amount *
|
||||
calculateInstallmentRemainingCount(currentInstallment, installmentCount);
|
||||
calculateInstallmentRemainingCount(
|
||||
currentInstallment,
|
||||
installmentCount,
|
||||
isSettled,
|
||||
);
|
||||
|
||||
const formatInstallmentEndDate = (
|
||||
period: string,
|
||||
@@ -69,7 +77,12 @@ const formatInstallmentEndDate = (
|
||||
installmentCount,
|
||||
);
|
||||
|
||||
return formatLastInstallmentDate(lastDate);
|
||||
const month = new Intl.DateTimeFormat("pt-BR", {
|
||||
month: "short",
|
||||
timeZone: "UTC",
|
||||
}).format(lastDate);
|
||||
|
||||
return `${capitalize(month)} de ${lastDate.getFullYear()}`;
|
||||
};
|
||||
|
||||
const buildInstallmentProgress = (
|
||||
@@ -89,7 +102,8 @@ const buildInstallmentProgress = (
|
||||
export const buildInstallmentExpenseDisplay = (
|
||||
expense: InstallmentExpense,
|
||||
): InstallmentExpenseDisplay => {
|
||||
const { amount, currentInstallment, installmentCount, period } = expense;
|
||||
const { amount, currentInstallment, installmentCount, isSettled, period } =
|
||||
expense;
|
||||
|
||||
return {
|
||||
compactLabel: buildInstallmentCompactLabel(
|
||||
@@ -97,14 +111,17 @@ export const buildInstallmentExpenseDisplay = (
|
||||
installmentCount,
|
||||
),
|
||||
isLast: isInstallmentLast(currentInstallment, installmentCount),
|
||||
remainingLabel: isSettled === true ? "Próx." : "Aberto",
|
||||
remainingInstallments: calculateInstallmentRemainingCount(
|
||||
currentInstallment,
|
||||
installmentCount,
|
||||
isSettled,
|
||||
),
|
||||
remainingAmount: calculateInstallmentRemainingAmount(
|
||||
amount,
|
||||
currentInstallment,
|
||||
installmentCount,
|
||||
isSettled,
|
||||
),
|
||||
endDate: formatInstallmentEndDate(
|
||||
period,
|
||||
|
||||
@@ -8,6 +8,7 @@ export type InstallmentExpense = {
|
||||
dueDate: Date | null;
|
||||
purchaseDate: Date;
|
||||
period: string;
|
||||
isSettled: boolean | null;
|
||||
};
|
||||
|
||||
export type InstallmentExpensesData = {
|
||||
|
||||
@@ -405,6 +405,7 @@ const buildInstallmentExpensesData = (
|
||||
dueDate: row.dueDate,
|
||||
purchaseDate: row.purchaseDate,
|
||||
period: row.period,
|
||||
isSettled: row.isSettled,
|
||||
}))
|
||||
.sort((a, b) => {
|
||||
const remainingA =
|
||||
|
||||
Reference in New Issue
Block a user