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,92 @@
|
||||
'use client';
|
||||
|
||||
import { useForm } from 'react-hook-form';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
|
||||
import { createCustomer, type Customer } from '@/lib/api/customers';
|
||||
import { customerSchema, type CustomerFormValues } from '@/lib/validators/customers';
|
||||
import { ApiError } from '@/lib/api/client';
|
||||
import { toast } from '@/lib/toast';
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { FormField } from '@/components/ui/form-field';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { ResponsiveDialog } from '@/components/ui/responsive-dialog';
|
||||
|
||||
interface CustomerQuickCreateDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
onCreated: (customer: Customer) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cadastro rápido de cliente sem sair do fluxo de nova venda (Etapa 17,
|
||||
* critério de aceite "não perder o que já foi preenchido na venda") — só
|
||||
* os campos mínimos; o resto pode ser completado depois em
|
||||
* `/app/clientes/:id`.
|
||||
*/
|
||||
export function CustomerQuickCreateDialog({ open, onOpenChange, onCreated }: CustomerQuickCreateDialogProps) {
|
||||
const queryClient = useQueryClient();
|
||||
const form = useForm<CustomerFormValues>({
|
||||
resolver: zodResolver(customerSchema),
|
||||
defaultValues: { razao_social: '', documento: '', email: '', telefone_whatsapp: '' },
|
||||
});
|
||||
|
||||
const mutation = useMutation({
|
||||
mutationFn: (values: CustomerFormValues) =>
|
||||
createCustomer({
|
||||
razao_social: values.razao_social,
|
||||
documento: values.documento || undefined,
|
||||
email: values.email || undefined,
|
||||
telefone_whatsapp: values.telefone_whatsapp || undefined,
|
||||
}),
|
||||
onSuccess: (customer) => {
|
||||
queryClient.invalidateQueries({ queryKey: ['tenant', 'customers'] });
|
||||
toast.success('Cliente cadastrado.');
|
||||
form.reset({ razao_social: '', documento: '', email: '', telefone_whatsapp: '' });
|
||||
onCreated(customer);
|
||||
onOpenChange(false);
|
||||
},
|
||||
onError: (err) => {
|
||||
toast.error(err instanceof ApiError ? err.message : 'Não foi possível cadastrar o cliente.');
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<ResponsiveDialog
|
||||
open={open}
|
||||
onOpenChange={onOpenChange}
|
||||
title="Cadastro rápido de cliente"
|
||||
description="Só o essencial — complete o resto depois no cadastro do cliente."
|
||||
footer={
|
||||
<>
|
||||
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
||||
Cancelar
|
||||
</Button>
|
||||
<Button loading={mutation.isPending} onClick={form.handleSubmit((values) => mutation.mutate(values))}>
|
||||
Cadastrar e usar
|
||||
</Button>
|
||||
</>
|
||||
}
|
||||
>
|
||||
<form
|
||||
className="flex flex-col gap-4 px-4 pb-4 sm:px-0 sm:pb-0"
|
||||
onSubmit={form.handleSubmit((values) => mutation.mutate(values))}
|
||||
>
|
||||
<FormField label="Razão social" error={form.formState.errors.razao_social?.message} required>
|
||||
<Input {...form.register('razao_social')} />
|
||||
</FormField>
|
||||
<FormField label="Documento (CPF/CNPJ)" error={form.formState.errors.documento?.message}>
|
||||
<Input {...form.register('documento')} />
|
||||
</FormField>
|
||||
<FormField label="Telefone/WhatsApp" error={form.formState.errors.telefone_whatsapp?.message}>
|
||||
<Input {...form.register('telefone_whatsapp')} />
|
||||
</FormField>
|
||||
<FormField label="E-mail" error={form.formState.errors.email?.message}>
|
||||
<Input type="email" {...form.register('email')} />
|
||||
</FormField>
|
||||
</form>
|
||||
</ResponsiveDialog>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user