forked from git.gladyson/openmonetis
feat(lancamentos): exibe resumo de parcelas e recorrência no formulário
- Adiciona cálculo e exibição do valor por parcela no select (ex: "3x de R$ 33,33") - Mostra resumo dentro do trigger do select ao invés de texto externo - Exibe valores calculados nas opções do dropdown quando há valor preenchido - Renomeia label "Lançamento fixo" para "Repetirá" - Aumenta opções de recorrência de 24 para 47 meses - Reduz espaçamento e altura do textarea de anotação para layout mais compacto Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -10,21 +10,72 @@ import {
|
|||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { LANCAMENTO_CONDITIONS } from "@/lib/lancamentos/constants";
|
import { LANCAMENTO_CONDITIONS } from "@/lib/lancamentos/constants";
|
||||||
import { cn } from "@/lib/utils/ui";
|
import { cn } from "@/lib/utils/ui";
|
||||||
|
import { useMemo } from "react";
|
||||||
import { ConditionSelectContent } from "../../select-items";
|
import { ConditionSelectContent } from "../../select-items";
|
||||||
import type { ConditionSectionProps } from "./lancamento-dialog-types";
|
import type { ConditionSectionProps } from "./lancamento-dialog-types";
|
||||||
|
|
||||||
|
function formatCurrency(value: number): string {
|
||||||
|
return value.toLocaleString("pt-BR", {
|
||||||
|
minimumFractionDigits: 2,
|
||||||
|
maximumFractionDigits: 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
export function ConditionSection({
|
export function ConditionSection({
|
||||||
formState,
|
formState,
|
||||||
onFieldChange,
|
onFieldChange,
|
||||||
showInstallments,
|
showInstallments,
|
||||||
showRecurrence,
|
showRecurrence,
|
||||||
}: ConditionSectionProps) {
|
}: ConditionSectionProps) {
|
||||||
|
const amount = useMemo(() => {
|
||||||
|
const value = Number(formState.amount);
|
||||||
|
return Number.isNaN(value) || value <= 0 ? null : value;
|
||||||
|
}, [formState.amount]);
|
||||||
|
|
||||||
|
const getInstallmentLabel = (count: number) => {
|
||||||
|
if (amount) {
|
||||||
|
const installmentValue = amount / count;
|
||||||
|
return `${count}x de R$ ${formatCurrency(installmentValue)}`;
|
||||||
|
}
|
||||||
|
return `${count}x`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getRecurrenceLabel = (count: number) => {
|
||||||
|
return `${count} meses`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const installmentSummary = useMemo(() => {
|
||||||
|
if (!showInstallments || !formState.installmentCount || !amount) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const count = Number(formState.installmentCount);
|
||||||
|
if (Number.isNaN(count) || count <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return getInstallmentLabel(count);
|
||||||
|
}, [showInstallments, formState.installmentCount, amount]);
|
||||||
|
|
||||||
|
const recurrenceSummary = useMemo(() => {
|
||||||
|
if (!showRecurrence || !formState.recurrenceCount) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const count = Number(formState.recurrenceCount);
|
||||||
|
if (Number.isNaN(count) || count <= 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `Por ${count} meses`;
|
||||||
|
}, [showRecurrence, formState.recurrenceCount]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex w-full flex-col gap-2 md:flex-row">
|
<div className="flex w-full flex-col gap-2 md:flex-row">
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"space-y-2 w-full",
|
"space-y-1 w-full",
|
||||||
showInstallments || showRecurrence ? "md:w-1/2" : "md:w-full"
|
showInstallments || showRecurrence ? "md:w-1/2" : "md:w-full",
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Label htmlFor="condition">Condição</Label>
|
<Label htmlFor="condition">Condição</Label>
|
||||||
@@ -50,40 +101,47 @@ export function ConditionSection({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{showInstallments ? (
|
{showInstallments ? (
|
||||||
<div className="space-y-2 w-full md:w-1/2">
|
<div className="space-y-1 w-full md:w-1/2">
|
||||||
<Label htmlFor="installmentCount">Parcelado em</Label>
|
<Label htmlFor="installmentCount">Parcelado em</Label>
|
||||||
<Select
|
<Select
|
||||||
value={formState.installmentCount}
|
value={formState.installmentCount}
|
||||||
onValueChange={(value) => onFieldChange("installmentCount", value)}
|
onValueChange={(value) => onFieldChange("installmentCount", value)}
|
||||||
>
|
>
|
||||||
<SelectTrigger id="installmentCount" className="w-full">
|
<SelectTrigger id="installmentCount" className="w-full">
|
||||||
<SelectValue placeholder="Selecione" />
|
<SelectValue placeholder="Selecione">
|
||||||
|
{installmentSummary}
|
||||||
|
</SelectValue>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{[...Array(24)].map((_, index) => (
|
{[...Array(24)].map((_, index) => {
|
||||||
<SelectItem key={index + 2} value={String(index + 2)}>
|
const count = index + 2;
|
||||||
{index + 2}x
|
return (
|
||||||
</SelectItem>
|
<SelectItem key={count} value={String(count)}>
|
||||||
))}
|
{getInstallmentLabel(count)}
|
||||||
|
</SelectItem>
|
||||||
|
);
|
||||||
|
})}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
</Select>
|
</Select>
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{showRecurrence ? (
|
{showRecurrence ? (
|
||||||
<div className="space-y-2 w-full md:w-1/2">
|
<div className="space-y-1 w-full md:w-1/2">
|
||||||
<Label htmlFor="recurrenceCount">Lançamento fixo</Label>
|
<Label htmlFor="recurrenceCount">Repetirá</Label>
|
||||||
<Select
|
<Select
|
||||||
value={formState.recurrenceCount}
|
value={formState.recurrenceCount}
|
||||||
onValueChange={(value) => onFieldChange("recurrenceCount", value)}
|
onValueChange={(value) => onFieldChange("recurrenceCount", value)}
|
||||||
>
|
>
|
||||||
<SelectTrigger id="recurrenceCount" className="w-full">
|
<SelectTrigger id="recurrenceCount" className="w-full">
|
||||||
<SelectValue placeholder="Selecione" />
|
<SelectValue placeholder="Selecione">
|
||||||
|
{recurrenceSummary}
|
||||||
|
</SelectValue>
|
||||||
</SelectTrigger>
|
</SelectTrigger>
|
||||||
<SelectContent>
|
<SelectContent>
|
||||||
{[...Array(24)].map((_, index) => (
|
{[...Array(47)].map((_, index) => (
|
||||||
<SelectItem key={index + 2} value={String(index + 2)}>
|
<SelectItem key={index + 2} value={String(index + 2)}>
|
||||||
Por {index + 2} meses
|
{index + 2} meses
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))}
|
||||||
</SelectContent>
|
</SelectContent>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type { NoteSectionProps } from "./lancamento-dialog-types";
|
|||||||
|
|
||||||
export function NoteSection({ formState, onFieldChange }: NoteSectionProps) {
|
export function NoteSection({ formState, onFieldChange }: NoteSectionProps) {
|
||||||
return (
|
return (
|
||||||
<div className="space-y-2">
|
<div className="space-y-1">
|
||||||
<Label htmlFor="note">Anotação</Label>
|
<Label htmlFor="note">Anotação</Label>
|
||||||
<Textarea
|
<Textarea
|
||||||
id="note"
|
id="note"
|
||||||
@@ -14,6 +14,7 @@ export function NoteSection({ formState, onFieldChange }: NoteSectionProps) {
|
|||||||
onChange={(event) => onFieldChange("note", event.target.value)}
|
onChange={(event) => onFieldChange("note", event.target.value)}
|
||||||
placeholder="Adicione observações sobre o lançamento"
|
placeholder="Adicione observações sobre o lançamento"
|
||||||
rows={2}
|
rows={2}
|
||||||
|
className="min-h-[36px] resize-none"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user