[MVPTN-119] feat(CRUD): Cria cruds para manipular GEmolumento e GSeloGrupo

This commit is contained in:
Keven Willian Pereira de Souza 2025-10-24 10:52:34 -03:00
parent 2340cee82a
commit 9b9f8b9454
48 changed files with 2089 additions and 2 deletions

View file

@ -0,0 +1,9 @@
import GEmolumentoIndex from "@/packages/administrativo/components/GEmolumento/GEmolumentoIndex";
export default function GEmolumentoPeriodoPage() {
return (
<GEmolumentoIndex />
);
}

View file

@ -0,0 +1,9 @@
import GSeloGrupoIndex from "@/packages/administrativo/components/GSeloGrupo/GSeloGrupoIndex";
export default function GSeloGrupoPage() {
return (
<GSeloGrupoIndex />
);
}

View file

@ -164,6 +164,14 @@ const data = {
{
title: "Financeiro/Periodo",
url: "/administrativo/financeiro/periodos",
},
{
title: "Financeiro/Emolumentos",
url: "/administrativo/financeiro/emolumentos",
},
{
title: "Selos/Grupos",
url: "/administrativo/selos/grupos",
}
],

View file

@ -0,0 +1,78 @@
import { ColumnDef } from '@tanstack/react-table';
import { EllipsisIcon, PencilIcon, Trash2Icon } from 'lucide-react';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import GetCapitalize from '@/shared/actions/text/GetCapitalize';
import { SortableHeader } from '@/shared/components/dataTable/SortableHeader';
import GEmolumentoInterface from '../../interfaces/GEmolumento/GEmolumentoInterface';
export default function GEmolumentoColumns(
onEdit: (item: GEmolumentoInterface, isEditingFormStatus: boolean) => void,
onDelete: (item: GEmolumentoInterface, isEditingFormStatus: boolean) => void,
): ColumnDef<GEmolumentoInterface>[] {
return [
// ID
{
accessorKey: 'emolumento_id',
header: ({ column }) => SortableHeader('ID', column),
cell: ({ row }) => Number(row.getValue('emolumento_id')),
enableSorting: true,
},
// Descrição
{
accessorKey: 'descricao',
header: ({ column }) => SortableHeader('Descrição', column),
cell: ({ row }) => GetCapitalize(String(row.getValue('descricao') || '')),
},
// Ações
{
id: 'actions',
header: 'Ações',
cell: ({ row }) => {
const natureza = row.original;
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon">
<EllipsisIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent side="left" align="start">
<DropdownMenuGroup>
<DropdownMenuItem onSelect={() => onEdit(natureza, true)}>
<PencilIcon className="mr-2 h-4 w-4" />
Editar
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
className="text-red-600"
onSelect={() => onDelete(natureza, true)}
>
<Trash2Icon className="mr-2 h-4 w-4" />
Remover
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
);
},
enableSorting: false,
enableHiding: false,
},
];
}

View file

@ -0,0 +1,367 @@
'use client';
import { HouseIcon, IdCardIcon } from 'lucide-react';
import { 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 { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { parseNumberInput } from '@/shared/actions/form/parseNumberInput';
import { ResetFormIfData } from '@/shared/actions/form/ResetFormIfData';
import LoadingButton from '@/shared/components/loadingButton/LoadingButton';
import SistemasSelect from '@/shared/components/sistemas/SistemasSelect';
import SituacoesSelect from '@/shared/components/situacoes/SituacoesSelect';
import TipoEmolumentoSelect from '@/shared/components/tipoEmolumento/TipoAtoAnteriorSelect';
import { useGEmolumentoFormHook } from '../../hooks/GEmolumento/useGEmolumentoFormHook';
import { GEmolumentoFormInterface } from '../../interfaces/GEmolumento/GEmolumentoFormInterface';
import GSeloGrupoSelect from '../GSeloGrupo/GSeloGrupoSelect';
/**
* Formulário de cadastro/edição de Natureza
* Baseado nos campos da tabela G_NATUREZA
*/
export default function GEmolumentoForm({
isOpen,
data,
onClose,
onSave,
buttonIsLoading,
}: GEmolumentoFormInterface) {
const form = useGEmolumentoFormHook({});
// Atualiza o formulário quando recebe dados para edição
useEffect(() => {
ResetFormIfData(form, data);
}, [data, form]);
function onError(error: any) {
console.log('Erro no formulário:', 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 className="text-lg sm:text-xl">Emolumento</DialogTitle>
<DialogDescription className="text-muted-foreground text-sm">
Formulário de Emolumento
</DialogDescription>
</DialogHeader>
{/* Formulário principal */}
<Form {...form}>
<form onSubmit={form.handleSubmit(onSave, onError)} className="space-y-6">
<Tabs defaultValue="geral" className="space-y-4">
<TabsList className="flex w-full">
<TabsTrigger className="flex-1 cursor-pointer text-center" value="geral">
<HouseIcon className="me-1 inline" />
Geral
</TabsTrigger>
<TabsTrigger className="flex-1 cursor-pointer text-center" value="registroImoveis">
<IdCardIcon className="inline" />
Registro de Imóveis
</TabsTrigger>
</TabsList>
<TabsContent value="geral" className="space-y-4">
{/* GRID MOBILE FIRST */}
<div className="grid w-full grid-cols-12 gap-4">
{/* Descrição */}
<div className="col-span-12 sm:col-span-6 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 */}
<div className="col-span-12 sm:col-span-4 md:col-span-6">
<FormField
control={form.control}
name="tipo"
render={({ field }) => (
<FormItem>
<FormLabel>Tipo</FormLabel>
<TipoEmolumentoSelect field={field} />
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Sistema ID */}
<div className="col-span-12 sm:col-span-4 md:col-span-6">
<FormField
control={form.control}
name="sistema_id"
render={({ field }) => (
<FormItem>
<FormLabel>Serventia</FormLabel>
<SistemasSelect
field={field}
onChange={(e) => field.onChange(parseNumberInput(e))}
/>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Selo Grupo ID */}
<div className="col-span-12 sm:col-span-4 md:col-span-6">
<FormField
control={form.control}
name="selo_grupo_id"
render={({ field }) => (
<FormItem className='w-full'>
<FormLabel>Selo Grupo</FormLabel>
<GSeloGrupoSelect field={field} />
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Registro/Averbação */}
<div className="col-span-12 sm:col-span-4 md:col-span-6">
<FormField
control={form.control}
name="reg_averb"
render={({ field }) => (
<FormItem className='w-full'>
<FormLabel>Registro/Averbação</FormLabel>
<Input {...field} type="text" maxLength={1} />
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Situação */}
<div className="col-span-12 sm:col-span-6 md:col-span-6">
<FormField
control={form.control}
name="situacao"
render={({ field }) => (
<FormItem>
<FormLabel>Situação</FormLabel>
<SituacoesSelect field={field} />
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Tipo Objetivo */}
<div className="col-span-12 sm:col-span-6 md:col-span-6">
<FormField
control={form.control}
name="tipo_objetivo"
render={({ field }) => (
<FormItem>
<FormLabel>Tipo Objetivo</FormLabel>
<Input {...field} type="text" maxLength={3} />
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Modelo Tag */}
<div className="col-span-12 sm:col-span-6 md:col-span-6">
<FormField
control={form.control}
name="modelo_tag"
render={({ field }) => (
<FormItem>
<FormLabel>Modelo Tag</FormLabel>
<Input {...field} type="text" maxLength={3} />
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Código Nota ID */}
<div className="col-span-12 sm:col-span-6 md:col-span-6">
<FormField
control={form.control}
name="codigo_nota_id"
render={({ field }) => (
<FormItem>
<FormLabel>Código Nota</FormLabel>
<FormControl>
<Input {...field}
type="number"
step="0.01"
onChange={(e) => field.onChange(parseNumberInput(e))}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
</div>
</TabsContent>
<TabsContent value="registroImoveis" className="space-y-4">
{/* Pré-Definido */}
<div className="col-span-12 sm:col-span-4 md:col-span-6">
<FormField
control={form.control}
name="pre_definido"
render={({ field }) => (
<FormItem>
<FormLabel>Pré-Definido</FormLabel>
<Input {...field} type="text" maxLength={1} />
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Motivo Redução */}
<div className="col-span-12 sm:col-span-8 md:col-span-6">
<FormField
control={form.control}
name="motivo_reducao"
render={({ field }) => (
<FormItem>
<FormLabel>Motivo da Redução</FormLabel>
<FormControl>
<Input {...field} type="text" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Valor Máximo Certidão */}
<div className="col-span-12 sm:col-span-6 md:col-span-6">
<FormField
control={form.control}
name="valor_maximo_certidao"
render={({ field }) => (
<FormItem>
<FormLabel>Valor Máximo Certidão</FormLabel>
<FormControl>
<Input {...field}
type="number"
step="0.001"
onChange={(e) => field.onChange(parseNumberInput(e))}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Com Redução */}
<div className="col-span-12 sm:col-span-4 md:col-span-6">
<FormField
control={form.control}
name="com_reducao"
render={({ field }) => (
<FormItem>
<FormLabel>Com Redução</FormLabel>
<Input {...field} type="text" maxLength={1} />
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="grid w-full grid-cols-12 gap-4">
{/* Item DF */}
<div className="col-span-12 sm:col-span-6 md:col-span-6">
<FormField
control={form.control}
name="item_df"
render={({ field }) => (
<FormItem>
<FormLabel>Item DF</FormLabel>
<Input {...field} type="text" maxLength={10} />
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Convênio CODHAB */}
<div className="col-span-12 sm:col-span-6 md:col-span-6">
<FormField
control={form.control}
name="convenio_codhab"
render={({ field }) => (
<FormItem>
<FormLabel>Convênio CODHAB</FormLabel>
<Input {...field} type="text" maxLength={1} />
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Situação RI */}
<div className="col-span-12 sm:col-span-6 md:col-span-6">
<FormField
control={form.control}
name="situacao_ri"
render={({ field }) => (
<FormItem>
<FormLabel>Situação RI</FormLabel>
<SituacoesSelect field={field} />
<FormMessage />
</FormItem>
)}
/>
</div>
</div>
</TabsContent>
</Tabs>
{/* Rodapé */}
<DialogFooter className="mt-6 flex flex-col justify-end gap-2 sm:flex-row">
<DialogClose asChild>
<Button variant="outline" type="button">
Cancelar
</Button>
</DialogClose>
<LoadingButton
text="Salvar"
textLoading="Salvando..."
type="submit"
loading={buttonIsLoading}
/>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
);
}

View file

@ -0,0 +1,161 @@
'use client';
import { useEffect, useState, useCallback } from 'react';
import { useGEmolumentoDeleteHook } from '@/packages/administrativo/hooks/GEmolumento/useGEmolumentoDeleteHook';
import { useGEmolumentoIndexHook } from '@/packages/administrativo/hooks/GEmolumento/useGEmolumentoIndexHook';
import { useGEmolumentoSaveHook } from '@/packages/administrativo/hooks/GEmolumento/useGEmolumentoSaveHook';
import GEmolumentoInterface from '@/packages/administrativo/interfaces/GEmolumento/GEmolumentoInterface';
import ConfirmDialog from '@/shared/components/confirmDialog/ConfirmDialog';
import { useConfirmDialog } from '@/shared/components/confirmDialog/useConfirmDialog';
import Loading from '@/shared/components/loading/loading';
import Header from '@/shared/components/structure/Header';
import GEmolumentoForm from './GEmolumentoForm';
import GEmolumentoTable from './GEmolumentoTable';
export default function GEmolumentoIndex() {
// Controle de estado do botão
const [buttonIsLoading, setButtonIsLoading] = useState(false);
// Hooks para leitura e salvamento
const { gGramatica, indexGEmolumento } = useGEmolumentoIndexHook();
const { saveGEmolumento } = useGEmolumentoSaveHook();
const { deleteGEmolumento } = useGEmolumentoDeleteHook();
// Estados
const [selectedData, setSelectedData] = useState<GEmolumentoInterface | null>(null);
const [isFormOpen, setIsFormOpen] = useState(false);
// Estado para saber qual item será deletado
const [itemToDelete, setItemToDelete] = useState<GEmolumentoInterface | null>(null);
/**
* Hook do modal de confirmação
*/
const { isOpen: isConfirmOpen, openDialog: openConfirmDialog, handleCancel } = useConfirmDialog();
/**
* Abre o formulário no modo de edição ou criação
*/
const handleOpenForm = useCallback((data: GEmolumentoInterface | null) => {
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: GEmolumentoInterface) => {
// Coloca o botão em estado de loading
setButtonIsLoading(true);
// Aguarda salvar o registro
await saveGEmolumento(formData);
// Remove o botão em estado de loading
setButtonIsLoading(false);
// Atualiza a lista de dados
indexGEmolumento();
},
[saveGEmolumento, indexGEmolumento, handleCloseForm],
);
/**
* Quando o usuário clica em "remover" na tabela
*/
const handleConfirmDelete = useCallback(
(item: GEmolumentoInterface) => {
// 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 deleteGEmolumento(itemToDelete);
// Atualiza a lista
await indexGEmolumento();
// Limpa o item selecionado
setItemToDelete(null);
// Fecha o modal
handleCancel();
}, [itemToDelete, indexGEmolumento, handleCancel]);
/**
* Busca inicial dos dados
*/
useEffect(() => {
indexGEmolumento();
}, []);
/**
* Tela de loading enquanto carrega os dados
*/
if (gGramatica?.length == 0) {
return <Loading type={2} />;
}
return (
<div>
{/* Cabeçalho */}
<Header
title={'Emolumentos'}
description={'Emolumentos'}
buttonText={'Novo emolumento'}
buttonAction={() => {
handleOpenForm(null);
}}
/>
{/* Tabela de andamentos */}
<GEmolumentoTable data={gGramatica} onEdit={handleOpenForm} onDelete={handleConfirmDelete} />
{/* Modal de confirmação */}
{isConfirmOpen && (
<ConfirmDialog
isOpen={isConfirmOpen}
title="Confirmar exclusão"
description="Atenção"
message={`Deseja realmente excluir o valor "${itemToDelete?.descricao}"?`}
confirmText="Sim, excluir"
cancelText="Cancelar"
onConfirm={handleDelete}
onCancel={handleCancel}
/>
)}
{/* Formulário de criação/edição */}
{isFormOpen && (
<GEmolumentoForm
isOpen={isFormOpen}
data={selectedData}
onClose={handleCloseForm}
onSave={handleSave}
buttonIsLoading={buttonIsLoading}
/>
)}
</div>
);
}

View file

@ -0,0 +1,23 @@
'use client';
import { DataTable } from '@/shared/components/dataTable/DataTable';
import GEmolumentoColumns from './GEmolumentoColumns';
import GEmolumentoTableInterface from '../../interfaces/GEmolumento/GEmolumentoTableInterface';
/**
* Componente principal da tabela de Naturezas
*/
export default function GEmolumentoTable({ data, onEdit, onDelete }: GEmolumentoTableInterface) {
const columns = GEmolumentoColumns(onEdit, onDelete);
return (
<div>
<DataTable
data={data}
columns={columns}
filterColumn="descricao"
filterPlaceholder="Buscar por descrição da natureza..."
/>
</div>
);
}

View file

@ -0,0 +1,78 @@
import { ColumnDef } from '@tanstack/react-table';
import { EllipsisIcon, PencilIcon, Trash2Icon } from 'lucide-react';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import GetCapitalize from '@/shared/actions/text/GetCapitalize';
import { SortableHeader } from '@/shared/components/dataTable/SortableHeader';
import GSeloGrupoInterface from '../../interfaces/GSeloGrupo/GSeloGrupoInterface';
export default function GSeloGrupoColumns(
onEdit: (item: GSeloGrupoInterface, isEditingFormStatus: boolean) => void,
onDelete: (item: GSeloGrupoInterface, isEditingFormStatus: boolean) => void,
): ColumnDef<GSeloGrupoInterface>[] {
return [
// ID
{
accessorKey: 'selo_grupo_id',
header: ({ column }) => SortableHeader('ID', column),
cell: ({ row }) => Number(row.getValue('selo_grupo_id')),
enableSorting: true,
},
// Descrição
{
accessorKey: 'descricao',
header: ({ column }) => SortableHeader('descricao', column),
cell: ({ row }) => GetCapitalize(String(row.getValue('descricao') || '')),
},
// Ações
{
id: 'actions',
header: 'Ações',
cell: ({ row }) => {
const natureza = row.original;
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon">
<EllipsisIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent side="left" align="start">
<DropdownMenuGroup>
<DropdownMenuItem onSelect={() => onEdit(natureza, true)}>
<PencilIcon className="mr-2 h-4 w-4" />
Editar
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
className="text-red-600"
onSelect={() => onDelete(natureza, true)}
>
<Trash2Icon className="mr-2 h-4 w-4" />
Remover
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
);
},
enableSorting: false,
enableHiding: false,
},
];
}

View file

@ -0,0 +1,301 @@
'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 { ResetFormIfData } from '@/shared/actions/form/ResetFormIfData';
import LoadingButton from '@/shared/components/loadingButton/LoadingButton';
import { useGSeloGrupoFormHook } from '../../hooks/GSeloGrupo/useGSeloGrupoFormHook';
import { GSeloGrupoFormInterface } from '../../interfaces/GSeloGrupo/GSeloGrupoFormInterface';
import SituacoesSelect from '@/shared/components/situacoes/SituacoesSelect';
import ConfirmacaoSelect from '@/shared/components/confirmacao/ConfirmacaoSelect';
import { parseNumberInput } from '@/shared/actions/form/parseNumberInput';
import TipoCartorioSelect from '@/shared/components/tipoCartorio/TipoCartorioSelect';
/**
* Formulário de cadastro/edição de Natureza
* Baseado nos campos da tabela G_NATUREZA
*/
export default function GSeloGrupoForm({
isOpen,
data,
onClose,
onSave,
buttonIsLoading,
}: GSeloGrupoFormInterface) {
const form = useGSeloGrupoFormHook({});
// Atualiza o formulário quando recebe dados para edição
useEffect(() => {
ResetFormIfData(form, data);
}, [data, form]);
function onError(error: any) {
console.log('Erro no formulário:', 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 className="text-lg sm:text-xl">Formulário de Gramática</DialogTitle>
<DialogDescription className="text-muted-foreground text-sm">
Formulário de Gramática
</DialogDescription>
</DialogHeader>
{/* Formulário principal */}
<Form {...form}>
<form onSubmit={form.handleSubmit(onSave, onError)} className="space-y-6">
{/* GRID MOBILE FIRST */}
<div className="grid w-full grid-cols-12 gap-4">
{/* Código */}
<div className="col-span-12 sm:col-span-6 md:col-span-6">
<FormField
control={form.control}
name="numero"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Código</FormLabel>
<FormControl>
<Input
{...field}
type="number"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Agrupador */}
<div className="col-span-12 sm:col-span-6 md:col-span-6">
<FormField
control={form.control}
name="agrupador"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Agrupador</FormLabel>
<ConfirmacaoSelect field={field} />
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Descrição */}
<div className="col-span-12 sm:col-span-6 md:col-span-12">
<FormField
control={form.control}
name="descricao"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Descrição</FormLabel>
<FormControl>
<Input
{...field}
type="text"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Descrição Completa */}
<div className="col-span-12 sm:col-span-6 md:col-span-12">
<FormField
control={form.control}
name="descricao_completa"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Descrição Completa</FormLabel>
<FormControl>
<Input
{...field}
type="text"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Controle Automático */}
<div className="col-span-12 sm:col-span-6 md:col-span-6">
<FormField
control={form.control}
name="controle_automatico"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Controle Automático</FormLabel>
<ConfirmacaoSelect field={field} />
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Código Conta */}
<div className="col-span-12 sm:col-span-6 md:col-span-6">
<FormField
control={form.control}
name="codigo_conta"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Código Conta</FormLabel>
<FormControl>
<Input
{...field}
type="number"
onChange={(e) => field.onChange(parseNumberInput(e))}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Selo Principal Inicial */}
<div className="col-span-12 sm:col-span-6 md:col-span-6">
<FormField
control={form.control}
name="numero_principal_ini"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Selo Principal Inicial</FormLabel>
<FormControl>
<Input
{...field}
type="number"
onChange={(e) => field.onChange(parseNumberInput(e))}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Selo Principal Final */}
<div className="col-span-12 sm:col-span-6 md:col-span-6">
<FormField
control={form.control}
name="numero_principal_fim"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Selo Principal Final</FormLabel>
<FormControl>
<Input
{...field}
type="number"
onChange={(e) => field.onChange(parseNumberInput(e))}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Grupos Principal */}
<div className="col-span-12 sm:col-span-6 md:col-span-12">
<FormField
control={form.control}
name="grupos_principal"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Grupos Principal</FormLabel>
<FormControl>
<Input
{...field}
type="text"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Um por protocolo */}
<div className="col-span-12 sm:col-span-6 md:col-span-6">
<FormField
control={form.control}
name="um_por_protocolo"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Um por Protocolo</FormLabel>
<ConfirmacaoSelect field={field} />
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Tipo Cartório */}
<div className="col-span-12 sm:col-span-6 md:col-span-6">
<FormField
control={form.control}
name="tipo_cartorio"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Tipo Cartório</FormLabel>
<TipoCartorioSelect field={field} />
<FormMessage />
</FormItem>
)}
/>
</div>
{/* Situação */}
<div className="col-span-12 sm:col-span-6 md:col-span-12">
<FormField
control={form.control}
name="situacao"
render={({ field }) => (
<FormItem className="col-span-1 sm:col-span-2">
<FormLabel>Situação</FormLabel>
<SituacoesSelect field={field} />
<FormMessage />
</FormItem>
)}
/>
</div>
</div>
{/* Rodapé */}
<DialogFooter className="mt-6 flex flex-col justify-end gap-2 sm:flex-row">
<DialogClose asChild>
<Button variant="outline" type="button">
Cancelar
</Button>
</DialogClose>
<LoadingButton
text="Salvar"
textLoading="Salvando..."
type="submit"
loading={buttonIsLoading}
/>
</DialogFooter>
</form>
</Form>
</DialogContent>
</Dialog>
);
}

View file

@ -0,0 +1,161 @@
'use client';
import { useEffect, useState, useCallback } from 'react';
import { useGSeloGrupoDeleteHook } from '@/packages/administrativo/hooks/GSeloGrupo/useGSeloGrupoDeleteHook';
import { useGSeloGrupoIndexHook } from '@/packages/administrativo/hooks/GSeloGrupo/useGSeloGrupoIndexHook';
import { useGSeloGrupoSaveHook } from '@/packages/administrativo/hooks/GSeloGrupo/useGSeloGrupoSaveHook';
import GSeloGrupoInterface from '@/packages/administrativo/interfaces/GSeloGrupo/GSeloGrupoInterface';
import ConfirmDialog from '@/shared/components/confirmDialog/ConfirmDialog';
import { useConfirmDialog } from '@/shared/components/confirmDialog/useConfirmDialog';
import Loading from '@/shared/components/loading/loading';
import Header from '@/shared/components/structure/Header';
import GSeloGrupoForm from './GSeloGrupoForm';
import GSeloGrupoTable from './GSeloGrupoTable';
export default function GSeloGrupoIndex() {
// Controle de estado do botão
const [buttonIsLoading, setButtonIsLoading] = useState(false);
// Hooks para leitura e salvamento
const { gSeloGrupo, indexGSeloGrupo } = useGSeloGrupoIndexHook();
const { saveGSeloGrupo } = useGSeloGrupoSaveHook();
const { deleteGSeloGrupo } = useGSeloGrupoDeleteHook();
// Estados
const [selectedData, setSelectedData] = useState<GSeloGrupoInterface | null>(null);
const [isFormOpen, setIsFormOpen] = useState(false);
// Estado para saber qual item será deletado
const [itemToDelete, setItemToDelete] = useState<GSeloGrupoInterface | null>(null);
/**
* Hook do modal de confirmação
*/
const { isOpen: isConfirmOpen, openDialog: openConfirmDialog, handleCancel } = useConfirmDialog();
/**
* Abre o formulário no modo de edição ou criação
*/
const handleOpenForm = useCallback((data: GSeloGrupoInterface | null) => {
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: GSeloGrupoInterface) => {
// Coloca o botão em estado de loading
setButtonIsLoading(true);
// Aguarda salvar o registro
await saveGSeloGrupo(formData);
// Remove o botão em estado de loading
setButtonIsLoading(false);
// Atualiza a lista de dados
indexGSeloGrupo();
},
[saveGSeloGrupo, indexGSeloGrupo, handleCloseForm],
);
/**
* Quando o usuário clica em "remover" na tabela
*/
const handleConfirmDelete = useCallback(
(item: GSeloGrupoInterface) => {
// 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 deleteGSeloGrupo(itemToDelete);
// Atualiza a lista
await indexGSeloGrupo();
// Limpa o item selecionado
setItemToDelete(null);
// Fecha o modal
handleCancel();
}, [itemToDelete, indexGSeloGrupo, handleCancel]);
/**
* Busca inicial dos dados
*/
useEffect(() => {
indexGSeloGrupo();
}, []);
/**
* Tela de loading enquanto carrega os dados
*/
if (gSeloGrupo?.length == 0) {
return <Loading type={2} />;
}
return (
<div>
{/* Cabeçalho */}
<Header
title={'Grupos de Selos'}
description={'Grupos de Selos'}
buttonText={'Novo grupo'}
buttonAction={() => {
handleOpenForm(null);
}}
/>
{/* Tabela de andamentos */}
<GSeloGrupoTable data={gSeloGrupo} onEdit={handleOpenForm} onDelete={handleConfirmDelete} />
{/* Modal de confirmação */}
{isConfirmOpen && (
<ConfirmDialog
isOpen={isConfirmOpen}
title="Confirmar exclusão"
description="Atenção"
message={`Deseja realmente excluir o grupo "${itemToDelete?.descricao}"?`}
confirmText="Sim, excluir"
cancelText="Cancelar"
onConfirm={handleDelete}
onCancel={handleCancel}
/>
)}
{/* Formulário de criação/edição */}
{isFormOpen && (
<GSeloGrupoForm
isOpen={isFormOpen}
data={selectedData}
onClose={handleCloseForm}
onSave={handleSave}
buttonIsLoading={buttonIsLoading}
/>
)}
</div>
);
}

View file

@ -0,0 +1,115 @@
'use client';
import { CheckIcon, ChevronsUpDownIcon } from 'lucide-react';
import React, { useState, useEffect, useCallback, useMemo } 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 { useGSeloGrupoIndexHook } from '@/packages/administrativo/hooks/GSeloGrupo/useGSeloGrupoIndexHook';
import GetCapitalize from '@/shared/actions/text/GetCapitalize';
import GTBairroSelectInterface from '../../interfaces/GSeloGrupo/GSeloGrupoSelectInterace';
export default function GSeloGrupoSelect({ field }: GTBairroSelectInterface) {
const [open, setOpen] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const { gSeloGrupo = [], indexGSeloGrupo } = useGSeloGrupoIndexHook();
/**
* Efeito para buscar os dados apenas uma vez.
* useCallback evita recriação desnecessária da função.
*/
const loadData = useCallback(async () => {
if (gSeloGrupo.length) return;
setIsLoading(true);
await indexGSeloGrupo();
setIsLoading(false);
}, [gSeloGrupo.length, indexGSeloGrupo]);
useEffect(() => {
loadData();
}, [loadData]);
/**
* Memoriza o bairro selecionado para evitar reprocessamentos.
*/
const selected = useMemo(
() => gSeloGrupo.find((b) => String(b.selo_grupo_id) === String(field?.value ?? '')),
[gSeloGrupo, field?.value],
);
/**
* Manipulador de seleção com verificação segura.
*/
const handleSelect = useCallback(
(bairroId: string | number) => {
if (!field?.onChange) return;
field.onChange(bairroId);
setOpen(false);
},
[field],
);
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 bairro..." disabled={isLoading} />
<CommandList>
<CommandEmpty>
{isLoading ? 'Carregando...' : 'Nenhum resultado encontrado.'}
</CommandEmpty>
<CommandGroup>
{gSeloGrupo.map((item) => (
<CommandItem
key={item.selo_grupo_id}
value={item.descricao?.toLowerCase() ?? ''}
onSelect={() => handleSelect(item.selo_grupo_id)}
>
<CheckIcon
className={cn(
'mr-2 h-4 w-4',
String(field?.value ?? '') === String(item.selo_grupo_id)
? 'opacity-100'
: 'opacity-0',
)}
/>
{GetCapitalize(item.descricao ?? '')}
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
}

View file

@ -0,0 +1,23 @@
'use client';
import { DataTable } from '@/shared/components/dataTable/DataTable';
import GSeloGrupoColumns from './GSeloGrupoColumns';
import GSeloGrupoTableInterface from '../../interfaces/GSeloGrupo/GSeloGrupoTableInterface';
/**
* Componente principal da tabela de Naturezas
*/
export default function GSeloGrupoTable({ data, onEdit, onDelete }: GSeloGrupoTableInterface) {
const columns = GSeloGrupoColumns(onEdit, onDelete);
return (
<div>
<DataTable
data={data}
columns={columns}
filterColumn="descricao"
filterPlaceholder="Buscar por descrição do grupo..."
/>
</div>
);
}

View file

@ -1,7 +1,7 @@
'use client';
import { HouseIcon, IdCardIcon } from 'lucide-react';
import React, { useEffect } from 'react';
import { useEffect } from 'react';
import { Button } from '@/components/ui/button';
import {
@ -27,12 +27,12 @@ import { parseNumberInput } from '@/shared/actions/form/parseNumberInput';
import { ResetFormIfData } from '@/shared/actions/form/ResetFormIfData';
import LoadingButton from '@/shared/components/loadingButton/LoadingButton';
import TImovelTipoRegistroSelect from './TImovelTipoRegistroSelect';
import { useTImovelFormHook } from '../../hooks/TImovel/useTImovelFormHook';
import { TImovelFormInterface } from '../../interfaces/TImovel/TImovelFormInterface';
import GTBBairroSelect from '../GTBBairro/GTBBairroSelect';
import TImovelUnidadeRuralIndex from '../TImovelUnidade/TImovelUnidadeRural/TImovelUnidadeRuralIndex';
import TImovelUnidadeUrbanoPage from '../TImovelUnidade/TImovelUnidadeUrbano/TImovelUnidadeUrbanoIndex';
import TImovelTipoRegistroSelect from './TImovelTipoRegistroSelect';

View file

@ -0,0 +1,16 @@
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';
import GEmolumentoInterface from '../../interfaces/GEmolumento/GEmolumentoInterface';
async function executeGEmolumentoDeleteData(data: GEmolumentoInterface): Promise<ApiResponseInterface> {
const api = new API();
return api.send({
method: Methods.DELETE,
endpoint: `administrativo/g_emolumento/${data.emolumento_id}`,
});
}
export const GEmolumentoDeleteData = withClientErrorHandler(executeGEmolumentoDeleteData);

View file

@ -0,0 +1,15 @@
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 executeGEmolumentoIndexData(): Promise<ApiResponseInterface> {
const api = new API();
return api.send({
method: Methods.GET,
endpoint: `administrativo/g_emolumento/`,
});
}
export const GEmolumentoIndexData = withClientErrorHandler(executeGEmolumentoIndexData);

View file

@ -0,0 +1,23 @@
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';
import GEmolumentoInterface from '../../interfaces/GEmolumento/GEmolumentoInterface';
async function executeGEmolumentoSaveData(data: GEmolumentoInterface): Promise<ApiResponseInterface> {
// Verifica se existe ID para decidir se é atualização (PUT) ou criação (POST)
const isUpdate = Boolean(data.emolumento_id);
// Instancia o cliente da API
const api = new API();
// Executa a requisição para a API com o método apropriado e envia os dados no corpo
return api.send({
method: isUpdate ? Methods.PUT : Methods.POST, // PUT se atualizar, POST se criar
endpoint: `administrativo/g_emolumento/${data.emolumento_id || ''}`, // endpoint dinâmico
body: data, // payload enviado para a API
});
}
export const GEmolumentoSaveData = withClientErrorHandler(executeGEmolumentoSaveData);

View file

@ -0,0 +1,17 @@
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';
import GSeloGrupoInterface from '../../interfaces/GSeloGrupo/GSeloGrupoInterface';
async function executeGSeloGrupoDeleteData(data: GSeloGrupoInterface): Promise<ApiResponseInterface> {
const api = new API();
return api.send({
method: Methods.DELETE,
endpoint: `administrativo/_selo_grupo/${data.selo_grupo_id}`,
});
}
export const GSeloGrupoDeleteData = withClientErrorHandler(executeGSeloGrupoDeleteData);

View file

@ -0,0 +1,15 @@
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 executeGSeloGrupoIndexData(): Promise<ApiResponseInterface> {
const api = new API();
return api.send({
method: Methods.GET,
endpoint: `administrativo/g_selo_grupo/`,
});
}
export const GSeloGrupoIndexData = withClientErrorHandler(executeGSeloGrupoIndexData);

View file

@ -0,0 +1,23 @@
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';
import GSeloGrupoInterface from '../../interfaces/GSeloGrupo/GSeloGrupoInterface';
async function executeGSeloGrupoSaveData(data: GSeloGrupoInterface): Promise<ApiResponseInterface> {
// Verifica se existe ID para decidir se é atualização (PUT) ou criação (POST)
const isUpdate = Boolean(data.selo_grupo_id);
// Instancia o cliente da API
const api = new API();
// Executa a requisição para a API com o método apropriado e envia os dados no corpo
return api.send({
method: isUpdate ? Methods.PUT : Methods.POST, // PUT se atualizar, POST se criar
endpoint: `administrativo/g_selo_grupo/${data.selo_grupo_id || ''}`, // endpoint dinâmico
body: data, // payload enviado para a API
});
}
export const GSeloGrupoSaveData = withClientErrorHandler(executeGSeloGrupoSaveData);

View file

@ -0,0 +1,21 @@
import { useState } from 'react';
import { useResponse } from '@/shared/components/response/ResponseContext';
import GEmolumentoInterface from '../../interfaces/GEmolumento/GEmolumentoInterface';
import { GEmolumentoDeleteService } from '../../services/GEmolumento/GEmolumentoDeleteService';
export const useGEmolumentoDeleteHook = () => {
const { setResponse } = useResponse();
const [gGramatica, setGEmolumento] = useState<GEmolumentoInterface>();
const deleteGEmolumento = async (data: GEmolumentoInterface) => {
const response = await GEmolumentoDeleteService(data);
setGEmolumento(data);
setResponse(response);
};
return { gGramatica, deleteGEmolumento };
};

View file

@ -0,0 +1,14 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import { GEmolumentoFormValues, GEmolumentoSchema } from '../../schemas/GEmolumento/GEmolumentoSchema';
export function useGEmolumentoFormHook(defaults?: Partial<GEmolumentoFormValues>) {
return useForm<GEmolumentoFormValues>({
resolver: zodResolver(GEmolumentoSchema),
defaultValues: {
emolumento_id: 0,
...defaults,
},
});
}

View file

@ -0,0 +1,27 @@
'use client';
import { useState } from 'react';
import { useResponse } from '@/shared/components/response/ResponseContext';
import GEmolumentoInterface from '../../interfaces/GEmolumento/GEmolumentoInterface';
import { GEmolumentoIndexService } from '../../services/GEmolumento/GEmolumentoIndexService';
export const useGEmolumentoIndexHook = () => {
const { setResponse } = useResponse();
const [gGramatica, setGEmolumento] = useState<GEmolumentoInterface[]>([]);
const indexGEmolumento = async () => {
const response = await GEmolumentoIndexService();
// Armazena os dados consultados
setGEmolumento(response.data);
// Define a resposta (toast, modal, feedback, etc.)
setResponse(response);
};
return {
gGramatica,
indexGEmolumento,
};
};

View file

@ -0,0 +1,35 @@
'use client';
import { useState } from 'react';
import { useResponse } from '@/shared/components/response/ResponseContext';
import GEmolumentoInterface from '../../interfaces/GEmolumento/GEmolumentoInterface';
import { GEmolumentoSaveService } from '../../services/GEmolumento/GEmolumentoSaveService';
export const useGEmolumentoSaveHook = () => {
const { setResponse } = useResponse();
const [gGramatica, setGEmolumento] = useState<GEmolumentoInterface | null>(null);
// controla se o formulário está aberto ou fechado
const [isOpen, setIsOpen] = useState(false);
const saveGEmolumento = async (data: GEmolumentoInterface) => {
const response = await GEmolumentoSaveService(data);
// Armazena os dados da resposta
setGEmolumento(response.data);
// Define os dados da resposta (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 { gGramatica, saveGEmolumento, isOpen, setIsOpen };
};

View file

@ -0,0 +1,21 @@
import { useState } from 'react';
import { useResponse } from '@/shared/components/response/ResponseContext';
import GSeloGrupoInterface from '../../interfaces/GSeloGrupo/GSeloGrupoInterface';
import { GSeloGrupoDeleteService } from '../../services/GSeloGrupo/GSeloGrupoDeleteService';
export const useGSeloGrupoDeleteHook = () => {
const { setResponse } = useResponse();
const [gSeloGrupo, setGSeloGrupo] = useState<GSeloGrupoInterface>();
const deleteGSeloGrupo = async (data: GSeloGrupoInterface) => {
const response = await GSeloGrupoDeleteService(data);
setGSeloGrupo(data);
setResponse(response);
};
return { gSeloGrupo, deleteGSeloGrupo };
};

View file

@ -0,0 +1,14 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import { GSeloGrupoFormValues, GSeloGrupoSchema } from '../../schemas/GSeloGrupo/GSeloGrupoSchema';
export function useGSeloGrupoFormHook(defaults?: Partial<GSeloGrupoFormValues>) {
return useForm<GSeloGrupoFormValues>({
resolver: zodResolver(GSeloGrupoSchema),
defaultValues: {
selo_grupo_id: 0,
...defaults,
},
});
}

View file

@ -0,0 +1,27 @@
'use client';
import { useState } from 'react';
import { useResponse } from '@/shared/components/response/ResponseContext';
import GSeloGrupoInterface from '../../interfaces/GSeloGrupo/GSeloGrupoInterface';
import { GSeloGrupoIndexService } from '../../services/GSeloGrupo/GSeloGrupoIndexService';
export const useGSeloGrupoIndexHook = () => {
const { setResponse } = useResponse();
const [gSeloGrupo, setGSeloGrupo] = useState<GSeloGrupoInterface[]>([]);
const indexGSeloGrupo = async () => {
const response = await GSeloGrupoIndexService();
// Armazena os dados consultados
setGSeloGrupo(response.data);
// Define a resposta (toast, modal, feedback, etc.)
setResponse(response);
};
return {
gSeloGrupo,
indexGSeloGrupo,
};
};

View file

@ -0,0 +1,35 @@
'use client';
import { useState } from 'react';
import { useResponse } from '@/shared/components/response/ResponseContext';
import GSeloGrupoInterface from '../../interfaces/GSeloGrupo/GSeloGrupoInterface';
import { GSeloGrupoSaveService } from '../../services/GSeloGrupo/GSeloGrupoSaveService';
export const useGSeloGrupoSaveHook = () => {
const { setResponse } = useResponse();
const [gSeloGrupo, setGSeloGrupo] = useState<GSeloGrupoInterface | null>(null);
// controla se o formulário está aberto ou fechado
const [isOpen, setIsOpen] = useState(false);
const saveGSeloGrupo = async (data: GSeloGrupoInterface) => {
const response = await GSeloGrupoSaveService(data);
// Armazena os dados da resposta
setGSeloGrupo(response.data);
// Define os dados da resposta (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 { gSeloGrupo, saveGSeloGrupo, isOpen, setIsOpen };
};

View file

@ -0,0 +1,9 @@
import { GEmolumentoFormValues } from '../../schemas/GEmolumento/GEmolumentoSchema';
export interface GEmolumentoFormInterface {
isOpen: boolean;
data: GEmolumentoFormValues | null;
onClose: (item: null, isFormStatus: boolean) => void;
onSave: (data: GEmolumentoFormValues) => void;
buttonIsLoading: boolean;
}

View file

@ -0,0 +1,19 @@
export default interface GEmolumentoInterface {
emolumento_id?: number;
descricao?: string;
tipo?: string;
sistema_id?: number;
selo_grupo_id?: number;
reg_averb?: string;
pre_definido?: string;
situacao?: string;
situacao_ri?: string;
com_reducao?: string;
motivo_reducao?: string;
valor_maximo_certidao?: number;
tipo_objetivo?: string;
modelo_tag?: string;
codigo_nota_id?: number;
convenio_codhab?: string;
item_df?: string;
}

View file

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

View file

@ -0,0 +1,9 @@
import { GSeloGrupoFormValues } from '../../schemas/GSeloGrupo/GSeloGrupoSchema';
export interface GSeloGrupoFormInterface {
isOpen: boolean;
data: GSeloGrupoFormValues | null;
onClose: (item: null, isFormStatus: boolean) => void;
onSave: (data: GSeloGrupoFormValues) => void;
buttonIsLoading: boolean;
}

View file

@ -0,0 +1,24 @@
export default interface GSeloGrupoInterface {
selo_grupo_id?: number;
descricao?: string;
numero?: number;
situacao?: string;
controle_automatico?: string;
sistema_id?: number;
valor?: number;
tipo_cartorio?: string;
descricao_completa?: string;
agrupador?: string;
um_por_protocolo?: string;
numero_principal_ini?: number;
numero_principal_fim?: number;
selo_grupo_id_principal?: number;
envio_automatico?: string;
selo_grupo_id_agrupador?: number;
codigo_conta?: number;
id_tipo_ato_antigo?: number;
grupos_principal?: string;
sigla?: string;
tipo_selo?: string;
natureza?: number;
}

View file

@ -0,0 +1,6 @@
export default interface GSeloGrupoSelectInterace {
field?: {
value?: number | string;
onChange?: (value: string | number) => void;
};
}

View file

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

View file

@ -0,0 +1,23 @@
import z from "zod";
export const GEmolumentoSchema = z.object({
emolumento_id: z.number().optional(),
descricao: z.string().optional(),
tipo: z.string().optional(),
sistema_id: z.number().optional(),
selo_grupo_id: z.number().optional(),
reg_averb: z.string().optional(),
pre_definido: z.string().optional(),
situacao: z.string().optional(),
situacao_ri: z.string().optional(),
com_reducao: z.string().optional(),
motivo_reducao: z.string().optional(),
valor_maximo_certidao: z.number().optional(),
tipo_objetivo: z.string().optional(),
modelo_tag: z.string().optional(),
codigo_nota_id: z.number().optional(),
convenio_codhab: z.string().optional(),
item_df: z.string().optional(),
});
export type GEmolumentoFormValues = z.infer<typeof GEmolumentoSchema>;

View file

@ -0,0 +1,28 @@
import { z } from "zod";
export const GSeloGrupoSchema = z.object({
selo_grupo_id: z.number().optional(),
descricao: z.string().optional(),
numero: z.number().optional(),
situacao: z.string().optional(),
controle_automatico: z.string().optional(),
sistema_id: z.number().optional(),
valor: z.number().optional(),
tipo_cartorio: z.string().optional(),
descricao_completa: z.string().optional(),
agrupador: z.string().optional(),
um_por_protocolo: z.string().optional(),
numero_principal_ini: z.number().optional(),
numero_principal_fim: z.number().optional(),
selo_grupo_id_principal: z.number().optional(),
envio_automatico: z.string().optional(),
selo_grupo_id_agrupador: z.number().optional(),
codigo_conta: z.number().optional(),
id_tipo_ato_antigo: z.number().optional(),
grupos_principal: z.string().optional(),
sigla: z.string().optional(),
tipo_selo: z.string().optional(),
natureza: z.number().optional(),
});
export type GSeloGrupoFormValues = z.infer<typeof GSeloGrupoSchema>;

View file

@ -0,0 +1,14 @@
import { GEmolumentoDeleteData } from '../../data/GEmolumento/GEmolumentoDeleteData';
import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler';
import GEmolumentoInterface from '../../interfaces/GEmolumento/GEmolumentoInterface';
async function executeGEmolumentoDeleteService(data: GEmolumentoInterface) {
const response = await GEmolumentoDeleteData(data);
return response;
}
export const GEmolumentoDeleteService = withClientErrorHandler(executeGEmolumentoDeleteService);

View file

@ -0,0 +1,11 @@
import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler';
import { GEmolumentoIndexData } from '../../data/GEmolumento/GEmolumentoIndexData';
export default async function executeGEmolumentoIndexService() {
const response = await GEmolumentoIndexData();
return response;
}
export const GEmolumentoIndexService = withClientErrorHandler(executeGEmolumentoIndexService);

View file

@ -0,0 +1,12 @@
import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler';
import { GEmolumentoSaveData } from '../../data/GEmolumento/GEmolumentoSaveData';
import GEmolumentoInterface from '../../interfaces/GEmolumento/GEmolumentoInterface';
async function executeGEmolumentoSaveService(data: GEmolumentoInterface) {
const response = await GEmolumentoSaveData(data);
return response;
}
export const GEmolumentoSaveService = withClientErrorHandler(executeGEmolumentoSaveService);

View file

@ -0,0 +1,10 @@
import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler';
import { GSeloGrupoDeleteData } from '../../data/GSeloGrupo/GSeloGrupoDeleteData';
import GSeloGrupoInterface from '../../interfaces/GSeloGrupo/GSeloGrupoInterface';
async function executeGSeloGrupoDeleteService(data: GSeloGrupoInterface) {
const response = await GSeloGrupoDeleteData(data);
return response;
}
export const GSeloGrupoDeleteService = withClientErrorHandler(executeGSeloGrupoDeleteService);

View file

@ -0,0 +1,11 @@
import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler';
import { GSeloGrupoIndexData } from '../../data/GSeloGrupo/GSeloGrupoIndexData';
export default async function executeGSeloGrupoIndexService() {
const response = await GSeloGrupoIndexData();
return response;
}
export const GSeloGrupoIndexService = withClientErrorHandler(executeGSeloGrupoIndexService);

View file

@ -0,0 +1,12 @@
import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler';
import { GSeloGrupoSaveData } from '../../data/GSeloGrupo/GSeloGrupoSaveData';
import GSeloGrupoInterface from '../../interfaces/GSeloGrupo/GSeloGrupoInterface';
async function executeGSeloGrupoSaveService(data: GSeloGrupoInterface) {
const response = await GSeloGrupoSaveData(data);
return response;
}
export const GSeloGrupoSaveService = withClientErrorHandler(executeGSeloGrupoSaveService);

View file

@ -0,0 +1,82 @@
import { Command } from 'cmdk';
import { CheckIcon, ChevronsUpDownIcon } from 'lucide-react';
import React from 'react';
import { ControllerRenderProps } from 'react-hook-form';
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 { SistemasEnum } from '@/shared/enums/SistemasEnum';
interface SistemasSelectProps {
field: ControllerRenderProps<any, any>;
}
const SistemasSelect: React.FC<SistemasSelectProps> = ({ field }) => {
const [open, setOpen] = React.useState(false);
const options = Object.entries(SistemasEnum).map(([key, label]) => ({
value: Number(key),
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) => Number(item.value) === Number(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 sistema..." />
<CommandList>
<CommandEmpty>Nenhum resultado encontrado.</CommandEmpty>
<CommandGroup>
{options.map((item) => (
<CommandItem
key={item.value}
value={item.label.toLowerCase()}
onSelect={() => {
field.onChange(Number(item.value));
setOpen(false);
}}
>
<CheckIcon
className={cn(
'mr-2 h-4 w-4',
Number(field.value) === Number(item.value)
? 'opacity-100'
: 'opacity-0',
)}
/>
{item.label}
</CommandItem>
))}
</CommandGroup>
</CommandList>
</Command>
</PopoverContent>
</Popover>
);
};
export default SistemasSelect;

View file

@ -0,0 +1,73 @@
import { Command } from 'cmdk';
import { CheckIcon, ChevronsUpDownIcon } from 'lucide-react';
import React from 'react';
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 { TipoCartorioEnum } from '@/shared/enums/TipoCartorioEnum';
export default function TipoCartorioSelect({ field }: any) {
const [open, setOpen] = React.useState(false);
const options = Object.entries(TipoCartorioEnum).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,73 @@
import { Command } from 'cmdk';
import { CheckIcon, ChevronsUpDownIcon } from 'lucide-react';
import React from 'react';
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 { TipoEmolumento } from '@/shared/enums/TipoEmolumentoEnum';
export default function TipoEmolumentoSelect({ field }: any) {
const [open, setOpen] = React.useState(false);
const options = Object.entries(TipoEmolumento).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,8 @@
export const SistemasEnum = {
1: 'Registro de Imóveis',
2: 'Tabelionato de Notas',
5: 'Caixa',
7: 'Registro de Títulos e Documentos',
12: 'Registro Civil',
13: 'Protesto de Títulos e Documentos',
} as const;

View file

@ -0,0 +1,7 @@
export const TipoCartorioEnum = {
1: 'Tabelionato de Notas',
2: 'Registro de Imóveis',
3: 'Registro Civil',
4: 'RTD',
5: 'Tabelionato de Protesto',
} as const;

View file

@ -0,0 +1,6 @@
export const TipoEmolumento = {
C: 'Certidões',
T: 'Títulos',
P: 'Personalizado',
B: 'Balcão',
} as const;