[MVPTN-99] feat(CRUD): Implmenta os endpoints reais para manipular pessoa fisica e juridica

This commit is contained in:
Keven Willian Pereira de Souza 2025-10-08 17:45:26 -03:00
parent 36dba45995
commit 3b38337358
28 changed files with 318 additions and 12587 deletions

View file

@ -4,7 +4,6 @@ import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
async function executeGTBEstadoCivilIndexData() {
const api = new API();
return await api.send({
method: Methods.GET,
endpoint: `administrativo/g_tb_estado_civil/`,

View file

@ -8,15 +8,11 @@ export const useGTBEstadoCivilReadHook = () => {
const [gTBEstadoCivil, setGTBEstadoCivil] = useState<GTBEstadoCivilInterface[]>([]);
const fetchGTBEstadoCivil = async () => {
try {
const response = await GTBEstadoCivilIndexService();
const response = await GTBEstadoCivilIndexService();
setGTBEstadoCivil(response.data);
setGTBEstadoCivil(response.data);
setResponse(response);
} catch (error) {
console.log(error);
}
setResponse(response);
};
return { gTBEstadoCivil, fetchGTBEstadoCivil };

View file

@ -2,12 +2,8 @@ import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/
import { GTBEstadoCivilIndexData } from '../../_data/GTBEstadoCivil/GTBEstadoCivilIndexData';
async function executeGTBEstadoCivilIndexService() {
try {
const response = await GTBEstadoCivilIndexData();
return response;
} catch (error) {
return error;
}
const response = await GTBEstadoCivilIndexData();
return response;
}
export const GTBEstadoCivilIndexService = withClientErrorHandler(executeGTBEstadoCivilIndexService);
export const GTBEstadoCivilIndexService = withClientErrorHandler(executeGTBEstadoCivilIndexService);

View file

@ -16,7 +16,7 @@ import TImovelUnidadeUrbanoTable from './TImovelUnidadeUrbanoTable';
import TImovelUnidadeUrbanoForm from './TImovelUnidadeUrbanoForm';
import TImovelUnidadePageInterface from '@/packages/administrativo/interfaces/TImovelUnidade/TImovelUnidadePageInterface';
export default function TImovelUnidadeUrbanoPage({imovel_id}: TImovelUnidadePageInterface) {
export default function TImovelUnidadeUrbanoPage({ imovel_id }: TImovelUnidadePageInterface) {
const TImovelUnidadePage: TImovelUnidadePageInterface = {
imovel_id: imovel_id

View file

@ -30,6 +30,7 @@ import { useTPessoaRepresentanteIndexHook } from '../../../hooks/TPessoaRepresen
import TPessoaRepresentantePage from '../../TPessoaRepresentante/TPessoaRepresentanteIndex';
import { useTPessoaJuridicaFormHook } from '@/packages/administrativo/hooks/TPessoa/TPessoaJuridica/useTPessoaJuridicaFormHook';
import TPessoaJuridicaFormInterface from '@/packages/administrativo/interfaces/TPessoa/TPessoaJuridica/TPessoaJuridicaFormInterface';
import { ResetFormIfData } from '@/shared/actions/form/ResetFormIfData';
export default function TPessoaJuridicaForm({
isOpen,
@ -39,24 +40,13 @@ export default function TPessoaJuridicaForm({
buttonIsLoading,
}: TPessoaJuridicaFormInterface) {
const { tPessoaRepresentante, fetchTPessoaRepresentante } = useTPessoaRepresentanteIndexHook();
// Inicializa o react-hook-form com schema zod
const form = useTPessoaJuridicaFormHook({});
// Atualiza o formulário quando recebe dados para edição
useEffect(() => {
// Carregamento de dados sincronos
const loadData = async () => {
// Se existir dados, reseta o formulário com os dados informados
if (data) form.reset(data);
// Aguarda a busca terminar
await fetchTPessoaRepresentante();
};
// Dispara a função
loadData();
// Se existir dados, reseta o formulário com os mesmos
ResetFormIfData(form, data);
}, [data, form]);
return (
@ -170,7 +160,6 @@ export default function TPessoaJuridicaForm({
)}
/>
</div>
{/* Observação */}
<div className="col-span-12 sm:col-span-12 md:col-span-12">
<FormField
@ -180,11 +169,7 @@ export default function TPessoaJuridicaForm({
<FormItem className="w-full">
<FormLabel>Observação</FormLabel>
<FormControl>
<Textarea
{...field}
placeholder="Digite alguma observação"
className="w-full"
/>
<Input {...field} className="w-full" />
</FormControl>
<FormMessage />
</FormItem>
@ -344,7 +329,7 @@ export default function TPessoaJuridicaForm({
</TabsContent>
{/* Documentos */}
<TabsContent value="documentos" className="space-y-4">
<TPessoaRepresentantePage />
<TPessoaRepresentantePage pessoa_id={data?.pessoa_id} />
</TabsContent>
</div>
</Tabs>

View file

@ -138,10 +138,8 @@ export default function TPessoaJuridicaIndex() {
handleOpenForm(null);
}}
/>
{/* Tabela de Registros */}
<TPessoaJuridicaTable data={tPessoa} onDelete={handleConfirmDelete} onEdit={handleOpenForm} />
{/* Modal de confirmação */}
<ConfirmDialog
isOpen={isConfirmOpen}
@ -153,7 +151,6 @@ export default function TPessoaJuridicaIndex() {
onConfirm={handleDelete}
onCancel={handleCancel}
/>
{/* Formulário de criação/edição */}
<TPessoaJuridicaForm
isOpen={isFormOpen}

View file

@ -3,11 +3,13 @@ import TPessoaRepresentanteInterface from "../../interfaces/TPessoaRepresentante
import { Button } from "@/components/ui/button";
import { ArrowUpDownIcon, EllipsisIcon, Trash2Icon } from "lucide-react";
import { DropdownMenu, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu";
import TPessoaRepresentanteJoinedInterface from "../../interfaces/TPessoaRepresentante/TPessoaRepresentanteJoinedInterface";
import GetNameInitials from "@/shared/actions/text/GetNameInitials";
import empty from "@/shared/actions/validations/empty";
export default function TPessoaRepresentanteColumns(
onEdit: (item: TPessoaRepresentanteInterface, isEditingFormStatus: boolean) => void,
onDelete: (item: TPessoaRepresentanteInterface, isEditingFormStatus: boolean) => void,
): ColumnDef<TPessoaRepresentanteInterface>[] {
): ColumnDef<TPessoaRepresentanteJoinedInterface>[] {
return [
// ID
{
@ -23,6 +25,41 @@ export default function TPessoaRepresentanteColumns(
cell: ({ row }) => Number(row.getValue('pessoa_representante_id')),
enableSorting: false,
},
// Nome / Email / Foto
{
id: 'tpf_nome',
accessorFn: (row) => row,
header: ({ column }) => (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
Nome / Email <ArrowUpDownIcon className="ml-1 h-4 w-4 cursor-pointer" />
</Button>
),
cell: ({ row }) => {
const pessoa = row.original;
return (
<div className="flex items-center gap-3">
{/* Foto ou Iniciais */}
<div className="flex h-10 w-10 items-center justify-center overflow-hidden rounded-full bg-gray-200">
<span className="text-sm font-medium text-gray-700">
{GetNameInitials(pessoa.tpf_nome)}
</span>
</div>
{/* Nome e Email */}
<div>
<div className="font-semibold text-gray-900 capitalize">{pessoa.tpf_nome || '-'}</div>
<div className="text-sm text-gray-500">
{empty(pessoa.tpf_email) ? 'Email não informado' : pessoa.tpf_email}
</div>
</div>
</div>
);
},
sortingFn: (a, b) =>
(a.original.tpf_nome?.toLowerCase() || '').localeCompare(b.original.tpf_nome?.toLowerCase() || ''),
},
// Ações
{

View file

@ -1,9 +1,6 @@
'use client';
import z from 'zod';
import React, { useEffect } from 'react';
import { useForm, Controller } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import React, { useEffect, useState } from 'react';
import { Button } from '@/components/ui/button';
import {
@ -15,239 +12,16 @@ import {
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { Form } from '@/components/ui/form';
import { TPessoaSchema } from '../../schemas/TPessoa/TPessoaSchema';
import LoadingButton from '@/shared/components/loadingButton/LoadingButton';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import {
ArrowUpDownIcon,
CheckIcon,
ChevronsUpDownIcon,
EllipsisIcon,
HouseIcon,
IdCardIcon,
PencilIcon,
Trash2Icon,
UserIcon,
} from 'lucide-react';
import { Sexo } from '@/shared/enums/SexoEnum';
import { useGTBEstadoCivilReadHook } from '../../../../app/(protected)/(cadastros)/cadastros/_hooks/g_tb_estadocivil/useGTBEstadoCivilReadHook';
import GetCapitalize from '@/shared/actions/text/GetCapitalize';
import { useGTBRegimeComunhaoReadHook } from '../../../../app/(protected)/(cadastros)/cadastros/_hooks/g_tb_regimecomunhao/useGTBRegimeComunhaoReadHook';
import { useGTBProfissaoReadHook } from '../../../../app/(protected)/(cadastros)/cadastros/_hooks/g_tb_profissao/useGTBProfissaoReadHook';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
CommandList,
} from '@/components/ui/command';
import { cn } from '@/lib/utils';
import TPessoaTable from '../TPessoa/TPessoaFisica/TPessoaFisicaTable';
import TPessoaInterface from '../../interfaces/TPessoa/TPessoaInterface';
import { ColumnDef } from '@tanstack/react-table';
import GetNameInitials from '@/shared/actions/text/GetNameInitials';
import empty from '@/shared/actions/validations/empty';
import { FormatCPF } from '@/shared/actions/CPF/FormatCPF';
import { FormatPhone } from '@/shared/actions/phone/FormatPhone';
import {
DropdownMenu,
DropdownMenuGroup,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { DropdownMenuContent } from '@radix-ui/react-dropdown-menu';
import { DataTable } from '@/shared/components/dataTable/DataTable';
import { Checkbox } from '@/components/ui/checkbox';
import { useTPessoaFisicaIndexHook } from '../../hooks/TPessoa/TPessoaFisica/useTPessoaFisicaIndexHook';
import { useTPessoaRepresentanteFormHook } from '../../hooks/TPessoaRepresentante/useTPessoaRepresentanteFormHook';
import { ResetFormIfData } from '@/shared/actions/form/ResetFormIfData';
type FormValues = z.infer<typeof TPessoaSchema>;
interface TPessoaRepresentanteFormProps {
isOpen: boolean;
data: FormValues | null;
onClose: (item: null, isFormStatus: boolean) => void;
onSave: (data: FormValues) => void;
buttonIsLoading: boolean;
}
/**
* Função para criar a definição das colunas da tabela
*/
function createPessoaColumns(
onEdit: (item: TPessoaInterface, isEditingFormStatus: boolean) => void,
onDelete: (item: TPessoaInterface, isEditingFormStatus: boolean) => void,
): ColumnDef<TPessoaInterface>[] {
return [
{
id: 'select',
header: ({ table }) => (
<Checkbox
checked={
table.getIsAllPageRowsSelected() ||
(table.getIsSomePageRowsSelected() && 'indeterminate')
}
onCheckedChange={(value) => table.toggleAllPageRowsSelected(!!value)}
aria-label="Select all"
/>
),
cell: ({ row }) => (
<Checkbox
checked={row.getIsSelected()}
onCheckedChange={(value) => row.toggleSelected(!!value)}
aria-label="Select row"
/>
),
enableSorting: false,
enableHiding: false,
},
// ID
{
accessorKey: 'pessoa_representante_id',
header: ({ column }) => (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
# <ArrowUpDownIcon className="ml-1 h-4 w-4" />
</Button>
),
cell: ({ row }) => Number(row.getValue('pessoa_representante_id')),
enableSorting: false,
},
// Nome / Email / Foto
{
id: 'nome_completo',
accessorFn: (row) => row,
header: ({ column }) => (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
Nome / Email <ArrowUpDownIcon className="ml-1 h-4 w-4 cursor-pointer" />
</Button>
),
cell: ({ row }) => {
const pessoa = row.original;
return (
<div className="flex items-center gap-3">
{/* Foto ou Iniciais */}
<div className="flex h-10 w-10 items-center justify-center overflow-hidden rounded-full bg-gray-200">
{pessoa.foto ? (
<img
src={pessoa.foto}
alt={pessoa.nome || 'Avatar'}
className="h-full w-full object-cover"
/>
) : (
<span className="text-sm font-medium text-gray-700">
{GetNameInitials(pessoa.nome)}
</span>
)}
</div>
{/* Nome e Email */}
<div>
<div className="font-semibold text-gray-900 capitalize">{pessoa.nome || '-'}</div>
<div className="text-sm text-gray-500">
{empty(pessoa.email) ? 'Email não informado' : pessoa.email}
</div>
</div>
</div>
);
},
sortingFn: (a, b) =>
(a.original.nome?.toLowerCase() || '').localeCompare(b.original.nome?.toLowerCase() || ''),
},
// CPF
{
accessorKey: 'cpf_cnpj',
header: ({ column }) => (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
CPF <ArrowUpDownIcon className="ml-1 h-4 w-4 cursor-pointer" />
</Button>
),
cell: ({ row }) => FormatCPF(row.getValue('cpf_cnpj')),
},
// Telefone
{
accessorKey: 'telefone',
header: ({ column }) => (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
Telefone <ArrowUpDownIcon className="ml-1 h-4 w-4 cursor-pointer" />
</Button>
),
cell: ({ row }) => FormatPhone(row.getValue('telefone')),
},
// Ações
{
id: 'actions',
header: 'Ações',
cell: ({ row }) => {
const pessoa = row.original;
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon" className="cursor-pointer">
<EllipsisIcon />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent side="left" align="start">
<DropdownMenuGroup>
<DropdownMenuItem className="cursor-pointer" onSelect={() => onEdit(pessoa, true)}>
<PencilIcon className="mr-2 h-4 w-4" />
Editar
</DropdownMenuItem>
<DropdownMenuSeparator />
<DropdownMenuItem
className="cursor-pointer text-red-600"
onSelect={() => onDelete(pessoa, true)}
>
<Trash2Icon className="mr-2 h-4 w-4" />
Remover
</DropdownMenuItem>
</DropdownMenuGroup>
</DropdownMenuContent>
</DropdownMenu>
);
},
enableSorting: false,
enableHiding: false,
},
];
}
import TPessoaRepresentanteFormInterface from '../../interfaces/TPessoaRepresentante/TPessoaRepresentnateFormInterface';
import TPessoaRepresentanteInterface from '../../interfaces/TPessoaRepresentante/TPessoaRepresentanteInterface';
import TPessoasRepresentanteFormColumns from './TPessoasRepresentanteFormColumns';
export default function TPessoaRepresentanteForm({
isOpen,
@ -255,31 +29,33 @@ export default function TPessoaRepresentanteForm({
onClose,
onSave,
buttonIsLoading,
}: TPessoaRepresentanteFormProps) {
}: TPessoaRepresentanteFormInterface) {
const { tPessoaFisica, fetchTPessoaFisica } = useTPessoaFisicaIndexHook();
const [selectedTPessoaRepresentante, setSelectedTPessoaRepresentante] = useState<TPessoaRepresentanteInterface | null>(null);
// Inicializa o react-hook-form com schema zod
const form = useTPessoaRepresentanteFormHook();
// Atualiza o formulário quando recebe dados para edição
useEffect(() => {
const loadData = async () => {
// Se existir dados, reseta o formulário com os dados informados
ResetFormIfData(form, data)
// Aguarda a busca terminar
ResetFormIfData(form, data);
await fetchTPessoaFisica();
};
// Dispara a função
loadData();
}, [data, form]);
const columns = createPessoaColumns(
() => { },
() => { },
);
useEffect(() => {
console.log('Selected TPessoaRepresentante:', selectedTPessoaRepresentante);
if (selectedTPessoaRepresentante) {
form.setValue('pessoa_representante_id', selectedTPessoaRepresentante.pessoa_id);
} else {
form.setValue('pessoa_representante_id', 0);
}
}, [selectedTPessoaRepresentante, form]);
const columns = TPessoasRepresentanteFormColumns(setSelectedTPessoaRepresentante);
return (
<Dialog
@ -290,12 +66,8 @@ export default function TPessoaRepresentanteForm({
>
<DialogContent className="w-full max-w-full p-6 sm:max-w-4xl md:max-w-4xl lg:max-w-4xl max-h-[70vh] overflow-auto">
<DialogHeader>
<DialogTitle>
Representante
</DialogTitle>
<DialogDescription>
Busque o representante desejado
</DialogDescription>
<DialogTitle>Representante</DialogTitle>
<DialogDescription>Busque o representante desejado</DialogDescription>
</DialogHeader>
<Form {...form}>
<form onSubmit={form.handleSubmit(onSave)} className="space-y-6">
@ -322,8 +94,8 @@ export default function TPessoaRepresentanteForm({
<LoadingButton
text="Salvar"
textLoading="Aguarde..."
type="submit"
loading={buttonIsLoading}
type="submit"
/>
</DialogFooter>
{/* Campo oculto */}

View file

@ -3,7 +3,6 @@
import React, { useEffect, useState, useCallback } from 'react';
import Loading from '@/shared/components/loading/loading';
import TPessoaForm from '../TPessoa/TPessoaFisica/TPessoaFisicaForm';
import { useTPessoaRepresentanteIndexHook } from '../../hooks/TPessoaRepresentante/useTPessoaRepresentanteIndexHook';
import { useTPessoaRepresentanteSaveHook } from '../../hooks/TPessoaRepresentante/useTPessoaRepresentanteSaveHook';
@ -16,8 +15,14 @@ import TPessoaInterface from '../../interfaces/TPessoa/TPessoaInterface';
import TPessoaRepresentanteTable from './TPessoaRepresentanteTable';
import Header from '@/shared/components/structure/Header';
import TPessoaRepresentanteForm from './TPessoaRepresentanteForm';
import TPessoaRepresentantePageInterface from '../../interfaces/TPessoaRepresentante/TPessoaRepresentantePageInterface';
export default function TPessoaRepresentantePage({ pessoa_id }: TPessoaRepresentantePageInterface) {
const TPessoaRepresentantePage: TPessoaRepresentantePageInterface = {
pessoa_id: pessoa_id
}
export default function TPessoaRepresentantePage() {
// Controle de estado do botão
const [buttonIsLoading, setButtonIsLoading] = useState(false);
@ -74,7 +79,7 @@ export default function TPessoaRepresentantePage() {
setButtonIsLoading(false);
// Atualiza a lista de dados
fetchTPessoaRepresentante();
fetchTPessoaRepresentante(TPessoaRepresentantePage);
},
[saveTPessoaRepresentante, fetchTPessoaRepresentante, handleCloseForm],
);
@ -104,7 +109,7 @@ export default function TPessoaRepresentantePage() {
await removeTPessaoRepresentante(itemToDelete);
// Atualiza a lista
await fetchTPessoaRepresentante();
await fetchTPessoaRepresentante(TPessoaRepresentantePage);
// Limpa o item selecionado
setItemToDelete(null);
@ -117,13 +122,13 @@ export default function TPessoaRepresentantePage() {
* Busca inicial dos dados
*/
useEffect(() => {
fetchTPessoaRepresentante();
fetchTPessoaRepresentante(TPessoaRepresentantePage);
}, []);
/**
* Tela de loading enquanto carrega os dados
*/
if (tPessoaRepresentante.length == 0) {
if (tPessoaRepresentante?.length == 0) {
return <Loading type={2} />;
}

View file

@ -0,0 +1,121 @@
import { ColumnDef } from "@tanstack/react-table";
import { Checkbox } from "@/components/ui/checkbox";
import { Button } from "@/components/ui/button";
import { ArrowUpDownIcon } from "lucide-react";
import empty from "@/shared/actions/validations/empty";
import { FormatCPF } from "@/shared/actions/CPF/FormatCPF";
import { FormatPhone } from "@/shared/actions/phone/FormatPhone";
import TPessoaFisicaInterface from "../../interfaces/TPessoa/TPessoaFisica/TPessoaFisicaInterface";
import GetNameInitials from "@/shared/actions/text/GetNameInitials";
import TPessoaRepresentanteInterface from "../../interfaces/TPessoaRepresentante/TPessoaRepresentanteInterface";
/**
* Função para criar a definição das colunas da tabela
*/
export default function TPessoasRepresentanteFormColumns(
setSelectedTPessoaRepresentante: React.Dispatch<React.SetStateAction<TPessoaRepresentanteInterface | null>>,
): ColumnDef<TPessoaFisicaInterface>[] {
return [
{
id: 'select',
header: '',
cell: ({ row, table }) => (
<Checkbox
checked={row.getIsSelected()}
onCheckedChange={(value) => {
// Limpa todas as seleções antes de selecionar uma nova
table.resetRowSelection();
row.toggleSelected(!!value);
setSelectedTPessoaRepresentante(value ? row.original : null);
}}
aria-label="Select row"
/>
),
enableSorting: false,
enableHiding: false,
},
// ID
{
accessorKey: 'pessoa_id',
header: ({ column }) => (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
# <ArrowUpDownIcon className="ml-1 h-4 w-4" />
</Button>
),
cell: ({ row }) => Number(row.getValue('pessoa_id')),
enableSorting: false,
},
// Nome / Email / Foto
{
id: 'nome_completo',
accessorFn: (row) => row,
header: ({ column }) => (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
Nome / Email <ArrowUpDownIcon className="ml-1 h-4 w-4 cursor-pointer" />
</Button>
),
cell: ({ row }) => {
const pessoa = row.original;
return (
<div className="flex items-center gap-3">
{/* Foto ou Iniciais */}
<div className="flex h-10 w-10 items-center justify-center overflow-hidden rounded-full bg-gray-200">
{pessoa.foto ? (
<img
src={pessoa.foto}
alt={pessoa.nome || 'Avatar'}
className="h-full w-full object-cover"
/>
) : (
<span className="text-sm font-medium text-gray-700">
{GetNameInitials(pessoa.nome)}
</span>
)}
</div>
{/* Nome e Email */}
<div>
<div className="font-semibold text-gray-900 capitalize">{pessoa.nome || '-'}</div>
<div className="text-sm text-gray-500">
{empty(pessoa.email) ? 'Email não informado' : pessoa.email}
</div>
</div>
</div>
);
},
sortingFn: (a, b) =>
(a.original.nome?.toLowerCase() || '').localeCompare(b.original.nome?.toLowerCase() || ''),
},
// CPF
{
accessorKey: 'cpf_cnpj',
header: ({ column }) => (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
CPF <ArrowUpDownIcon className="ml-1 h-4 w-4 cursor-pointer" />
</Button>
),
cell: ({ row }) => FormatCPF(row.getValue('cpf_cnpj')),
},
// Telefone
{
accessorKey: 'telefone',
header: ({ column }) => (
<Button
variant="ghost"
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
>
Telefone <ArrowUpDownIcon className="ml-1 h-4 w-4 cursor-pointer" />
</Button>
),
cell: ({ row }) => FormatPhone(row.getValue('telefone')),
},
];
}

View file

@ -4,7 +4,7 @@ import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
async function executeTPessoaFisicaIndexData() {
const api = new API();
return api.send({
return await api.send({
method: Methods.GET,
endpoint: `administrativo/t_pessoa/tipo/F`,
});

View file

@ -1,10 +1,13 @@
import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler';
import TPessoaJuridicaInterface from '@/packages/administrativo/interfaces/TPessoa/TPessoaJuridica/TPessoaJuridicaInterface';
import API from '@/shared/services/api/Api';
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
async function executeTPessoaJuridicaRemoveData(data: TPessoaJuridicaInterface) {
return Promise.resolve({
status: 200,
message: 'Dados removidos'
const api = new API();
return api.send({
method: Methods.DELETE,
endpoint: `administrativo/t_pessoa/${data.pessoa_id}`,
});
}

View file

@ -1,10 +1,20 @@
import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler';
import TPessoaJuridicaInterface from '@/packages/administrativo/interfaces/TPessoa/TPessoaJuridica/TPessoaJuridicaInterface';
import API from '@/shared/services/api/Api';
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
async function executeTPessoaJuridicaSaveData(data: TPessoaJuridicaInterface) {
return Promise.resolve({
status: 200,
message: 'Dados removidos'
// Verifica se existe ID da cidade para decidir se é atualização (PUT) ou criação (POST)
const isUpdate = Boolean(data.pessoa_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_pessoa/${data.pessoa_id || ''}`, // endpoint dinâmico
body: data, // payload enviado para a API
});
}

View file

@ -1,11 +1,14 @@
import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler';
import TPessoaRepresentanteInterface from '../../interfaces/TPessoaRepresentante/TPessoaRepresentanteInterface';
import API from '@/shared/services/api/Api';
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
async function executeTPessoaRepresentanteIndexData(data: TPessoaRepresentanteInterface) {
return Promise.resolve({
status: 200,
message: 'Dados removidos'
async function executeTPessoaRepresentanteRemoveData(data: TPessoaRepresentanteInterface) {
const api = new API();
return api.send({
method: Methods.DELETE,
endpoint: `administrativo/t_pessoa_representante/${data.pessoa_representante_id}`,
});
}
export const TPessoaRepresentanteRemoveData = withClientErrorHandler(executeTPessoaRepresentanteIndexData);
export const TPessoaRepresentanteRemoveData = withClientErrorHandler(executeTPessoaRepresentanteRemoveData);

View file

@ -1,10 +1,18 @@
import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler';
import TPessoaRepresentanteInterface from '../../interfaces/TPessoaRepresentante/TPessoaRepresentanteInterface';
import API from '@/shared/services/api/Api';
import { Methods } from '@/shared/services/api/enums/ApiMethodEnum';
async function executeTPessoaRepresentanteSaveData(data: TPessoaRepresentanteInterface) {
return Promise.resolve({
status: 200,
message: 'Dados removidos'
// 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: Methods.POST,
endpoint: `administrativo/t_pessoa_representante/pessoa/${data.pessoa_id}`, // endpoint dinâmico
body: data, // payload enviado para a API
});
}

View file

@ -14,7 +14,7 @@ export const useTPessoaFisicaSaveHook = () => {
const [isOpen, setIsOpen] = useState(false);
const saveTPessoaFisica = async (data: TPessoaFisicaInterface) => {
const response = await TPessoaFisicaSaveService(data);
// Armazena os dados da repsota

View file

@ -8,8 +8,9 @@ export const useTPessoaRepresentanteIndexHook = () => {
const [tPessoaRepresentante, setTPessoaRepresentante] = useState<TPessoaRepresentanteInterface[]>([]);
const fetchTPessoaRepresentante = async () => {
const response = await TPessoaRepresentanteIndexService();
const fetchTPessoaRepresentante = async (data: TPessoaRepresentanteInterface) => {
const response = await TPessoaRepresentanteIndexService(data);
setTPessoaRepresentante(response.data);

View file

@ -0,0 +1,8 @@
export default interface TPessoaRepresentanteJoinedInterface {
pessoa_id?: number,
representante_id?: number,
tpj_nome?: string,
tpf_nome?: string,
tpf_email?: string,
tpf_telefon?: string,
}

View file

@ -0,0 +1,3 @@
export default interface TPessoaRepresentantePageInterface {
pessoa_id?: number
}

View file

@ -0,0 +1,9 @@
import { TPessoaRepresentanteFormValues } from "../../schemas/TPessoaRepresentante/TPessoaRepresentanteSchema";
export default interface TPessoaRepresentanteFormInterface {
isOpen: boolean;
data: TPessoaRepresentanteFormValues | null;
onClose: (item: null, isFormStatus: boolean) => void;
onSave: (data: TPessoaRepresentanteFormValues) => void;
buttonIsLoading: boolean;
}

View file

@ -2,7 +2,7 @@ import { TPessoaSchema } from "./TPessoaSchema";
import z from "zod";
export const TPessoaJuridicaSchema = TPessoaSchema.extend({
razao_social: z.string().min(1, "Razão social é obrigatória"),
razao_social: z.string().optional(),
nome_fantasia: z.string().optional(),
inscricao_estadual: z.string().optional(),
inscricao_municipal: z.string().optional(),

View file

@ -3,7 +3,10 @@ import { TPessoaFisicaSaveData } from '@/packages/administrativo/data/TPessoa/TP
import TPessoaFisicaInterface from '@/packages/administrativo/interfaces/TPessoa/TPessoaFisica/TPessoaFisicaInterface';
async function executeTPessoaFisicaSaveService(data: TPessoaFisicaInterface) {
const response = TPessoaFisicaSaveData(data);
console.log('TPessoaFisicaSaveService', data);
const response = await TPessoaFisicaSaveData(data);
return response;
}

View file

@ -2,7 +2,7 @@ import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/
import { TPessoaJuridicaIndexData } from '@/packages/administrativo/data/TPessoa/TPessoaJuridica/TPessoaJuridicaIndexData';
async function executeTPessoaJuridicaIndexService() {
const response = TPessoaJuridicaIndexData();
const response = await TPessoaJuridicaIndexData();
return response;
}

View file

@ -1,9 +1,10 @@
import { withClientErrorHandler } from '@/shared/actions/withClientErrorHandler/withClientErrorHandler';
import { TPessoaRepresentanteIndexData } from '../../data/TPessoaRepresentante/TPessoaRepresentanteIndexData';
import TPessoaRepresentanteInterface from '../../interfaces/TPessoaRepresentante/TPessoaRepresentanteInterface';
async function executeTPessoaRepresentanteIndexService() {
const response = TPessoaRepresentanteIndexData();
async function executeTPessoaRepresentanteIndexService(data: TPessoaRepresentanteInterface) {
console.log('TPessoaRepresentanteIndexService', data);
const response = await TPessoaRepresentanteIndexData(data);
return response;
}

View file

@ -3,7 +3,7 @@ import { TPessoaRepresentanteSaveData } from '../../data/TPessoaRepresentante/TP
import TPessoaRepresentanteInterface from '../../interfaces/TPessoaRepresentante/TPessoaRepresentanteInterface';
async function executeTPessoaRepresentanteSaveService(data: TPessoaRepresentanteInterface) {
const response = TPessoaRepresentanteSaveData(data);
const response = await TPessoaRepresentanteSaveData(data);
return response;
}

View file

@ -4,16 +4,38 @@ import { forwardRef } from 'react';
import { Button } from '@/components/ui/button';
import { Loader2 } from 'lucide-react';
import clsx from 'clsx';
import LoadingButtonProps from './LoadingButtonInterface';
import type LoadingButtonProps from './LoadingButtonInterface';
const LoadingButton = forwardRef<HTMLButtonElement, LoadingButtonProps>(
({ text, textLoading, loading = false, className, disabled, ...props }, ref) => {
(
{
text,
textLoading,
loading = false,
className,
disabled,
type = 'button',
onClick,
...props
},
ref,
) => {
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
if (loading) {
e.preventDefault();
return;
}
if (onClick) onClick(e);
};
return (
<Button
ref={ref}
type={type}
disabled={loading || disabled}
aria-busy={loading}
aria-live="polite"
onClick={handleClick}
className={clsx('cursor-pointer', className)}
{...props}
>