Files
tiagoandClaude Sonnet 5 323912748f Frontend completo (Etapas 11-18) e extensões de backend (Etapas 13 e 18)
Adiciona o frontend Next.js em frontend/ (setup, design system,
autenticação/tema, layout/navegação, área master, área tenant completa:
clientes, produtos, vendas, recebimentos, whatsapp, e-mail, alertas,
equipe e perfil). Estende o backend de forma aditiva e autorizada:
cookie httpOnly para refresh token (Etapa 13) e CRUD de equipe +
edição de perfil do tenant (Etapa 18, módulo src/modules/equipe/ novo
e PUT /tenant/perfil). Detalhes completos em Etapas/V2/*.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-16 09:41:13 -03:00

82 lines
2.7 KiB
TypeScript

'use client';
import * as React from 'react';
import { useMutation } from '@tanstack/react-query';
import { marcarRecebimentoPago, type FormaPagamento } from '@/lib/api/sale-payments';
import { ApiError } from '@/lib/api/client';
import { formatCurrency } from '@/lib/format';
import { toast } from '@/lib/toast';
import { Button } from '@/components/ui/button';
import { ResponsiveDialog } from '@/components/ui/responsive-dialog';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
const FORMAS: { value: FormaPagamento; label: string }[] = [
{ value: 'pix', label: 'Pix' },
{ value: 'boleto', label: 'Boleto' },
{ value: 'cartao', label: 'Cartão' },
{ value: 'dinheiro', label: 'Dinheiro' },
];
interface MarcarPagoDialogProps {
parcela: { id: string; valor: string } | null;
onOpenChange: (open: boolean) => void;
onSuccess: () => void;
}
/** Reaproveitado no detalhe da venda (aba Parcelas) e em `/app/recebimentos`. */
export function MarcarPagoDialog({ parcela, onOpenChange, onSuccess }: MarcarPagoDialogProps) {
const [forma, setForma] = React.useState<FormaPagamento | ''>('');
React.useEffect(() => {
setForma('');
}, [parcela?.id]);
const mutation = useMutation({
mutationFn: () => marcarRecebimentoPago(parcela!.id, forma as FormaPagamento),
onSuccess: () => {
toast.success('Parcela marcada como paga.');
onOpenChange(false);
onSuccess();
},
onError: (err) => {
toast.error(err instanceof ApiError ? err.message : 'Não foi possível marcar a parcela como paga.');
},
});
return (
<ResponsiveDialog
open={parcela !== null}
onOpenChange={onOpenChange}
title="Marcar parcela como paga"
description={parcela ? `Valor: ${formatCurrency(parcela.valor)}` : undefined}
footer={
<>
<Button variant="outline" onClick={() => onOpenChange(false)} disabled={mutation.isPending}>
Cancelar
</Button>
<Button disabled={!forma} loading={mutation.isPending} onClick={() => mutation.mutate()}>
Confirmar pagamento
</Button>
</>
}
>
<div className="flex flex-col gap-4 px-4 pb-4 sm:px-0 sm:pb-0">
<Select value={forma} onValueChange={(value) => setForma(value as FormaPagamento)}>
<SelectTrigger className="w-full" aria-label="Forma de pagamento">
<SelectValue placeholder="Selecione a forma de pagamento" />
</SelectTrigger>
<SelectContent>
{FORMAS.map((f) => (
<SelectItem key={f.value} value={f.value}>
{f.label}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</ResponsiveDialog>
);
}