[MVPTN-109] feat(CRUD): Cria o CRUD de T_CENSEC_TIPONATUREZA

This commit is contained in:
Keven Willian Pereira de Souza 2025-10-16 12:54:13 -03:00
parent aaaa04975e
commit 3bd3134d33
26 changed files with 998 additions and 1 deletions

View file

@ -0,0 +1,9 @@
'use client';
import TCensecTipoNaturezaIndex from "@/packages/administrativo/components/TCensecTipoNatureza/TCensecTipoNaturezaIndex";
export default function TCensecTipoNaturezaPage() {
return (
< TCensecTipoNaturezaIndex />
);
}

View file

@ -2,7 +2,7 @@
import TCensecQualidadeIndex from "@/packages/administrativo/components/TCensecQualidade/TCensecQualidadeIndex";
export default function TImovelRuralPage() {
export default function TCensecQualidadePage() {
return (
< TCensecQualidadeIndex />
);

View file

@ -137,6 +137,10 @@ const data = {
title: "Censec/Qualidades",
url: "/administrativo/censec/qualidades"
},
{
title: "Censec/Qualidades",
url: "/administrativo/censec/naturezas"
},
{
title: 'Censec/Centrais',
url: '/cadastros/censec/',

View file

@ -0,0 +1,92 @@
'use client';
import React from "react";
import { Button } from "@/components/ui/button";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";
import { FormControl } from "@/components/ui/form";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { cn } from "@/lib/utils";
import { CheckIcon, ChevronsUpDownIcon } from "lucide-react";
import GetCapitalize from "@/shared/actions/text/GetCapitalize";
import { useTCensecTipoAtoReadHook } from "@/app/(protected)/(cadastros)/cadastros/_hooks/t_censec_tipoato/useTCensecTipoAtoReadHook";
export default function TCensecTipoAtoSelect({ field }: any) {
const [open, setOpen] = React.useState(false);
const [isLoading, setIsLoading] = React.useState(false);
const { tCensecTipoAto, fetchTCensecTipoAto } = useTCensecTipoAtoReadHook();
// Busca os dados uma única vez ao montar
React.useEffect(() => {
const loadData = async () => {
if (!tCensecTipoAto.length) {
setIsLoading(true);
await fetchTCensecTipoAto();
setIsLoading(false);
}
};
loadData();
}, []);
const selected = tCensecTipoAto.find(
(item) => String(item.censec_tipoato_id) === String(field.value)
);
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<FormControl className="w-full">
<Button
variant="outline"
role="combobox"
aria-expanded={open}
disabled={isLoading}
className="justify-between"
>
{isLoading
? "Carregando..."
: selected
? GetCapitalize(selected.descricao)
: "Selecione..."}
<ChevronsUpDownIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-full p-0">
<Command>
<CommandInput placeholder="Buscar tipo logradouro..." />
<CommandList>
<CommandEmpty>
{isLoading ? "Carregando..." : "Nenhum resultado encontrado."}
</CommandEmpty>
<CommandGroup>
{tCensecTipoAto?.map((item) => (
<CommandItem
key={item.censec_tipoato_id}
value={item.descricao?.toLowerCase() ?? ""}
onSelect={() => {
field.onChange(Number(item.censec_tipoato_id));
setOpen(false);
}}
>
<CheckIcon
className={cn(
"mr-2 h-4 w-4",
String(field.value) === String(item.censec_tipoato_id)
? "opacity-100"
: "opacity-0"
)}
/>
{GetCapitalize(item.descricao)}
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
}

View file

@ -0,0 +1,97 @@
import { ColumnDef } from "@tanstack/react-table";
import TCensecTipoNaturezaInterface from "../../interfaces/TCensecTipoNatureza/TCensecTipoNaturezaInterface";
import { Button } from "@/components/ui/button";
import {
EllipsisIcon,
PencilIcon,
Trash2Icon,
} from "lucide-react";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { SortableHeader } from "@/shared/components/dataTable/SortableHeader";
import GetCapitalize from "@/shared/actions/text/GetCapitalize";
import { ConfirmacaoEnum } from "@/shared/enums/ConfirmacaoEnum";
import { TipoAtoAnteriorEnum } from "@/shared/enums/TipoAtoAnteriorEnum";
export default function TCensecTipoNaturezaColumns(
onEdit: (item: TCensecTipoNaturezaInterface, isEditingFormStatus: boolean) => void,
onDelete: (item: TCensecTipoNaturezaInterface, isEditingFormStatus: boolean) => void
): ColumnDef<TCensecTipoNaturezaInterface>[] {
return [
// ID
{
accessorKey: "censec_tiponatureza_id",
header: ({ column }) => SortableHeader("#", column),
cell: ({ row }) => Number(row.getValue("censec_tiponatureza_id")),
enableSorting: false,
},
// descricao
{
accessorKey: "descricao",
header: ({ column }) => SortableHeader("Descrição", column),
cell: ({ row }) => GetCapitalize(row.getValue("descricao")),
},
// possui_ato_anterior
{
accessorKey: "possui_ato_anterior",
header: ({ column }) => SortableHeader("Possui Ato Anterior", column),
cell: ({ row }) => {
const value = row.getValue("possui_ato_anterior") as keyof typeof ConfirmacaoEnum;
return ConfirmacaoEnum[value] ?? "-";
},
},
// situacao_ato_anterior
{
accessorKey: "situacao_ato_anterior",
header: ({ column }) => SortableHeader("Situação Ato Anterior", column),
cell: ({ row }) => {
const value = Number(row.getValue("situacao_ato_anterior"));
const label = Object.prototype.hasOwnProperty.call(TipoAtoAnteriorEnum, value)
? TipoAtoAnteriorEnum[value as keyof typeof TipoAtoAnteriorEnum]
: "-";
return label;
},
},
// Ações
{
id: "actions",
header: "Ações",
cell: ({ row }) => {
const imovel = row.original;
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon">
<EllipsisIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent side="left" align="start">
<DropdownMenuGroup>
<DropdownMenuItem onSelect={() => onEdit(imovel, true)}>
<PencilIcon className="mr-2 h-4 w-4" />
Editar
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
className="text-red-600"
onSelect={() => onDelete(imovel, true)}
>
<Trash2Icon className="mr-2 h-4 w-4" />
Remover
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
);
},
enableSorting: false,
enableHiding: false,
},
];
}

View file

@ -0,0 +1,199 @@
'use client';
import React, { useEffect } from 'react';
import { Button } from '@/components/ui/button';
import {
Dialog,
DialogClose,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import LoadingButton from '@/shared/components/loadingButton/LoadingButton';
import { ResetFormIfData } from '@/shared/actions/form/ResetFormIfData';
import { useTCensecTipoNaturezaFormHook } from '../../hooks/TCensecTipoNatureza/useTCensecTipoNaturezaFormHook';
import { TCensecTipoNaturezaFormInterface } from '../../interfaces/TCensecTipoNatureza/TCensecTipoNaturezaFormInterface';
import { parseNumberInput } from '@/shared/actions/form/parseNumberInput';
import TCensecTipoAtoSelect from '../TCensecTipoAto/TCensecTipoAtoSelect';
import ConfirmacaoSelect from '@/shared/components/confirmacao/ConfirmacaoSelect';
import TipoAtoAnteriorSelect from '@/shared/components/tipoAtoAnterior/TipoAtoAnteriorSelect';
import TipoNaturezaSelect from '@/shared/components/tipoNatureza/TipoNaturezaSelect';
export default function TCensecTipoNaturezaForm({ isOpen, data, onClose, onSave, buttonIsLoading }: TCensecTipoNaturezaFormInterface) {
// Inicializa o react-hook-form com schema zod
const form = useTCensecTipoNaturezaFormHook({});
// Atualiza o formulário quando recebe dados para edição
useEffect(() => {
// Se existir dados, reseta o formulário com os mesmos
ResetFormIfData(form, data);
}, [data, form]);
function onError(error: any) {
console.log(error);
}
return (
<Dialog
open={isOpen}
onOpenChange={(open) => {
if (!open) onClose(null, false);
}}
>
<DialogContent className="w-full max-w-full p-6 sm:max-w-3xl md:max-w-2xl lg:max-w-2xl">
<DialogHeader>
<DialogTitle>
Censec tipos de natureza
</DialogTitle>
<DialogDescription>
Censec tipos de natureza
</DialogDescription>
</DialogHeader>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSave, onError)} className="space-y-6">
<div className="grid w-full grid-cols-12 gap-4">
{/* Descrição */}
<div className="col-span-12 sm:col-span-12 md:col-span-12">
<FormField
control={form.control}
name="descricao"
render={({ field }) => (
<FormItem>
<FormLabel>Descrição</FormLabel>
<FormControl>
<Input {...field} type='text' />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Tipo do Ato */}
<div className="col-span-12 sm:col-span-12 md:col-span-12">
<FormField
control={form.control}
name="censec_tipoato_id"
render={({ field }) => (
<FormItem>
<FormLabel>Tipo do Ato</FormLabel>
< TCensecTipoAtoSelect field={field} />
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Possui Ato Anterior */}
<div className="col-span-12 sm:col-span-12 md:col-span-4">
<FormField
control={form.control}
name="possui_ato_anterior"
render={({ field }) => (
<FormItem>
<FormLabel>Possui Ato Anterior</FormLabel>
<FormControl>
<ConfirmacaoSelect field={field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* tipo_ato_anterior */}
<div className="col-span-12 sm:col-span-12 md:col-span-4">
<FormField
control={form.control}
name="tipo_ato_anterior"
render={({ field }) => (
<FormItem>
<FormLabel>Natureza</FormLabel>
<TipoNaturezaSelect field={field} />
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Situação */}
<div className="col-span-12 sm:col-span-12 md:col-span-4">
<FormField
control={form.control}
name="situacao_ato_anterior"
render={({ field }) => (
<FormItem>
<FormLabel>Situação</FormLabel>
<TipoAtoAnteriorSelect field={field} />
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Código CENSEC */}
<div className="col-span-12 sm:col-span-12 md:col-span-6">
<FormField
control={form.control}
name="obrigatorio"
render={({ field }) => (
<FormItem>
<FormLabel>Código CENSEC</FormLabel>
<FormControl>
<ConfirmacaoSelect field={field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Código */}
<div className="col-span-12 sm:col-span-12 md:col-span-6">
<FormField
control={form.control}
name="codigo"
render={({ field }) => (
<FormItem>
<FormLabel>Código</FormLabel>
<FormControl>
<Input {...field} type='number' onChange={e => field.onChange(parseNumberInput(e))} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
</div>
{/* Rodapé do Dialog */}
<DialogFooter className="mt-4 flex flex-col sm:flex-row gap-2 justify-end">
<DialogClose asChild>
<Button variant="outline" type="button" onClick={() => onClose(null, false)}>
Cancelar
</Button>
</DialogClose>
<LoadingButton
text="Salvar"
textLoading="Aguarde..."
type="submit"
loading={buttonIsLoading}
/>
</DialogFooter>
</form>
</Form>
</DialogContent >
</Dialog >
);
}

View file

@ -0,0 +1,172 @@
'use client';
import { useEffect, useState, useCallback } from 'react';
import Loading from '@/shared/components/loading/loading';
import { useTCensecTipoNaturezaIndexHook } from '@/packages/administrativo/hooks/TCensecTipoNatureza/useTCensecTipoNaturezaIndexHook';
import { useTCensecTipoNaturezaSaveHook } from '@/packages/administrativo/hooks/TCensecTipoNatureza/useTCensecTipoNaturezaSaveHook';
import { useTCensecTipoNaturezaDeleteHook } from '@/packages/administrativo/hooks/TCensecTipoNatureza/useTCensecTipoNaturezaDeleteHook';
import ConfirmDialog from '@/shared/components/confirmDialog/ConfirmDialog';
import { useConfirmDialog } from '@/shared/components/confirmDialog/useConfirmDialog';
import TCensecTipoNaturezaInterface from '@/packages/administrativo/interfaces/TCensecTipoNatureza/TCensecTipoNaturezaInterface';
import Header from '@/shared/components/structure/Header';
import TCensecTipoNaturezaTable from './TCensecTipoNaturezaTable';
import TCensecTipoNaturezaForm from './TCensecTipoNaturezaForm';
export default function TCensecTipoNaturezaIndex() {
// Controle de estado do botão
const [buttonIsLoading, setButtonIsLoading] = useState(false);
// Hooks para leitura e salvamento
const { tCensecNaturezaTipo, indexTCensecTipoNatureza } = useTCensecTipoNaturezaIndexHook();
const { saveTCensecTipoNatureza } = useTCensecTipoNaturezaSaveHook();
const { deleteTCensecTipoNatureza } = useTCensecTipoNaturezaDeleteHook();
// Estados
const [selectedData, setSelectedData] = useState<TCensecTipoNaturezaInterface | null>(null);
const [isFormOpen, setIsFormOpen] = useState(false);
// Estado para saber qual item será deletado
const [itemToDelete, setItemToDelete] = useState<TCensecTipoNaturezaInterface | null>(null);
/**
* Hook do modal de confirmação
*/
const {
isOpen: isConfirmOpen,
openDialog: openConfirmDialog,
handleConfirm,
handleCancel,
} = useConfirmDialog();
/**
* Abre o formulário no modo de edição ou criação
*/
const handleOpenForm = useCallback((data: TCensecTipoNaturezaInterface | null) => {
// Se não houver dados (criação), cria um objeto inicial com pessoa_tipo
setSelectedData(data);
setIsFormOpen(true);
}, []);
/**
* Fecha o formulário e limpa o andamento selecionado
*/
const handleCloseForm = useCallback(() => {
setSelectedData(null);
setIsFormOpen(false);
}, []);
/**
* Salva os dados do formulário
*/
const handleSave = useCallback(
async (formData: TCensecTipoNaturezaInterface) => {
// Coloca o botão em estado de loading
setButtonIsLoading(true);
// Aguarda salvar o registro
await saveTCensecTipoNatureza(formData);
// Remove o botão em estado de loading
setButtonIsLoading(false);
// Atualiza a lista de dados
indexTCensecTipoNatureza();
},
[saveTCensecTipoNatureza, indexTCensecTipoNatureza, handleCloseForm],
);
/**
* Quando o usuário clica em "remover" na tabela
*/
const handleConfirmDelete = useCallback(
(item: TCensecTipoNaturezaInterface) => {
// Define o item atual para remoção
setItemToDelete(item);
// Abre o modal de confirmação
openConfirmDialog();
},
[openConfirmDialog],
);
/**
* Executa a exclusão de fato quando o usuário confirma
*/
const handleDelete = useCallback(async () => {
// Protege contra null
if (!itemToDelete) return;
// Executa o Hook de remoção
await deleteTCensecTipoNatureza(itemToDelete);
// Atualiza a lista
await indexTCensecTipoNatureza();
// Limpa o item selecionado
setItemToDelete(null);
// Fecha o modal
handleCancel();
}, [itemToDelete, indexTCensecTipoNatureza, handleCancel]);
/**
* Busca inicial dos dados
*/
useEffect(() => {
indexTCensecTipoNatureza();
}, []);
/**
* Tela de loading enquanto carrega os dados
*/
if (tCensecNaturezaTipo?.length == 0) {
return <Loading type={2} />;
}
return (
<div>
{/* Cabeçalho */}
<Header
title={"CENSEC Tipos de Natureza"}
description={"CENSEC Tipos de Natureza"}
buttonText={'Novo tipo de natureza'}
buttonAction={() => {
handleOpenForm(null);
}}
/>
{/* Tabela de andamentos */}
<TCensecTipoNaturezaTable
data={tCensecNaturezaTipo}
onEdit={handleOpenForm}
onDelete={handleConfirmDelete}
/>
{/* Modal de confirmação */}
{isConfirmOpen && (
<ConfirmDialog
isOpen={isConfirmOpen}
title="Confirmar exclusão"
description="Atenção"
message={`Deseja realmente excluir o Tipo de Natureza "${itemToDelete?.descricao}"?`}
confirmText="Sim, excluir"
cancelText="Cancelar"
onConfirm={handleDelete}
onCancel={handleCancel}
/>
)}
{/* Formulário de criação/edição */}
{isFormOpen && (
<TCensecTipoNaturezaForm
isOpen={isFormOpen}
data={selectedData}
onClose={handleCloseForm}
onSave={handleSave}
buttonIsLoading={buttonIsLoading}
/>
)}
</div>
);
}

View file

@ -0,0 +1,22 @@
'use client';
import { DataTable } from '@/shared/components/dataTable/DataTable';
import TCensecTipoNaturezaColumns from './TCensecTipoNaturezaColumns';
import TCensecTipoNaturezaTableInterface from '../../interfaces/TCensecTipoNatureza/TCensecTipoNaturezaTableInterface';
/**
* Componente principal da tabela
*/
export default function TCensecTipoNaturezaTable({ data, onEdit, onDelete }: TCensecTipoNaturezaTableInterface) {
const columns = TCensecTipoNaturezaColumns(onEdit, onDelete);
return (
<div>
<DataTable
data={data}
columns={columns}
filterColumn="descricao"
filterPlaceholder="Busque pela descrição..."
/>
</div>
);
}

View file

@ -0,0 +1,18 @@
import { withClientErrorHandler } from "@/shared/actions/withClientErrorHandler/withClientErrorHandler";
import ApiResponseInterface from "@/shared/services/api/interfaces/ApiResponseInterface";
import API from "@/shared/services/api/Api";
import { Methods } from "@/shared/services/api/enums/ApiMethodEnum";
import TCensecTipoNaturezaInterface from "../../interfaces/TCensecTipoNatureza/TCensecTipoNaturezaInterface";
async function executeTCensecTipoNaturezaDeleteData(data: TCensecTipoNaturezaInterface): Promise<ApiResponseInterface> {
const api = new API();
return await api.send({
method: Methods.DELETE,
endpoint: `administrativo/t_censec_tiponatureza/${data.censec_tiponatureza_id}`
});
}
export const TCensecTipoNaturezaDeleteData = withClientErrorHandler(executeTCensecTipoNaturezaDeleteData);

View file

@ -0,0 +1,14 @@
import { withClientErrorHandler } from "@/shared/actions/withClientErrorHandler/withClientErrorHandler";
import API from "@/shared/services/api/Api";
import { Methods } from "@/shared/services/api/enums/ApiMethodEnum";
import ApiResponseInterface from "@/shared/services/api/interfaces/ApiResponseInterface";
async function executeTCensecTipoNaturezaIndexData(): Promise<ApiResponseInterface> {
const api = new API();
return api.send({
method: Methods.GET,
endpoint: `administrativo/t_censec_tiponatureza`
});
}
export const TCensecTipoNaturezaIndexData = withClientErrorHandler(executeTCensecTipoNaturezaIndexData);

View file

@ -0,0 +1,26 @@
import { withClientErrorHandler } from "@/shared/actions/withClientErrorHandler/withClientErrorHandler";
import TCensecTipoNaturezaInterface from "../../interfaces/TCensecTipoNatureza/TCensecTipoNaturezaInterface";
import ApiResponseInterface from "@/shared/services/api/interfaces/ApiResponseInterface";
import API from "@/shared/services/api/Api";
import { Methods } from "@/shared/services/api/enums/ApiMethodEnum";
async function executeTCensecTipoNaturezaSaveData(data: TCensecTipoNaturezaInterface): Promise<ApiResponseInterface> {
console.log('executeTCensecTipoNaturezaSaveData', data)
// Verifica se existe ID da cidade para decidir se é atualização (PUT) ou criação (POST)
const isUpdate = Boolean(data.censec_tiponatureza_id);
// Instancia o cliente da API para enviar a requisição
const api = new API();
// Executa a requisição para a API com o método apropriado e envia os dados no corpo
return await api.send({
method: isUpdate ? Methods.PUT : Methods.POST, // PUT se atualizar, POST se criar
endpoint: `administrativo/t_censec_tiponatureza/${data.censec_tiponatureza_id || ''}`, // endpoint dinâmico
body: data, // payload enviado para a API
});
}
export const TCensecTipoNaturezaSaveData = withClientErrorHandler(executeTCensecTipoNaturezaSaveData);

View file

@ -0,0 +1,20 @@
import { useResponse } from '@/shared/components/response/ResponseContext';
import { useState } from 'react';
import TCensecTipoNaturezaInterface from '../../interfaces/TCensecTipoNatureza/TCensecTipoNaturezaInterface';
import { TCensecTipoNaturezaDeleteService } from '../../services/TCensecTipoNatureza/TCensecTipoNaturezaDeleteService';
export const useTCensecTipoNaturezaDeleteHook = () => {
const { setResponse } = useResponse();
const [tAtoParteTipo, setTCensecTipoNatureza] = useState<TCensecTipoNaturezaInterface>();
const deleteTCensecTipoNatureza = async (data: TCensecTipoNaturezaInterface) => {
const response = await TCensecTipoNaturezaDeleteService(data);
setTCensecTipoNatureza(data);
setResponse(response);
};
return { tAtoParteTipo, deleteTCensecTipoNatureza };
};

View file

@ -0,0 +1,13 @@
import { useForm } from "react-hook-form";
import { TCensecTipoNaturezaFormValues, TCensecTipoNaturezaSchema } from "../../schemas/TCensecTipoNatureza/TCensecTipoNaturezaSchema";
import { zodResolver } from "@hookform/resolvers/zod";
export function useTCensecTipoNaturezaFormHook(defaults?: Partial<TCensecTipoNaturezaFormValues>) {
return useForm<TCensecTipoNaturezaFormValues>({
resolver: zodResolver(TCensecTipoNaturezaSchema),
defaultValues: {
censec_tiponatureza_id: 0,
...defaults,
},
});
}

View file

@ -0,0 +1,25 @@
'use client';
import { useResponse } from '@/shared/components/response/ResponseContext';
import { useState } from 'react';
import TCensecTipoNaturezaInterface from '../../interfaces/TCensecTipoNatureza/TCensecTipoNaturezaInterface';
import { TCensecTipoNaturezaIndexService } from '../../services/TCensecTipoNatureza/TCensecTipoNaturezaIndexService';
export const useTCensecTipoNaturezaIndexHook = () => {
const { setResponse } = useResponse();
const [tCensecNaturezaTipo, setTCensecTipoNatureza] = useState<TCensecTipoNaturezaInterface[]>([]);
const indexTCensecTipoNatureza = async () => {
const response = await TCensecTipoNaturezaIndexService();
// Armazena os dados consultados
setTCensecTipoNatureza(response.data);
// Define os dados do componente de resposta (toast, modal, etc)
setResponse(response);
};
return {
tCensecNaturezaTipo,
indexTCensecTipoNatureza
};
};

View file

@ -0,0 +1,33 @@
'use client';
import { useResponse } from '@/shared/components/response/ResponseContext';
import { useState } from 'react';
import TCensecTipoNaturezaInterface from '../../interfaces/TCensecTipoNatureza/TCensecTipoNaturezaInterface';
import { TCensecTipoNaturezaSaveService } from '../../services/TCensecTipoNatureza/TCensecTipoNaturezaSaveService';
export const useTCensecTipoNaturezaSaveHook = () => {
const { setResponse } = useResponse();
const [tAtoParteTipo, setTCensecTipoNatureza] = useState<TCensecTipoNaturezaInterface>();
// controla se o formulário está aberto ou fechado
const [isOpen, setIsOpen] = useState(false);
const saveTCensecTipoNatureza = async (data: TCensecTipoNaturezaInterface) => {
const response = await TCensecTipoNaturezaSaveService(data);
// Armazena os dados da repsota
setTCensecTipoNatureza(response.data);
// Define os dados da respota(toast, modal, etc)
setResponse(response);
// Fecha o formulário automaticamente após salvar
setIsOpen(false);
// Retorna os valores de forma imediata
return response.data;
};
return { tAtoParteTipo, saveTCensecTipoNatureza };
};

View file

@ -0,0 +1,9 @@
import { TCensecTipoNaturezaFormValues } from "../../schemas/TCensecTipoNatureza/TCensecTipoNaturezaSchema";
export interface TCensecTipoNaturezaFormInterface {
isOpen: boolean;
data: TCensecTipoNaturezaFormValues | null;
onClose: (item: null, isFormStatus: boolean) => void;
onSave: (data: TCensecTipoNaturezaFormValues) => void;
buttonIsLoading: boolean;
}

View file

@ -0,0 +1,10 @@
export default interface TCensecTipoNaturezaInterface {
censec_tiponatureza_id?: number;
censec_tipoato_id?: number;
descricao?: string;
possui_ato_anterior?: string;
codigo?: number;
obrigatorio?: string;
tipo_ato_anterior?: string;
situacao_ato_anterior?: string;
}

View file

@ -0,0 +1,7 @@
import TCensecTipoNaturezaInterface from "./TCensecTipoNaturezaInterface";
export default interface TCensecTipoNaturezaTableInterface {
data?: TCensecTipoNaturezaInterface[];
onEdit: (item: TCensecTipoNaturezaInterface, isEditingFormStatus: boolean) => void;
onDelete: (item: TCensecTipoNaturezaInterface, isEditingFormStatus: boolean) => void;
}

View file

@ -0,0 +1,14 @@
import z from "zod";
export const TCensecTipoNaturezaSchema = z.object({
censec_tiponatureza_id: z.number().optional(),
censec_tipoato_id: z.number().optional(),
descricao: z.string().optional(),
possui_ato_anterior: z.string().optional(),
codigo: z.number().optional(),
obrigatorio: z.string().optional(),
tipo_ato_anterior: z.string().optional(),
situacao_ato_anterior: z.string().optional(),
});
export type TCensecTipoNaturezaFormValues = z.infer<typeof TCensecTipoNaturezaSchema>;

View file

@ -0,0 +1,11 @@
import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler';
import TCensecTipoNaturezaInterface from '../../interfaces/TCensecTipoNatureza/TCensecTipoNaturezaInterface';
import { TCensecTipoNaturezaDeleteData } from '../../data/TCensecTipoNatureza/TCensecTipoNaturezaDeleteData';
async function executeTCensecTipoNaturezaDeleteService(data: TCensecTipoNaturezaInterface) {
const response = await TCensecTipoNaturezaDeleteData(data);
return response;
}
export const TCensecTipoNaturezaDeleteService = withClientErrorHandler(executeTCensecTipoNaturezaDeleteService);

View file

@ -0,0 +1,9 @@
import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler';
import { TCensecTipoNaturezaIndexData } from '../../data/TCensecTipoNatureza/TCensecTipoNaturezaIndexData';
export default async function executeTCensecTipoNaturezaIndexService() {
const response = await TCensecTipoNaturezaIndexData();
return response;
}
export const TCensecTipoNaturezaIndexService = withClientErrorHandler(executeTCensecTipoNaturezaIndexService);

View file

@ -0,0 +1,11 @@
import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler';
import TCensecTipoNaturezaInterface from '../../interfaces/TCensecTipoNatureza/TCensecTipoNaturezaInterface';
import { TCensecTipoNaturezaSaveData } from '../../data/TCensecTipoNatureza/TCensecTipoNaturezaSaveData';
async function executeTCensecTipoNaturezaSaveService(data: TCensecTipoNaturezaInterface) {
const response = await TCensecTipoNaturezaSaveData(data);
return response;
}
export const TCensecTipoNaturezaSaveService = withClientErrorHandler(executeTCensecTipoNaturezaSaveService);

View file

@ -0,0 +1,61 @@
import { Button } from "@/components/ui/button";
import { CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList } from "@/components/ui/command";
import { FormControl } from "@/components/ui/form";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { cn } from "@/lib/utils";
import { TipoAtoAnteriorEnum } from "@/shared/enums/TipoAtoAnteriorEnum";
import { Command } from "cmdk";
import { CheckIcon, ChevronsUpDownIcon } from "lucide-react";
import React from "react";
export default function TipoAtoAnteriorSelect({ field }: any) {
const [open, setOpen] = React.useState(false);
const options = Object.entries(TipoAtoAnteriorEnum).map(([value, label]) => ({
value,
label,
}));
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<FormControl className="w-full">
<Button variant="outline" role="combobox" aria-expanded={open} className="justify-between">
{field.value
? options.find((item) => item.value === field.value)?.label
: "Selecione..."}
<ChevronsUpDownIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-full p-0">
<Command>
<CommandInput placeholder="Buscar situação..." />
<CommandList>
<CommandEmpty>Nenhum resultado encontrado.</CommandEmpty>
<CommandGroup>
{options.map((item) => (
<CommandItem
key={item.value}
value={item.label.toLowerCase()}
onSelect={() => {
field.onChange(item.value);
setOpen(false);
}}
>
<CheckIcon
className={cn(
"mr-2 h-4 w-4",
field.value === item.value ? "opacity-100" : "opacity-0"
)}
/>
{item.label}
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
}

View file

@ -0,0 +1,107 @@
import { Button } from "@/components/ui/button";
import {
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from "@/components/ui/command";
import { FormControl } from "@/components/ui/form";
import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover";
import { cn } from "@/lib/utils";
import { TipoNaturezaEnum } from "@/shared/enums/TipoNaturezaEnum";
import { Command } from "cmdk";
import { CheckIcon, ChevronsUpDownIcon } from "lucide-react";
import React from "react";
export default function TipoNaturezaSelect({ field }: any) {
const [open, setOpen] = React.useState(false);
// Gera opções a partir do Enum
const options = Object.entries(TipoNaturezaEnum).map(([id, label]) => ({
value: Number(id),
label,
}));
// 🔹 Converte valor recebido (string tipo ";0,1,2,3") para array de números
const parseToArray = (value: any): number[] => {
if (!value) return [];
if (Array.isArray(value)) return value.map(Number);
if (typeof value === "string") {
return value
.replace(/^;/, "") // remove o primeiro ";"
.split(",")
.filter(Boolean)
.map((v) => Number(v.trim()));
}
return [];
};
// Array de valores selecionados
const selectedValues: number[] = parseToArray(field.value);
// Atualiza valor selecionado (e reenvia como string ";0,1,2,3")
const toggleSelect = (value: number) => {
let newValues;
if (selectedValues.includes(value)) {
newValues = selectedValues.filter((v) => v !== value);
} else {
newValues = [...selectedValues, value];
}
// Converte para formato ";0,1,2,3"
const formatted = ";" + newValues.join(",");
field.onChange(formatted);
};
// Label do botão (nomes selecionados)
const selectedLabels = options
.filter((opt) => selectedValues.includes(opt.value))
.map((opt) => opt.label)
.join(", ");
return (
<Popover open={open} onOpenChange={setOpen}>
<PopoverTrigger asChild>
<FormControl className="w-full">
<Button
variant="outline"
role="combobox"
aria-expanded={open}
className="justify-between"
>
{selectedLabels || "Selecione..."}
<ChevronsUpDownIcon className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-full p-0">
<Command>
<CommandInput placeholder="Buscar tipo natureza..." />
<CommandList>
<CommandEmpty>Nenhum resultado encontrado.</CommandEmpty>
<CommandGroup>
{options.map((item) => (
<CommandItem
key={item.value}
value={item.label.toLowerCase()}
onSelect={() => toggleSelect(item.value)}
>
<CheckIcon
className={cn(
"mr-2 h-4 w-4",
selectedValues.includes(item.value)
? "opacity-100"
: "opacity-0"
)}
/>
{item.label}
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
}

View file

@ -0,0 +1,8 @@
export const TipoAtoAnteriorEnum = {
1: 'Substabelecido',
2: 'Revogado',
3: 'Reratificado',
4: 'Renunciado',
5: 'Renúncia',
6: 'Renúncia Parcial'
} as const;

View file

@ -0,0 +1,6 @@
export const TipoNaturezaEnum = {
1: 'Escritura',
2: 'Procuração',
3: 'Substabelecimento',
4: 'Testamento',
} as const;