'use client'; import * as React from 'react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { useForm } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { Plus } from 'lucide-react'; import { createPlano, deletePlano, listPlanos, updatePlano, type Plano } from '@/lib/api/planos'; import { planoSchema, type PlanoFormValues } from '@/lib/validators/planos'; import { ApiError } from '@/lib/api/client'; import { formatCurrency } from '@/lib/format'; import { toast } from '@/lib/toast'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { DataTable, type DataTableColumn } from '@/components/ui/data-table'; import { FormField } from '@/components/ui/form-field'; import { Input } from '@/components/ui/input'; import { ResponsiveDialog } from '@/components/ui/responsive-dialog'; import { Textarea } from '@/components/ui/textarea'; import { ConfirmDialog } from '@/components/domain/confirm-dialog'; /** `` vazio deve virar `undefined` (ilimitado), não `NaN`/`0`. */ function toOptionalInt(value: string): number | undefined { return value === '' ? undefined : Number(value); } export default function PlanosPage() { const queryClient = useQueryClient(); const { data: planos, isLoading } = useQuery({ queryKey: ['master', 'planos'], queryFn: () => listPlanos(), }); const [editing, setEditing] = React.useState(null); const [formOpen, setFormOpen] = React.useState(false); const [deleting, setDeleting] = React.useState(null); const form = useForm({ resolver: zodResolver(planoSchema) }); function openCreate() { setEditing(null); form.reset({ nome: '', descricao: '', valor_mensal: 0, limite_customers: undefined, limite_usuarios: undefined, limite_instancias_whatsapp: undefined, }); setFormOpen(true); } function openEdit(plano: Plano) { setEditing(plano); form.reset({ nome: plano.nome, descricao: plano.descricao ?? '', valor_mensal: Number(plano.valor_mensal), limite_customers: plano.limite_customers ?? undefined, limite_usuarios: plano.limite_usuarios ?? undefined, limite_instancias_whatsapp: plano.limite_instancias_whatsapp, }); setFormOpen(true); } const saveMutation = useMutation({ mutationFn: (values: PlanoFormValues) => editing ? updatePlano(editing.id, values) : createPlano(values), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['master', 'planos'] }); toast.success(editing ? 'Plano atualizado.' : 'Plano criado.'); setFormOpen(false); }, onError: (err) => { toast.error(err instanceof ApiError ? err.message : 'Não foi possível salvar o plano.'); }, }); const deleteMutation = useMutation({ mutationFn: (id: string) => deletePlano(id), onSuccess: (result) => { queryClient.invalidateQueries({ queryKey: ['master', 'planos'] }); toast.success( result.deletado ? 'Plano excluído.' : 'Plano em uso por tenant(s) — desativado em vez de excluído.', ); setDeleting(null); }, onError: (err) => { toast.error(err instanceof ApiError ? err.message : 'Não foi possível excluir o plano.'); setDeleting(null); }, }); const columns: DataTableColumn[] = [ { id: 'nome', header: 'Plano', primary: true, cell: (row) => row.nome }, { id: 'valor', header: 'Valor mensal', cell: (row) => formatCurrency(row.valor_mensal), }, { id: 'limites', header: 'Limites', cell: (row) => ( {row.limite_customers ?? '∞'} clientes · {row.limite_usuarios ?? '∞'} usuários ·{' '} {row.limite_instancias_whatsapp} WhatsApp ), }, { id: 'status', header: 'Status', primary: true, cell: (row) => ( {row.ativo ? 'Ativo' : 'Inativo'} ), }, ]; return (

Planos

Planos de assinatura oferecidos aos tenants

row.id} loading={isLoading} emptyTitle="Nenhum plano cadastrado" emptyDescription="Crie o primeiro plano para poder cadastrar tenants." emptyAction={ } rowActions={(row) => ( <> )} /> } >
saveMutation.mutate(values))} >