forked from git.gladyson/openmonetis
refactor: melhorar visualização e contagem de parcelas pagas
Ajustes finais na análise de parcelas: - Corrigir contagem de parcelas pagas (agora conta isSettled corretamente) - Estilização aprimorada para parcelas pagas: * Cores verdes do sistema (green-50, green-700, dark mode suportado) * Fundo verde claro com bordas verdes * Texto e valores em verde * Badge "Paga" com variant outline e cores verdes * Line-through com decoração verde semi-transparente - Remover card "Resumo" lateral (informação já está no card principal) - Remover título "Lançamentos Parcelados" da página - Layout full-width sem grid lateral - Remover imports não utilizados (Separator, AnalysisSummaryPanel)
This commit is contained in:
@@ -2,12 +2,10 @@
|
|||||||
|
|
||||||
import MoneyValues from "@/components/money-values";
|
import MoneyValues from "@/components/money-values";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { Separator } from "@/components/ui/separator";
|
|
||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import type { InstallmentAnalysisData } from "./types";
|
import type { InstallmentAnalysisData } from "./types";
|
||||||
import { InstallmentGroupCard } from "./installment-group-card";
|
import { InstallmentGroupCard } from "./installment-group-card";
|
||||||
import { AnalysisSummaryPanel } from "./analysis-summary-panel";
|
|
||||||
import {
|
import {
|
||||||
RiCalculatorLine,
|
RiCalculatorLine,
|
||||||
RiCheckboxBlankLine,
|
RiCheckboxBlankLine,
|
||||||
@@ -98,7 +96,7 @@ export function InstallmentAnalysisPage({
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Calcular totais
|
// Calcular totais
|
||||||
const { totalInstallments, grandTotal, selectedCount } = useMemo(() => {
|
const { grandTotal, selectedCount } = useMemo(() => {
|
||||||
let installmentsSum = 0;
|
let installmentsSum = 0;
|
||||||
let installmentsCount = 0;
|
let installmentsCount = 0;
|
||||||
|
|
||||||
@@ -120,7 +118,6 @@ export function InstallmentAnalysisPage({
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
totalInstallments: installmentsSum,
|
|
||||||
grandTotal: installmentsSum,
|
grandTotal: installmentsSum,
|
||||||
selectedCount: installmentsCount,
|
selectedCount: installmentsCount,
|
||||||
};
|
};
|
||||||
@@ -178,70 +175,46 @@ export function InstallmentAnalysisPage({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="grid gap-4 lg:grid-cols-[1fr_280px]">
|
{/* Seção de Lançamentos Parcelados */}
|
||||||
{/* Conteúdo principal */}
|
{data.installmentGroups.length > 0 && (
|
||||||
<div className="flex flex-col gap-6">
|
<div className="flex flex-col gap-3">
|
||||||
{/* Seção de Lançamentos Parcelados */}
|
{data.installmentGroups.map((group) => (
|
||||||
{data.installmentGroups.length > 0 && (
|
<InstallmentGroupCard
|
||||||
<div className="flex flex-col gap-4">
|
key={group.seriesId}
|
||||||
<div className="flex items-center gap-2">
|
group={group}
|
||||||
<Separator className="flex-1" />
|
selectedInstallments={
|
||||||
<h2 className="text-lg font-semibold">Lançamentos Parcelados</h2>
|
selectedInstallments.get(group.seriesId) || new Set()
|
||||||
<Separator className="flex-1" />
|
}
|
||||||
</div>
|
onToggleGroup={() =>
|
||||||
|
toggleGroupSelection(
|
||||||
<div className="flex flex-col gap-3">
|
group.seriesId,
|
||||||
{data.installmentGroups.map((group) => (
|
group.pendingInstallments
|
||||||
<InstallmentGroupCard
|
.filter((i) => !i.isSettled)
|
||||||
key={group.seriesId}
|
.map((i) => i.id)
|
||||||
group={group}
|
)
|
||||||
selectedInstallments={
|
}
|
||||||
selectedInstallments.get(group.seriesId) || new Set()
|
onToggleInstallment={(installmentId) =>
|
||||||
}
|
toggleInstallmentSelection(group.seriesId, installmentId)
|
||||||
onToggleGroup={() =>
|
}
|
||||||
toggleGroupSelection(
|
|
||||||
group.seriesId,
|
|
||||||
group.pendingInstallments
|
|
||||||
.filter((i) => !i.isSettled)
|
|
||||||
.map((i) => i.id)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
onToggleInstallment={(installmentId) =>
|
|
||||||
toggleInstallmentSelection(group.seriesId, installmentId)
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Estado vazio */}
|
|
||||||
{hasNoData && (
|
|
||||||
<Card>
|
|
||||||
<CardContent className="flex flex-col items-center justify-center gap-3 py-12">
|
|
||||||
<RiCalculatorLine className="size-12 text-muted-foreground/50" />
|
|
||||||
<div className="text-center">
|
|
||||||
<p className="font-medium">Nenhuma parcela pendente</p>
|
|
||||||
<p className="text-sm text-muted-foreground">
|
|
||||||
Você está em dia com seus pagamentos!
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Painel lateral de resumo (sticky) */}
|
|
||||||
{!hasNoData && (
|
|
||||||
<div className="lg:sticky lg:top-4 lg:self-start">
|
|
||||||
<AnalysisSummaryPanel
|
|
||||||
totalInstallments={totalInstallments}
|
|
||||||
grandTotal={grandTotal}
|
|
||||||
selectedCount={selectedCount}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
))}
|
||||||
)}
|
</div>
|
||||||
</div>
|
)}
|
||||||
|
|
||||||
|
{/* Estado vazio */}
|
||||||
|
{hasNoData && (
|
||||||
|
<Card>
|
||||||
|
<CardContent className="flex flex-col items-center justify-center gap-3 py-12">
|
||||||
|
<RiCalculatorLine className="size-12 text-muted-foreground/50" />
|
||||||
|
<div className="text-center">
|
||||||
|
<p className="font-medium">Nenhuma parcela pendente</p>
|
||||||
|
<p className="text-sm text-muted-foreground">
|
||||||
|
Você está em dia com seus pagamentos!
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,9 +41,7 @@ export function InstallmentGroupCard({
|
|||||||
|
|
||||||
const progress =
|
const progress =
|
||||||
group.totalInstallments > 0
|
group.totalInstallments > 0
|
||||||
? ((group.paidInstallments + selectedInstallments.size) /
|
? (group.paidInstallments / group.totalInstallments) * 100
|
||||||
group.totalInstallments) *
|
|
||||||
100
|
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
const selectedAmount = group.pendingInstallments
|
const selectedAmount = group.pendingInstallments
|
||||||
@@ -156,7 +154,7 @@ export function InstallmentGroupCard({
|
|||||||
className={cn(
|
className={cn(
|
||||||
"flex items-center gap-3 rounded-md border p-2 transition-colors",
|
"flex items-center gap-3 rounded-md border p-2 transition-colors",
|
||||||
isSelected && !isPaid && "border-primary/50 bg-primary/5",
|
isSelected && !isPaid && "border-primary/50 bg-primary/5",
|
||||||
isPaid && "bg-muted/50 opacity-60"
|
isPaid && "border-green-200 bg-green-50 dark:border-green-900 dark:bg-green-950/30"
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@@ -168,23 +166,32 @@ export function InstallmentGroupCard({
|
|||||||
|
|
||||||
<div className="flex min-w-0 flex-1 items-center justify-between gap-3">
|
<div className="flex min-w-0 flex-1 items-center justify-between gap-3">
|
||||||
<div className="min-w-0">
|
<div className="min-w-0">
|
||||||
<p className={cn("text-sm font-medium", isPaid && "line-through")}>
|
<p className={cn(
|
||||||
|
"text-sm font-medium",
|
||||||
|
isPaid && "text-green-700 dark:text-green-400 line-through decoration-green-600/50"
|
||||||
|
)}>
|
||||||
Parcela {installment.currentInstallment}/
|
Parcela {installment.currentInstallment}/
|
||||||
{group.totalInstallments}
|
{group.totalInstallments}
|
||||||
{isPaid && (
|
{isPaid && (
|
||||||
<Badge variant="secondary" className="ml-2 text-xs">
|
<Badge variant="outline" className="ml-2 text-xs border-green-500 text-green-700 dark:text-green-400">
|
||||||
Paga
|
Paga
|
||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className={cn(
|
||||||
|
"text-xs",
|
||||||
|
isPaid ? "text-green-600 dark:text-green-500" : "text-muted-foreground"
|
||||||
|
)}>
|
||||||
Vencimento: {dueDate}
|
Vencimento: {dueDate}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MoneyValues
|
<MoneyValues
|
||||||
amount={installment.amount}
|
amount={installment.amount}
|
||||||
className={cn("shrink-0 text-sm", isPaid && "opacity-60")}
|
className={cn(
|
||||||
|
"shrink-0 text-sm",
|
||||||
|
isPaid && "text-green-700 dark:text-green-400"
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -143,10 +143,11 @@ export async function fetchInstallmentAnalysis(
|
|||||||
|
|
||||||
// Calcular quantas parcelas já foram pagas para cada grupo
|
// Calcular quantas parcelas já foram pagas para cada grupo
|
||||||
const installmentGroups = Array.from(seriesMap.values()).map((group) => {
|
const installmentGroups = Array.from(seriesMap.values()).map((group) => {
|
||||||
const minPendingInstallment = Math.min(
|
// Contar quantas parcelas estão marcadas como pagas (settled)
|
||||||
...group.pendingInstallments.map((i) => i.currentInstallment)
|
const paidCount = group.pendingInstallments.filter(
|
||||||
);
|
(i) => i.isSettled
|
||||||
group.paidInstallments = minPendingInstallment - 1;
|
).length;
|
||||||
|
group.paidInstallments = paidCount;
|
||||||
return group;
|
return group;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user