forked from git.gladyson/openmonetis
fix: corrige timezone e seleção de parcelas na análise de parcelas
- Ajusta timezone em datas de vencimento para evitar dia anterior - Corrige bug ao selecionar parcelas individuais (React state) - Melhora UI: exibe Total/Pendente sempre, Selecionado inline
This commit is contained in:
@@ -81,7 +81,8 @@ export function InstallmentAnalysisPage({
|
|||||||
installmentId: string
|
installmentId: string
|
||||||
) => {
|
) => {
|
||||||
const newMap = new Map(selectedInstallments);
|
const newMap = new Map(selectedInstallments);
|
||||||
const current = newMap.get(seriesId) || new Set<string>();
|
// Criar uma NOVA instância do Set para React detectar a mudança
|
||||||
|
const current = new Set(newMap.get(seriesId) || []);
|
||||||
|
|
||||||
if (current.has(installmentId)) {
|
if (current.has(installmentId)) {
|
||||||
current.delete(installmentId);
|
current.delete(installmentId);
|
||||||
|
|||||||
@@ -41,18 +41,26 @@ export function InstallmentGroupCard({
|
|||||||
selectedInstallments.size === unpaidInstallments.length &&
|
selectedInstallments.size === unpaidInstallments.length &&
|
||||||
unpaidInstallments.length > 0;
|
unpaidInstallments.length > 0;
|
||||||
|
|
||||||
const isPartiallySelected =
|
|
||||||
selectedInstallments.size > 0 &&
|
|
||||||
selectedInstallments.size < unpaidInstallments.length;
|
|
||||||
|
|
||||||
const progress =
|
const progress =
|
||||||
group.totalInstallments > 0
|
group.totalInstallments > 0
|
||||||
? (group.paidInstallments / group.totalInstallments) * 100
|
? (group.paidInstallments / group.totalInstallments) * 100
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
const selectedAmount = group.pendingInstallments
|
const selectedAmount = group.pendingInstallments
|
||||||
.filter((i) => selectedInstallments.has(i.id))
|
.filter((i) => selectedInstallments.has(i.id) && !i.isSettled)
|
||||||
.reduce((sum, i) => sum + i.amount, 0);
|
.reduce((sum, i) => sum + Number(i.amount), 0);
|
||||||
|
|
||||||
|
// Calcular valor total de todas as parcelas (pagas + pendentes)
|
||||||
|
const totalAmount = group.pendingInstallments.reduce(
|
||||||
|
(sum, i) => sum + i.amount,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
|
// Calcular valor pendente (apenas não pagas)
|
||||||
|
const pendingAmount = unpaidInstallments.reduce(
|
||||||
|
(sum, i) => sum + i.amount,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card className={cn(isFullySelected && "border-primary/50")}>
|
<Card className={cn(isFullySelected && "border-primary/50")}>
|
||||||
@@ -84,22 +92,23 @@ export function InstallmentGroupCard({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="shrink-0 flex items-center gap-0.5">
|
<div className="shrink-0 flex items-center gap-3">
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<span className="text-xs text-muted-foreground">Total:</span>
|
||||||
<MoneyValues
|
<MoneyValues
|
||||||
amount={group.totalPendingAmount}
|
amount={totalAmount}
|
||||||
className="text-base font-bold"
|
className="text-base font-bold"
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
{selectedInstallments.size > 0 && (
|
<div className="flex items-center gap-1">
|
||||||
<span className="mx-1 text-muted-foreground">|</span>
|
<span className="text-xs text-muted-foreground">
|
||||||
)}
|
Pendente:
|
||||||
|
</span>
|
||||||
{selectedInstallments.size > 0 && (
|
|
||||||
<MoneyValues
|
<MoneyValues
|
||||||
amount={selectedAmount}
|
amount={pendingAmount}
|
||||||
className="text-base text-primary"
|
className="text-sm font-medium text-primary"
|
||||||
/>
|
/>
|
||||||
)}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -109,22 +118,20 @@ export function InstallmentGroupCard({
|
|||||||
<span>
|
<span>
|
||||||
{group.paidInstallments} de {group.totalInstallments} pagas
|
{group.paidInstallments} de {group.totalInstallments} pagas
|
||||||
</span>
|
</span>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
<span>
|
<span>
|
||||||
{unpaidCount} {unpaidCount === 1 ? "pendente" : "pendentes"}
|
{unpaidCount} {unpaidCount === 1 ? "pendente" : "pendentes"}
|
||||||
</span>
|
</span>
|
||||||
|
{selectedInstallments.size > 0 && (
|
||||||
|
<span className="text-primary font-medium">
|
||||||
|
• Selecionado: <MoneyValues amount={selectedAmount} className="text-xs font-medium text-primary inline" />
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Progress value={progress} className="h-2" />
|
<Progress value={progress} className="h-2" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Badges de status */}
|
|
||||||
<div className="mt-2 flex flex-wrap gap-2">
|
|
||||||
{isPartiallySelected && (
|
|
||||||
<Badge variant="secondary" className="text-xs">
|
|
||||||
{selectedInstallments.size} de {unpaidCount} selecionadas
|
|
||||||
</Badge>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Botão de expandir */}
|
{/* Botão de expandir */}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|||||||
@@ -19,7 +19,8 @@ function calculateDueDate(period: string, dueDay: string | null): Date | null {
|
|||||||
const day = parseInt(dueDay, 10);
|
const day = parseInt(dueDay, 10);
|
||||||
if (isNaN(day)) return null;
|
if (isNaN(day)) return null;
|
||||||
|
|
||||||
return new Date(parseInt(year), parseInt(month) - 1, day);
|
// Criar data ao meio-dia para evitar problemas de timezone
|
||||||
|
return new Date(parseInt(year), parseInt(month) - 1, day, 12, 0, 0);
|
||||||
} catch {
|
} catch {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,7 @@
|
|||||||
"@types/pg": "^8.15.6",
|
"@types/pg": "^8.15.6",
|
||||||
"@types/react": "19.2.7",
|
"@types/react": "19.2.7",
|
||||||
"@types/react-dom": "19.2.3",
|
"@types/react-dom": "19.2.3",
|
||||||
|
"baseline-browser-mapping": "^2.8.32",
|
||||||
"depcheck": "^1.4.7",
|
"depcheck": "^1.4.7",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"drizzle-kit": "0.31.7",
|
"drizzle-kit": "0.31.7",
|
||||||
|
|||||||
11
pnpm-lock.yaml
generated
11
pnpm-lock.yaml
generated
@@ -174,6 +174,9 @@ importers:
|
|||||||
'@types/react-dom':
|
'@types/react-dom':
|
||||||
specifier: 19.2.3
|
specifier: 19.2.3
|
||||||
version: 19.2.3(@types/react@19.2.7)
|
version: 19.2.3(@types/react@19.2.7)
|
||||||
|
baseline-browser-mapping:
|
||||||
|
specifier: ^2.8.32
|
||||||
|
version: 2.8.32
|
||||||
depcheck:
|
depcheck:
|
||||||
specifier: ^1.4.7
|
specifier: ^1.4.7
|
||||||
version: 1.4.7
|
version: 1.4.7
|
||||||
@@ -2037,8 +2040,8 @@ packages:
|
|||||||
base64-js@1.5.1:
|
base64-js@1.5.1:
|
||||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||||
|
|
||||||
baseline-browser-mapping@2.8.25:
|
baseline-browser-mapping@2.8.32:
|
||||||
resolution: {integrity: sha512-2NovHVesVF5TXefsGX1yzx1xgr7+m9JQenvz6FQY3qd+YXkKkYiv+vTCc7OriP9mcDZpTC5mAOYN4ocd29+erA==}
|
resolution: {integrity: sha512-OPz5aBThlyLFgxyhdwf/s2+8ab3OvT7AdTNvKHBwpXomIYeXqpUUuT8LrdtxZSsWJ4R4CU1un4XGh5Ez3nlTpw==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
better-auth@1.4.1:
|
better-auth@1.4.1:
|
||||||
@@ -5666,7 +5669,7 @@ snapshots:
|
|||||||
base64-js@1.5.1:
|
base64-js@1.5.1:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
baseline-browser-mapping@2.8.25: {}
|
baseline-browser-mapping@2.8.32: {}
|
||||||
|
|
||||||
better-auth@1.4.1(next@16.0.4(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
|
better-auth@1.4.1(next@16.0.4(@babel/core@7.28.5)(@opentelemetry/api@1.9.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.0(react@19.2.0))(react@19.2.0))(react-dom@19.2.0(react@19.2.0))(react@19.2.0):
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -5728,7 +5731,7 @@ snapshots:
|
|||||||
|
|
||||||
browserslist@4.27.0:
|
browserslist@4.27.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
baseline-browser-mapping: 2.8.25
|
baseline-browser-mapping: 2.8.32
|
||||||
caniuse-lite: 1.0.30001754
|
caniuse-lite: 1.0.30001754
|
||||||
electron-to-chromium: 1.5.249
|
electron-to-chromium: 1.5.249
|
||||||
node-releases: 2.0.27
|
node-releases: 2.0.27
|
||||||
|
|||||||
Reference in New Issue
Block a user