[MVPTN-86] feat(CRUD): Criação do CRUD configuração de serviços de balcão

This commit is contained in:
Kenio 2025-10-13 13:51:42 -03:00
parent 9e37520615
commit a26fc881b2
2 changed files with 510 additions and 59 deletions

View file

@ -14,6 +14,8 @@ import {
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { DollarSign, Settings, SquarePen } from 'lucide-react';
import {
Form,
FormControl,
@ -23,63 +25,65 @@ import {
FormMessage,
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { Checkbox } from '@/components/ui/checkbox';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import { TServicoTipoSchema } from '../../_schemas/TServicoTipoSchema'; // Novo Schema
import { TServicoTipoSchema } from '../../_schemas/TServicoTipoSchema';
import { useEffect } from 'react';
// Define o tipo do formulário com base no novo schema Zod
// Tipo inferido a partir do schema Zod
type FormValues = z.infer<typeof TServicoTipoSchema>;
// Propriedades esperadas pelo componente
interface Props {
isOpen: boolean; // controla se o Dialog está aberto
data: FormValues | null; // dados para edição (se existirem)
onClose: (item: null, isFormStatus: boolean) => void; // callback para fechar
onSave: (data: FormValues) => void; // callback para salvar
isOpen: boolean;
data: FormValues | null;
onClose: (item: null, isFormStatus: boolean) => void;
onSave: (data: FormValues) => void;
}
// Componente principal do formulário
export default function TServicoTipoForm({ isOpen, data, onClose, onSave }: Props) {
// Inicializa o react-hook-form integrado ao Zod para validação
// Inicializa o react-hook-form com validação via Zod
const form = useForm<FormValues>({
resolver: zodResolver(TServicoTipoSchema),
defaultValues: {
servico_tipo_id: 0, // Assumindo 'servico_tipo_id' como o ID
descricao: '', // Assumindo 'descricao' como o campo de nome
servico_tipo_id: 0,
descricao: '',
categoria: '',
servico_padrao: '',
servico_caixa: '',
averbacao: false,
transferencia_veiculo: false,
usar_a4: false,
etiqueta_unica: '',
},
});
// Quando recebe dados para edição, atualiza os valores do formulário
// Atualiza os valores quando há dados para edição
useEffect(() => {
if (data) {
// Se for edição, carrega os dados recebidos
form.reset({
servico_tipo_id: data.servico_tipo_id,
descricao: data.descricao ?? '',
// Adicione outros campos específicos de TServicoTipo aqui
});
form.reset(data);
} else {
// Se for novo cadastro, limpa o formulário com valores padrão
form.reset({
servico_tipo_id: 0,
descricao: '',
// Adicione outros campos específicos de TServicoTipo aqui
});
form.reset();
}
}, [data, form]); // Não há dependência de fetchGUf, pois foi removido.
}, [data, form]);
return (
<Dialog
open={isOpen}
// Fecha o diálogo quando alterado para "false"
onOpenChange={(open) => {
if (!open) onClose(null, false);
}}
>
<DialogContent className="sm:max-w-[425px]">
<DialogContent className="sm:max-w-[850px]">
{/* Cabeçalho do diálogo */}
<DialogHeader>
<DialogTitle>Tipos de Serviço</DialogTitle>
@ -89,46 +93,488 @@ export default function TServicoTipoForm({ isOpen, data, onClose, onSave }: Prop
{/* Estrutura do formulário */}
<Form {...form}>
<form onSubmit={form.handleSubmit(onSave)} className="space-y-6">
{/* Campo: Nome do Tipo de Serviço (descricao) */}
<FormField
control={form.control}
name="descricao"
render={({ field }) => (
<FormItem>
<FormLabel>Descrição do Tipo</FormLabel>
<FormControl>
<Input {...field} value={field.value ?? ''} placeholder="Digite a descrição do tipo de serviço" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{/* Rodapé do diálogo com botões */}
<Tabs defaultValue="dadosTipoServico" className="space-y-4">
{/* Abas principais */}
<TabsList className="flex w-full">
<TabsTrigger className="flex-1 text-center cursor-pointer" value="dadosTipoServico">
<SquarePen className="me-1 inline" />
Dados do Tipo de Serviço
</TabsTrigger>
<TabsTrigger className="flex-1 text-center cursor-pointer" value="configuracoes">
<Settings className="inline" />
Configurações
</TabsTrigger>
<TabsTrigger className="flex-1 text-center cursor-pointer" value="valores">
<DollarSign className="inline" />
Valores
</TabsTrigger>
</TabsList>
{/* Aba: Dados do Tipo de Serviço */}
<TabsContent value="dadosTipoServico" className="space-y-4">
<div className="grid w-full grid-cols-12 gap-4 border p-4 rounded-md">
{/* Campo: Descrição */}
<div className="col-span-12">
<FormField
control={form.control}
name="descricao"
render={({ field }) => (
<FormItem>
<FormLabel>Descrição do Tipo</FormLabel>
<FormControl>
<Input {...field} value={field.value ?? ''} placeholder="Digite a descrição do tipo de serviço" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Campo: Categoria (Select) */}
<div className="col-span-12 sm:col-span-6 md:col-span-4">
<FormField
control={form.control}
name="categoria"
render={({ field }) => (
<FormItem className="w-full">
<FormLabel>Categoria</FormLabel>
<Select
value={field.value}
onValueChange={field.onChange}
>
<FormControl className="w-full">
<SelectTrigger className="w-full cursor-pointer">
<SelectValue placeholder="Selecione uma categoria" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="1" className="cursor-pointer">Autenticação</SelectItem>
<SelectItem value="2" className="cursor-pointer">Certidão</SelectItem>
<SelectItem value="3" className="cursor-pointer">Serviços Gerais</SelectItem>
<SelectItem value="4" className="cursor-pointer">Reconhecimento</SelectItem>
<SelectItem value="5" className="cursor-pointer">Geral</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Campo: Serviço Padrão (Select) */}
<div className="col-span-12 sm:col-span-6 md:col-span-4">
<FormField
control={form.control}
name="servico_padrao"
render={({ field }) => (
<FormItem className="w-full">
<FormLabel>Serviço Padrão</FormLabel>
<Select
value={field.value}
onValueChange={field.onChange}
>
<FormControl className="w-full">
<SelectTrigger className="w-full cursor-pointer">
<SelectValue placeholder="Selecione o tipo padrão" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="S" className="cursor-pointer">Sim</SelectItem>
<SelectItem value="N" className="cursor-pointer">Não</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Campo: Serviço Caixa (Select) */}
<div className="col-span-12 sm:col-span-6 md:col-span-4">
<FormField
control={form.control}
name="servico_caixa"
render={({ field }) => (
<FormItem className="w-full">
<FormLabel>Serviço Caixa</FormLabel>
<Select
value={field.value}
onValueChange={field.onChange}
>
<FormControl className="w-full">
<SelectTrigger className="w-full cursor-pointer">
<SelectValue placeholder="Selecione uma opção" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="1" className="cursor-pointer">Autenticação</SelectItem>
<SelectItem value="2" className="cursor-pointer">Reconhecimento</SelectItem>
<SelectItem value="3" className="cursor-pointer">Aluguel</SelectItem>
<SelectItem value="4" className="cursor-pointer">Papelaria</SelectItem>
<SelectItem value="5" className="cursor-pointer">Limpeza Cartório</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Campo: Averbação (Checkbox) */}
<div className="col-span-12 sm:col-span-6 md:col-span-4">
<FormField
control={form.control}
name="averbacao"
render={({ field }) => (
<FormItem className="flex flex-row items-center space-x-3 space-y-0">
<FormControl>
<Checkbox className="cursor-pointer"
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<FormLabel className="font-normal cursor-pointer">Averbação</FormLabel>
</FormItem>
)}
/>
</div>
{/* Campo: Transferência de Veículo (Checkbox) */}
<div className="col-span-12 sm:col-span-6 md:col-span-4">
<FormField
control={form.control}
name="transferencia_veiculo"
render={({ field }) => (
<FormItem className="flex flex-row items-center space-x-3 space-y-0">
<FormControl>
<Checkbox className="cursor-pointer"
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<FormLabel className="font-normal cursor-pointer">Transferência Veículo</FormLabel>
</FormItem>
)}
/>
</div>
{/* Campo: Usar A4 (Checkbox) */}
<div className="col-span-12 sm:col-span-6 md:col-span-4">
<FormField
control={form.control}
name="usar_a4"
render={({ field }) => (
<FormItem className="flex flex-row items-center space-x-3 space-y-0">
<FormControl>
<Checkbox className="cursor-pointer"
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<FormLabel className="font-normal cursor-pointer">Usar A4</FormLabel>
</FormItem>
)}
/>
</div>
{/* Campo: Etiqueta Única (Texto normal) */}
<div className="col-span-12 sm:col-span-6 md:col-span-4">
<FormField
control={form.control}
name="etiqueta_unica"
render={({ field }) => (
<FormItem className="flex flex-row items-center space-x-3 space-y-0">
<FormControl>
<Checkbox className="cursor-pointer"
checked={field.value}
onCheckedChange={field.onChange}
/>
</FormControl>
<FormLabel className="font-normal cursor-pointer">Etiqueta Única</FormLabel>
</FormItem>
)}
/>
</div>
</div>
</TabsContent>
{/* Outras abas */}
<TabsContent value="configuracoes" className="space-y-4">
<div className="grid w-full grid-cols-12 gap-4 border p-4 rounded-md">
{/* Campo: Serviço tipo (Select) */}
<div className="col-span-12 sm:col-span-6 md:col-span-4">
<FormField
control={form.control}
name="servico_tipo"
render={({ field }) => (
<FormItem className="w-full">
<FormLabel>Tipo</FormLabel>
<Select
value={field.value}
onValueChange={field.onChange}
>
<FormControl className="w-full">
<SelectTrigger className="w-full cursor-pointer">
<SelectValue placeholder="Selecione uma opção" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="1" className="cursor-pointer">Semelhança</SelectItem>
<SelectItem value="2" className="cursor-pointer">Verdadeiro</SelectItem>
<SelectItem value="3" className="cursor-pointer">Abono</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Campo: Pessoa (Select) */}
<div className="col-span-12 sm:col-span-6 md:col-span-4">
<FormField
control={form.control}
name="pessoa_tipo"
render={({ field }) => (
<FormItem className="w-full">
<FormLabel>Pessoa</FormLabel>
<Select
value={field.value}
onValueChange={field.onChange}
>
<FormControl className="w-full">
<SelectTrigger className="w-full cursor-pointer">
<SelectValue placeholder="Selecione uma opção" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="F" className="cursor-pointer">Física</SelectItem>
<SelectItem value="J" className="cursor-pointer">Juridica</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Campo: Quantidade de pessoas */}
<div className="col-span-12 sm:col-span-6 md:col-span-4">
<FormField
control={form.control}
name="quantidade_pessoas"
render={({ field }) => (
<FormItem>
{/* Rótulo do campo */}
<FormLabel>Quantidade de pessoas</FormLabel>
<FormControl>
<Input
{...field}
// Garante que sempre exista um valor (evita undefined)
value={field.value ?? ''}
// Placeholder explicativo
placeholder="Digite a quantidade de pessoas"
// Mostra teclado numérico em celulares
inputMode="numeric"
// Sugere ao navegador que aceite apenas números
pattern="[0-9]*"
// Intercepta a digitação e remove tudo que não for número
onChange={(e) => {
const apenasNumeros = e.target.value.replace(/[^0-9]/g, '');
field.onChange(apenasNumeros);
}}
/>
</FormControl>
{/* Exibe mensagens de erro do React Hook Form */}
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="col-span-12">Requerer</div>
{/* Campo: Biometria (Select) */}
<div className="col-span-12 sm:col-span-6 md:col-span-3">
<FormField
control={form.control}
name="biometria"
render={({ field }) => (
<FormItem className="w-full">
<FormLabel>Biometria</FormLabel>
<Select
value={field.value}
onValueChange={field.onChange}
>
<FormControl className="w-full">
<SelectTrigger className="w-full cursor-pointer">
<SelectValue placeholder="Selecione uma opção" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="S" className="cursor-pointer">Sim</SelectItem>
<SelectItem value="N" className="cursor-pointer">Não</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Campo: CPF/CNPJ (Select) */}
<div className="col-span-12 sm:col-span-6 md:col-span-3">
<FormField
control={form.control}
name="cpf_cnpj"
render={({ field }) => (
<FormItem className="w-full">
<FormLabel>CPF/CNPJ</FormLabel>
<Select
value={field.value}
onValueChange={field.onChange}
>
<FormControl className="w-full">
<SelectTrigger className="w-full cursor-pointer">
<SelectValue placeholder="Selecione uma opção" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="S" className="cursor-pointer">Sim</SelectItem>
<SelectItem value="N" className="cursor-pointer">Não</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Campo: autorização (Select) */}
<div className="col-span-12 sm:col-span-6 md:col-span-3">
<FormField
control={form.control}
name="autorizacao"
render={({ field }) => (
<FormItem className="w-full">
<FormLabel>Autorização</FormLabel>
<Select
value={field.value}
onValueChange={field.onChange}
>
<FormControl className="w-full">
<SelectTrigger className="w-full cursor-pointer">
<SelectValue placeholder="Selecione uma opção" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="S" className="cursor-pointer">Sim</SelectItem>
<SelectItem value="N" className="cursor-pointer">Não</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Campo: abonador (Select) */}
<div className="col-span-12 sm:col-span-6 md:col-span-3">
<FormField
control={form.control}
name="abonador"
render={({ field }) => (
<FormItem className="w-full">
<FormLabel>Abonador</FormLabel>
<Select
value={field.value}
onValueChange={field.onChange}
>
<FormControl className="w-full">
<SelectTrigger className="w-full cursor-pointer">
<SelectValue placeholder="Selecione uma opção" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="S" className="cursor-pointer">Sim</SelectItem>
<SelectItem value="N" className="cursor-pointer">Não</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Campo: etiquetas/carimbos (Select) */}
<div className="col-span-12 sm:col-span-6 md:col-span-12">
<FormField
control={form.control}
name="etiquetas_carimbos"
render={({ field }) => (
<FormItem className="w-full">
<FormLabel>Abonador</FormLabel>
<Select
value={field.value}
onValueChange={field.onChange}
>
<FormControl className="w-full">
<SelectTrigger className="w-full cursor-pointer">
<SelectValue placeholder="Selecione uma opção" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="S" className="cursor-pointer">Sim</SelectItem>
<SelectItem value="N" className="cursor-pointer">Não</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
</div>
</div>
</TabsContent>
<TabsContent value="valores" className="space-y-4">
003
</TabsContent>
{/* Campo oculto: ID */}
<input type="hidden" {...form.register('servico_tipo_id', { valueAsNumber: true })} />
</Tabs>
{/* Rodapé do diálogo */}
<DialogFooter className="mt-4">
{/* Botão de cancelar */}
<DialogClose asChild>
<Button
variant="outline"
type="button"
onClick={() => onClose(null, false)}
className="cursor-pointer"
>
<Button variant="outline" type="button" onClick={() => onClose(null, false)}>
Cancelar
</Button>
</DialogClose>
{/* Botão de salvar */}
<Button type="submit" className="cursor-pointer">
Salvar
</Button>
<Button type="submit">Salvar</Button>
</DialogFooter>
{/* Campo oculto: ID do Tipo de Serviço */}
<input type="hidden" {...form.register('servico_tipo_id', { valueAsNumber: true })} />
</form>
</Form>
</DialogContent>
</Dialog>
);
}
}

View file

@ -77,6 +77,7 @@ export default function TImovelForm({ isOpen, data, onClose, onSave, buttonIsLoa
}}
>
<DialogContent className="w-full max-w-full p-6 sm:max-w-3xl md:max-w-4xl lg:max-w-5xl">
<DialogHeader>
<DialogTitle>
Imóvel Urbano
@ -85,10 +86,13 @@ export default function TImovelForm({ isOpen, data, onClose, onSave, buttonIsLoa
Cadastro de imóvel urbano
</DialogDescription>
</DialogHeader>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSave)} className="space-y-6">
{/* Tabs */}
<Tabs defaultValue="dadosDoImovel" className="space-y-4">
<TabsList className="flex w-full">
<TabsTrigger className="flex-1 text-center cursor-pointer" value="dadosDoImovel">
<HouseIcon className="me-1 inline" />
@ -99,6 +103,7 @@ export default function TImovelForm({ isOpen, data, onClose, onSave, buttonIsLoa
Unidades
</TabsTrigger>
</TabsList>
{/* Dados do Imóvel */}
<TabsContent value="dadosDoImovel" className="space-y-4">
<div className="grid w-full grid-cols-12 gap-4">