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>
24 lines
1017 B
TypeScript
24 lines
1017 B
TypeScript
import { z } from 'zod';
|
|
|
|
/**
|
|
* Campos numéricos ficam como `z.number()` puro (sem `coerce`/`preprocess`)
|
|
* de propósito — a conversão string→number do `<input type="number">`
|
|
* acontece no `register(...)` do react-hook-form (`valueAsNumber`/
|
|
* `setValueAs`), não no schema. Misturar as duas coisas faz o tipo de
|
|
* entrada do formulário (`z.input`) divergir do tipo de saída (`z.infer`) e
|
|
* quebra a inferência de tipos do `zodResolver`.
|
|
*/
|
|
const optionalInt = z.number().int().min(1, 'Deve ser pelo menos 1').optional();
|
|
|
|
/** Espelha `CreatePlanoDto`/`UpdatePlanoDto` (`src/modules/planos/dto/`). */
|
|
export const planoSchema = z.object({
|
|
nome: z.string().min(2, 'Informe pelo menos 2 caracteres'),
|
|
descricao: z.string().optional(),
|
|
valor_mensal: z.number('Informe um valor').positive('O valor deve ser maior que zero'),
|
|
limite_customers: optionalInt,
|
|
limite_usuarios: optionalInt,
|
|
limite_instancias_whatsapp: optionalInt,
|
|
});
|
|
|
|
export type PlanoFormValues = z.infer<typeof planoSchema>;
|