mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-05-09 11:01:45 +00:00
fix(lançamentos): reforçar validações e revisar formulário
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
RiCheckboxBlankCircleLine,
|
||||
RiCheckboxCircleFill,
|
||||
} from "@remixicon/react";
|
||||
import { useState } from "react";
|
||||
import { PAYMENT_METHODS } from "@/features/transactions/constants";
|
||||
import { Button } from "@/shared/components/ui/button";
|
||||
import { Label } from "@/shared/components/ui/label";
|
||||
import { MonthPicker } from "@/shared/components/ui/month-picker";
|
||||
import {
|
||||
@@ -71,6 +76,7 @@ export function PaymentMethodSection({
|
||||
isUpdateMode,
|
||||
disablePaymentMethod,
|
||||
disableCardSelect,
|
||||
showSettledToggle,
|
||||
}: PaymentMethodSectionProps) {
|
||||
const isCartaoSelected = formState.paymentMethod === "Cartão de crédito";
|
||||
const showContaSelect = [
|
||||
@@ -92,154 +98,200 @@ export function PaymentMethodSection({
|
||||
const hasSecondaryColumn = isCartaoSelected || showContaSelect;
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-col gap-2 md:flex-row">
|
||||
{!isUpdateMode ? (
|
||||
<div
|
||||
className={cn(
|
||||
"w-full space-y-1",
|
||||
hasSecondaryColumn ? "md:w-1/2" : "md:w-full",
|
||||
)}
|
||||
>
|
||||
<Label htmlFor="paymentMethod">Forma de pagamento</Label>
|
||||
<Select
|
||||
value={formState.paymentMethod}
|
||||
onValueChange={(value) => onFieldChange("paymentMethod", value)}
|
||||
disabled={disablePaymentMethod}
|
||||
<div className="space-y-3">
|
||||
<div className="flex w-full flex-col gap-2 md:flex-row">
|
||||
{!isUpdateMode ? (
|
||||
<div
|
||||
className={cn(
|
||||
"w-full space-y-1",
|
||||
hasSecondaryColumn ? "md:w-1/2" : "md:w-full",
|
||||
)}
|
||||
>
|
||||
<SelectTrigger
|
||||
id="paymentMethod"
|
||||
className="w-full"
|
||||
<Label htmlFor="paymentMethod">Forma de pagamento</Label>
|
||||
<Select
|
||||
value={formState.paymentMethod}
|
||||
onValueChange={(value) => onFieldChange("paymentMethod", value)}
|
||||
disabled={disablePaymentMethod}
|
||||
>
|
||||
<SelectValue placeholder="Selecione" className="w-full">
|
||||
{formState.paymentMethod && (
|
||||
<PaymentMethodSelectContent label={formState.paymentMethod} />
|
||||
)}
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{PAYMENT_METHODS.map((method) => (
|
||||
<SelectItem key={method} value={method}>
|
||||
<PaymentMethodSelectContent label={method} />
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
) : null}
|
||||
<SelectTrigger
|
||||
id="paymentMethod"
|
||||
className="w-full"
|
||||
disabled={disablePaymentMethod}
|
||||
>
|
||||
<SelectValue placeholder="Selecione" className="w-full">
|
||||
{formState.paymentMethod && (
|
||||
<PaymentMethodSelectContent
|
||||
label={formState.paymentMethod}
|
||||
/>
|
||||
)}
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{PAYMENT_METHODS.map((method) => (
|
||||
<SelectItem key={method} value={method}>
|
||||
<PaymentMethodSelectContent label={method} />
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{isCartaoSelected ? (
|
||||
<div
|
||||
className={cn(
|
||||
"w-full space-y-1",
|
||||
!isUpdateMode ? "md:w-1/2" : "md:w-full",
|
||||
)}
|
||||
>
|
||||
<Label htmlFor="cartao">Cartão</Label>
|
||||
<Select
|
||||
value={formState.cardId}
|
||||
onValueChange={(value) => onFieldChange("cardId", value)}
|
||||
disabled={disableCardSelect}
|
||||
{isCartaoSelected ? (
|
||||
<div
|
||||
className={cn(
|
||||
"w-full space-y-1",
|
||||
!isUpdateMode ? "md:w-1/2" : "md:w-full",
|
||||
)}
|
||||
>
|
||||
<SelectTrigger
|
||||
id="cartao"
|
||||
className="w-full"
|
||||
<Label htmlFor="cartao">Cartão</Label>
|
||||
<Select
|
||||
value={formState.cardId ?? ""}
|
||||
onValueChange={(value) => onFieldChange("cardId", value)}
|
||||
disabled={disableCardSelect}
|
||||
>
|
||||
<SelectValue placeholder="Selecione">
|
||||
{formState.cardId &&
|
||||
(() => {
|
||||
const selectedOption = cardOptions.find(
|
||||
(opt) => opt.value === formState.cardId,
|
||||
);
|
||||
return selectedOption ? (
|
||||
<SelectTrigger
|
||||
id="cartao"
|
||||
className="w-full"
|
||||
disabled={disableCardSelect}
|
||||
>
|
||||
<SelectValue placeholder="Selecione">
|
||||
{formState.cardId &&
|
||||
(() => {
|
||||
const selectedOption = cardOptions.find(
|
||||
(opt) => opt.value === formState.cardId,
|
||||
);
|
||||
return selectedOption ? (
|
||||
<AccountCardSelectContent
|
||||
label={selectedOption.label}
|
||||
logo={selectedOption.logo}
|
||||
isCartao={true}
|
||||
/>
|
||||
) : null;
|
||||
})()}
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{cardOptions.length === 0 ? (
|
||||
<div className="px-2 py-6 text-center">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Nenhum cartão cadastrado
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
cardOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
<AccountCardSelectContent
|
||||
label={selectedOption.label}
|
||||
logo={selectedOption.logo}
|
||||
label={option.label}
|
||||
logo={option.logo}
|
||||
isCartao={true}
|
||||
/>
|
||||
) : null;
|
||||
})()}
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{cardOptions.length === 0 ? (
|
||||
<div className="px-2 py-6 text-center">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Nenhum cartão cadastrado
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
cardOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
<AccountCardSelectContent
|
||||
label={option.label}
|
||||
logo={option.logo}
|
||||
isCartao={true}
|
||||
/>
|
||||
</SelectItem>
|
||||
))
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{formState.cardId ? (
|
||||
<InlinePeriodPicker
|
||||
period={formState.period}
|
||||
onPeriodChange={(value) => onFieldChange("period", value)}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
</SelectItem>
|
||||
))
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{formState.cardId ? (
|
||||
<InlinePeriodPicker
|
||||
period={formState.period}
|
||||
onPeriodChange={(value) => onFieldChange("period", value)}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{!isCartaoSelected && showContaSelect ? (
|
||||
<div
|
||||
className={cn(
|
||||
"w-full space-y-1",
|
||||
!isUpdateMode ? "md:w-1/2" : "md:w-full",
|
||||
)}
|
||||
>
|
||||
<Label htmlFor="conta">Conta</Label>
|
||||
<Select
|
||||
value={formState.accountId}
|
||||
onValueChange={(value) => onFieldChange("accountId", value)}
|
||||
{!isCartaoSelected && showContaSelect ? (
|
||||
<div
|
||||
className={cn(
|
||||
"w-full space-y-1",
|
||||
!isUpdateMode ? "md:w-1/2" : "md:w-full",
|
||||
)}
|
||||
>
|
||||
<SelectTrigger id="conta" className="w-full">
|
||||
<SelectValue placeholder="Selecione">
|
||||
{formState.accountId &&
|
||||
(() => {
|
||||
const selectedOption = filteredContaOptions.find(
|
||||
(opt) => opt.value === formState.accountId,
|
||||
);
|
||||
return selectedOption ? (
|
||||
<Label htmlFor="conta">Conta</Label>
|
||||
<Select
|
||||
value={formState.accountId ?? ""}
|
||||
onValueChange={(value) => onFieldChange("accountId", value)}
|
||||
>
|
||||
<SelectTrigger id="conta" className="w-full">
|
||||
<SelectValue placeholder="Selecione">
|
||||
{formState.accountId &&
|
||||
(() => {
|
||||
const selectedOption = filteredContaOptions.find(
|
||||
(opt) => opt.value === formState.accountId,
|
||||
);
|
||||
return selectedOption ? (
|
||||
<AccountCardSelectContent
|
||||
label={selectedOption.label}
|
||||
logo={selectedOption.logo}
|
||||
isCartao={false}
|
||||
/>
|
||||
) : null;
|
||||
})()}
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{filteredContaOptions.length === 0 ? (
|
||||
<div className="px-2 py-6 text-center">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Nenhuma conta cadastrada
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
filteredContaOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
<AccountCardSelectContent
|
||||
label={selectedOption.label}
|
||||
logo={selectedOption.logo}
|
||||
label={option.label}
|
||||
logo={option.logo}
|
||||
isCartao={false}
|
||||
/>
|
||||
) : null;
|
||||
})()}
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{filteredContaOptions.length === 0 ? (
|
||||
<div className="px-2 py-6 text-center">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Nenhuma conta cadastrada
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
filteredContaOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
<AccountCardSelectContent
|
||||
label={option.label}
|
||||
logo={option.logo}
|
||||
isCartao={false}
|
||||
/>
|
||||
</SelectItem>
|
||||
))
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</SelectItem>
|
||||
))
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{showSettledToggle ? (
|
||||
<div
|
||||
className={cn(
|
||||
"flex items-center justify-between rounded-lg border px-3 py-2.5 transition-colors",
|
||||
formState.isSettled
|
||||
? "border-success/20 bg-success/5"
|
||||
: "border-border bg-transparent",
|
||||
)}
|
||||
>
|
||||
<div>
|
||||
<p className="text-sm text-foreground text-left">
|
||||
Marcar como pago
|
||||
</p>
|
||||
<p className="text-xs text-muted-foreground text-left">
|
||||
Indica que o valor já foi pago.
|
||||
</p>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="icon-sm"
|
||||
onClick={() => onFieldChange("isSettled", !formState.isSettled)}
|
||||
aria-label={
|
||||
formState.isSettled ? "Desfazer pagamento" : "Marcar como pago"
|
||||
}
|
||||
aria-pressed={Boolean(formState.isSettled)}
|
||||
className={cn(
|
||||
"transition-colors",
|
||||
formState.isSettled
|
||||
? "bg-success/10 text-success hover:bg-success/20 hover:text-success"
|
||||
: "text-muted-foreground hover:text-foreground",
|
||||
)}
|
||||
>
|
||||
{formState.isSettled ? (
|
||||
<RiCheckboxCircleFill className="size-4" />
|
||||
) : (
|
||||
<RiCheckboxBlankCircleLine className="size-4" />
|
||||
)}
|
||||
</Button>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user