[MVPTN-99] feat(CRUD): Inicio do formulário de cadastro de pessoa juridica com representantes
This commit is contained in:
parent
fb35c19a84
commit
b91ce54475
57 changed files with 16748 additions and 7984 deletions
|
|
@ -5,19 +5,24 @@ export default [
|
|||
next,
|
||||
{
|
||||
plugins: {
|
||||
import: importPlugin
|
||||
import: importPlugin,
|
||||
},
|
||||
rules: {
|
||||
"import/order": [
|
||||
"error",
|
||||
{
|
||||
groups: ["builtin", "external", "internal", ["parent", "sibling", "index"]],
|
||||
groups: [
|
||||
"builtin",
|
||||
"external",
|
||||
"internal",
|
||||
["parent", "sibling", "index"],
|
||||
],
|
||||
"newlines-between": "always",
|
||||
alphabetize: { order: "asc", caseInsensitive: true }
|
||||
}
|
||||
alphabetize: { order: "asc", caseInsensitive: true },
|
||||
},
|
||||
],
|
||||
"semi": ["error", "always"],
|
||||
"quotes": ["error", "double"]
|
||||
}
|
||||
}
|
||||
semi: ["error", "always"],
|
||||
quotes: ["error", "double"],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
|
|||
940
package-lock.json
generated
940
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -55,8 +55,16 @@
|
|||
"@types/node": "^20",
|
||||
"@types/react": "^19",
|
||||
"@types/react-dom": "^19",
|
||||
"@typescript-eslint/eslint-plugin": "^8.45.0",
|
||||
"@typescript-eslint/parser": "^8.45.0",
|
||||
"eslint": "^8.57.1",
|
||||
"eslint-config-next": "^15.5.4",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-prettier": "^5.5.4",
|
||||
"eslint-plugin-unused-imports": "^4.2.0",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier-plugin-tailwindcss": "^0.6.14",
|
||||
"tailwindcss": "^4",
|
||||
"tw-animate-css": "^1.3.7",
|
||||
"typescript": "^5"
|
||||
|
|
|
|||
23
src/actions/CNPJ/FormatCNPJ.ts
Normal file
23
src/actions/CNPJ/FormatCNPJ.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* Formata um número de CNPJ no padrão 99.999.999/9999-99
|
||||
*
|
||||
* @param value - CNPJ em string ou number
|
||||
* @returns CNPJ formatado ou string parcial se incompleto
|
||||
*/
|
||||
export function FormatCNPJ(value: string | number): string {
|
||||
if (!value) return "";
|
||||
|
||||
// Converte para string e remove tudo que não seja número
|
||||
const digits = String(value).replace(/\D/g, "");
|
||||
|
||||
// Garante que tenha no máximo 14 dígitos
|
||||
const cleanValue = digits.slice(0, 14);
|
||||
|
||||
// Retorna parcialmente formatado se ainda não tiver 14 dígitos
|
||||
if (cleanValue.length < 14) return cleanValue;
|
||||
|
||||
return cleanValue.replace(
|
||||
/(\d{2})(\d{3})(\d{3})(\d{4})(\d{2})/,
|
||||
"$1.$2.$3/$4-$5",
|
||||
);
|
||||
}
|
||||
|
|
@ -1,23 +1,20 @@
|
|||
/**
|
||||
* Formata um número de CPF no padrão 999.999.999-99
|
||||
*
|
||||
*
|
||||
* @param value - CPF em string ou number
|
||||
* @returns CPF formatado ou string vazia se inválido
|
||||
*/
|
||||
export function FormatCPF(value: string | number): string {
|
||||
if (!value) return "";
|
||||
if (!value) return "";
|
||||
|
||||
// Converte para string e remove tudo que não seja número
|
||||
const digits = String(value).replace(/\D/g, "");
|
||||
// Converte para string e remove tudo que não seja número
|
||||
const digits = String(value).replace(/\D/g, "");
|
||||
|
||||
// Garante que tenha no máximo 11 dígitos
|
||||
const cleanValue = digits.slice(0, 11);
|
||||
// Garante que tenha no máximo 11 dígitos
|
||||
const cleanValue = digits.slice(0, 11);
|
||||
|
||||
// Retorna formatado ou vazio se não tiver tamanho suficiente
|
||||
if (cleanValue.length !== 11) return cleanValue;
|
||||
// Retorna formatado ou vazio se não tiver tamanho suficiente
|
||||
if (cleanValue.length !== 11) return cleanValue;
|
||||
|
||||
return cleanValue.replace(
|
||||
/(\d{3})(\d{3})(\d{3})(\d{2})/,
|
||||
"$1.$2.$3-$4"
|
||||
);
|
||||
return cleanValue.replace(/(\d{3})(\d{3})(\d{3})(\d{2})/, "$1.$2.$3-$4");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,53 +1,55 @@
|
|||
/**
|
||||
* Formata uma data e hora brasileira (DD/MM/YYYY HH:mm)
|
||||
*
|
||||
*
|
||||
* Suporta:
|
||||
* - Entrada como string, Date ou number (timestamp)
|
||||
* - Dados incompletos (apenas dia/mês, sem hora, etc.)
|
||||
* - Retorna "-" se vazio ou inválido
|
||||
*
|
||||
*
|
||||
* @param value - Data ou hora em string, Date ou timestamp
|
||||
* @returns Data formatada no padrão DD/MM/YYYY HH:mm ou parcial
|
||||
*/
|
||||
export function FormatDateTime(value: string | Date | number | null | undefined): string {
|
||||
if (!value) return "-";
|
||||
export function FormatDateTime(
|
||||
value: string | Date | number | null | undefined,
|
||||
): string {
|
||||
if (!value) return "-";
|
||||
|
||||
let date: Date;
|
||||
let date: Date;
|
||||
|
||||
// Converte entrada para Date
|
||||
if (value instanceof Date) {
|
||||
date = value;
|
||||
} else if (typeof value === "number") {
|
||||
date = new Date(value);
|
||||
} else if (typeof value === "string") {
|
||||
// Remove caracteres extras e tenta criar Date
|
||||
const cleanValue = value.trim().replace(/[^0-9]/g, "");
|
||||
// Converte entrada para Date
|
||||
if (value instanceof Date) {
|
||||
date = value;
|
||||
} else if (typeof value === "number") {
|
||||
date = new Date(value);
|
||||
} else if (typeof value === "string") {
|
||||
// Remove caracteres extras e tenta criar Date
|
||||
const cleanValue = value.trim().replace(/[^0-9]/g, "");
|
||||
|
||||
if (cleanValue.length === 8) {
|
||||
// DDMMYYYY
|
||||
const day = parseInt(cleanValue.slice(0, 2), 10);
|
||||
const month = parseInt(cleanValue.slice(2, 4), 10) - 1;
|
||||
const year = parseInt(cleanValue.slice(4, 8), 10);
|
||||
date = new Date(year, month, day);
|
||||
} else {
|
||||
// Tenta parse padrão
|
||||
const parsed = new Date(value);
|
||||
if (isNaN(parsed.getTime())) return "-";
|
||||
date = parsed;
|
||||
}
|
||||
if (cleanValue.length === 8) {
|
||||
// DDMMYYYY
|
||||
const day = parseInt(cleanValue.slice(0, 2), 10);
|
||||
const month = parseInt(cleanValue.slice(2, 4), 10) - 1;
|
||||
const year = parseInt(cleanValue.slice(4, 8), 10);
|
||||
date = new Date(year, month, day);
|
||||
} else {
|
||||
return "-";
|
||||
// Tenta parse padrão
|
||||
const parsed = new Date(value);
|
||||
if (isNaN(parsed.getTime())) return "-";
|
||||
date = parsed;
|
||||
}
|
||||
} else {
|
||||
return "-";
|
||||
}
|
||||
|
||||
// Extrai partes da data
|
||||
const day = date.getDate().toString().padStart(2, "0");
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, "0");
|
||||
const year = date.getFullYear();
|
||||
const hours = date.getHours().toString().padStart(2, "0");
|
||||
const minutes = date.getMinutes().toString().padStart(2, "0");
|
||||
// Extrai partes da data
|
||||
const day = date.getDate().toString().padStart(2, "0");
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, "0");
|
||||
const year = date.getFullYear();
|
||||
const hours = date.getHours().toString().padStart(2, "0");
|
||||
const minutes = date.getMinutes().toString().padStart(2, "0");
|
||||
|
||||
// Monta string parcialmente, dependendo da hora estar disponível
|
||||
const hasTime = !(hours === "00" && minutes === "00");
|
||||
// Monta string parcialmente, dependendo da hora estar disponível
|
||||
const hasTime = !(hours === "00" && minutes === "00");
|
||||
|
||||
return `${day}/${month}/${year}${hasTime ? ` ${hours}:${minutes}` : ""}`;
|
||||
return `${day}/${month}/${year}${hasTime ? ` ${hours}:${minutes}` : ""}`;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,47 +1,51 @@
|
|||
/**
|
||||
* Formata um número de telefone brasileiro.
|
||||
*
|
||||
*
|
||||
* Suporta:
|
||||
* - Com ou sem DDD
|
||||
* - Números incompletos
|
||||
* - Telefones com 8 ou 9 dígitos
|
||||
*
|
||||
*
|
||||
* @param value - Número de telefone em string ou number
|
||||
* @returns Telefone formatado ou "-" se vazio
|
||||
*/
|
||||
export function FormatPhone(value: string | number): string {
|
||||
if (!value) return "-";
|
||||
if (!value) return "-";
|
||||
|
||||
// Converte para string e remove tudo que não for número
|
||||
const digits = String(value).replace(/\D/g, "");
|
||||
// Converte para string e remove tudo que não for número
|
||||
const digits = String(value).replace(/\D/g, "");
|
||||
|
||||
// Se não tiver nada após limpar, retorna "-"
|
||||
if (digits.length === 0) return "-";
|
||||
// Se não tiver nada após limpar, retorna "-"
|
||||
if (digits.length === 0) return "-";
|
||||
|
||||
// Garante no máximo 11 dígitos
|
||||
const cleanValue = digits.slice(0, 11);
|
||||
// Garante no máximo 11 dígitos
|
||||
const cleanValue = digits.slice(0, 11);
|
||||
|
||||
// -------------------------------
|
||||
// SEM DDD
|
||||
// -------------------------------
|
||||
if (cleanValue.length <= 8) {
|
||||
// Até 8 dígitos → formato parcial
|
||||
return cleanValue.replace(/(\d{4})(\d{0,4})/, "$1-$2").replace(/-$/, "");
|
||||
}
|
||||
// -------------------------------
|
||||
// SEM DDD
|
||||
// -------------------------------
|
||||
if (cleanValue.length <= 8) {
|
||||
// Até 8 dígitos → formato parcial
|
||||
return cleanValue.replace(/(\d{4})(\d{0,4})/, "$1-$2").replace(/-$/, "");
|
||||
}
|
||||
|
||||
// -------------------------------
|
||||
// COM DDD
|
||||
// -------------------------------
|
||||
if (cleanValue.length === 9 || cleanValue.length === 10) {
|
||||
// DDD + telefone de 8 dígitos
|
||||
return cleanValue.replace(/^(\d{2})(\d{4})(\d{0,4})$/, "($1) $2-$3").replace(/-$/, "");
|
||||
}
|
||||
// -------------------------------
|
||||
// COM DDD
|
||||
// -------------------------------
|
||||
if (cleanValue.length === 9 || cleanValue.length === 10) {
|
||||
// DDD + telefone de 8 dígitos
|
||||
return cleanValue
|
||||
.replace(/^(\d{2})(\d{4})(\d{0,4})$/, "($1) $2-$3")
|
||||
.replace(/-$/, "");
|
||||
}
|
||||
|
||||
if (cleanValue.length === 11) {
|
||||
// DDD + telefone de 9 dígitos
|
||||
return cleanValue.replace(/^(\d{2})(\d{5})(\d{0,4})$/, "($1) $2-$3").replace(/-$/, "");
|
||||
}
|
||||
if (cleanValue.length === 11) {
|
||||
// DDD + telefone de 9 dígitos
|
||||
return cleanValue
|
||||
.replace(/^(\d{2})(\d{5})(\d{0,4})$/, "($1) $2-$3")
|
||||
.replace(/-$/, "");
|
||||
}
|
||||
|
||||
// Caso genérico, se não cair em nenhuma regra
|
||||
return cleanValue;
|
||||
// Caso genérico, se não cair em nenhuma regra
|
||||
return cleanValue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/**
|
||||
* Capitaliza a primeira letra de uma string.
|
||||
*
|
||||
*
|
||||
* @param text - Texto que será capitalizado
|
||||
* @returns String com a primeira letra em maiúscula
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
export default function GetNameInitials(data?: string): string {
|
||||
if (!data) return "";
|
||||
if (!data) return "";
|
||||
|
||||
// Remove espaços extras no início e no fim e divide em palavras
|
||||
const palavras = data.trim().split(/\s+/);
|
||||
// Remove espaços extras no início e no fim e divide em palavras
|
||||
const palavras = data.trim().split(/\s+/);
|
||||
|
||||
if (palavras.length === 0) return "";
|
||||
if (palavras.length === 0) return "";
|
||||
|
||||
if (palavras.length === 1) {
|
||||
// Apenas uma palavra → retorna as duas primeiras letras
|
||||
return palavras[0].substring(0, 2).toUpperCase();
|
||||
}
|
||||
if (palavras.length === 1) {
|
||||
// Apenas uma palavra → retorna as duas primeiras letras
|
||||
return palavras[0].substring(0, 2).toUpperCase();
|
||||
}
|
||||
|
||||
// Duas ou mais palavras → retorna a primeira letra das duas primeiras palavras
|
||||
return (palavras[0].charAt(0) + palavras[1].charAt(0)).toUpperCase();
|
||||
}
|
||||
// Duas ou mais palavras → retorna a primeira letra das duas primeiras palavras
|
||||
return (palavras[0].charAt(0) + palavras[1].charAt(0)).toUpperCase();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
/**
|
||||
* Verifica se um valor é vazio, null ou undefined
|
||||
*
|
||||
*
|
||||
* @param data - Qualquer valor
|
||||
* @returns true se estiver vazio, null ou undefined
|
||||
*/
|
||||
export default function empty(data: unknown): boolean {
|
||||
return data == null || data === "" || data === false;
|
||||
return data == null || data === "" || data === false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useState, useCallback } from "react";
|
||||
|
||||
|
|
@ -17,153 +17,157 @@ import TPessoaInterface from "../../../_interfaces/TPessoaInterface";
|
|||
import Header from "@/app/_components/structure/Header";
|
||||
|
||||
export default function TPessoaFisica() {
|
||||
// Controle de estado do botão
|
||||
const [buttonIsLoading, setButtonIsLoading] = useState(false);
|
||||
|
||||
// Controle de estado do botão
|
||||
const [buttonIsLoading, setButtonIsLoading] = useState(false);
|
||||
// Hooks para leitura e salvamento
|
||||
const { tPessoa, fetchTPessoa } = useTPessoaIndexHook();
|
||||
const { saveTCensec } = useTPessoaSaveHook();
|
||||
const { deleteTCensec } = useTPessoaDeleteHook();
|
||||
|
||||
// Hooks para leitura e salvamento
|
||||
const { tPessoa, fetchTPessoa } = useTPessoaIndexHook();
|
||||
const { saveTCensec } = useTPessoaSaveHook();
|
||||
const { deleteTCensec } = useTPessoaDeleteHook();
|
||||
// Estados
|
||||
const [selectedAndamento, setSelectedAndamento] =
|
||||
useState<TPessoaInterface | null>(null);
|
||||
const [isFormOpen, setIsFormOpen] = useState(false);
|
||||
|
||||
// Estados
|
||||
const [selectedAndamento, setSelectedAndamento] = useState<TPessoaInterface | null>(null);
|
||||
const [isFormOpen, setIsFormOpen] = useState(false);
|
||||
// Estado para saber qual item será deletado
|
||||
const [itemToDelete, setItemToDelete] = useState<TPessoaInterface | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
// Estado para saber qual item será deletado
|
||||
const [itemToDelete, setItemToDelete] = useState<TPessoaInterface | null>(null);
|
||||
/**
|
||||
* Hook do modal de confirmação
|
||||
*/
|
||||
const {
|
||||
isOpen: isConfirmOpen,
|
||||
openDialog: openConfirmDialog,
|
||||
handleConfirm,
|
||||
handleCancel,
|
||||
} = useConfirmDialog();
|
||||
|
||||
/**
|
||||
* Hook do modal de confirmação
|
||||
*/
|
||||
const {
|
||||
isOpen: isConfirmOpen,
|
||||
openDialog: openConfirmDialog,
|
||||
handleConfirm,
|
||||
handleCancel,
|
||||
} = useConfirmDialog();
|
||||
/**
|
||||
* Abre o formulário no modo de edição ou criação
|
||||
*/
|
||||
const handleOpenForm = useCallback((data: TPessoaInterface | null) => {
|
||||
setSelectedAndamento(data);
|
||||
setIsFormOpen(true);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Abre o formulário no modo de edição ou criação
|
||||
*/
|
||||
const handleOpenForm = useCallback((data: TPessoaInterface | null) => {
|
||||
setSelectedAndamento(data);
|
||||
setIsFormOpen(true);
|
||||
}, []);
|
||||
/**
|
||||
* Fecha o formulário e limpa o andamento selecionado
|
||||
*/
|
||||
const handleCloseForm = useCallback(() => {
|
||||
setSelectedAndamento(null);
|
||||
setIsFormOpen(false);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Fecha o formulário e limpa o andamento selecionado
|
||||
*/
|
||||
const handleCloseForm = useCallback(() => {
|
||||
setSelectedAndamento(null);
|
||||
setIsFormOpen(false);
|
||||
}, []);
|
||||
/**
|
||||
* Salva os dados do formulário
|
||||
*/
|
||||
const handleSave = useCallback(
|
||||
async (formData: TPessoaInterface) => {
|
||||
// Coloca o botão em estado de loading
|
||||
setButtonIsLoading(true);
|
||||
|
||||
/**
|
||||
* Salva os dados do formulário
|
||||
*/
|
||||
const handleSave = useCallback(async (formData: TPessoaInterface) => {
|
||||
// Aguarda salvar o registro
|
||||
await saveTCensec(formData);
|
||||
|
||||
// Coloca o botão em estado de loading
|
||||
setButtonIsLoading(true);
|
||||
// Remove o botão em estado de loading
|
||||
setButtonIsLoading(false);
|
||||
|
||||
// Aguarda salvar o registro
|
||||
await saveTCensec(formData);
|
||||
// Atualiza a lista de dados
|
||||
fetchTPessoa();
|
||||
},
|
||||
[saveTCensec, fetchTPessoa, handleCloseForm],
|
||||
);
|
||||
|
||||
// Remove o botão em estado de loading
|
||||
setButtonIsLoading(false);
|
||||
/**
|
||||
* Quando o usuário clica em "remover" na tabela
|
||||
*/
|
||||
const handleConfirmDelete = useCallback(
|
||||
(item: TPessoaInterface) => {
|
||||
// Define o item atual para remoção
|
||||
setItemToDelete(item);
|
||||
|
||||
// Atualiza a lista de dados
|
||||
fetchTPessoa();
|
||||
// Abre o modal de confirmação
|
||||
openConfirmDialog();
|
||||
},
|
||||
[openConfirmDialog],
|
||||
);
|
||||
|
||||
}, [saveTCensec, fetchTPessoa, handleCloseForm]);
|
||||
/**
|
||||
* Executa a exclusão de fato quando o usuário confirma
|
||||
*/
|
||||
const handleDelete = useCallback(async () => {
|
||||
// Protege contra null
|
||||
if (!itemToDelete) return;
|
||||
|
||||
/**
|
||||
* Quando o usuário clica em "remover" na tabela
|
||||
*/
|
||||
const handleConfirmDelete = useCallback((item: TPessoaInterface) => {
|
||||
// Executa o Hook de remoção
|
||||
await deleteTCensec(itemToDelete);
|
||||
|
||||
// Define o item atual para remoção
|
||||
setItemToDelete(item);
|
||||
// Atualiza a lista
|
||||
await fetchTPessoa();
|
||||
|
||||
// Abre o modal de confirmação
|
||||
openConfirmDialog();
|
||||
// Limpa o item selecionado
|
||||
setItemToDelete(null);
|
||||
|
||||
}, [openConfirmDialog]);
|
||||
// Fecha o modal
|
||||
handleCancel();
|
||||
}, [itemToDelete, fetchTPessoa, handleCancel]);
|
||||
|
||||
/**
|
||||
* Executa a exclusão de fato quando o usuário confirma
|
||||
*/
|
||||
const handleDelete = useCallback(async () => {
|
||||
/**
|
||||
* Busca inicial dos dados
|
||||
*/
|
||||
useEffect(() => {
|
||||
fetchTPessoa();
|
||||
}, []);
|
||||
|
||||
// Protege contra null
|
||||
if (!itemToDelete) return;
|
||||
/**
|
||||
* Tela de loading enquanto carrega os dados
|
||||
*/
|
||||
if (tPessoa.length == 0) {
|
||||
return <Loading type={2} />;
|
||||
}
|
||||
|
||||
// Executa o Hook de remoção
|
||||
await deleteTCensec(itemToDelete);
|
||||
return (
|
||||
<div>
|
||||
{/* Cabeçalho */}
|
||||
<Header
|
||||
title={"Pessoas Físicas"}
|
||||
description={"Gerenciamento de pessoas físicas"}
|
||||
buttonText={"Nova Pessoa"}
|
||||
buttonAction={() => {
|
||||
handleOpenForm(null);
|
||||
}}
|
||||
/>
|
||||
|
||||
// Atualiza a lista
|
||||
await fetchTPessoa();
|
||||
{/* Tabela de Registros */}
|
||||
<TPessoaTable
|
||||
data={tPessoa}
|
||||
onDelete={handleConfirmDelete}
|
||||
onEdit={handleOpenForm}
|
||||
/>
|
||||
|
||||
// Limpa o item selecionado
|
||||
setItemToDelete(null);
|
||||
{/* Modal de confirmação */}
|
||||
<ConfirmDialog
|
||||
isOpen={isConfirmOpen}
|
||||
title="Confirmar exclusão"
|
||||
description="Atenção"
|
||||
message={`Deseja realmente excluir o andamento "${itemToDelete?.nome}"?`}
|
||||
confirmText="Sim, excluir"
|
||||
cancelText="Cancelar"
|
||||
onConfirm={handleDelete}
|
||||
onCancel={handleCancel}
|
||||
/>
|
||||
|
||||
// Fecha o modal
|
||||
handleCancel();
|
||||
|
||||
}, [itemToDelete, fetchTPessoa, handleCancel]);
|
||||
|
||||
/**
|
||||
* Busca inicial dos dados
|
||||
*/
|
||||
useEffect(() => {
|
||||
fetchTPessoa();
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Tela de loading enquanto carrega os dados
|
||||
*/
|
||||
if (tPessoa.length == 0) {
|
||||
return <Loading type={2} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Cabeçalho */}
|
||||
<Header
|
||||
title={"Pessoas Físicas"}
|
||||
description={"Gerenciamento de pessoas físicas"}
|
||||
buttonText={"Nova Pessoa"}
|
||||
buttonAction={() => { handleOpenForm(null) }}
|
||||
/>
|
||||
|
||||
{/* Tabela de Registros */}
|
||||
<TPessoaTable
|
||||
data={tPessoa}
|
||||
onDelete={handleConfirmDelete}
|
||||
onEdit={handleOpenForm}
|
||||
/>
|
||||
|
||||
{/* Modal de confirmação */}
|
||||
<ConfirmDialog
|
||||
isOpen={isConfirmOpen}
|
||||
title="Confirmar exclusão"
|
||||
description="Atenção"
|
||||
message={`Deseja realmente excluir o andamento "${itemToDelete?.nome}"?`}
|
||||
confirmText="Sim, excluir"
|
||||
cancelText="Cancelar"
|
||||
onConfirm={handleDelete}
|
||||
onCancel={handleCancel}
|
||||
/>
|
||||
|
||||
{/* Formulário de criação/edição */}
|
||||
<TPessoaForm
|
||||
isOpen={isFormOpen}
|
||||
data={selectedAndamento}
|
||||
onClose={handleCloseForm}
|
||||
onSave={handleSave}
|
||||
buttonIsLoading={buttonIsLoading}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
{/* Formulário de criação/edição */}
|
||||
<TPessoaForm
|
||||
isOpen={isFormOpen}
|
||||
data={selectedAndamento}
|
||||
onClose={handleCloseForm}
|
||||
onSave={handleSave}
|
||||
buttonIsLoading={buttonIsLoading}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,173 @@
|
|||
"use client";
|
||||
|
||||
import React, { useEffect, useState, useCallback } from "react";
|
||||
|
||||
import Loading from "@/app/_components/loading/loading";
|
||||
|
||||
import { useTPessoaSaveHook } from "../../../_hooks/t_pessoa/useTPessoaSaveHook";
|
||||
import { useTPessoaDeleteHook } from "../../../_hooks/t_pessoa/useTPessoaDeleteHook";
|
||||
|
||||
import ConfirmDialog from "@/app/_components/confirm_dialog/ConfirmDialog";
|
||||
import { useConfirmDialog } from "@/app/_components/confirm_dialog/useConfirmDialog";
|
||||
|
||||
import TPessoaInterface from "../../../_interfaces/TPessoaInterface";
|
||||
import Header from "@/app/_components/structure/Header";
|
||||
import TPessoaJuridicaTable from "../../../_components/t_pessoa/juridica/TPessoaJuridicaTable";
|
||||
import { useTPessoaJuridicaIndexHook } from "../../../_hooks/t_pessoa/juridica/useTPessoaJuridicaIndexHook";
|
||||
import TPessoaJuridicaForm from "../../../_components/t_pessoa/juridica/TPessoaJuridicaForm";
|
||||
|
||||
export default function TPessoaFisica() {
|
||||
// Controle de estado do botão
|
||||
const [buttonIsLoading, setButtonIsLoading] = useState(false);
|
||||
|
||||
// Hooks para leitura e salvamento
|
||||
const { tPessoa, fetchTPessoa } = useTPessoaJuridicaIndexHook();
|
||||
const { saveTCensec } = useTPessoaSaveHook();
|
||||
const { deleteTCensec } = useTPessoaDeleteHook();
|
||||
|
||||
// Estados
|
||||
const [selectedAndamento, setSelectedAndamento] =
|
||||
useState<TPessoaInterface | null>(null);
|
||||
const [isFormOpen, setIsFormOpen] = useState(false);
|
||||
|
||||
// Estado para saber qual item será deletado
|
||||
const [itemToDelete, setItemToDelete] = useState<TPessoaInterface | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
/**
|
||||
* Hook do modal de confirmação
|
||||
*/
|
||||
const {
|
||||
isOpen: isConfirmOpen,
|
||||
openDialog: openConfirmDialog,
|
||||
handleConfirm,
|
||||
handleCancel,
|
||||
} = useConfirmDialog();
|
||||
|
||||
/**
|
||||
* Abre o formulário no modo de edição ou criação
|
||||
*/
|
||||
const handleOpenForm = useCallback((data: TPessoaInterface | null) => {
|
||||
setSelectedAndamento(data);
|
||||
setIsFormOpen(true);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Fecha o formulário e limpa o andamento selecionado
|
||||
*/
|
||||
const handleCloseForm = useCallback(() => {
|
||||
setSelectedAndamento(null);
|
||||
setIsFormOpen(false);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Salva os dados do formulário
|
||||
*/
|
||||
const handleSave = useCallback(
|
||||
async (formData: TPessoaInterface) => {
|
||||
// Coloca o botão em estado de loading
|
||||
setButtonIsLoading(true);
|
||||
|
||||
// Aguarda salvar o registro
|
||||
await saveTCensec(formData);
|
||||
|
||||
// Remove o botão em estado de loading
|
||||
setButtonIsLoading(false);
|
||||
|
||||
// Atualiza a lista de dados
|
||||
fetchTPessoa();
|
||||
},
|
||||
[saveTCensec, fetchTPessoa, handleCloseForm],
|
||||
);
|
||||
|
||||
/**
|
||||
* Quando o usuário clica em "remover" na tabela
|
||||
*/
|
||||
const handleConfirmDelete = useCallback(
|
||||
(item: TPessoaInterface) => {
|
||||
// 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 deleteTCensec(itemToDelete);
|
||||
|
||||
// Atualiza a lista
|
||||
await fetchTPessoa();
|
||||
|
||||
// Limpa o item selecionado
|
||||
setItemToDelete(null);
|
||||
|
||||
// Fecha o modal
|
||||
handleCancel();
|
||||
}, [itemToDelete, fetchTPessoa, handleCancel]);
|
||||
|
||||
/**
|
||||
* Busca inicial dos dados
|
||||
*/
|
||||
useEffect(() => {
|
||||
fetchTPessoa();
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Tela de loading enquanto carrega os dados
|
||||
*/
|
||||
if (tPessoa.length == 0) {
|
||||
return <Loading type={2} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Cabeçalho */}
|
||||
<Header
|
||||
title={"Pessoas Jurídicas"}
|
||||
description={"Gerenciamento de pessoas jurídicas"}
|
||||
buttonText={"Nova Pessoa"}
|
||||
buttonAction={() => {
|
||||
handleOpenForm(null);
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Tabela de Registros */}
|
||||
<TPessoaJuridicaTable
|
||||
data={tPessoa}
|
||||
onDelete={handleConfirmDelete}
|
||||
onEdit={handleOpenForm}
|
||||
/>
|
||||
|
||||
{/* Modal de confirmação */}
|
||||
<ConfirmDialog
|
||||
isOpen={isConfirmOpen}
|
||||
title="Confirmar exclusão"
|
||||
description="Atenção"
|
||||
message={`Deseja realmente excluir o andamento "${itemToDelete?.nome}"?`}
|
||||
confirmText="Sim, excluir"
|
||||
cancelText="Cancelar"
|
||||
onConfirm={handleDelete}
|
||||
onCancel={handleCancel}
|
||||
/>
|
||||
|
||||
{/* Formulário de criação/edição */}
|
||||
<TPessoaJuridicaForm
|
||||
isOpen={isFormOpen}
|
||||
data={selectedAndamento}
|
||||
onClose={handleCloseForm}
|
||||
onSave={handleSave}
|
||||
buttonIsLoading={buttonIsLoading}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -34,7 +34,7 @@ import {
|
|||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
|
||||
// Define o tipo do formulário com base no schema Zod
|
||||
|
|
@ -50,38 +50,34 @@ interface Props {
|
|||
|
||||
// Componente principal do formulário
|
||||
export default function GCidadeForm({ isOpen, data, onClose, onSave }: Props) {
|
||||
//
|
||||
const { gUf, fetchGUf } = useGUfReadHook();
|
||||
|
||||
//
|
||||
const { gUf, fetchGUf } = useGUfReadHook();
|
||||
// Inicializa o react-hook-form integrado ao Zod para validação
|
||||
const form = useForm<FormValues>({
|
||||
resolver: zodResolver(GCidadeSchema),
|
||||
defaultValues: {
|
||||
cidade_id: 0,
|
||||
uf: "",
|
||||
cidade_nome: "",
|
||||
codigo_ibge: "",
|
||||
codigo_gyn: "",
|
||||
},
|
||||
});
|
||||
|
||||
// Inicializa o react-hook-form integrado ao Zod para validação
|
||||
const form = useForm<FormValues>({
|
||||
resolver: zodResolver(GCidadeSchema),
|
||||
defaultValues: {
|
||||
cidade_id: 0,
|
||||
uf: "",
|
||||
cidade_nome: "",
|
||||
codigo_ibge: "",
|
||||
codigo_gyn: ""
|
||||
},
|
||||
});
|
||||
// Quando recebe dados para edição, atualiza os valores do formulário
|
||||
useEffect(() => {
|
||||
// Se existir dados, reseta o formulário com os dados informados
|
||||
if (data) form.reset(data);
|
||||
|
||||
// Quando recebe dados para edição, atualiza os valores do formulário
|
||||
useEffect(() => {
|
||||
const loadData = async () => {
|
||||
// Aguarda a busca terminar
|
||||
await fetchGUf();
|
||||
};
|
||||
|
||||
// Se existir dados, reseta o formulário com os dados informados
|
||||
if (data) form.reset(data);
|
||||
|
||||
const loadData = async () => {
|
||||
|
||||
// Aguarda a busca terminar
|
||||
await fetchGUf();
|
||||
};
|
||||
|
||||
// Dispara a função
|
||||
loadData();
|
||||
|
||||
}, [data, form]);
|
||||
// Dispara a função
|
||||
loadData();
|
||||
}, [data, form]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
|
|
@ -131,38 +127,38 @@ export default function GCidadeForm({ isOpen, data, onClose, onSave }: Props) {
|
|||
)}
|
||||
/>
|
||||
|
||||
{/* Tipo */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="uf"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>
|
||||
UF
|
||||
</FormLabel>
|
||||
<Select
|
||||
value={String(field.value)}
|
||||
|
||||
// Carrega o valor selecionado
|
||||
onValueChange={(val) => field.onChange(val)}
|
||||
>
|
||||
<FormControl className="w-full">
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Selecione o estado desejado" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{gUf.map((item) => (
|
||||
<SelectItem key={item.g_uf_id} value={String(item.sigla)}>
|
||||
{item.nome}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
{/* Tipo */}
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="uf"
|
||||
render={({ field }) => (
|
||||
<FormItem>
|
||||
<FormLabel>UF</FormLabel>
|
||||
<Select
|
||||
value={String(field.value)}
|
||||
// Carrega o valor selecionado
|
||||
onValueChange={(val) => field.onChange(val)}
|
||||
>
|
||||
<FormControl className="w-full">
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Selecione o estado desejado" />
|
||||
</SelectTrigger>
|
||||
</FormControl>
|
||||
<SelectContent>
|
||||
{gUf.map((item) => (
|
||||
<SelectItem
|
||||
key={item.g_uf_id}
|
||||
value={String(item.sigla)}
|
||||
>
|
||||
{item.nome}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
|
||||
{/* Rodapé do diálogo com botões */}
|
||||
<DialogFooter className="mt-4">
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,16 +1,21 @@
|
|||
'use client';
|
||||
"use client";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
|
||||
import { ArrowUpDownIcon, EllipsisIcon, PencilIcon, Trash2Icon } from "lucide-react";
|
||||
import {
|
||||
ArrowUpDownIcon,
|
||||
EllipsisIcon,
|
||||
PencilIcon,
|
||||
Trash2Icon,
|
||||
} from "lucide-react";
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
|
||||
import GetNameInitials from "@/actions/text/GetNameInitials";
|
||||
|
|
@ -24,210 +29,205 @@ import empty from "@/actions/validations/empty";
|
|||
|
||||
// Tipagem das props
|
||||
interface TPessoaTableProps {
|
||||
data: TPessoaInterface[];
|
||||
onEdit: (item: TPessoaInterface, isEditingFormStatus: boolean) => void;
|
||||
onDelete: (item: TPessoaInterface, isEditingFormStatus: boolean) => void;
|
||||
data: TPessoaInterface[];
|
||||
onEdit: (item: TPessoaInterface, isEditingFormStatus: boolean) => void;
|
||||
onDelete: (item: TPessoaInterface, isEditingFormStatus: boolean) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
onEdit: (item: TPessoaInterface, isEditingFormStatus: boolean) => void,
|
||||
onDelete: (item: TPessoaInterface, isEditingFormStatus: boolean) => void,
|
||||
): ColumnDef<TPessoaInterface>[] {
|
||||
return [
|
||||
// ID
|
||||
{
|
||||
accessorKey: "pessoa_id",
|
||||
header: ({ column }) => (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
return [
|
||||
// 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="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center overflow-hidden">
|
||||
{pessoa.foto ? (
|
||||
<img
|
||||
src={pessoa.foto}
|
||||
alt={pessoa.nome || "Avatar"}
|
||||
className="w-full h-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")),
|
||||
},
|
||||
|
||||
// Cidade / UF
|
||||
{
|
||||
id: "cidade_uf",
|
||||
accessorFn: (row) => `${row.cidade}/${row.uf}`,
|
||||
header: ({ column }) => (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
Cidade/UF <ArrowUpDownIcon className="ml-1 h-4 w-4 cursor-pointer" />
|
||||
</Button>
|
||||
),
|
||||
cell: ({ row }) => <span>{row.getValue("cidade_uf") || "-"}</span>,
|
||||
sortingFn: (a, b) =>
|
||||
`${a.original.cidade}/${a.original.uf}`
|
||||
.toLowerCase()
|
||||
.localeCompare(`${b.original.cidade}/${b.original.uf}`.toLowerCase()),
|
||||
},
|
||||
|
||||
// Data de cadastro
|
||||
{
|
||||
accessorKey: "data_cadastro",
|
||||
header: ({ column }) => (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
Cadastro <ArrowUpDownIcon className="ml-1 h-4 w-4 cursor-pointer" />
|
||||
</Button>
|
||||
),
|
||||
cell: ({ row }) => FormatDateTime(row.getValue("data_cadastro")),
|
||||
sortingFn: "datetime",
|
||||
},
|
||||
|
||||
// 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)}
|
||||
>
|
||||
# <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")}
|
||||
<PencilIcon className="mr-2 h-4 w-4" />
|
||||
Editar
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DropdownMenuItem
|
||||
className="cursor-pointer text-red-600"
|
||||
onSelect={() => onDelete(pessoa, true)}
|
||||
>
|
||||
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="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center overflow-hidden">
|
||||
{pessoa.foto ? (
|
||||
<img
|
||||
src={pessoa.foto}
|
||||
alt={pessoa.nome || "Avatar"}
|
||||
className="w-full h-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"))
|
||||
),
|
||||
},
|
||||
|
||||
// Cidade / UF
|
||||
{
|
||||
id: "cidade_uf",
|
||||
accessorFn: (row) => `${row.cidade}/${row.uf}`,
|
||||
header: ({ column }) => (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
Cidade/UF <ArrowUpDownIcon className="ml-1 h-4 w-4 cursor-pointer" />
|
||||
</Button>
|
||||
),
|
||||
cell: ({ row }) => <span>{row.getValue("cidade_uf") || "-"}</span>,
|
||||
sortingFn: (a, b) =>
|
||||
`${a.original.cidade}/${a.original.uf}`.toLowerCase()
|
||||
.localeCompare(`${b.original.cidade}/${b.original.uf}`.toLowerCase()),
|
||||
},
|
||||
|
||||
// Data de cadastro
|
||||
{
|
||||
accessorKey: "data_cadastro",
|
||||
header: ({ column }) => (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
Cadastro <ArrowUpDownIcon className="ml-1 h-4 w-4 cursor-pointer" />
|
||||
</Button>
|
||||
),
|
||||
cell: ({ row }) => (
|
||||
FormatDateTime(row.getValue("data_cadastro"))
|
||||
),
|
||||
sortingFn: "datetime",
|
||||
},
|
||||
|
||||
// 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,
|
||||
},
|
||||
];
|
||||
<Trash2Icon className="mr-2 h-4 w-4" />
|
||||
Remover
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
},
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Componente principal da tabela
|
||||
*/
|
||||
export default function TPessoaTable({
|
||||
data,
|
||||
onEdit,
|
||||
onDelete,
|
||||
data,
|
||||
onEdit,
|
||||
onDelete,
|
||||
}: TPessoaTableProps) {
|
||||
|
||||
const columns = createPessoaColumns(onEdit, onDelete);
|
||||
return (
|
||||
<div>
|
||||
<DataTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
filterColumn="nome_completo"
|
||||
filterPlaceholder="Buscar por nome ou email..."
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
const columns = createPessoaColumns(onEdit, onDelete);
|
||||
return (
|
||||
<div>
|
||||
<DataTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
filterColumn="nome_completo"
|
||||
filterPlaceholder="Buscar por nome ou email..."
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,464 @@
|
|||
"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 { 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 { TPessoaSchema } from "../../../_schemas/TPessoaSchema";
|
||||
import LoadingButton from "@/app/_components/loadingButton/LoadingButton";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { HouseIcon, IdCardIcon, UserIcon } from "lucide-react";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { useTPessoaRepresentanteIndexHook } from "../../../_hooks/t_pessoa_representante/useTPessoaRepresentanteIndexHook";
|
||||
import TPessoaRepresentantePage from "../../t_pessoa_representante/TPessoaRepresentantePage";
|
||||
|
||||
type FormValues = z.infer<typeof TPessoaSchema>;
|
||||
|
||||
interface TPessoaFormProps {
|
||||
isOpen: boolean;
|
||||
data: FormValues | null;
|
||||
onClose: (item: null, isFormStatus: boolean) => void;
|
||||
onSave: (data: FormValues) => void;
|
||||
buttonIsLoading: boolean;
|
||||
}
|
||||
|
||||
export default function TPessoaJuridicaForm({
|
||||
isOpen,
|
||||
data,
|
||||
onClose,
|
||||
onSave,
|
||||
buttonIsLoading,
|
||||
}: TPessoaFormProps) {
|
||||
const { tPessoaRepresentante, fetchTPessoaRepresentante } =
|
||||
useTPessoaRepresentanteIndexHook();
|
||||
|
||||
// Inicializa o react-hook-form com schema zod
|
||||
const form = useForm<FormValues>({
|
||||
resolver: zodResolver(TPessoaSchema),
|
||||
defaultValues: {
|
||||
nome: "",
|
||||
pessoa_id: 0,
|
||||
},
|
||||
});
|
||||
|
||||
// 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();
|
||||
}, [data, form]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={isOpen}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) onClose(null, false);
|
||||
}}
|
||||
>
|
||||
<DialogContent className="w-full max-w-full sm:max-w-3xl md:max-w-4xl lg:max-w-5xl p-6">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Pessoa</DialogTitle>
|
||||
<DialogDescription>Preencha os dados da pessoa</DialogDescription>
|
||||
</DialogHeader>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSave)} className="space-y-6">
|
||||
{/* Tabs */}
|
||||
<Tabs defaultValue="dadosPessoais" className="space-y-4">
|
||||
<TabsList className="w-full flex">
|
||||
<TabsTrigger
|
||||
className="flex-1 text-center cursor-pointer"
|
||||
value="dadosPessoais"
|
||||
>
|
||||
<UserIcon className="me-1" />
|
||||
Dados Pessoais
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
className="flex-1 text-center cursor-pointer"
|
||||
value="endereco"
|
||||
>
|
||||
<HouseIcon /> Endereço
|
||||
</TabsTrigger>
|
||||
<TabsTrigger
|
||||
className="flex-1 text-center cursor-pointer"
|
||||
value="documentos"
|
||||
>
|
||||
<IdCardIcon /> Representantes
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
<div className="max-h-[80vh] overflow-y-auto">
|
||||
{/* Dados Pessoais */}
|
||||
<TabsContent value="dadosPessoais" className="space-y-4">
|
||||
<div className="grid grid-cols-12 gap-4">
|
||||
{/* Razão Social */}
|
||||
<div className="col-span-12 sm:col-span-12 md:col-span-6">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="nome"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormLabel>Razão Social</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="Digite a razão social"
|
||||
className="w-full"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Nome Fantasia */}
|
||||
<div className="col-span-12 sm:col-span-12 md:col-span-6">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="nome_fantasia"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormLabel>Nome Fantasia</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="Digite o nome fantasia"
|
||||
className="w-full"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Inscrição Estadual */}
|
||||
<div className="col-span-12 sm:col-span-12 md:col-span-6">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="inscricao_municipal"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormLabel>Inscrição Estadual</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="Digite a inscrição estadual"
|
||||
className="w-full"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* CNPJ */}
|
||||
<div className="col-span-12 sm:col-span-12 md:col-span-6">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="cpf_cnpj"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormLabel>CNPJ</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="Digite o CNPJ"
|
||||
className="w-full"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Observação */}
|
||||
<div className="col-span-12 sm:col-span-12 md:col-span-12">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="observacao"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormLabel>Observação</FormLabel>
|
||||
<FormControl>
|
||||
<Textarea
|
||||
{...field}
|
||||
placeholder="Digite alguma observação"
|
||||
className="w-full"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
{/* Endereço */}
|
||||
<TabsContent value="endereco" className="space-y-4">
|
||||
<div className="grid grid-cols-12 gap-4 w-full">
|
||||
{/* País */}
|
||||
<div className="col-span-12 sm:col-span-12 md:col-span-6">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="uf_residencia"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormLabel>País</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="Digite o nome"
|
||||
className="w-full"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{/* UF */}
|
||||
<div className="col-span-12 sm:col-span-12 md:col-span-2">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="uf_residencia"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormLabel>UF</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="Digite o nome"
|
||||
className="w-full"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{/* CEP */}
|
||||
<div className="col-span-12 sm:col-span-12 md:col-span-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="cep"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormLabel>CEP</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="Digite o nome"
|
||||
className="w-full"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{/* Cidade */}
|
||||
<div className="col-span-12 sm:col-span-12 md:col-span-6">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="cidade_nat_id"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormLabel>Cidade</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="Digite o nome"
|
||||
className="w-full"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{/* Município */}
|
||||
<div className="col-span-12 sm:col-span-12 md:col-span-6">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="municipio_id"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormLabel>Município</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="Digite o nome"
|
||||
className="w-full"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{/* Bairro */}
|
||||
<div className="col-span-12 sm:col-span-12 md:col-span-6">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="bairro"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormLabel>Bairro</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="Digite o nome"
|
||||
className="w-full"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{/* Logradouro */}
|
||||
<div className="col-span-12 sm:col-span-12 md:col-span-6">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="tb_tipologradouro_id"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormLabel>Logradouro</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="Digite o nome"
|
||||
className="w-full"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{/* Número */}
|
||||
<div className="col-span-12 sm:col-span-12 md:col-span-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="numero_end"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormLabel>Número</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="Digite o nome"
|
||||
className="w-full"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{/* Unidade */}
|
||||
<div className="col-span-12 sm:col-span-12 md:col-span-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="unidade"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormLabel>Unidade</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="Digite o nome"
|
||||
className="w-full"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{/* Cidade não encontrada */}
|
||||
<div className="col-span-12 sm:col-span-12 md:col-span-4">
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="uf_residencia"
|
||||
render={({ field }) => (
|
||||
<FormItem className="w-full">
|
||||
<FormLabel>Cidade não encontrada</FormLabel>
|
||||
<FormControl>
|
||||
<Input
|
||||
{...field}
|
||||
placeholder="Digite o nome"
|
||||
className="w-full"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
</FormItem>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</TabsContent>
|
||||
{/* Documentos */}
|
||||
<TabsContent value="documentos" className="space-y-4">
|
||||
<TPessoaRepresentantePage />
|
||||
</TabsContent>
|
||||
</div>
|
||||
</Tabs>
|
||||
{/* Rodapé do Dialog */}
|
||||
<DialogFooter className="mt-4">
|
||||
<DialogClose asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
type="button"
|
||||
onClick={() => onClose(null, false)}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
Cancelar
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<LoadingButton
|
||||
text="Salvar"
|
||||
textLoading="Aguarde..."
|
||||
type="submit"
|
||||
loading={buttonIsLoading}
|
||||
/>
|
||||
</DialogFooter>
|
||||
{/* Campo oculto */}
|
||||
<input type="hidden" {...form.register("pessoa_id")} />
|
||||
</form>
|
||||
</Form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,218 @@
|
|||
"use client";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
|
||||
import {
|
||||
ArrowUpDownIcon,
|
||||
EllipsisIcon,
|
||||
PencilIcon,
|
||||
Trash2Icon,
|
||||
} from "lucide-react";
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
|
||||
import GetNameInitials from "@/actions/text/GetNameInitials";
|
||||
import { DataTable } from "@/app/_components/dataTable/DataTable";
|
||||
|
||||
import TPessoaInterface from "../../../_interfaces/TPessoaInterface";
|
||||
import { FormatCPF } from "@/actions/CPF/FormatCPF";
|
||||
import { FormatPhone } from "@/actions/phone/FormatPhone";
|
||||
import { FormatDateTime } from "@/actions/dateTime/FormatDateTime";
|
||||
import empty from "@/actions/validations/empty";
|
||||
import { FormatCNPJ } from "@/actions/CNPJ/FormatCNPJ";
|
||||
|
||||
// Tipagem das props
|
||||
interface TPessoaJuridicaTableProps {
|
||||
data: TPessoaInterface[];
|
||||
onEdit: (item: TPessoaInterface, isEditingFormStatus: boolean) => void;
|
||||
onDelete: (item: TPessoaInterface, isEditingFormStatus: boolean) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
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">
|
||||
{/* 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")}
|
||||
>
|
||||
CNPJ <ArrowUpDownIcon className="ml-1 h-4 w-4 cursor-pointer" />
|
||||
</Button>
|
||||
),
|
||||
cell: ({ row }) => FormatCNPJ(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")),
|
||||
},
|
||||
|
||||
// Cidade / UF
|
||||
{
|
||||
id: "cidade_uf",
|
||||
accessorFn: (row) => `${row.cidade}/${row.uf}`,
|
||||
header: ({ column }) => (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
Cidade/UF <ArrowUpDownIcon className="ml-1 h-4 w-4 cursor-pointer" />
|
||||
</Button>
|
||||
),
|
||||
cell: ({ row }) => <span>{row.getValue("cidade_uf") || "-"}</span>,
|
||||
sortingFn: (a, b) =>
|
||||
`${a.original.cidade}/${a.original.uf}`
|
||||
.toLowerCase()
|
||||
.localeCompare(`${b.original.cidade}/${b.original.uf}`.toLowerCase()),
|
||||
},
|
||||
|
||||
// Data de cadastro
|
||||
{
|
||||
accessorKey: "data_cadastro",
|
||||
header: ({ column }) => (
|
||||
<Button
|
||||
variant="ghost"
|
||||
onClick={() => column.toggleSorting(column.getIsSorted() === "asc")}
|
||||
>
|
||||
Cadastro <ArrowUpDownIcon className="ml-1 h-4 w-4 cursor-pointer" />
|
||||
</Button>
|
||||
),
|
||||
cell: ({ row }) => FormatDateTime(row.getValue("data_cadastro")),
|
||||
sortingFn: "datetime",
|
||||
},
|
||||
|
||||
// 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,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Componente principal da tabela
|
||||
*/
|
||||
export default function TPessoaJuridicaTable({
|
||||
data,
|
||||
onEdit,
|
||||
onDelete,
|
||||
}: TPessoaJuridicaTableProps) {
|
||||
const columns = createPessoaColumns(onEdit, onDelete);
|
||||
return (
|
||||
<div>
|
||||
<DataTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
filterColumn="nome_completo"
|
||||
filterPlaceholder="Buscar por nome ou email..."
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,346 @@
|
|||
"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 { 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 { TPessoaSchema } from "../../_schemas/TPessoaSchema";
|
||||
import LoadingButton from "@/app/_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 "@/enums/SexoEnum";
|
||||
import { useGTBEstadoCivilReadHook } from "../../_hooks/g_tb_estadocivil/useGTBEstadoCivilReadHook";
|
||||
import GetCapitalize from "@/actions/text/GetCapitalize";
|
||||
import { useGTBRegimeComunhaoReadHook } from "../../_hooks/g_tb_regimecomunhao/useGTBRegimeComunhaoReadHook";
|
||||
import { useGTBProfissaoReadHook } from "../../_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 { useTPessoaIndexHook } from "../../_hooks/t_pessoa/useTPessoaIndexHook";
|
||||
import TPessoaTable from "../t_pessoa/TPessoaTable";
|
||||
import TPessoaInterface from "../../_interfaces/TPessoaInterface";
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
import GetNameInitials from "@/actions/text/GetNameInitials";
|
||||
import empty from "@/actions/validations/empty";
|
||||
import { FormatCPF } from "@/actions/CPF/FormatCPF";
|
||||
import { FormatPhone } from "@/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 "@/app/_components/dataTable/DataTable";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
|
||||
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="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center overflow-hidden">
|
||||
{pessoa.foto ? (
|
||||
<img
|
||||
src={pessoa.foto}
|
||||
alt={pessoa.nome || "Avatar"}
|
||||
className="w-full h-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,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export default function TPessoaRepresentanteForm({
|
||||
isOpen,
|
||||
data,
|
||||
onClose,
|
||||
onSave,
|
||||
buttonIsLoading,
|
||||
}: TPessoaRepresentanteFormProps) {
|
||||
const { tPessoa, fetchTPessoa } = useTPessoaIndexHook();
|
||||
|
||||
// Inicializa o react-hook-form com schema zod
|
||||
const form = useForm<FormValues>({
|
||||
resolver: zodResolver(TPessoaSchema),
|
||||
defaultValues: {
|
||||
nome: "",
|
||||
pessoa_id: 0,
|
||||
},
|
||||
});
|
||||
|
||||
// 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
|
||||
if (data) form.reset(data);
|
||||
|
||||
// Aguarda a busca terminar
|
||||
await fetchTPessoa();
|
||||
};
|
||||
|
||||
// Dispara a função
|
||||
loadData();
|
||||
}, [data, form]);
|
||||
|
||||
const columns = createPessoaColumns(
|
||||
() => {},
|
||||
() => {},
|
||||
);
|
||||
return (
|
||||
<Dialog
|
||||
open={isOpen}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) onClose(null, false);
|
||||
}}
|
||||
>
|
||||
<DialogContent className="w-full max-w-full sm:max-w-3xl md:max-w-4xl lg:max-w-5xl p-6">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Representante</DialogTitle>
|
||||
<DialogDescription>Busque o representante desejado</DialogDescription>
|
||||
</DialogHeader>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSave)} className="space-y-6">
|
||||
<div className="max-h-[50vh] overflow-y-auto">
|
||||
<DataTable
|
||||
data={tPessoa}
|
||||
columns={columns}
|
||||
filterColumn="nome_completo"
|
||||
filterPlaceholder="Buscar por nome ou email..."
|
||||
/>
|
||||
</div>
|
||||
{/* Rodapé do Dialog */}
|
||||
<DialogFooter className="mt-4">
|
||||
<DialogClose asChild>
|
||||
<Button
|
||||
variant="outline"
|
||||
type="button"
|
||||
onClick={() => onClose(null, false)}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
Cancelar
|
||||
</Button>
|
||||
</DialogClose>
|
||||
<LoadingButton
|
||||
text="Salvar"
|
||||
textLoading="Aguarde..."
|
||||
type="submit"
|
||||
loading={buttonIsLoading}
|
||||
/>
|
||||
</DialogFooter>
|
||||
{/* Campo oculto */}
|
||||
<input type="hidden" {...form.register("pessoa_id")} />
|
||||
</form>
|
||||
</Form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
"use client";
|
||||
|
||||
import React, { useEffect, useState, useCallback } from "react";
|
||||
|
||||
import Loading from "@/app/_components/loading/loading";
|
||||
import TPessoaForm from "../../_components/t_pessoa/TPessoaForm";
|
||||
|
||||
import { useTPessoaIndexHook } from "../../_hooks/t_pessoa/useTPessoaIndexHook";
|
||||
import { useTPessoaSaveHook } from "../../_hooks/t_pessoa/useTPessoaSaveHook";
|
||||
import { useTPessoaDeleteHook } from "../../_hooks/t_pessoa/useTPessoaDeleteHook";
|
||||
|
||||
import ConfirmDialog from "@/app/_components/confirm_dialog/ConfirmDialog";
|
||||
import { useConfirmDialog } from "@/app/_components/confirm_dialog/useConfirmDialog";
|
||||
|
||||
import TPessoaInterface from "../../_interfaces/TPessoaInterface";
|
||||
import TPessoaRepresentanteTable from "./TPessoaRepresentanteTable";
|
||||
import { useTPessoaRepresentanteIndexHook } from "../../_hooks/t_pessoa_representante/useTPessoaRepresentanteIndexHook";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import Header from "@/app/_components/structure/Header";
|
||||
import TPessoaRepresentanteForm from "./TPessoaRepresentanteForm";
|
||||
|
||||
export default function TPessoaRepresentantePage() {
|
||||
// Controle de estado do botão
|
||||
const [buttonIsLoading, setButtonIsLoading] = useState(false);
|
||||
|
||||
// Hooks para leitura e salvamento
|
||||
const { tPessoaRepresentante, fetchTPessoaRepresentante } =
|
||||
useTPessoaRepresentanteIndexHook();
|
||||
const { saveTCensec } = useTPessoaSaveHook();
|
||||
const { deleteTCensec } = useTPessoaDeleteHook();
|
||||
|
||||
// Estados
|
||||
const [selectedAndamento, setSelectedAndamento] =
|
||||
useState<TPessoaInterface | null>(null);
|
||||
const [isFormOpen, setIsFormOpen] = useState(false);
|
||||
|
||||
// Estado para saber qual item será deletado
|
||||
const [itemToDelete, setItemToDelete] = useState<TPessoaInterface | null>(
|
||||
null,
|
||||
);
|
||||
|
||||
/**
|
||||
* Hook do modal de confirmação
|
||||
*/
|
||||
const {
|
||||
isOpen: isConfirmOpen,
|
||||
openDialog: openConfirmDialog,
|
||||
handleConfirm,
|
||||
handleCancel,
|
||||
} = useConfirmDialog();
|
||||
|
||||
/**
|
||||
* Abre o formulário no modo de edição ou criação
|
||||
*/
|
||||
const handleOpenForm = useCallback((data: TPessoaInterface | null) => {
|
||||
setSelectedAndamento(data);
|
||||
setIsFormOpen(true);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Fecha o formulário e limpa o andamento selecionado
|
||||
*/
|
||||
const handleCloseForm = useCallback(() => {
|
||||
setSelectedAndamento(null);
|
||||
setIsFormOpen(false);
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Salva os dados do formulário
|
||||
*/
|
||||
const handleSave = useCallback(
|
||||
async (formData: TPessoaInterface) => {
|
||||
// Coloca o botão em estado de loading
|
||||
setButtonIsLoading(true);
|
||||
|
||||
// Aguarda salvar o registro
|
||||
await saveTCensec(formData);
|
||||
|
||||
// Remove o botão em estado de loading
|
||||
setButtonIsLoading(false);
|
||||
|
||||
// Atualiza a lista de dados
|
||||
fetchTPessoaRepresentante();
|
||||
},
|
||||
[saveTCensec, fetchTPessoaRepresentante, handleCloseForm],
|
||||
);
|
||||
|
||||
/**
|
||||
* Quando o usuário clica em "remover" na tabela
|
||||
*/
|
||||
const handleConfirmDelete = useCallback(
|
||||
(item: TPessoaInterface) => {
|
||||
// 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 deleteTCensec(itemToDelete);
|
||||
|
||||
// Atualiza a lista
|
||||
await fetchTPessoaRepresentante();
|
||||
|
||||
// Limpa o item selecionado
|
||||
setItemToDelete(null);
|
||||
|
||||
// Fecha o modal
|
||||
handleCancel();
|
||||
}, [itemToDelete, fetchTPessoaRepresentante, handleCancel]);
|
||||
|
||||
/**
|
||||
* Busca inicial dos dados
|
||||
*/
|
||||
useEffect(() => {
|
||||
fetchTPessoaRepresentante();
|
||||
}, []);
|
||||
|
||||
/**
|
||||
* Tela de loading enquanto carrega os dados
|
||||
*/
|
||||
if (tPessoaRepresentante.length == 0) {
|
||||
return <Loading type={2} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Cabeçalho */}
|
||||
<Header
|
||||
title={"Representantes"}
|
||||
description={"Gerenciamento de representantes"}
|
||||
buttonText={"Novo representante"}
|
||||
buttonAction={() => {
|
||||
handleOpenForm(null);
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Tabela de Registros */}
|
||||
<TPessoaRepresentanteTable
|
||||
data={tPessoaRepresentante}
|
||||
onDelete={handleConfirmDelete}
|
||||
onEdit={handleOpenForm}
|
||||
/>
|
||||
|
||||
{/* Modal de confirmação */}
|
||||
<ConfirmDialog
|
||||
isOpen={isConfirmOpen}
|
||||
title="Confirmar exclusão"
|
||||
description="Atenção"
|
||||
message={`Deseja realmente excluir o andamento "${itemToDelete?.nome}"?`}
|
||||
confirmText="Sim, excluir"
|
||||
cancelText="Cancelar"
|
||||
onConfirm={handleDelete}
|
||||
onCancel={handleCancel}
|
||||
/>
|
||||
|
||||
{/* Formulário de criação/edição */}
|
||||
<TPessoaRepresentanteForm
|
||||
isOpen={isFormOpen}
|
||||
data={selectedAndamento}
|
||||
onClose={handleCloseForm}
|
||||
onSave={handleSave}
|
||||
buttonIsLoading={buttonIsLoading}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,191 @@
|
|||
"use client";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuGroup,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
|
||||
import {
|
||||
ArrowUpDownIcon,
|
||||
EllipsisIcon,
|
||||
PencilIcon,
|
||||
Trash2Icon,
|
||||
} from "lucide-react";
|
||||
import { ColumnDef } from "@tanstack/react-table";
|
||||
|
||||
import GetNameInitials from "@/actions/text/GetNameInitials";
|
||||
import { DataTable } from "@/app/_components/dataTable/DataTable";
|
||||
|
||||
import TPessoaInterface from "../../_interfaces/TPessoaInterface";
|
||||
import { FormatCPF } from "@/actions/CPF/FormatCPF";
|
||||
import { FormatPhone } from "@/actions/phone/FormatPhone";
|
||||
import empty from "@/actions/validations/empty";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
|
||||
// Tipagem das props
|
||||
interface TPessoaRepresentanteTableProps {
|
||||
data: TPessoaInterface[];
|
||||
onEdit: (item: TPessoaInterface, isEditingFormStatus: boolean) => void;
|
||||
onDelete: (item: TPessoaInterface, isEditingFormStatus: boolean) => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
{
|
||||
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="w-10 h-10 rounded-full bg-gray-200 flex items-center justify-center overflow-hidden">
|
||||
{pessoa.foto ? (
|
||||
<img
|
||||
src={pessoa.foto}
|
||||
alt={pessoa.nome || "Avatar"}
|
||||
className="w-full h-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 text-red-600"
|
||||
onSelect={() => onDelete(pessoa, true)}
|
||||
>
|
||||
<Trash2Icon className="mr-2 h-4 w-4" />
|
||||
Remover
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuGroup>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
},
|
||||
enableSorting: false,
|
||||
enableHiding: false,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Componente principal da tabela
|
||||
*/
|
||||
export default function TPessoaRepresentanteTable({
|
||||
data,
|
||||
onEdit,
|
||||
onDelete,
|
||||
}: TPessoaRepresentanteTableProps) {
|
||||
const columns = createPessoaColumns(onEdit, onDelete);
|
||||
return (
|
||||
<div>
|
||||
<DataTable
|
||||
data={data}
|
||||
columns={columns}
|
||||
filterColumn="nome_completo"
|
||||
filterPlaceholder="Buscar por nome ou email..."
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
// Importa o serviço de API que será utilizado para realizar requisições HTTP
|
||||
import API from "@/services/api/Api";
|
||||
import API from "@/services/api/Api";
|
||||
|
||||
// Importa o enum que contém os métodos HTTP disponíveis (GET, POST, PUT, DELETE)
|
||||
import { Methods } from "@/services/api/enums/ApiMethodEnum";
|
||||
import { Methods } from "@/services/api/enums/ApiMethodEnum";
|
||||
|
||||
// Importa a interface tipada que define a estrutura dos dados de uma cidade
|
||||
import GCidadeInterface from "../../_interfaces/GCidadeInterface";
|
||||
|
|
@ -10,22 +10,20 @@ import GCidadeInterface from "../../_interfaces/GCidadeInterface";
|
|||
// Importa função que encapsula chamadas assíncronas e trata erros automaticamente
|
||||
import { withClientErrorHandler } from "@/actions/withClientErrorHandler/withClientErrorHandler";
|
||||
|
||||
|
||||
// Função assíncrona que implementa a lógica de salvar (criar/atualizar) uma cidade
|
||||
async function executeGcidadeSaveData(data: GCidadeInterface) {
|
||||
// Verifica se existe ID da cidade para decidir se é atualização (PUT) ou criação (POST)
|
||||
const isUpdate = Boolean(data.cidade_id);
|
||||
|
||||
// Verifica se existe ID da cidade para decidir se é atualização (PUT) ou criação (POST)
|
||||
const isUpdate = Boolean(data.cidade_id);
|
||||
// Instancia o cliente da API para enviar a requisição
|
||||
const api = new API();
|
||||
|
||||
// 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/g_cidade/${data.cidade_id || ''}`, // endpoint dinâmico
|
||||
body: data // payload enviado para a 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/g_cidade/${data.cidade_id || ""}`, // endpoint dinâmico
|
||||
body: data, // payload enviado para a API
|
||||
});
|
||||
}
|
||||
|
||||
// Exporta a função de salvar cidade já encapsulada com tratamento de erros
|
||||
|
|
|
|||
|
|
@ -6,14 +6,12 @@ import { Methods } from "@/services/api/enums/ApiMethodEnum";
|
|||
|
||||
// Exporta por padrão a função assíncrona GUfIndexData
|
||||
export default async function GUfIndexData() {
|
||||
// Cria uma instância da classe API para executar a requisição
|
||||
const api = new API();
|
||||
|
||||
// Cria uma instância da classe API para executar a requisição
|
||||
const api = new API();
|
||||
|
||||
// Executa a chamada GET para o endpoint "administrativo/g_uf/" e retorna a resposta
|
||||
return await api.send({
|
||||
method: Methods.GET, // Define que o método HTTP é GET
|
||||
endpoint: `administrativo/g_uf/` // Define o endpoint a ser acessado
|
||||
});
|
||||
|
||||
// Executa a chamada GET para o endpoint "administrativo/g_uf/" e retorna a resposta
|
||||
return await api.send({
|
||||
method: Methods.GET, // Define que o método HTTP é GET
|
||||
endpoint: `administrativo/g_uf/`, // Define o endpoint a ser acessado
|
||||
});
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -0,0 +1,54 @@
|
|||
import { withClientErrorHandler } from "@/actions/withClientErrorHandler/withClientErrorHandler";
|
||||
|
||||
async function executeTPessoaRepresentanteIndexData() {
|
||||
return Promise.resolve({
|
||||
status: 200,
|
||||
message: "Dados localizados",
|
||||
data: [
|
||||
{
|
||||
pessoa_representante_id: 1,
|
||||
nome: "Keven Willian",
|
||||
email: "keven@oriustecnologia.com.br",
|
||||
tipo: "Proprietário",
|
||||
cpf_cnpj: "70341828173",
|
||||
telefone: "62983372306",
|
||||
},
|
||||
{
|
||||
pessoa_representante_id: 2,
|
||||
nome: "Mariana Silva",
|
||||
email: "keven@oriustecnologia.com.br",
|
||||
tipo: "Sócio",
|
||||
cpf_cnpj: "70341828173",
|
||||
telefone: "62983372306",
|
||||
},
|
||||
{
|
||||
pessoa_representante_id: 3,
|
||||
nome: "Lucas Oliveira",
|
||||
tipo: "Administrador",
|
||||
email: "keven@oriustecnologia.com.br",
|
||||
cpf_cnpj: "70341828173",
|
||||
telefone: "62983372306",
|
||||
},
|
||||
{
|
||||
pessoa_representante_id: 4,
|
||||
nome: "Fernanda Costa",
|
||||
email: "keven@oriustecnologia.com.br",
|
||||
tipo: "Procurador",
|
||||
cpf_cnpj: "70341828173",
|
||||
telefone: "62983372306",
|
||||
},
|
||||
{
|
||||
pessoa_representante_id: 5,
|
||||
nome: "Rafael Gomes",
|
||||
email: "keven@oriustecnologia.com.br",
|
||||
tipo: "Sócio",
|
||||
cpf_cnpj: "70341828173",
|
||||
telefone: "62983372306",
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
export const TPessoaRepresentanteIndexData = withClientErrorHandler(
|
||||
executeTPessoaRepresentanteIndexData,
|
||||
);
|
||||
|
|
@ -15,10 +15,10 @@ export const useGCidadeSaveHook = () => {
|
|||
|
||||
// Guardar os dados localizados
|
||||
setGCidade(response.data);
|
||||
// Guardar os dados localizados
|
||||
setGCidade(response.data);
|
||||
// Guardar os dados localizados
|
||||
setGCidade(response.data);
|
||||
|
||||
console.log(response)
|
||||
console.log(response);
|
||||
|
||||
// Manda a resposta para o verificador de resposta
|
||||
setResponse(response);
|
||||
|
|
@ -26,13 +26,11 @@ export const useGCidadeSaveHook = () => {
|
|||
// Fecha o formulário automaticamente após salvar
|
||||
setIsOpen(false);
|
||||
|
||||
console.log(response)
|
||||
console.log(response);
|
||||
|
||||
// Manda a resposta para o verificador de resposta
|
||||
setResponse(response);
|
||||
// Manda a resposta para o verificador de resposta
|
||||
setResponse(response);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
return { gCidade, saveGCidade }
|
||||
|
||||
}
|
||||
return { gCidade, saveGCidade };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,30 +1,26 @@
|
|||
'use client'
|
||||
"use client";
|
||||
|
||||
import { useResponse } from "@/app/_response/ResponseContext"
|
||||
import { useResponse } from "@/app/_response/ResponseContext";
|
||||
import { use, useState } from "react";
|
||||
import GUfInterface from "../../_interfaces/GUfInterface";
|
||||
import GUfIndexService from "../../_services/g_uf/GUfIndexService";
|
||||
|
||||
export const useGUfReadHook = () => {
|
||||
const { setResponse } = useResponse();
|
||||
|
||||
const { setResponse } = useResponse();
|
||||
// Controle dos dados obtidos via API
|
||||
const [gUf, setGUf] = useState<GUfInterface[]>([]);
|
||||
|
||||
// Controle dos dados obtidos via API
|
||||
const [gUf, setGUf] = useState<GUfInterface[]>([]);
|
||||
const fetchGUf = async () => {
|
||||
// Realiza a requisição para a api
|
||||
const response = await GUfIndexService();
|
||||
|
||||
const fetchGUf = async () => {
|
||||
// Armazena os dados da resposta
|
||||
setGUf(response.data);
|
||||
|
||||
// Realiza a requisição para a api
|
||||
const response = await GUfIndexService();
|
||||
// Envia os dados da resposta para ser tratado
|
||||
setResponse(response);
|
||||
};
|
||||
|
||||
// Armazena os dados da resposta
|
||||
setGUf(response.data);
|
||||
|
||||
// Envia os dados da resposta para ser tratado
|
||||
setResponse(response);
|
||||
|
||||
}
|
||||
|
||||
return { gUf, fetchGUf }
|
||||
|
||||
}
|
||||
return { gUf, fetchGUf };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
import { useResponse } from "@/app/_response/ResponseContext";
|
||||
import TPessoaJuridicaInterface from "../../../_interfaces/TPessoaJuridicaInterface";
|
||||
import { TCensecDeleteService } from "../../../_services/t_censec/TCensecDeleteService";
|
||||
|
||||
export const useTPessoaJuridicaDeleteHook = () => {
|
||||
const { setResponse } = useResponse();
|
||||
|
||||
const deleteTCensec = async (data: TPessoaJuridicaInterface) => {
|
||||
const response = await TCensecDeleteService(data);
|
||||
|
||||
setResponse(response);
|
||||
};
|
||||
|
||||
return { deleteTCensec };
|
||||
};
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
import { useResponse } from "@/app/_response/ResponseContext";
|
||||
import { TPessoaJuridicaIndexService } from "../../../_services/t_pessoa/juridica/TPessoaJuridicaIndexService";
|
||||
import { useState } from "react";
|
||||
import TPessoaJuridicaInterface from "../../../_interfaces/TPessoaJuridicaInterface";
|
||||
|
||||
export const useTPessoaJuridicaIndexHook = () => {
|
||||
const { setResponse } = useResponse();
|
||||
|
||||
const [tPessoa, setTPessoa] = useState<TPessoaJuridicaInterface[]>([]);
|
||||
|
||||
const fetchTPessoa = async () => {
|
||||
const response = await TPessoaJuridicaIndexService();
|
||||
|
||||
setTPessoa(response.data);
|
||||
|
||||
setResponse(response);
|
||||
};
|
||||
|
||||
return { tPessoa, fetchTPessoa };
|
||||
};
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
"use client";
|
||||
|
||||
import { useResponse } from "@/app/_response/ResponseContext";
|
||||
import { useState } from "react";
|
||||
import TPessoaInterface from "../../../_interfaces/TPessoaInterface";
|
||||
import { TCensecSaveService } from "../../../_services/t_censec/TCensecSaveService";
|
||||
|
||||
export const useTPessoaJuridicaSaveHook = () => {
|
||||
const { setResponse } = useResponse();
|
||||
|
||||
const [tCensec, setTCensec] = useState<TPessoaInterface>();
|
||||
|
||||
// controla se o formulário está aberto ou fechado
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const saveTCensec = async (data: TPessoaInterface) => {
|
||||
const response = await TCensecSaveService(data);
|
||||
|
||||
// Armazena os dados da repsota
|
||||
setTCensec(response.data);
|
||||
|
||||
// Define os dados da respota(toast, modal, etc)
|
||||
setResponse(response);
|
||||
|
||||
// Fecha o formulário automaticamente após salvar
|
||||
setIsOpen(false);
|
||||
|
||||
// Retorna os valores de forma imediata
|
||||
return response.data;
|
||||
};
|
||||
|
||||
return { tCensec, saveTCensec };
|
||||
};
|
||||
|
|
@ -1,19 +1,15 @@
|
|||
import { useResponse } from "@/app/_response/ResponseContext"
|
||||
import { useResponse } from "@/app/_response/ResponseContext";
|
||||
import TPessoaInterface from "../../_interfaces/TPessoaInterface";
|
||||
import { TCensecDeleteService } from "../../_services/t_censec/TCensecDeleteService";
|
||||
|
||||
export const useTPessoaDeleteHook = () => {
|
||||
const { setResponse } = useResponse();
|
||||
|
||||
const { setResponse } = useResponse();
|
||||
const deleteTCensec = async (data: TPessoaInterface) => {
|
||||
const response = await TCensecDeleteService(data);
|
||||
|
||||
const deleteTCensec = async (data: TPessoaInterface) => {
|
||||
setResponse(response);
|
||||
};
|
||||
|
||||
const response = await TCensecDeleteService(data);
|
||||
|
||||
setResponse(response);
|
||||
|
||||
}
|
||||
|
||||
return { deleteTCensec }
|
||||
|
||||
}
|
||||
return { deleteTCensec };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,24 +1,20 @@
|
|||
import { useResponse } from "@/app/_response/ResponseContext";
|
||||
import { TPessoaIndexService } from "../../_services/t_pessoa/TPessoaIndexService"
|
||||
import { TPessoaIndexService } from "../../_services/t_pessoa/TPessoaIndexService";
|
||||
import { useState } from "react";
|
||||
import TPessoaInterface from "../../_interfaces/TPessoaInterface";
|
||||
|
||||
export const useTPessoaIndexHook = () => {
|
||||
const { setResponse } = useResponse();
|
||||
|
||||
const { setResponse } = useResponse();
|
||||
const [tPessoa, setTPessoa] = useState<TPessoaInterface[]>([]);
|
||||
|
||||
const [tPessoa, setTPessoa] = useState<TPessoaInterface[]>([]);
|
||||
const fetchTPessoa = async () => {
|
||||
const response = await TPessoaIndexService();
|
||||
|
||||
const fetchTPessoa = async () => {
|
||||
setTPessoa(response.data);
|
||||
|
||||
const response = await TPessoaIndexService();
|
||||
setResponse(response);
|
||||
};
|
||||
|
||||
setTPessoa(response.data);
|
||||
|
||||
setResponse(response)
|
||||
|
||||
}
|
||||
|
||||
return { tPessoa, fetchTPessoa }
|
||||
|
||||
}
|
||||
return { tPessoa, fetchTPessoa };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,37 +1,33 @@
|
|||
'use client'
|
||||
"use client";
|
||||
|
||||
import { useResponse } from "@/app/_response/ResponseContext"
|
||||
import { useResponse } from "@/app/_response/ResponseContext";
|
||||
import { useState } from "react";
|
||||
import TPessoaInterface from "../../_interfaces/TPessoaInterface";
|
||||
import { TCensecSaveService } from "../../_services/t_censec/TCensecSaveService";
|
||||
|
||||
export const useTPessoaSaveHook = () => {
|
||||
const { setResponse } = useResponse();
|
||||
|
||||
const { setResponse } = useResponse();
|
||||
const [tCensec, setTCensec] = useState<TPessoaInterface>();
|
||||
|
||||
const [tCensec, setTCensec] = useState<TPessoaInterface>();
|
||||
// controla se o formulário está aberto ou fechado
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
// controla se o formulário está aberto ou fechado
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
const saveTCensec = async (data: TPessoaInterface) => {
|
||||
const response = await TCensecSaveService(data);
|
||||
|
||||
const saveTCensec = async (data: TPessoaInterface) => {
|
||||
// Armazena os dados da repsota
|
||||
setTCensec(response.data);
|
||||
|
||||
const response = await TCensecSaveService(data);
|
||||
// Define os dados da respota(toast, modal, etc)
|
||||
setResponse(response);
|
||||
|
||||
// Armazena os dados da repsota
|
||||
setTCensec(response.data);
|
||||
// Fecha o formulário automaticamente após salvar
|
||||
setIsOpen(false);
|
||||
|
||||
// Define os dados da respota(toast, modal, etc)
|
||||
setResponse(response);
|
||||
// Retorna os valores de forma imediata
|
||||
return response.data;
|
||||
};
|
||||
|
||||
// Fecha o formulário automaticamente após salvar
|
||||
setIsOpen(false);
|
||||
|
||||
// Retorna os valores de forma imediata
|
||||
return response.data;
|
||||
|
||||
}
|
||||
|
||||
return { tCensec, saveTCensec }
|
||||
|
||||
}
|
||||
return { tCensec, saveTCensec };
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,15 @@
|
|||
import { useResponse } from "@/app/_response/ResponseContext";
|
||||
import TPessoaInterface from "../../_interfaces/TPessoaInterface";
|
||||
import { TCensecDeleteService } from "../../_services/t_censec/TCensecDeleteService";
|
||||
|
||||
export const useTPessoaDeleteHook = () => {
|
||||
const { setResponse } = useResponse();
|
||||
|
||||
const deleteTCensec = async (data: TPessoaInterface) => {
|
||||
const response = await TCensecDeleteService(data);
|
||||
|
||||
setResponse(response);
|
||||
};
|
||||
|
||||
return { deleteTCensec };
|
||||
};
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
import { useResponse } from "@/app/_response/ResponseContext";
|
||||
import { TPessoaIndexService } from "../../_services/t_pessoa/TPessoaIndexService";
|
||||
import { useState } from "react";
|
||||
import TPessoaInterface from "../../_interfaces/TPessoaInterface";
|
||||
import { TPessoaRepresentanteIndexData } from "../../_data/TPessoaRepresentante/TPessoaRepresentanteIndexData";
|
||||
|
||||
export const useTPessoaRepresentanteIndexHook = () => {
|
||||
const { setResponse } = useResponse();
|
||||
|
||||
const [tPessoaRepresentante, setTPessoaRepresentante] = useState<
|
||||
TPessoaInterface[]
|
||||
>([]);
|
||||
|
||||
const fetchTPessoaRepresentante = async () => {
|
||||
const response = await TPessoaRepresentanteIndexData();
|
||||
|
||||
setTPessoaRepresentante(response.data);
|
||||
|
||||
setResponse(response);
|
||||
};
|
||||
|
||||
return { tPessoaRepresentante, fetchTPessoaRepresentante };
|
||||
};
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
"use client";
|
||||
|
||||
import { useResponse } from "@/app/_response/ResponseContext";
|
||||
import { useState } from "react";
|
||||
import TPessoaInterface from "../../_interfaces/TPessoaInterface";
|
||||
import { TCensecSaveService } from "../../_services/t_censec/TCensecSaveService";
|
||||
|
||||
export const useTPessoaSaveHook = () => {
|
||||
const { setResponse } = useResponse();
|
||||
|
||||
const [tCensec, setTCensec] = useState<TPessoaInterface>();
|
||||
|
||||
// controla se o formulário está aberto ou fechado
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
|
||||
const saveTCensec = async (data: TPessoaInterface) => {
|
||||
const response = await TCensecSaveService(data);
|
||||
|
||||
// Armazena os dados da repsota
|
||||
setTCensec(response.data);
|
||||
|
||||
// Define os dados da respota(toast, modal, etc)
|
||||
setResponse(response);
|
||||
|
||||
// Fecha o formulário automaticamente após salvar
|
||||
setIsOpen(false);
|
||||
|
||||
// Retorna os valores de forma imediata
|
||||
return response.data;
|
||||
};
|
||||
|
||||
return { tCensec, saveTCensec };
|
||||
};
|
||||
|
|
@ -1,8 +1,6 @@
|
|||
export default interface GUfInterface {
|
||||
|
||||
g_uf_id?: number,
|
||||
sigla: string,
|
||||
nome: string,
|
||||
codigo_uf_ibge?: string,
|
||||
|
||||
g_uf_id?: number;
|
||||
sigla: string;
|
||||
nome: string;
|
||||
codigo_uf_ibge?: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,63 +1,63 @@
|
|||
export default interface TPessoaInterface {
|
||||
pessoa_id?: number;
|
||||
pessoa_tipo?: string;
|
||||
nome?: string;
|
||||
nacionalidade?: string;
|
||||
documento?: string;
|
||||
tb_documentotipo_id?: number;
|
||||
tb_profissao_id?: number;
|
||||
tb_estadocivil_id?: number;
|
||||
nome_pai?: string;
|
||||
nome_mae?: string;
|
||||
data_cadastro?: string; // ou Date
|
||||
naturalidade?: string;
|
||||
telefone?: string;
|
||||
endereco?: string;
|
||||
cidade?: string;
|
||||
uf?: string;
|
||||
data_nascimento?: string; // ou Date
|
||||
sexo?: string;
|
||||
tb_regimecomunhao_id?: number;
|
||||
pessoa_conjuge_id?: number;
|
||||
email?: string;
|
||||
documento_numero?: string;
|
||||
bairro?: string;
|
||||
cep?: string;
|
||||
documento_expedicao?: string; // ou Date
|
||||
documento_validade?: string; // ou Date
|
||||
observacao?: string;
|
||||
cpf_cnpj?: string;
|
||||
cpf_terceiro?: string;
|
||||
nome_fantasia?: string;
|
||||
texto?: string;
|
||||
ddd?: number;
|
||||
cert_casamento_numero?: string;
|
||||
cert_casamento_folha?: string;
|
||||
cert_casamento_livro?: string;
|
||||
cert_casamento_cartorio?: string;
|
||||
cert_casamento_data?: string; // ou Date
|
||||
cert_casamento_lei?: string;
|
||||
pessoa_conjuge_nome?: string;
|
||||
estrangeiro_nat?: string;
|
||||
estrangeiro_nat_tb_pais_id?: number;
|
||||
estrangeiro_res_tb_pais_id?: number;
|
||||
estrangeiro_res?: string;
|
||||
municipio_id?: number;
|
||||
documento_orgao?: string;
|
||||
documento_uf?: string;
|
||||
uf_residencia?: string;
|
||||
inscricao_municipal?: string;
|
||||
enviado_cnncnb?: boolean;
|
||||
data_auteracao?: string; // ou Date
|
||||
data_envioccn?: string; // ou Date
|
||||
ccnregistros_id?: number;
|
||||
observacao_envioccn?: string;
|
||||
observacao_envio_ccn?: string;
|
||||
deficiencias?: string;
|
||||
grau_instrucao?: string;
|
||||
cidade_nat_id?: number;
|
||||
tb_tipologradouro_id?: number;
|
||||
unidade?: string;
|
||||
numero_end?: string;
|
||||
foto?: string;
|
||||
}
|
||||
pessoa_id?: number;
|
||||
pessoa_tipo?: string;
|
||||
nome?: string;
|
||||
nacionalidade?: string;
|
||||
documento?: string;
|
||||
tb_documentotipo_id?: number;
|
||||
tb_profissao_id?: number;
|
||||
tb_estadocivil_id?: number;
|
||||
nome_pai?: string;
|
||||
nome_mae?: string;
|
||||
data_cadastro?: string; // ou Date
|
||||
naturalidade?: string;
|
||||
telefone?: string;
|
||||
endereco?: string;
|
||||
cidade?: string;
|
||||
uf?: string;
|
||||
data_nascimento?: string; // ou Date
|
||||
sexo?: string;
|
||||
tb_regimecomunhao_id?: number;
|
||||
pessoa_conjuge_id?: number;
|
||||
email?: string;
|
||||
documento_numero?: string;
|
||||
bairro?: string;
|
||||
cep?: string;
|
||||
documento_expedicao?: string; // ou Date
|
||||
documento_validade?: string; // ou Date
|
||||
observacao?: string;
|
||||
cpf_cnpj?: string;
|
||||
cpf_terceiro?: string;
|
||||
nome_fantasia?: string;
|
||||
texto?: string;
|
||||
ddd?: number;
|
||||
cert_casamento_numero?: string;
|
||||
cert_casamento_folha?: string;
|
||||
cert_casamento_livro?: string;
|
||||
cert_casamento_cartorio?: string;
|
||||
cert_casamento_data?: string; // ou Date
|
||||
cert_casamento_lei?: string;
|
||||
pessoa_conjuge_nome?: string;
|
||||
estrangeiro_nat?: string;
|
||||
estrangeiro_nat_tb_pais_id?: number;
|
||||
estrangeiro_res_tb_pais_id?: number;
|
||||
estrangeiro_res?: string;
|
||||
municipio_id?: number;
|
||||
documento_orgao?: string;
|
||||
documento_uf?: string;
|
||||
uf_residencia?: string;
|
||||
inscricao_municipal?: string;
|
||||
enviado_cnncnb?: boolean;
|
||||
data_auteracao?: string; // ou Date
|
||||
data_envioccn?: string; // ou Date
|
||||
ccnregistros_id?: number;
|
||||
observacao_envioccn?: string;
|
||||
observacao_envio_ccn?: string;
|
||||
deficiencias?: string;
|
||||
grau_instrucao?: string;
|
||||
cidade_nat_id?: number;
|
||||
tb_tipologradouro_id?: number;
|
||||
unidade?: string;
|
||||
numero_end?: string;
|
||||
foto?: string;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,63 @@
|
|||
export default interface TPessoaJuridicaInterface {
|
||||
pessoa_id?: number;
|
||||
pessoa_tipo?: string;
|
||||
nome?: string;
|
||||
nacionalidade?: string;
|
||||
documento?: string;
|
||||
tb_documentotipo_id?: number;
|
||||
tb_profissao_id?: number;
|
||||
tb_estadocivil_id?: number;
|
||||
nome_pai?: string;
|
||||
nome_mae?: string;
|
||||
data_cadastro?: string; // ou Date
|
||||
naturalidade?: string;
|
||||
telefone?: string;
|
||||
endereco?: string;
|
||||
cidade?: string;
|
||||
uf?: string;
|
||||
data_nascimento?: string; // ou Date
|
||||
sexo?: string;
|
||||
tb_regimecomunhao_id?: number;
|
||||
pessoa_conjuge_id?: number;
|
||||
email?: string;
|
||||
documento_numero?: string;
|
||||
bairro?: string;
|
||||
cep?: string;
|
||||
documento_expedicao?: string; // ou Date
|
||||
documento_validade?: string; // ou Date
|
||||
observacao?: string;
|
||||
cpf_cnpj?: string;
|
||||
cpf_terceiro?: string;
|
||||
nome_fantasia?: string;
|
||||
texto?: string;
|
||||
ddd?: number;
|
||||
cert_casamento_numero?: string;
|
||||
cert_casamento_folha?: string;
|
||||
cert_casamento_livro?: string;
|
||||
cert_casamento_cartorio?: string;
|
||||
cert_casamento_data?: string; // ou Date
|
||||
cert_casamento_lei?: string;
|
||||
pessoa_conjuge_nome?: string;
|
||||
estrangeiro_nat?: string;
|
||||
estrangeiro_nat_tb_pais_id?: number;
|
||||
estrangeiro_res_tb_pais_id?: number;
|
||||
estrangeiro_res?: string;
|
||||
municipio_id?: number;
|
||||
documento_orgao?: string;
|
||||
documento_uf?: string;
|
||||
uf_residencia?: string;
|
||||
inscricao_municipal?: string;
|
||||
enviado_cnncnb?: boolean;
|
||||
data_auteracao?: string; // ou Date
|
||||
data_envioccn?: string; // ou Date
|
||||
ccnregistros_id?: number;
|
||||
observacao_envioccn?: string;
|
||||
observacao_envio_ccn?: string;
|
||||
deficiencias?: string;
|
||||
grau_instrucao?: string;
|
||||
cidade_nat_id?: number;
|
||||
tb_tipologradouro_id?: number;
|
||||
unidade?: string;
|
||||
numero_end?: string;
|
||||
foto?: string;
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
export default interface TPessoaJuridicaInterface {
|
||||
pessoa_representante_id: number;
|
||||
nome: string;
|
||||
tipo: string;
|
||||
}
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
import z from "zod";
|
||||
|
||||
export const GCidadeSchema = z.object({
|
||||
|
||||
cidade_id: z.number().optional(),
|
||||
uf: z.string().min(1, "A UF é obrigatória").max(2, "A UF deve ter no máximo 2 caracteres"),
|
||||
cidade_nome: z.string().min(1, "O nome da cidade é obrigatório"),
|
||||
codigo_ibge: z.string().optional(),
|
||||
codigo_gyn: z.string().optional()
|
||||
})
|
||||
cidade_id: z.number().optional(),
|
||||
uf: z
|
||||
.string()
|
||||
.min(1, "A UF é obrigatória")
|
||||
.max(2, "A UF deve ter no máximo 2 caracteres"),
|
||||
cidade_nome: z.string().min(1, "O nome da cidade é obrigatório"),
|
||||
codigo_ibge: z.string().optional(),
|
||||
codigo_gyn: z.string().optional(),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
import z from "zod";
|
||||
|
||||
export const TPessoaJuridicaSchema = z.object({
|
||||
pessoa_id: z.number().optional(),
|
||||
pessoa_tipo: z.string().optional(),
|
||||
nome: z
|
||||
.string()
|
||||
.min(1, "O campo deve ser preenchido")
|
||||
.max(120, "O nome excede 120 caracteres"),
|
||||
nacionalidade: z.string().optional(),
|
||||
documento: z.string().optional(),
|
||||
tb_documentotipo_id: z.number().optional(),
|
||||
tb_profissao_id: z.number().optional(),
|
||||
tb_estadocivil_id: z.number().optional(),
|
||||
nome_pai: z.string().optional(),
|
||||
nome_mae: z.string().optional(),
|
||||
data_cadastro: z.string().optional(), // ou z.string().datetime()
|
||||
naturalidade: z.string().optional(),
|
||||
telefone: z.string().optional(),
|
||||
endereco: z.string().optional(),
|
||||
cidade: z.string().optional(),
|
||||
uf: z.string().optional(),
|
||||
data_nascimento: z.string().optional(), // ou z.string().datetime()
|
||||
sexo: z.string().optional(),
|
||||
tb_regimecomunhao_id: z.number().optional(),
|
||||
pessoa_conjuge_id: z.number().optional(),
|
||||
email: z.string().email().optional(),
|
||||
documento_numero: z.string().optional(),
|
||||
bairro: z.string().optional(),
|
||||
cep: z.string().optional(),
|
||||
documento_expedicao: z.string().optional(), // ou z.string().datetime()
|
||||
documento_validade: z.string().optional(), // ou z.string().datetime()
|
||||
observacao: z.string().optional(),
|
||||
cpf_cnpj: z.string().optional(),
|
||||
cpf_terceiro: z.string().optional(),
|
||||
nome_fantasia: z.string().optional(),
|
||||
texto: z.string().optional(),
|
||||
ddd: z.number().optional(),
|
||||
cert_casamento_numero: z.string().optional(),
|
||||
cert_casamento_folha: z.string().optional(),
|
||||
cert_casamento_livro: z.string().optional(),
|
||||
cert_casamento_cartorio: z.string().optional(),
|
||||
cert_casamento_data: z.string().optional(), // ou z.string().datetime()
|
||||
cert_casamento_lei: z.string().optional(),
|
||||
pessoa_conjuge_nome: z.string().optional(),
|
||||
estrangeiro_nat: z.string().optional(),
|
||||
estrangeiro_nat_tb_pais_id: z.number().optional(),
|
||||
estrangeiro_res_tb_pais_id: z.number().optional(),
|
||||
estrangeiro_res: z.string().optional(),
|
||||
municipio_id: z.number().optional(),
|
||||
documento_orgao: z.string().optional(),
|
||||
documento_uf: z.string().optional(),
|
||||
uf_residencia: z.string().optional(),
|
||||
inscricao_municipal: z.string().optional(),
|
||||
enviado_cnncnb: z.boolean().optional(),
|
||||
data_auteracao: z.string().optional(), // ou z.string().datetime()
|
||||
data_envioccn: z.string().optional(), // ou z.string().datetime()
|
||||
ccnregistros_id: z.number().optional(),
|
||||
observacao_envioccn: z.string().optional(),
|
||||
observacao_envio_ccn: z.string().optional(),
|
||||
deficiencias: z.string().optional(),
|
||||
grau_instrucao: z.string().optional(),
|
||||
cidade_nat_id: z.number().optional(),
|
||||
tb_tipologradouro_id: z.number().optional(),
|
||||
unidade: z.string().optional(),
|
||||
numero_end: z.string().optional(),
|
||||
});
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
import z, { number } from "zod";
|
||||
|
||||
export const TPessoaRepresentante = z.object({
|
||||
pessoa_representante_id: z.number().optional,
|
||||
nome: z.string(),
|
||||
tipo: z.string(),
|
||||
});
|
||||
|
|
@ -1,64 +1,67 @@
|
|||
import z from "zod";
|
||||
|
||||
export const TPessoaSchema = z.object({
|
||||
pessoa_id: z.number().optional(),
|
||||
pessoa_tipo: z.string().optional(),
|
||||
nome: z.string().min(1, "O campo deve ser preenchido").max(120, "O nome excede 120 caracteres"),
|
||||
nacionalidade: z.string().optional(),
|
||||
documento: z.string().optional(),
|
||||
tb_documentotipo_id: z.number().optional(),
|
||||
tb_profissao_id: z.number().optional(),
|
||||
tb_estadocivil_id: z.number().optional(),
|
||||
nome_pai: z.string().optional(),
|
||||
nome_mae: z.string().optional(),
|
||||
data_cadastro: z.string().optional(), // ou z.string().datetime()
|
||||
naturalidade: z.string().optional(),
|
||||
telefone: z.string().optional(),
|
||||
endereco: z.string().optional(),
|
||||
cidade: z.string().optional(),
|
||||
uf: z.string().optional(),
|
||||
data_nascimento: z.string().optional(), // ou z.string().datetime()
|
||||
sexo: z.string().optional(),
|
||||
tb_regimecomunhao_id: z.number().optional(),
|
||||
pessoa_conjuge_id: z.number().optional(),
|
||||
email: z.string().email().optional(),
|
||||
documento_numero: z.string().optional(),
|
||||
bairro: z.string().optional(),
|
||||
cep: z.string().optional(),
|
||||
documento_expedicao: z.string().optional(), // ou z.string().datetime()
|
||||
documento_validade: z.string().optional(), // ou z.string().datetime()
|
||||
observacao: z.string().optional(),
|
||||
cpf_cnpj: z.string().optional(),
|
||||
cpf_terceiro: z.string().optional(),
|
||||
nome_fantasia: z.string().optional(),
|
||||
texto: z.string().optional(),
|
||||
ddd: z.number().optional(),
|
||||
cert_casamento_numero: z.string().optional(),
|
||||
cert_casamento_folha: z.string().optional(),
|
||||
cert_casamento_livro: z.string().optional(),
|
||||
cert_casamento_cartorio: z.string().optional(),
|
||||
cert_casamento_data: z.string().optional(), // ou z.string().datetime()
|
||||
cert_casamento_lei: z.string().optional(),
|
||||
pessoa_conjuge_nome: z.string().optional(),
|
||||
estrangeiro_nat: z.string().optional(),
|
||||
estrangeiro_nat_tb_pais_id: z.number().optional(),
|
||||
estrangeiro_res_tb_pais_id: z.number().optional(),
|
||||
estrangeiro_res: z.string().optional(),
|
||||
municipio_id: z.number().optional(),
|
||||
documento_orgao: z.string().optional(),
|
||||
documento_uf: z.string().optional(),
|
||||
uf_residencia: z.string().optional(),
|
||||
inscricao_municipal: z.string().optional(),
|
||||
enviado_cnncnb: z.boolean().optional(),
|
||||
data_auteracao: z.string().optional(), // ou z.string().datetime()
|
||||
data_envioccn: z.string().optional(), // ou z.string().datetime()
|
||||
ccnregistros_id: z.number().optional(),
|
||||
observacao_envioccn: z.string().optional(),
|
||||
observacao_envio_ccn: z.string().optional(),
|
||||
deficiencias: z.string().optional(),
|
||||
grau_instrucao: z.string().optional(),
|
||||
cidade_nat_id: z.number().optional(),
|
||||
tb_tipologradouro_id: z.number().optional(),
|
||||
unidade: z.string().optional(),
|
||||
numero_end: z.string().optional(),
|
||||
pessoa_id: z.number().optional(),
|
||||
pessoa_tipo: z.string().optional(),
|
||||
nome: z
|
||||
.string()
|
||||
.min(1, "O campo deve ser preenchido")
|
||||
.max(120, "O nome excede 120 caracteres"),
|
||||
nacionalidade: z.string().optional(),
|
||||
documento: z.string().optional(),
|
||||
tb_documentotipo_id: z.number().optional(),
|
||||
tb_profissao_id: z.number().optional(),
|
||||
tb_estadocivil_id: z.number().optional(),
|
||||
nome_pai: z.string().optional(),
|
||||
nome_mae: z.string().optional(),
|
||||
data_cadastro: z.string().optional(), // ou z.string().datetime()
|
||||
naturalidade: z.string().optional(),
|
||||
telefone: z.string().optional(),
|
||||
endereco: z.string().optional(),
|
||||
cidade: z.string().optional(),
|
||||
uf: z.string().optional(),
|
||||
data_nascimento: z.string().optional(), // ou z.string().datetime()
|
||||
sexo: z.string().optional(),
|
||||
tb_regimecomunhao_id: z.number().optional(),
|
||||
pessoa_conjuge_id: z.number().optional(),
|
||||
email: z.string().email().optional(),
|
||||
documento_numero: z.string().optional(),
|
||||
bairro: z.string().optional(),
|
||||
cep: z.string().optional(),
|
||||
documento_expedicao: z.string().optional(), // ou z.string().datetime()
|
||||
documento_validade: z.string().optional(), // ou z.string().datetime()
|
||||
observacao: z.string().optional(),
|
||||
cpf_cnpj: z.string().optional(),
|
||||
cpf_terceiro: z.string().optional(),
|
||||
nome_fantasia: z.string().optional(),
|
||||
texto: z.string().optional(),
|
||||
ddd: z.number().optional(),
|
||||
cert_casamento_numero: z.string().optional(),
|
||||
cert_casamento_folha: z.string().optional(),
|
||||
cert_casamento_livro: z.string().optional(),
|
||||
cert_casamento_cartorio: z.string().optional(),
|
||||
cert_casamento_data: z.string().optional(), // ou z.string().datetime()
|
||||
cert_casamento_lei: z.string().optional(),
|
||||
pessoa_conjuge_nome: z.string().optional(),
|
||||
estrangeiro_nat: z.string().optional(),
|
||||
estrangeiro_nat_tb_pais_id: z.number().optional(),
|
||||
estrangeiro_res_tb_pais_id: z.number().optional(),
|
||||
estrangeiro_res: z.string().optional(),
|
||||
municipio_id: z.number().optional(),
|
||||
documento_orgao: z.string().optional(),
|
||||
documento_uf: z.string().optional(),
|
||||
uf_residencia: z.string().optional(),
|
||||
inscricao_municipal: z.string().optional(),
|
||||
enviado_cnncnb: z.boolean().optional(),
|
||||
data_auteracao: z.string().optional(), // ou z.string().datetime()
|
||||
data_envioccn: z.string().optional(), // ou z.string().datetime()
|
||||
ccnregistros_id: z.number().optional(),
|
||||
observacao_envioccn: z.string().optional(),
|
||||
observacao_envio_ccn: z.string().optional(),
|
||||
deficiencias: z.string().optional(),
|
||||
grau_instrucao: z.string().optional(),
|
||||
cidade_nat_id: z.number().optional(),
|
||||
tb_tipologradouro_id: z.number().optional(),
|
||||
unidade: z.string().optional(),
|
||||
numero_end: z.string().optional(),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,18 +1,16 @@
|
|||
// Função que envolve qualquer ação assíncrona para capturar e tratar erros do cliente
|
||||
import { withClientErrorHandler } from "@/actions/withClientErrorHandler/withClientErrorHandler";
|
||||
import { withClientErrorHandler } from "@/actions/withClientErrorHandler/withClientErrorHandler";
|
||||
|
||||
// Função que salva os dados da cidade via API (ou mock)
|
||||
import { GCidadeSaveData } from "../../_data/GCidade/GCidadeSaveData";
|
||||
import { GCidadeSaveData } from "../../_data/GCidade/GCidadeSaveData";
|
||||
|
||||
// Interface tipada da cidade
|
||||
import GCidadeInterface from "../../_interfaces/GCidadeInterface";
|
||||
|
||||
import GCidadeInterface from "../../_interfaces/GCidadeInterface";
|
||||
|
||||
// Função assíncrona que executa o salvamento de uma cidade
|
||||
async function executeGCidadeSaveService(data: GCidadeInterface) {
|
||||
|
||||
// Chama a função que salva os dados da cidade
|
||||
const response = await GCidadeSaveData(data);
|
||||
// Chama a função que salva os dados da cidade
|
||||
const response = await GCidadeSaveData(data);
|
||||
|
||||
// Retorna a resposta do salvamento
|
||||
return response;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import GUfIndexData from "../../_data/GUf/GUfIndexData";
|
||||
|
||||
export default async function GUfIndexService() {
|
||||
const response = await GUfIndexData();
|
||||
|
||||
const response = await GUfIndexData();
|
||||
|
||||
return response;
|
||||
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,11 +2,11 @@ import { withClientErrorHandler } from "@/actions/withClientErrorHandler/withCli
|
|||
import { TPessoaIndexData } from "../../_data/TPessoa/TPessoaIndexData";
|
||||
|
||||
async function executeTPessoaIndexService() {
|
||||
const response = TPessoaIndexData();
|
||||
|
||||
const response = TPessoaIndexData();
|
||||
|
||||
return response;
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
export const TPessoaIndexService = withClientErrorHandler(executeTPessoaIndexService);
|
||||
export const TPessoaIndexService = withClientErrorHandler(
|
||||
executeTPessoaIndexService,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
import { withClientErrorHandler } from "@/actions/withClientErrorHandler/withClientErrorHandler";
|
||||
import { TPessoaJuridicaIndexData } from "../../../_data/TPessoa/juridica/TPessoaJuridicaIndexData";
|
||||
|
||||
async function executeTPessoaJuridicaIndexService() {
|
||||
const response = TPessoaJuridicaIndexData();
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
export const TPessoaJuridicaIndexService = withClientErrorHandler(
|
||||
executeTPessoaJuridicaIndexService,
|
||||
);
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import { withClientErrorHandler } from "@/actions/withClientErrorHandler/withClientErrorHandler";
|
||||
import { TPessoaRepresentanteIndexData } from "../../_data/TPessoaRepresentante/TPessoaRepresentanteIndexData";
|
||||
|
||||
async function executeTPessoaRepresentanteIndexService() {
|
||||
const response = TPessoaRepresentanteIndexData();
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
export const TPessoaRepresentanteIndexService = withClientErrorHandler(
|
||||
executeTPessoaRepresentanteIndexService,
|
||||
);
|
||||
|
|
@ -2,178 +2,199 @@
|
|||
|
||||
import React from "react";
|
||||
import {
|
||||
ColumnDef,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
SortingState,
|
||||
ColumnFiltersState,
|
||||
VisibilityState,
|
||||
RowSelectionState,
|
||||
ColumnDef,
|
||||
flexRender,
|
||||
getCoreRowModel,
|
||||
getFilteredRowModel,
|
||||
getPaginationRowModel,
|
||||
getSortedRowModel,
|
||||
useReactTable,
|
||||
SortingState,
|
||||
ColumnFiltersState,
|
||||
VisibilityState,
|
||||
RowSelectionState,
|
||||
} from "@tanstack/react-table";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenu,
|
||||
DropdownMenuCheckboxItem,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuTrigger,
|
||||
} from "@/components/ui/dropdown-menu";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
import { ChevronLeftIcon, ChevronRightIcon, EyeIcon } from "lucide-react";
|
||||
|
||||
// Tipagem genérica
|
||||
export interface DataTableProps<TData> {
|
||||
data: TData[];
|
||||
columns: ColumnDef<TData, any>[];
|
||||
filterColumn?: string; // Define qual coluna será usada para filtro
|
||||
filterPlaceholder?: string;
|
||||
onEdit?: (item: TData) => void;
|
||||
onDelete?: (item: TData) => void;
|
||||
data: TData[];
|
||||
columns: ColumnDef<TData, any>[];
|
||||
filterColumn?: string; // Define qual coluna será usada para filtro
|
||||
filterPlaceholder?: string;
|
||||
onEdit?: (item: TData) => void;
|
||||
onDelete?: (item: TData) => void;
|
||||
}
|
||||
|
||||
export function DataTable<TData>({
|
||||
data,
|
||||
columns,
|
||||
filterColumn,
|
||||
filterPlaceholder = "Buscar...",
|
||||
onEdit,
|
||||
onDelete,
|
||||
}: DataTableProps<TData>) {
|
||||
// Estados internos da tabela
|
||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>(
|
||||
[],
|
||||
);
|
||||
const [columnVisibility, setColumnVisibility] =
|
||||
React.useState<VisibilityState>({});
|
||||
const [rowSelection, setRowSelection] = React.useState<RowSelectionState>({});
|
||||
|
||||
// Configuração da tabela
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
filterColumn,
|
||||
filterPlaceholder = "Buscar...",
|
||||
onEdit,
|
||||
onDelete,
|
||||
}: DataTableProps<TData>) {
|
||||
// Estados internos da tabela
|
||||
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||
const [columnFilters, setColumnFilters] = React.useState<ColumnFiltersState>([]);
|
||||
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>({});
|
||||
const [rowSelection, setRowSelection] = React.useState<RowSelectionState>({});
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
columnVisibility,
|
||||
rowSelection,
|
||||
},
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
});
|
||||
|
||||
// Configuração da tabela
|
||||
const table = useReactTable({
|
||||
data,
|
||||
columns,
|
||||
state: {
|
||||
sorting,
|
||||
columnFilters,
|
||||
columnVisibility,
|
||||
rowSelection,
|
||||
},
|
||||
onSortingChange: setSorting,
|
||||
onColumnFiltersChange: setColumnFilters,
|
||||
onColumnVisibilityChange: setColumnVisibility,
|
||||
onRowSelectionChange: setRowSelection,
|
||||
getCoreRowModel: getCoreRowModel(),
|
||||
getSortedRowModel: getSortedRowModel(),
|
||||
getFilteredRowModel: getFilteredRowModel(),
|
||||
getPaginationRowModel: getPaginationRowModel(),
|
||||
});
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* Filtros e colunas */}
|
||||
<div className="flex items-center gap-2">
|
||||
{filterColumn && (
|
||||
<Input
|
||||
placeholder={filterPlaceholder}
|
||||
value={
|
||||
(table
|
||||
.getColumn(filterColumn as string)
|
||||
?.getFilterValue() as string) ?? ""
|
||||
}
|
||||
onChange={(e) =>
|
||||
table
|
||||
.getColumn(filterColumn as string)
|
||||
?.setFilterValue(e.target.value)
|
||||
}
|
||||
className="w-full"
|
||||
/>
|
||||
)}
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" className="ml-auto cursor-pointer">
|
||||
<EyeIcon /> Colunas visíveis
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
{table
|
||||
.getAllColumns()
|
||||
.filter((col) => col.getCanHide())
|
||||
.map((col) => (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={col.id}
|
||||
checked={col.getIsVisible()}
|
||||
onCheckedChange={(v) => col.toggleVisibility(!!v)}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
{col.id}
|
||||
</DropdownMenuCheckboxItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
|
||||
{/* Filtros e colunas */}
|
||||
<div className="flex items-center gap-2">
|
||||
{filterColumn && (
|
||||
<Input
|
||||
placeholder={filterPlaceholder}
|
||||
value={(table.getColumn(filterColumn as string)?.getFilterValue() as string) ?? ""}
|
||||
onChange={(e) => table.getColumn(filterColumn as string)?.setFilterValue(e.target.value)}
|
||||
className="w-full"
|
||||
/>
|
||||
)}
|
||||
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" className="ml-auto cursor-pointer">
|
||||
<EyeIcon /> Colunas visíveis
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end">
|
||||
{table
|
||||
.getAllColumns()
|
||||
.filter((col) => col.getCanHide())
|
||||
.map((col) => (
|
||||
<DropdownMenuCheckboxItem
|
||||
key={col.id}
|
||||
checked={col.getIsVisible()}
|
||||
onCheckedChange={(v) => col.toggleVisibility(!!v)}
|
||||
className="cursor-pointer"
|
||||
>
|
||||
{col.id}
|
||||
</DropdownMenuCheckboxItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
|
||||
{/* Tabela */}
|
||||
<div className="overflow-hidden rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())}
|
||||
</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow key={row.id} className="cursor-pointer">
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell colSpan={columns.length} className="h-24 text-center">
|
||||
Nenhum resultado encontrado.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
{/* Tabela */}
|
||||
<div className="overflow-hidden rounded-md border">
|
||||
<Table>
|
||||
<TableHeader>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id}>
|
||||
{headerGroup.headers.map((header) => (
|
||||
<TableHead key={header.id}>
|
||||
{header.isPlaceholder
|
||||
? null
|
||||
: flexRender(
|
||||
header.column.columnDef.header,
|
||||
header.getContext(),
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</TableHead>
|
||||
))}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{table.getRowModel().rows.length ? (
|
||||
table.getRowModel().rows.map((row) => (
|
||||
<TableRow key={row.id} className="cursor-pointer">
|
||||
{row.getVisibleCells().map((cell) => (
|
||||
<TableCell key={cell.id}>
|
||||
{flexRender(
|
||||
cell.column.columnDef.cell,
|
||||
cell.getContext(),
|
||||
)}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
))
|
||||
) : (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={columns.length}
|
||||
className="h-24 text-center"
|
||||
>
|
||||
Nenhum resultado encontrado.
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
|
||||
{/* Paginação */}
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
<Button
|
||||
className="cursor-pointer"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<ChevronLeftIcon />
|
||||
Anterior
|
||||
</Button>
|
||||
<Button
|
||||
className="cursor-pointer"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
Próxima
|
||||
<ChevronRightIcon />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
{/* Paginação */}
|
||||
<div className="flex items-center justify-end gap-2">
|
||||
<Button
|
||||
className="cursor-pointer"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.previousPage()}
|
||||
disabled={!table.getCanPreviousPage()}
|
||||
>
|
||||
<ChevronLeftIcon />
|
||||
Anterior
|
||||
</Button>
|
||||
<Button
|
||||
className="cursor-pointer"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={() => table.nextPage()}
|
||||
disabled={!table.getCanNextPage()}
|
||||
>
|
||||
Próxima
|
||||
<ChevronRightIcon />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ export const ResponseProvider: React.FC<{ children: ReactNode }> = ({
|
|||
};
|
||||
|
||||
export const useResponse = () => {
|
||||
|
||||
const context = useContext(ResponseContext);
|
||||
if (!context)
|
||||
throw new Error("useResponse must be used within ResponseProvider");
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { toast } from "sonner";
|
|||
export default function Response() {
|
||||
const { response, clearResponse } = useResponse();
|
||||
|
||||
console.log(response)
|
||||
console.log(response);
|
||||
|
||||
useEffect(() => {
|
||||
switch (Number(response?.status)) {
|
||||
|
|
|
|||
|
|
@ -148,6 +148,10 @@ const data = {
|
|||
title: "Pessoas/Físicas",
|
||||
url: "/cadastros/pessoa/fisica",
|
||||
},
|
||||
{
|
||||
title: "Pessoas/Jurídica",
|
||||
url: "/cadastros/pessoa/juridica",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
"use client"
|
||||
"use client";
|
||||
|
||||
import * as React from "react"
|
||||
import { Command as CommandPrimitive } from "cmdk"
|
||||
import { SearchIcon } from "lucide-react"
|
||||
import * as React from "react";
|
||||
import { Command as CommandPrimitive } from "cmdk";
|
||||
import { SearchIcon } from "lucide-react";
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog"
|
||||
} from "@/components/ui/dialog";
|
||||
|
||||
function Command({
|
||||
className,
|
||||
|
|
@ -22,11 +22,11 @@ function Command({
|
|||
data-slot="command"
|
||||
className={cn(
|
||||
"bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md",
|
||||
className
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function CommandDialog({
|
||||
|
|
@ -37,10 +37,10 @@ function CommandDialog({
|
|||
showCloseButton = true,
|
||||
...props
|
||||
}: React.ComponentProps<typeof Dialog> & {
|
||||
title?: string
|
||||
description?: string
|
||||
className?: string
|
||||
showCloseButton?: boolean
|
||||
title?: string;
|
||||
description?: string;
|
||||
className?: string;
|
||||
showCloseButton?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<Dialog {...props}>
|
||||
|
|
@ -57,7 +57,7 @@ function CommandDialog({
|
|||
</Command>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function CommandInput({
|
||||
|
|
@ -74,12 +74,12 @@ function CommandInput({
|
|||
data-slot="command-input"
|
||||
className={cn(
|
||||
"placeholder:text-muted-foreground flex h-10 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function CommandList({
|
||||
|
|
@ -91,11 +91,11 @@ function CommandList({
|
|||
data-slot="command-list"
|
||||
className={cn(
|
||||
"max-h-[300px] scroll-py-1 overflow-x-hidden overflow-y-auto",
|
||||
className
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function CommandEmpty({
|
||||
|
|
@ -107,7 +107,7 @@ function CommandEmpty({
|
|||
className="py-6 text-center text-sm"
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function CommandGroup({
|
||||
|
|
@ -119,11 +119,11 @@ function CommandGroup({
|
|||
data-slot="command-group"
|
||||
className={cn(
|
||||
"text-foreground [&_[cmdk-group-heading]]:text-muted-foreground overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium",
|
||||
className
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function CommandSeparator({
|
||||
|
|
@ -136,7 +136,7 @@ function CommandSeparator({
|
|||
className={cn("bg-border -mx-1 h-px", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function CommandItem({
|
||||
|
|
@ -148,11 +148,11 @@ function CommandItem({
|
|||
data-slot="command-item"
|
||||
className={cn(
|
||||
"data-[selected=true]:bg-accent data-[selected=true]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function CommandShortcut({
|
||||
|
|
@ -164,11 +164,11 @@ function CommandShortcut({
|
|||
data-slot="command-shortcut"
|
||||
className={cn(
|
||||
"text-muted-foreground ml-auto text-xs tracking-widest",
|
||||
className
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export {
|
||||
|
|
@ -181,4 +181,4 @@ export {
|
|||
CommandItem,
|
||||
CommandShortcut,
|
||||
CommandSeparator,
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
"use client"
|
||||
"use client";
|
||||
|
||||
import * as React from "react"
|
||||
import * as TabsPrimitive from "@radix-ui/react-tabs"
|
||||
import * as React from "react";
|
||||
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
function Tabs({
|
||||
className,
|
||||
|
|
@ -15,7 +15,7 @@ function Tabs({
|
|||
className={cn("flex flex-col gap-2", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function TabsList({
|
||||
|
|
@ -27,11 +27,11 @@ function TabsList({
|
|||
data-slot="tabs-list"
|
||||
className={cn(
|
||||
"bg-muted text-muted-foreground inline-flex h-9 w-fit items-center justify-center rounded-lg p-[3px]",
|
||||
className
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function TabsTrigger({
|
||||
|
|
@ -43,11 +43,11 @@ function TabsTrigger({
|
|||
data-slot="tabs-trigger"
|
||||
className={cn(
|
||||
"data-[state=active]:bg-background dark:data-[state=active]:text-foreground focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:outline-ring dark:data-[state=active]:border-input dark:data-[state=active]:bg-input/30 text-foreground dark:text-muted-foreground inline-flex h-[calc(100%-1px)] flex-1 items-center justify-center gap-1.5 rounded-md border border-transparent px-2 py-1 text-sm font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
||||
className
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function TabsContent({
|
||||
|
|
@ -60,7 +60,7 @@ function TabsContent({
|
|||
className={cn("flex-1 outline-none", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export { Tabs, TabsList, TabsTrigger, TabsContent }
|
||||
export { Tabs, TabsList, TabsTrigger, TabsContent };
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import * as React from "react"
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
||||
return (
|
||||
|
|
@ -8,11 +8,11 @@ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
|
|||
data-slot="textarea"
|
||||
className={cn(
|
||||
"border-input placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 flex field-sizing-content min-h-16 w-full rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 md:text-sm",
|
||||
className
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export { Textarea }
|
||||
export { Textarea };
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
export enum Sexo {
|
||||
M = 'Masculino',
|
||||
F = 'Feminino',
|
||||
}
|
||||
M = "Masculino",
|
||||
F = "Feminino",
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue