mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-05-09 11:01:45 +00:00
feat: melhora os dialogs e detalhes de lançamentos
This commit is contained in:
@@ -7,17 +7,15 @@ import {
|
|||||||
formatPeriod,
|
formatPeriod,
|
||||||
} from "@/features/transactions/formatting-helpers";
|
} from "@/features/transactions/formatting-helpers";
|
||||||
import { TransactionTypeBadge } from "@/shared/components/transaction-type-badge";
|
import { TransactionTypeBadge } from "@/shared/components/transaction-type-badge";
|
||||||
|
import { Badge } from "@/shared/components/ui/badge";
|
||||||
import { Button } from "@/shared/components/ui/button";
|
import { Button } from "@/shared/components/ui/button";
|
||||||
import {
|
|
||||||
CardContent,
|
|
||||||
CardDescription,
|
|
||||||
CardHeader,
|
|
||||||
} from "@/shared/components/ui/card";
|
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogClose,
|
DialogClose,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
|
DialogDescription,
|
||||||
DialogFooter,
|
DialogFooter,
|
||||||
|
DialogHeader,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
} from "@/shared/components/ui/dialog";
|
} from "@/shared/components/ui/dialog";
|
||||||
import { Separator } from "@/shared/components/ui/separator";
|
import { Separator } from "@/shared/components/ui/separator";
|
||||||
@@ -30,12 +28,14 @@ interface TransactionDetailsDialogProps {
|
|||||||
open: boolean;
|
open: boolean;
|
||||||
onOpenChange: (open: boolean) => void;
|
onOpenChange: (open: boolean) => void;
|
||||||
transaction: TransactionItem | null;
|
transaction: TransactionItem | null;
|
||||||
|
onEdit?: (transaction: TransactionItem) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function TransactionDetailsDialog({
|
export function TransactionDetailsDialog({
|
||||||
open,
|
open,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
transaction,
|
transaction,
|
||||||
|
onEdit,
|
||||||
}: TransactionDetailsDialogProps) {
|
}: TransactionDetailsDialogProps) {
|
||||||
if (!transaction) return null;
|
if (!transaction) return null;
|
||||||
|
|
||||||
@@ -54,139 +54,163 @@ export function TransactionDetailsDialog({
|
|||||||
? valorParcela * (totalParcelas - parcelaAtual)
|
? valorParcela * (totalParcelas - parcelaAtual)
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
|
const isBoleto = transaction.paymentMethod === "Boleto";
|
||||||
|
|
||||||
|
const handleEdit = () => {
|
||||||
|
onOpenChange(false);
|
||||||
|
onEdit?.(transaction);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||||
<DialogContent className="p-0 sm:max-w-xl sm:border-0 sm:p-2">
|
<DialogContent className="sm:max-w-xl">
|
||||||
<div className="gap-2 space-y-4 py-4">
|
<DialogHeader>
|
||||||
<CardHeader className="flex flex-row items-start border-b sm:border-b-0">
|
<DialogTitle>{transaction.name}</DialogTitle>
|
||||||
<div>
|
<DialogDescription>
|
||||||
<DialogTitle className="group flex items-center gap-2 text-lg">
|
{formatDate(transaction.purchaseDate)}
|
||||||
#{transaction.id}
|
</DialogDescription>
|
||||||
</DialogTitle>
|
</DialogHeader>
|
||||||
<CardDescription>
|
|
||||||
{formatDate(transaction.purchaseDate)}
|
|
||||||
</CardDescription>
|
|
||||||
</div>
|
|
||||||
</CardHeader>
|
|
||||||
|
|
||||||
<CardContent className="text-sm">
|
<div className="max-h-[60vh] overflow-y-auto text-sm">
|
||||||
<div className="grid gap-3">
|
<div className="grid gap-3">
|
||||||
<ul className="grid gap-3">
|
<ul className="grid gap-3">
|
||||||
<DetailRow label="Descrição" value={transaction.name} />
|
<DetailRow
|
||||||
|
label="Período"
|
||||||
|
value={formatPeriod(transaction.period)}
|
||||||
|
/>
|
||||||
|
|
||||||
<DetailRow
|
<li className="flex items-center justify-between">
|
||||||
label="Período"
|
<span className="text-muted-foreground">
|
||||||
value={formatPeriod(transaction.period)}
|
Forma de Pagamento
|
||||||
|
</span>
|
||||||
|
<span className="flex items-center gap-1.5">
|
||||||
|
{getPaymentMethodIcon(transaction.paymentMethod)}
|
||||||
|
<span>{transaction.paymentMethod}</span>
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<DetailRow
|
||||||
|
label={transaction.cartaoName ? "Cartão" : "Conta"}
|
||||||
|
value={transaction.cartaoName ?? transaction.contaName ?? "—"}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<DetailRow
|
||||||
|
label="Categoria"
|
||||||
|
value={transaction.categoriaName ?? "—"}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<li className="flex items-center justify-between">
|
||||||
|
<span className="text-muted-foreground">Tipo de Transação</span>
|
||||||
|
<TransactionTypeBadge
|
||||||
|
kind={
|
||||||
|
transaction.categoriaName === "Saldo inicial"
|
||||||
|
? "Saldo inicial"
|
||||||
|
: transaction.transactionType
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<DetailRow
|
||||||
|
label="Condição"
|
||||||
|
value={formatCondition(transaction.condition)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<li className="flex items-center justify-between">
|
||||||
|
<span className="text-muted-foreground">Responsável</span>
|
||||||
|
<span>{transaction.pagadorName}</span>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
<li className="flex items-center justify-between">
|
||||||
|
<span className="text-muted-foreground">Status</span>
|
||||||
|
<Badge
|
||||||
|
variant="secondary"
|
||||||
|
className={
|
||||||
|
transaction.isSettled
|
||||||
|
? "text-success bg-success/10"
|
||||||
|
: "text-muted-foreground"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{transaction.isSettled ? "Pago" : "Pendente"}
|
||||||
|
</Badge>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
{isBoleto && transaction.dueDate && (
|
||||||
|
<DetailRow
|
||||||
|
label="Vencimento"
|
||||||
|
value={formatDate(transaction.dueDate)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{transaction.isDivided && (
|
||||||
<li className="flex items-center justify-between">
|
<li className="flex items-center justify-between">
|
||||||
<span className="text-muted-foreground">
|
<span className="text-muted-foreground">Divisão</span>
|
||||||
Forma de Pagamento
|
<Badge variant="outline">Dividido</Badge>
|
||||||
</span>
|
|
||||||
<span className="flex items-center gap-1.5">
|
|
||||||
{getPaymentMethodIcon(transaction.paymentMethod)}
|
|
||||||
<span className="capitalize">
|
|
||||||
{transaction.paymentMethod}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</li>
|
</li>
|
||||||
|
)}
|
||||||
|
|
||||||
<DetailRow
|
{transaction.note && (
|
||||||
label={transaction.cartaoName ? "Cartão" : "Conta"}
|
<li className="flex flex-col gap-1">
|
||||||
value={transaction.cartaoName ?? transaction.contaName ?? "—"}
|
<span className="text-muted-foreground">Notas</span>
|
||||||
/>
|
<span className="text-foreground">{transaction.note}</span>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
</ul>
|
||||||
|
|
||||||
<DetailRow
|
<ul className="mb-2 grid gap-3">
|
||||||
label="Categoria"
|
{isInstallment && (
|
||||||
value={transaction.categoriaName ?? "—"}
|
<li className="mt-4">
|
||||||
/>
|
<InstallmentTimeline
|
||||||
|
purchaseDate={parseLocalDateString(
|
||||||
<li className="flex items-center justify-between">
|
transaction.purchaseDate,
|
||||||
<span className="text-muted-foreground">
|
)}
|
||||||
Tipo de Transação
|
currentInstallment={parcelaAtual}
|
||||||
</span>
|
totalInstallments={totalParcelas}
|
||||||
<TransactionTypeBadge
|
period={transaction.period}
|
||||||
kind={
|
|
||||||
transaction.categoriaName === "Saldo inicial"
|
|
||||||
? "Saldo inicial"
|
|
||||||
: transaction.transactionType
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</li>
|
</li>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<DetailRow
|
||||||
|
label={isInstallment ? "Valor da Parcela" : "Valor"}
|
||||||
|
value={currencyFormatter.format(valorParcela)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{isInstallment && (
|
||||||
<DetailRow
|
<DetailRow
|
||||||
label="Condição"
|
label="Valor Restante"
|
||||||
value={formatCondition(transaction.condition)}
|
value={currencyFormatter.format(valorRestante)}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<li className="flex items-center justify-between">
|
{transaction.recurrenceCount && (
|
||||||
<span className="text-muted-foreground">Responsável</span>
|
|
||||||
<span className="flex items-center gap-2 capitalize">
|
|
||||||
<span>{transaction.pagadorName}</span>
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<DetailRow
|
<DetailRow
|
||||||
label="Status"
|
label="Quantidade de Recorrências"
|
||||||
value={transaction.isSettled ? "Pago" : "Pendente"}
|
value={`${transaction.recurrenceCount} meses`}
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
{transaction.note && (
|
{!isInstallment && <Separator className="my-2" />}
|
||||||
<DetailRow label="Notas" value={transaction.note} />
|
|
||||||
)}
|
|
||||||
</ul>
|
|
||||||
|
|
||||||
<ul className="mb-6 grid gap-3">
|
<li className="flex items-center justify-between font-semibold">
|
||||||
{isInstallment && (
|
<span className="text-muted-foreground">Total da Compra</span>
|
||||||
<li className="mt-4">
|
<span className="text-lg">
|
||||||
<InstallmentTimeline
|
{currencyFormatter.format(valorTotal)}
|
||||||
purchaseDate={parseLocalDateString(
|
</span>
|
||||||
transaction.purchaseDate,
|
</li>
|
||||||
)}
|
</ul>
|
||||||
currentInstallment={parcelaAtual}
|
</div>
|
||||||
totalInstallments={totalParcelas}
|
|
||||||
period={transaction.period}
|
|
||||||
/>
|
|
||||||
</li>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<DetailRow
|
|
||||||
label={isInstallment ? "Valor da Parcela" : "Valor"}
|
|
||||||
value={currencyFormatter.format(valorParcela)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{isInstallment && (
|
|
||||||
<DetailRow
|
|
||||||
label="Valor Restante"
|
|
||||||
value={currencyFormatter.format(valorRestante)}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{transaction.recurrenceCount && (
|
|
||||||
<DetailRow
|
|
||||||
label="Quantidade de Recorrências"
|
|
||||||
value={`${transaction.recurrenceCount} meses`}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{!isInstallment && <Separator className="my-2" />}
|
|
||||||
|
|
||||||
<li className="flex items-center justify-between font-semibold">
|
|
||||||
<span className="text-muted-foreground">Total da Compra</span>
|
|
||||||
<span className="text-lg">
|
|
||||||
{currencyFormatter.format(valorTotal)}
|
|
||||||
</span>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<DialogFooter>
|
|
||||||
<DialogClose asChild>
|
|
||||||
<Button type="button">Entendi</Button>
|
|
||||||
</DialogClose>
|
|
||||||
</DialogFooter>
|
|
||||||
</CardContent>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<DialogFooter>
|
||||||
|
{onEdit && !transaction.readonly && (
|
||||||
|
<Button variant="outline" onClick={handleEdit}>
|
||||||
|
Editar
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
<DialogClose asChild>
|
||||||
|
<Button type="button">Fechar</Button>
|
||||||
|
</DialogClose>
|
||||||
|
</DialogFooter>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
@@ -201,7 +225,7 @@ function DetailRow({ label, value }: DetailRowProps) {
|
|||||||
return (
|
return (
|
||||||
<li className="flex items-center justify-between">
|
<li className="flex items-center justify-between">
|
||||||
<span className="text-muted-foreground">{label}</span>
|
<span className="text-muted-foreground">{label}</span>
|
||||||
<span className="capitalize">{value}</span>
|
<span>{value}</span>
|
||||||
</li>
|
</li>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ export function BasicFieldsSection({
|
|||||||
return (
|
return (
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="space-y-1">
|
<div className="space-y-1">
|
||||||
<Label htmlFor="name">Estabelecimento</Label>
|
<Label htmlFor="name">Descrição</Label>
|
||||||
<EstabelecimentoInput
|
<EstabelecimentoInput
|
||||||
id="name"
|
id="name"
|
||||||
value={formState.name}
|
value={formState.name}
|
||||||
onChange={(value) => onFieldChange("name", value)}
|
onChange={(value) => onFieldChange("name", value)}
|
||||||
estabelecimentos={estabelecimentos}
|
estabelecimentos={estabelecimentos}
|
||||||
placeholder="Ex.: Restaurante do Zé"
|
placeholder="Ex.: Restaurante do Zé"
|
||||||
maxLength={20}
|
maxLength={60}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export function PayerSection({
|
|||||||
>
|
>
|
||||||
<SelectTrigger
|
<SelectTrigger
|
||||||
id="payer"
|
id="payer"
|
||||||
className={formState.isSplit ? "w-[55%]" : "w-full"}
|
className={formState.isSplit ? "min-w-0 flex-1" : "w-full"}
|
||||||
>
|
>
|
||||||
<SelectValue placeholder="Selecione">
|
<SelectValue placeholder="Selecione">
|
||||||
{formState.payerId &&
|
{formState.payerId &&
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ function InlinePeriodPicker({
|
|||||||
<PopoverTrigger asChild>
|
<PopoverTrigger asChild>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
className="text-xs text-primary underline-offset-2 hover:underline cursor-pointer lowercase"
|
className="cursor-pointer text-xs text-primary underline-offset-2 hover:underline lowercase"
|
||||||
>
|
>
|
||||||
{displayPeriod(period)}
|
{displayPeriod(period)}
|
||||||
</button>
|
</button>
|
||||||
@@ -82,7 +82,6 @@ export function PaymentMethodSection({
|
|||||||
"Transferência bancária",
|
"Transferência bancária",
|
||||||
].includes(formState.paymentMethod);
|
].includes(formState.paymentMethod);
|
||||||
|
|
||||||
// Filtrar contas apenas do tipo "Pré-Pago | VR/VA" quando forma de pagamento for "Pré-Pago | VR/VA"
|
|
||||||
const filteredContaOptions =
|
const filteredContaOptions =
|
||||||
formState.paymentMethod === "Pré-Pago | VR/VA"
|
formState.paymentMethod === "Pré-Pago | VR/VA"
|
||||||
? accountOptions.filter(
|
? accountOptions.filter(
|
||||||
@@ -90,270 +89,159 @@ export function PaymentMethodSection({
|
|||||||
)
|
)
|
||||||
: accountOptions;
|
: accountOptions;
|
||||||
|
|
||||||
|
const hasSecondaryColumn = isCartaoSelected || showContaSelect;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div className="flex w-full flex-col gap-2 md:flex-row">
|
||||||
{!isUpdateMode ? (
|
{!isUpdateMode ? (
|
||||||
<div className="flex w-full flex-col gap-2 md:flex-row">
|
<div
|
||||||
<div
|
className={cn(
|
||||||
className={cn(
|
"w-full space-y-1",
|
||||||
"space-y-1 w-full",
|
hasSecondaryColumn ? "md:w-1/2" : "md:w-full",
|
||||||
isCartaoSelected || showContaSelect ? "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}
|
||||||
>
|
>
|
||||||
<Label htmlFor="paymentMethod">Forma de pagamento</Label>
|
<SelectTrigger
|
||||||
<Select
|
id="paymentMethod"
|
||||||
value={formState.paymentMethod}
|
className="w-full"
|
||||||
onValueChange={(value) => onFieldChange("paymentMethod", value)}
|
|
||||||
disabled={disablePaymentMethod}
|
disabled={disablePaymentMethod}
|
||||||
>
|
>
|
||||||
<SelectTrigger
|
<SelectValue placeholder="Selecione" className="w-full">
|
||||||
id="paymentMethod"
|
{formState.paymentMethod && (
|
||||||
className="w-full"
|
<PaymentMethodSelectContent label={formState.paymentMethod} />
|
||||||
disabled={disablePaymentMethod}
|
)}
|
||||||
>
|
</SelectValue>
|
||||||
<SelectValue placeholder="Selecione" className="w-full">
|
</SelectTrigger>
|
||||||
{formState.paymentMethod && (
|
<SelectContent>
|
||||||
<PaymentMethodSelectContent
|
{PAYMENT_METHODS.map((method) => (
|
||||||
label={formState.paymentMethod}
|
<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}
|
||||||
|
>
|
||||||
|
<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={option.label}
|
||||||
|
logo={option.logo}
|
||||||
|
isCartao={true}
|
||||||
/>
|
/>
|
||||||
)}
|
|
||||||
</SelectValue>
|
|
||||||
</SelectTrigger>
|
|
||||||
<SelectContent>
|
|
||||||
{PAYMENT_METHODS.map((method) => (
|
|
||||||
<SelectItem key={method} value={method}>
|
|
||||||
<PaymentMethodSelectContent label={method} />
|
|
||||||
</SelectItem>
|
</SelectItem>
|
||||||
))}
|
))
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{isCartaoSelected ? (
|
|
||||||
<div className="space-y-1 w-full md:w-1/2">
|
|
||||||
<Label htmlFor="cartao">Cartão</Label>
|
|
||||||
<Select
|
|
||||||
value={formState.cardId}
|
|
||||||
onValueChange={(value) => onFieldChange("cardId", value)}
|
|
||||||
disabled={disableCardSelect}
|
|
||||||
>
|
|
||||||
<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={option.label}
|
|
||||||
logo={option.logo}
|
|
||||||
isCartao={true}
|
|
||||||
/>
|
|
||||||
</SelectItem>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
{formState.cardId ? (
|
|
||||||
<InlinePeriodPicker
|
|
||||||
period={formState.period}
|
|
||||||
onPeriodChange={(value) => onFieldChange("period", value)}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{!isCartaoSelected && showContaSelect ? (
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"space-y-1 w-full",
|
|
||||||
!isUpdateMode ? "md:w-1/2" : "md:w-full",
|
|
||||||
)}
|
)}
|
||||||
>
|
</SelectContent>
|
||||||
<Label htmlFor="conta">Conta</Label>
|
</Select>
|
||||||
<Select
|
{formState.cardId ? (
|
||||||
value={formState.accountId}
|
<InlinePeriodPicker
|
||||||
onValueChange={(value) => onFieldChange("accountId", value)}
|
period={formState.period}
|
||||||
>
|
onPeriodChange={(value) => onFieldChange("period", 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={option.label}
|
|
||||||
logo={option.logo}
|
|
||||||
isCartao={false}
|
|
||||||
/>
|
|
||||||
</SelectItem>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
) : null}
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{isUpdateMode ? (
|
{!isCartaoSelected && showContaSelect ? (
|
||||||
<div className="flex w-full flex-col gap-2 md:flex-row">
|
<div
|
||||||
{isCartaoSelected ? (
|
className={cn(
|
||||||
<div
|
"w-full space-y-1",
|
||||||
className={cn(
|
!isUpdateMode ? "md:w-1/2" : "md:w-full",
|
||||||
"space-y-1 w-full",
|
)}
|
||||||
!isUpdateMode ? "md:w-1/2" : "md:w-full",
|
>
|
||||||
|
<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={option.label}
|
||||||
|
logo={option.logo}
|
||||||
|
isCartao={false}
|
||||||
|
/>
|
||||||
|
</SelectItem>
|
||||||
|
))
|
||||||
)}
|
)}
|
||||||
>
|
</SelectContent>
|
||||||
<Label htmlFor="cartaoUpdate">Cartão</Label>
|
</Select>
|
||||||
<Select
|
|
||||||
value={formState.cardId}
|
|
||||||
onValueChange={(value) => onFieldChange("cardId", value)}
|
|
||||||
>
|
|
||||||
<SelectTrigger id="cartaoUpdate" className="w-full">
|
|
||||||
<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={option.label}
|
|
||||||
logo={option.logo}
|
|
||||||
isCartao={true}
|
|
||||||
/>
|
|
||||||
</SelectItem>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
{formState.cardId ? (
|
|
||||||
<InlinePeriodPicker
|
|
||||||
period={formState.period}
|
|
||||||
onPeriodChange={(value) => onFieldChange("period", value)}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
{!isCartaoSelected && showContaSelect ? (
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"space-y-1 w-full",
|
|
||||||
!isUpdateMode ? "md:w-1/2" : "md:w-full",
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<Label htmlFor="contaUpdate">Conta</Label>
|
|
||||||
<Select
|
|
||||||
value={formState.accountId}
|
|
||||||
onValueChange={(value) => onFieldChange("accountId", value)}
|
|
||||||
>
|
|
||||||
<SelectTrigger id="contaUpdate" 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={option.label}
|
|
||||||
logo={option.logo}
|
|
||||||
isCartao={false}
|
|
||||||
/>
|
|
||||||
</SelectItem>
|
|
||||||
))
|
|
||||||
)}
|
|
||||||
</SelectContent>
|
|
||||||
</Select>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</div>
|
</div>
|
||||||
) : null}
|
) : null}
|
||||||
</>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export function SplitAndSettlementSection({
|
|||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-foreground">Dividir lançamento</p>
|
<p className="text-sm text-foreground">Dividir lançamento</p>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
Selecione para atribuir parte do valor a outro pagador.
|
Atribuir parte do valor a outro pagador.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
@@ -40,7 +40,7 @@ export function SplitAndSettlementSection({
|
|||||||
<div>
|
<div>
|
||||||
<p className="text-sm text-foreground">Marcar como pago</p>
|
<p className="text-sm text-foreground">Marcar como pago</p>
|
||||||
<p className="text-xs text-muted-foreground">
|
<p className="text-xs text-muted-foreground">
|
||||||
Indica que o lançamento já foi pago ou recebido.
|
Indica que o valor já foi pago.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<Checkbox
|
<Checkbox
|
||||||
|
|||||||
@@ -418,92 +418,92 @@ export function TransactionDialog({
|
|||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
<form
|
<form
|
||||||
className="space-y-3 -mx-6 max-h-[80vh] overflow-y-auto px-6 pb-1"
|
className="flex flex-col gap-0"
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
noValidate
|
noValidate
|
||||||
>
|
>
|
||||||
<BasicFieldsSection
|
<div className="space-y-3 -mx-6 max-h-[70vh] overflow-y-auto px-6 pb-1">
|
||||||
formState={formState}
|
<BasicFieldsSection
|
||||||
onFieldChange={handleFieldChange}
|
formState={formState}
|
||||||
estabelecimentos={estabelecimentos}
|
onFieldChange={handleFieldChange}
|
||||||
/>
|
estabelecimentos={estabelecimentos}
|
||||||
|
/>
|
||||||
|
|
||||||
<CategorySection
|
<CategorySection
|
||||||
formState={formState}
|
formState={formState}
|
||||||
onFieldChange={handleFieldChange}
|
onFieldChange={handleFieldChange}
|
||||||
categoryOptions={categoryOptions}
|
categoryOptions={categoryOptions}
|
||||||
categoryGroups={categoryGroups}
|
categoryGroups={categoryGroups}
|
||||||
isUpdateMode={isUpdateMode}
|
isUpdateMode={isUpdateMode}
|
||||||
hideTransactionType={
|
hideTransactionType={
|
||||||
Boolean(isNewWithType) && !forceShowTransactionType
|
Boolean(isNewWithType) && !forceShowTransactionType
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{!isUpdateMode ? (
|
|
||||||
<SplitAndSettlementSection
|
<SplitAndSettlementSection
|
||||||
formState={formState}
|
formState={formState}
|
||||||
onFieldChange={handleFieldChange}
|
onFieldChange={handleFieldChange}
|
||||||
showSettledToggle={showSettledToggle}
|
showSettledToggle={showSettledToggle}
|
||||||
/>
|
/>
|
||||||
) : null}
|
|
||||||
|
|
||||||
<PayerSection
|
<PayerSection
|
||||||
formState={formState}
|
|
||||||
onFieldChange={handleFieldChange}
|
|
||||||
payerOptions={payerOptions}
|
|
||||||
secondaryPayerOptions={secondaryPayerOptions}
|
|
||||||
totalAmount={totalAmount}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<PaymentMethodSection
|
|
||||||
formState={formState}
|
|
||||||
onFieldChange={handleFieldChange}
|
|
||||||
accountOptions={accountOptions}
|
|
||||||
cardOptions={cardOptions}
|
|
||||||
isUpdateMode={isUpdateMode}
|
|
||||||
disablePaymentMethod={disablePaymentMethod}
|
|
||||||
disableCardSelect={disableCardSelect}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{showDueDate ? (
|
|
||||||
<BoletoFieldsSection
|
|
||||||
formState={formState}
|
formState={formState}
|
||||||
onFieldChange={handleFieldChange}
|
onFieldChange={handleFieldChange}
|
||||||
showPaymentDate={showPaymentDate}
|
payerOptions={payerOptions}
|
||||||
|
secondaryPayerOptions={secondaryPayerOptions}
|
||||||
|
totalAmount={totalAmount}
|
||||||
/>
|
/>
|
||||||
) : null}
|
|
||||||
|
|
||||||
<Collapsible
|
<PaymentMethodSection
|
||||||
defaultOpen={
|
formState={formState}
|
||||||
formState.condition !== "À vista" || formState.note.length > 0
|
onFieldChange={handleFieldChange}
|
||||||
}
|
accountOptions={accountOptions}
|
||||||
>
|
cardOptions={cardOptions}
|
||||||
<CollapsibleTrigger className="flex w-full items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer [&[data-state=open]>svg]:rotate-180 mt-4">
|
isUpdateMode={isUpdateMode}
|
||||||
<RiArrowDropDownLine className="text-primary size-4 transition-transform duration-200" />
|
disablePaymentMethod={disablePaymentMethod}
|
||||||
Condições e anotações
|
disableCardSelect={disableCardSelect}
|
||||||
</CollapsibleTrigger>
|
/>
|
||||||
<CollapsibleContent className="space-y-3 pt-3">
|
|
||||||
{!isUpdateMode ? (
|
|
||||||
<ConditionSection
|
|
||||||
formState={formState}
|
|
||||||
onFieldChange={handleFieldChange}
|
|
||||||
showInstallments={showInstallments}
|
|
||||||
showRecurrence={showRecurrence}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
|
{showDueDate ? (
|
||||||
|
<BoletoFieldsSection
|
||||||
|
formState={formState}
|
||||||
|
onFieldChange={handleFieldChange}
|
||||||
|
showPaymentDate={showPaymentDate}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
|
||||||
|
{isUpdateMode ? (
|
||||||
<NoteSection
|
<NoteSection
|
||||||
formState={formState}
|
formState={formState}
|
||||||
onFieldChange={handleFieldChange}
|
onFieldChange={handleFieldChange}
|
||||||
/>
|
/>
|
||||||
</CollapsibleContent>
|
) : (
|
||||||
</Collapsible>
|
<Collapsible defaultOpen={formState.condition !== "À vista"}>
|
||||||
|
<CollapsibleTrigger className="flex w-full items-center gap-1 text-xs text-muted-foreground hover:text-foreground transition-colors cursor-pointer [&[data-state=open]>svg]:rotate-180 mt-4">
|
||||||
|
<RiArrowDropDownLine className="text-primary size-4 transition-transform duration-200" />
|
||||||
|
Condições e anotações
|
||||||
|
</CollapsibleTrigger>
|
||||||
|
<CollapsibleContent className="space-y-3 pt-3">
|
||||||
|
<ConditionSection
|
||||||
|
formState={formState}
|
||||||
|
onFieldChange={handleFieldChange}
|
||||||
|
showInstallments={showInstallments}
|
||||||
|
showRecurrence={showRecurrence}
|
||||||
|
/>
|
||||||
|
<NoteSection
|
||||||
|
formState={formState}
|
||||||
|
onFieldChange={handleFieldChange}
|
||||||
|
/>
|
||||||
|
</CollapsibleContent>
|
||||||
|
</Collapsible>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
{errorMessage ? (
|
{errorMessage ? (
|
||||||
<p className="text-sm text-destructive">{errorMessage}</p>
|
<p className="mt-3 text-sm text-destructive">{errorMessage}</p>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
<DialogFooter>
|
<DialogFooter className="mt-4">
|
||||||
<Button
|
<Button
|
||||||
type="button"
|
type="button"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
|
|||||||
@@ -506,6 +506,7 @@ export function TransactionsPage({
|
|||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
transaction={detailsOpen ? selectedTransaction : null}
|
transaction={detailsOpen ? selectedTransaction : null}
|
||||||
|
onEdit={handleEdit}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ConfirmActionDialog
|
<ConfirmActionDialog
|
||||||
|
|||||||
Reference in New Issue
Block a user