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>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
'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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user