[MVPTN-122] feat(CRUD): Habiltia o CRUD

This commit is contained in:
Keven Willian Pereira de Souza 2025-10-25 16:58:42 -03:00
parent 6051afc3e0
commit 8e035546e9
14 changed files with 234 additions and 129 deletions

View file

@ -11,6 +11,7 @@ export default function GGramaticaPage() {
return (
<GEmolumentoItemIndex
emolumento_id={Number(params.emolumentoId)}
emolumento_periodo_id={Number(params.emolumentoPeriodoId)}
/>
);

View file

@ -19,8 +19,11 @@ import GEmolumentoInterface from '../../interfaces/GEmolumento/GEmolumentoInterf
export default function GEmolumentoColumns(
onEdit: (item: GEmolumentoInterface, isEditingFormStatus: boolean) => void,
onDelete: (item: GEmolumentoInterface, isEditingFormStatus: boolean) => void,
emolumentoPeriodoId: (id: number) => void,
): ColumnDef<GEmolumentoInterface>[] {
return [
// ID
{
accessorKey: 'emolumento_id',
@ -57,7 +60,7 @@ export default function GEmolumentoColumns(
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Link href={`/administrativo/financeiro/emolumentos/${Number(row.getValue('emolumento_id'))}/itens`}>
<Link href={`/administrativo/financeiro/emolumentos/${Number(row.getValue('emolumento_id'))}/${emolumentoPeriodoId}/itens`}>
<ListIcon className="mr-2 h-4 w-4" />
Itens
</Link>

View file

@ -31,11 +31,21 @@ export default function GEmolumentoIndex() {
// Estado para saber qual item será deletado
const [itemToDelete, setItemToDelete] = useState<GEmolumentoInterface | null>(null);
// Estado para controlar o período selecionado
const [getEmolumentoPeriodoId, setEmolumentoPeriodoId] = useState<number | 0>(0);
/**
* Hook do modal de confirmação
*/
const { isOpen: isConfirmOpen, openDialog: openConfirmDialog, handleCancel } = useConfirmDialog();
/**
* Executa consulta ao selecionar um período
*/
const handleSelectedEmolumentoPeriodo = (emolumentoPeriodoId: number) => {
setEmolumentoPeriodoId(emolumentoPeriodoId);
};
/**
* Abre o formulário no modo de edição ou criação
*/
@ -131,7 +141,13 @@ export default function GEmolumentoIndex() {
}}
/>
{/* Tabela de andamentos */}
<GEmolumentoTable data={gGramatica} onEdit={handleOpenForm} onDelete={handleConfirmDelete} />
<GEmolumentoTable
data={gGramatica}
onEdit={handleOpenForm}
onDelete={handleConfirmDelete}
onSelectedEmolumentoPeriodo={handleSelectedEmolumentoPeriodo}
emolumentoPeriodoId={Number(getEmolumentoPeriodoId)}
/>
{/* Modal de confirmação */}
{isConfirmOpen && (
<ConfirmDialog

View file

@ -3,15 +3,19 @@
import { DataTable } from '@/shared/components/dataTable/DataTable';
import GEmolumentoTableInterface from '../../interfaces/GEmolumento/GEmolumentoTableInterface';
import GEmolumentoPeriodoSelect from '../GEmolumentoPeriodo/GEmolumentoPeriodoSelect';
import GEmolumentoColumns from './GEmolumentoColumns';
/**
* Componente principal da tabela de Naturezas
*/
export default function GEmolumentoTable({ data, onEdit, onDelete }: GEmolumentoTableInterface) {
const columns = GEmolumentoColumns(onEdit, onDelete);
export default function GEmolumentoTable({ data, onEdit, onDelete, onSelectedEmolumentoPeriodo, emolumentoPeriodoId }: GEmolumentoTableInterface) {
const columns = GEmolumentoColumns(onEdit, onDelete, emolumentoPeriodoId);
return (
<div>
<GEmolumentoPeriodoSelect
onSelectedEmolumentoPeriodo={onSelectedEmolumentoPeriodo}
/>
<DataTable
data={data}
columns={columns}
@ -20,4 +24,4 @@ export default function GEmolumentoTable({ data, onEdit, onDelete }: GEmolumento
/>
</div>
);
}
}

View file

@ -22,17 +22,31 @@ export default function GEmolumentoItemColumns(
return [
// ID
{
accessorKey: 'emolumento_id',
accessorKey: 'emolumento_item_id',
header: ({ column }) => SortableHeader('ID', column),
cell: ({ row }) => Number(row.getValue('emolumento_id')),
cell: ({ row }) => Number(row.getValue('emolumento_item_id')),
enableSorting: true,
},
// Descrição
// Valor Início
{
accessorKey: 'descricao',
header: ({ column }) => SortableHeader('Descrição', column),
cell: ({ row }) => GetCapitalize(String(row.getValue('descricao') || '')),
accessorKey: 'valor_inicio',
header: ({ column }) => SortableHeader('Valor Inicial', column),
cell: ({ row }) => GetCapitalize(String(row.getValue('valor_inicio') || '')),
},
// Valor Fim
{
accessorKey: 'valor_fim',
header: ({ column }) => SortableHeader('Valor Final', column),
cell: ({ row }) => GetCapitalize(String(row.getValue('valor_fim') || '')),
},
// Taxa Judiciárioa
{
accessorKey: 'valor_taxa_judiciaria',
header: ({ column }) => SortableHeader('Taxa Judiciária', column),
cell: ({ row }) => GetCapitalize(String(row.getValue('valor_taxa_judiciaria') || '')),
},
// Ações

View file

@ -1,6 +1,6 @@
'use client';
import { HouseIcon, IdCardIcon } from 'lucide-react';
import { HouseIcon } from 'lucide-react';
import { useEffect } from 'react';
import { Button } from '@/components/ui/button';
@ -23,6 +23,7 @@ import {
} 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';
@ -40,6 +41,8 @@ export default function GEmolumentoItemForm({
onSave,
buttonIsLoading,
}: GEmolumentoItemFormInterface) {
// Inicializa o formulário
const form = useGEmolumentoItemFormHook({});
// Atualiza o formulário quando recebe dados para edição
@ -62,7 +65,7 @@ export default function GEmolumentoItemForm({
<DialogHeader>
<DialogTitle className="text-lg sm:text-xl">Emolumento</DialogTitle>
<DialogDescription className="text-muted-foreground text-sm">
Formulário de Emolumento
Formulário de Itens de Emolumento
</DialogDescription>
</DialogHeader>
{/* Formulário principal */}
@ -72,48 +75,67 @@ export default function GEmolumentoItemForm({
<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
Valores
</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">
<div className="col-span-12 sm:col-span-6 md:col-span-4">
<FormField
control={form.control}
name="descricao"
name="valor_inicio"
render={({ field }) => (
<FormItem>
<FormLabel>Descrição</FormLabel>
<FormLabel>Valor Início</FormLabel>
<FormControl>
<Input {...field} type="text" />
<Input {...field}
type="number"
onChange={(e) => field.onChange(parseNumberInput(e))}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="col-span-12 sm:col-span-6 md:col-span-4">
<FormField
control={form.control}
name="valor_fim"
render={({ field }) => (
<FormItem>
<FormLabel>Valor Fim</FormLabel>
<FormControl>
<Input {...field}
type="number"
onChange={(e) => field.onChange(parseNumberInput(e))}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="col-span-12 sm:col-span-6 md:col-span-4">
<FormField
control={form.control}
name="valor_taxa_judiciaria"
render={({ field }) => (
<FormItem>
<FormLabel>Valor Taxa Judiciária</FormLabel>
<FormControl>
<Input {...field}
type="number"
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>
</TabsContent>
</Tabs>

View file

@ -1,20 +1,27 @@
'use client';
import { useCallback, useEffect, useRef, useState } from 'react';
import { useCallback, useEffect, useState } from 'react';
import { useGEmolumentoItemDeleteHook } from '@/packages/administrativo/hooks/GEmolumentoItem/useGEmolumentoItemDeleteHook';
import { useGEmolumentoItemIndexHook } from '@/packages/administrativo/hooks/GEmolumentoItem/useGEmolumentoItemIndexHook';
import { useGEmolumentoItemSaveHook } from '@/packages/administrativo/hooks/GEmolumentoItem/useGEmolumentoItemSaveHook';
import GEmolumentoItemIndexInterface from '@/packages/administrativo/interfaces/GEmolumentoItem/GEmolumentoItemIndexInterface';
import GEmolumentoItemInterface from '@/packages/administrativo/interfaces/GEmolumentoItem/GEmolumentoItemInterface';
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 { useGEmolumentoItemDeleteHook } from '../../hooks/GEmolumentoItem/useGEmolumentoItemDeleteHook';
import { useGEmolumentoItemIndexHook } from '../../hooks/GEmolumentoItem/useGEmolumentoItemIndexHook';
import { useGEmolumentoItemSaveHook } from '../../hooks/GEmolumentoItem/useGEmolumentoItemSaveHook';
import GEmolumentoItemInterface from '../../interfaces/GEmolumentoItem/GEmolumentoItemInterface';
import GEmolumentoItemForm from './GEmolumentoItemForm';
import GEmolumentoItemTable from './GEmolumentoItemTable';
export default function GEmolumentoItemIndex({ emolumento_id }: GEmolumentoItemIndexInterface) {
export default function GEmolumentoItemIndex({ emolumento_id, emolumento_periodo_id }: GEmolumentoItemIndexInterface) {
const gEmolumentoItemParams = {
emolumento_id: emolumento_id,
emolumento_periodo_id: emolumento_periodo_id
}
// Controle de estado do botão
const [buttonIsLoading, setButtonIsLoading] = useState(false);
@ -23,25 +30,25 @@ export default function GEmolumentoItemIndex({ emolumento_id }: GEmolumentoItemI
const { saveGEmolumentoItem } = useGEmolumentoItemSaveHook();
const { deleteGEmolumentoItem } = useGEmolumentoItemDeleteHook();
// Estados do componente
// Estados
const [selectedData, setSelectedData] = useState<GEmolumentoItemInterface | null>(null);
const [isFormOpen, setIsFormOpen] = useState(false);
// Estado para saber qual item será deletado
const [itemToDelete, setItemToDelete] = useState<GEmolumentoItemInterface | null>(null);
// Estado para controlar o período selecionado
const [getEmolumentoPeriodoId, setEmolumentoPeriodoId] = useState<number | null>(null);
// Hook do modal de confirmação
/**
* Hook do modal de confirmação
*/
const { isOpen: isConfirmOpen, openDialog: openConfirmDialog, handleCancel } = useConfirmDialog();
// Flag para evitar duplas execuções em modo Strict
const isMounted = useRef(false);
/**
* Abre o formulário no modo de edição ou criação
*/
const handleOpenForm = useCallback((data: GEmolumentoItemInterface | null) => {
setSelectedData(data);
// Se não houver dados (criação), cria um objeto inicial com imovel_id
const initialData: GEmolumentoItemInterface = data ?? ({ emolumento_id, emolumento_periodo_id } as GEmolumentoItemInterface);
setSelectedData(initialData);
setIsFormOpen(true);
}, []);
@ -53,51 +60,24 @@ export default function GEmolumentoItemIndex({ emolumento_id }: GEmolumentoItemI
setIsFormOpen(false);
}, []);
/**
* Sempre que o emolumento_id ou o período mudar, busca novamente os dados.
* Evita loop infinito e dispara quando necessário.
*/
useEffect(() => {
if (!emolumento_id) return;
// Evita execução duplicada no modo Strict
if (isMounted.current) {
indexGEmolumentoItem({
emolumento_id,
emolumento_periodo_id: getEmolumentoPeriodoId ?? 0,
});
} else {
isMounted.current = true;
indexGEmolumentoItem({
emolumento_id,
emolumento_periodo_id: getEmolumentoPeriodoId ?? 0,
});
}
}, [emolumento_id, getEmolumentoPeriodoId]);
/**
* Executa consulta ao selecionar um período
*/
const handleSelectedEmolumentoPeriodo = (emolumentoPeriodoId: number) => {
setEmolumentoPeriodoId(emolumentoPeriodoId); // 🔥 Isso dispara o useEffect acima
};
/**
* Salva os dados do formulário
*/
const handleSave = useCallback(
async (formData: GEmolumentoItemInterface) => {
// Coloca o botão em estado de loading
setButtonIsLoading(true);
// Aguarda salvar o registro
await saveGEmolumentoItem(formData);
// Remove o botão em estado de loading
setButtonIsLoading(false);
// Recarrega a lista usando os valores atuais
await indexGEmolumentoItem({
emolumento_id,
emolumento_periodo_id: getEmolumentoPeriodoId ?? 0,
});
// Atualiza a lista de dados
indexGEmolumentoItem(gEmolumentoItemParams);
},
[saveGEmolumentoItem, indexGEmolumentoItem, emolumento_id, getEmolumentoPeriodoId]
[saveGEmolumentoItem, indexGEmolumentoItem, handleCloseForm],
);
/**
@ -105,25 +85,47 @@ export default function GEmolumentoItemIndex({ emolumento_id }: GEmolumentoItemI
*/
const handleConfirmDelete = useCallback(
(item: GEmolumentoItemInterface) => {
// Define o item atual para remoção
setItemToDelete(item);
// Abre o modal de confirmação
openConfirmDialog();
},
[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 deleteGEmolumentoItem(itemToDelete);
await indexGEmolumentoItem({
emolumento_id,
emolumento_periodo_id: getEmolumentoPeriodoId ?? 0,
});
// Atualiza a lista
await indexGEmolumentoItem(gEmolumentoItemParams);
// Limpa o item selecionado
setItemToDelete(null);
// Fecha o modal
handleCancel();
}, [itemToDelete, deleteGEmolumentoItem, indexGEmolumentoItem, emolumento_id, getEmolumentoPeriodoId, handleCancel]);
}, [itemToDelete, indexGEmolumentoItem, handleCancel]);
/**
* Busca inicial dos dados
*/
useEffect(() => {
indexGEmolumentoItem(gEmolumentoItemParams);
}, []);
/**
* Tela de loading enquanto carrega os dados
*/
if (gEmolumentoItem?.length == 0) {
return <Loading type={2} />;
}
return (
<div>
@ -140,7 +142,6 @@ export default function GEmolumentoItemIndex({ emolumento_id }: GEmolumentoItemI
data={gEmolumentoItem}
onEdit={handleOpenForm}
onDelete={handleConfirmDelete}
onSelectedEmolumentoPeriodo={handleSelectedEmolumentoPeriodo}
/>
{/* Modal de confirmação */}

View file

@ -2,24 +2,20 @@
import { DataTable } from '@/shared/components/dataTable/DataTable';
import GEmolumentoItemColumns from './GEmolumentoItemColumns';
import GEmolumentoItemTableInterface from '../../interfaces/GEmolumentoItem/GEmolumentoItemTableInterface';
import GEmolumentoPeriodoSelect from '../GEmolumentoPeriodo/GEmolumentoPeriodoSelect';
import GEmolumentoItemColumns from './GEmolumentoItemColumns';
/**
* Componente principal da tabela de Naturezas
*/
export default function GEmolumentoItemTable({ data, onEdit, onDelete, onSelectedEmolumentoPeriodo }: GEmolumentoItemTableInterface) {
export default function GEmolumentoItemTable({ data, onEdit, onDelete }: GEmolumentoItemTableInterface) {
const columns = GEmolumentoItemColumns(onEdit, onDelete);
return (
<div>
<GEmolumentoPeriodoSelect
onSelectedEmolumentoPeriodo={onSelectedEmolumentoPeriodo}
/>
<DataTable
data={data}
columns={columns}
filterColumn="descricao"
filterColumn="valor_inicio"
filterPlaceholder="Buscar por descrição da natureza..."
/>
</div>

View file

@ -9,7 +9,7 @@ async function executeGEmolumentoItemIndexData(data: GEmolumentoItemIndexInterfa
const api = new API();
return api.send({
method: Methods.GET,
endpoint: `administrativo/g_emolumento_item/${data.emolumento_id}/1`,
endpoint: `administrativo/g_emolumento_item/${data.emolumento_id}/${data.emolumento_periodo_id}`,
});
}

View file

@ -1,13 +1,18 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import { GEmolumentoItemFormValues, GEmolumentoItemSchema } from '../../schemas/GEmolumentoItem/GEmolumentoItemSchema';
import {
GEmolumentoItemFormValues,
GEmolumentoItemSchema,
} from '../../schemas/GEmolumentoItem/GEmolumentoItemSchema';
export function useGEmolumentoItemFormHook(defaults?: Partial<GEmolumentoItemFormValues>) {
return useForm<GEmolumentoItemFormValues>({
resolver: zodResolver(GEmolumentoItemSchema),
defaultValues: {
emolumento_item_id: 0,
emolumento_id: 0,
emolumento_periodo_id: 0,
...defaults,
},
});

View file

@ -4,4 +4,6 @@ export default interface GEmolumentoTableInterface {
data?: GEmolumentoInterface[];
onEdit: (item: GEmolumentoInterface, isEditingFormStatus: boolean) => void;
onDelete: (item: GEmolumentoInterface, isEditingFormStatus: boolean) => void;
}
onSelectedEmolumentoPeriodo: (emolumentoPeriodId: number) => void;
emolumentoPeriodoId: (id: number) => void;
}

View file

@ -1,13 +1,31 @@
export default interface GEmolumentoItemInterface {
emolumento_item_id?: number;
emolumento_id?: number;
descricao?: string;
tipo?: string;
tipo_lancamento?: string;
reg_averb?: string;
valor_fixo?: number;
percentual?: number;
situacao?: string;
emolumento_item_ref?: number;
observacao?: string;
valor_emolumento?: number,
emolumento_item_id?: number,
emolumento_id?: number,
valor_inicio?: number,
valor_fim?: number,
valor_taxa_judiciaria?: number,
emolumento_periodo_id?: number,
codigo?: number,
pagina_extra?: number,
valor_pagina_extra?: number,
valor_outra_taxa1?: number,
codigo_selo?: string,
valor_fundo_ri?: number,
codigo_tabela?: string,
selo_grupo_id?: number,
codigo_km?: string,
emolumento_acresce?: number,
taxa_acresce?: number,
funcivil_acresce?: number,
valor_fracao?: number,
valor_por_excedente_emol?: number,
valor_por_excedente_tj?: number,
valor_por_excedente_fundo?: number,
valor_limite_excedente_emol?: number,
valor_limite_excedente_tj?: number,
valor_limite_excedente_fundo?: number,
fundo_selo?: number,
distribuicao?: number,
vrcext?: number,
}

View file

@ -4,5 +4,4 @@ export default interface GEmolumentoItemTableInterface {
data?: GEmolumentoItemInterface[];
onEdit: (item: GEmolumentoItemInterface, isEditingFormStatus: boolean) => void;
onDelete: (item: GEmolumentoItemInterface, isEditingFormStatus: boolean) => void;
onSelectedEmolumentoPeriodo: (emolumentoPeriodoId: number) => void;
}

View file

@ -1,17 +1,41 @@
import z from "zod";
export const GEmolumentoItemSchema = z.object({
emolumento_item_id: z.number().optional(),
emolumento_id: z.number().optional(),
descricao: z.string().max(260).optional(),
tipo: z.string().max(1).optional(),
tipo_lancamento: z.string().max(1).optional(),
reg_averb: z.string().max(1).optional(),
valor_fixo: z.number().optional(),
percentual: z.number().optional(),
situacao: z.string().max(1).optional(),
emolumento_item_ref: z.number().optional(),
observacao: z.string().max(260).optional(),
emolumento_item_id: z.coerce.number().optional(),
emolumento_id: z.coerce.number().optional(),
emolumento_periodo_id: z.coerce.number().optional(),
selo_grupo_id: z.coerce.number().optional(),
codigo: z.coerce.number().min(0).optional(),
codigo_tabela: z.string().max(30).optional(),
codigo_selo: z.string().max(30).optional(),
codigo_km: z.string().max(30).optional(),
valor_emolumento: z.coerce.number().min(0).optional(),
valor_inicio: z.coerce.number().min(0).optional(),
valor_fim: z.coerce.number().min(0).optional(),
valor_taxa_judiciaria: z.coerce.number().min(0).optional(),
valor_fundo_ri: z.coerce.number().min(0).optional(),
valor_outra_taxa1: z.coerce.number().min(0).optional(),
valor_pagina_extra: z.coerce.number().min(0).optional(),
pagina_extra: z.coerce.number().min(0).optional(),
emolumento_acresce: z.coerce.number().min(0).optional(),
taxa_acresce: z.coerce.number().min(0).optional(),
funcivil_acresce: z.coerce.number().min(0).optional(),
valor_fracao: z.coerce.number().min(0).optional(),
valor_por_excedente_emol: z.coerce.number().min(0).optional(),
valor_por_excedente_tj: z.coerce.number().min(0).optional(),
valor_por_excedente_fundo: z.coerce.number().min(0).optional(),
valor_limite_excedente_emol: z.coerce.number().min(0).optional(),
valor_limite_excedente_tj: z.coerce.number().min(0).optional(),
valor_limite_excedente_fundo: z.coerce.number().min(0).optional(),
fundo_selo: z.coerce.number().min(0).optional(),
distribuicao: z.coerce.number().min(0).optional(),
vrcext: z.coerce.number().min(0).optional(),
});
export type GEmolumentoItemFormValues = z.infer<typeof GEmolumentoItemSchema>;
export type GEmolumentoItemFormValues = z.infer<typeof GEmolumentoItemSchema>;