[MVPTN-99] feat(CRUD): Implmenta os endpoints reais para manipular pessoa fisica e juridica
This commit is contained in:
parent
36dba45995
commit
3b38337358
28 changed files with 318 additions and 12587 deletions
|
|
@ -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/`,
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 */}
|
||||
|
|
|
|||
|
|
@ -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} />;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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')),
|
||||
},
|
||||
];
|
||||
}
|
||||
|
|
@ -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`,
|
||||
});
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -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}`,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
export default interface TPessoaRepresentantePageInterface {
|
||||
pessoa_id?: number
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue