Add CEP autocompletion by ViaCEP/ECT
Signed-off-by: Rodrigo Martins <rodrigo.sousa.577@gmail.com>
This commit is contained in:
1 parent
5559eb98b8
commit
afcef9749e
1 file changed
+100
-8
@@ -175,6 +175,7 @@ export const ClienteModal: React.FC<ClienteModalProps> = ({
|
|||||||
initialTab = 0,
|
initialTab = 0,
|
||||||
}) => {
|
}) => {
|
||||||
const [activeTab, setActiveTab] = useState(initialTab);
|
const [activeTab, setActiveTab] = useState(initialTab);
|
||||||
|
const [isLoadingCep, setIsLoadingCep] = useState(false);
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
@@ -186,6 +187,51 @@ export const ClienteModal: React.FC<ClienteModalProps> = ({
|
|||||||
const { createCliente, updateCliente } = useClientes();
|
const { createCliente, updateCliente } = useClientes();
|
||||||
const formPopulated = useRef(false);
|
const formPopulated = useRef(false);
|
||||||
|
|
||||||
|
// Monitorar o valor do CEP para autopreenchimento
|
||||||
|
const cepValue = watch("cep");
|
||||||
|
|
||||||
|
// Função para buscar dados do CEP
|
||||||
|
const fetchAddressByCep = useCallback(async (cep: string) => {
|
||||||
|
if (!cep || cep.length !== 8) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
setIsLoadingCep(true);
|
||||||
|
const formattedCep = cep.replace(/\D/g, ""); // Remove caracteres não numéricos
|
||||||
|
const response = await axios.get(
|
||||||
|
`https://viacep.com.br/ws/${formattedCep}/json/`,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (response.data && !response.data.erro) {
|
||||||
|
// Preencher os campos do formulário com os dados retornados
|
||||||
|
setValue("endereco", response.data.logradouro);
|
||||||
|
setValue("bairro", response.data.bairro);
|
||||||
|
setValue("cidade", response.data.localidade);
|
||||||
|
setValue("estado", response.data.uf);
|
||||||
|
|
||||||
|
// Focar no campo número após o preenchimento automático
|
||||||
|
const numeroInput = document.querySelector(
|
||||||
|
'input[name="numero"]',
|
||||||
|
) as HTMLInputElement;
|
||||||
|
if (numeroInput) numeroInput.focus();
|
||||||
|
} else {
|
||||||
|
console.error("CEP não encontrado");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erro ao buscar CEP:", error);
|
||||||
|
} finally {
|
||||||
|
setIsLoadingCep(false);
|
||||||
|
}
|
||||||
|
}, [setValue]);
|
||||||
|
|
||||||
|
// Efeito para monitorar mudanças no CEP
|
||||||
|
useEffect(() => {
|
||||||
|
// Verificar se o CEP tem 8 dígitos (sem formatação)
|
||||||
|
const cleanCep = cepValue?.replace(/\D/g, "");
|
||||||
|
if (cleanCep && cleanCep.length === 8) {
|
||||||
|
fetchAddressByCep(cleanCep);
|
||||||
|
}
|
||||||
|
}, [cepValue, fetchAddressByCep]);
|
||||||
|
|
||||||
// Função para preencher o formulário com os dados do cliente
|
// Função para preencher o formulário com os dados do cliente
|
||||||
const populateForm = useCallback(
|
const populateForm = useCallback(
|
||||||
(clienteData: Cliente) => {
|
(clienteData: Cliente) => {
|
||||||
@@ -372,7 +418,10 @@ export const ClienteModal: React.FC<ClienteModalProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Formulário */}
|
{/* Formulário */}
|
||||||
<form onSubmit={handleSubmit(onSubmit)} className="p-6 bg-white dark:bg-gray-800">
|
<form
|
||||||
|
onSubmit={handleSubmit(onSubmit)}
|
||||||
|
className="p-6 bg-white dark:bg-gray-800"
|
||||||
|
>
|
||||||
<div className="space-y-8 mb-8">
|
<div className="space-y-8 mb-8">
|
||||||
{/* Conteúdo das abas */}
|
{/* Conteúdo das abas */}
|
||||||
{activeTab === 0 && (
|
{activeTab === 0 && (
|
||||||
@@ -459,13 +508,56 @@ export const ClienteModal: React.FC<ClienteModalProps> = ({
|
|||||||
|
|
||||||
{activeTab === 1 && (
|
{activeTab === 1 && (
|
||||||
<div className="grid grid-cols-2 gap-4">
|
<div className="grid grid-cols-2 gap-4">
|
||||||
<FormInput
|
<div className="relative">
|
||||||
label="CEP"
|
<FormInput
|
||||||
{...register("cep", {
|
label="CEP"
|
||||||
required: "CEP é obrigatório",
|
{...register("cep", {
|
||||||
})}
|
required: "CEP é obrigatório",
|
||||||
error={errors.cep?.message}
|
pattern: {
|
||||||
/>
|
value: /^[0-9]{5}-?[0-9]{3}$/,
|
||||||
|
message: "CEP inválido",
|
||||||
|
},
|
||||||
|
})}
|
||||||
|
error={errors.cep?.message}
|
||||||
|
placeholder="00000-000"
|
||||||
|
maxLength={9}
|
||||||
|
onChange={(e) => {
|
||||||
|
// Formatação automática do CEP (00000-000)
|
||||||
|
let value = e.target.value.replace(/\D/g, "");
|
||||||
|
if (value.length > 5) {
|
||||||
|
value =
|
||||||
|
value.substring(0, 5) +
|
||||||
|
"-" +
|
||||||
|
value.substring(5, 8);
|
||||||
|
}
|
||||||
|
e.target.value = value;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{isLoadingCep && (
|
||||||
|
<div className="absolute right-3 top-9">
|
||||||
|
<svg
|
||||||
|
className="animate-spin h-5 w-5 text-blue-500"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<circle
|
||||||
|
className="opacity-25"
|
||||||
|
cx="12"
|
||||||
|
cy="12"
|
||||||
|
r="10"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth="4"
|
||||||
|
></circle>
|
||||||
|
<path
|
||||||
|
className="opacity-75"
|
||||||
|
fill="currentColor"
|
||||||
|
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||||
|
></path>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
<FormInput
|
<FormInput
|
||||||
className="md:col-span-2"
|
className="md:col-span-2"
|
||||||
label="Endereço"
|
label="Endereço"
|
||||||
|
|||||||
Reference in new issue
Block a user